diff options
author | 2024-11-12 22:22:43 -0800 | |
---|---|---|
committer | 2024-11-12 22:22:43 -0800 | |
commit | 6dc15689f6bb4b04c102ac3107f31377ee91f05e (patch) | |
tree | 3ca9d372bc99ac1182fdb4bc895c556d0f108bd3 | |
parent | d4d02798539f29d00059d458fd01e5b2869acbb6 (diff) | |
parent | f151262626f1c08a104cc35d9864493ea8a72dec (diff) |
Merge 24Q4 (ab/12406339) into aosp-main-future
Bug: 370570306
Merged-In: I9be1254c3e2685b0aa950b314c581824f40ce26c
Change-Id: I35bc501a2b1d9eb100aaab25cd660cf2e0542f99
562 files changed, 20899 insertions, 15338 deletions
diff --git a/Android.bp b/Android.bp index 119c7ea5a4..13954ef181 100644 --- a/Android.bp +++ b/Android.bp @@ -38,7 +38,13 @@ license { cc_library_headers { name: "native_headers", + vendor_available: true, host_supported: true, + target: { + windows: { + enabled: true, + }, + }, export_include_dirs: [ "include/", ], diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg index 2ce3fb05cc..df1ef297bf 100644 --- a/PREUPLOAD.cfg +++ b/PREUPLOAD.cfg @@ -24,6 +24,7 @@ clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp libs/nativewindow/ libs/renderengine/ libs/ui/ + libs/vibrator/ libs/vr/ opengl/libs/ services/bufferhub/ diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index add9f9297b..5e83e3337f 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -1560,6 +1560,13 @@ static void DumpstateLimitedOnly() { CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10)); printf("========================================================\n"); + printf("== Networking Policy\n"); + printf("========================================================\n"); + + RunDumpsys("DUMPSYS NETWORK POLICY", {"netpolicy"}, CommandOptions::WithTimeout(90).Build(), + SEC_TO_MSEC(10)); + + printf("========================================================\n"); printf("== Dropbox crashes\n"); printf("========================================================\n"); diff --git a/cmds/evemu-record/main.rs b/cmds/evemu-record/main.rs index db3fd77520..e91e5daec4 100644 --- a/cmds/evemu-record/main.rs +++ b/cmds/evemu-record/main.rs @@ -50,8 +50,10 @@ enum TimestampBase { /// The first event received from the device. FirstEvent, - /// The time when the system booted. - Boot, + /// The Unix epoch (00:00:00 UTC on 1st January 1970), so that all timestamps are Unix + /// timestamps. This makes the events in the recording easier to match up with those from other + /// log sources. + Epoch, } fn get_choice(max: u32) -> u32 { @@ -188,7 +190,7 @@ fn print_events( // // [0]: https://gitlab.freedesktop.org/libevdev/evemu/-/commit/eba96a4d2be7260b5843e65c4b99c8b06a1f4c9d TimestampBase::FirstEvent => event.time - TimeVal::new(0, 1), - TimestampBase::Boot => TimeVal::new(0, 0), + TimestampBase::Epoch => TimeVal::new(0, 0), }; print_event(output, &event.offset_time_by(start_time))?; loop { diff --git a/cmds/flatland/GLHelper.cpp b/cmds/flatland/GLHelper.cpp index c163095c50..77e732805e 100644 --- a/cmds/flatland/GLHelper.cpp +++ b/cmds/flatland/GLHelper.cpp @@ -18,6 +18,7 @@ #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> +#include <com_android_graphics_libgui_flags.h> #include <gui/SurfaceComposerClient.h> #include <ui/DisplayMode.h> @@ -202,6 +203,14 @@ bool GLHelper::getShaderProgram(const char* name, GLuint* outPgm) { bool GLHelper::createNamedSurfaceTexture(GLuint name, uint32_t w, uint32_t h, sp<GLConsumer>* glConsumer, EGLSurface* surface) { +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) + sp<GLConsumer> glc = new GLConsumer(name, GL_TEXTURE_EXTERNAL_OES, false, true); + glc->setDefaultBufferSize(w, h); + glc->getSurface()->setMaxDequeuedBufferCount(2); + glc->setConsumerUsageBits(GRALLOC_USAGE_HW_COMPOSER); + + sp<ANativeWindow> anw = glc->getSurface(); +#else sp<IGraphicBufferProducer> producer; sp<IGraphicBufferConsumer> consumer; BufferQueue::createBufferQueue(&producer, &consumer); @@ -212,6 +221,7 @@ bool GLHelper::createNamedSurfaceTexture(GLuint name, uint32_t w, uint32_t h, glc->setConsumerUsageBits(GRALLOC_USAGE_HW_COMPOSER); sp<ANativeWindow> anw = new Surface(producer); +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) EGLSurface s = eglCreateWindowSurface(mDisplay, mConfig, anw.get(), nullptr); if (s == EGL_NO_SURFACE) { fprintf(stderr, "eglCreateWindowSurface error: %#x\n", eglGetError()); diff --git a/cmds/idlcli/Android.bp b/cmds/idlcli/Android.bp index c18d3f5021..50c2cd8be2 100644 --- a/cmds/idlcli/Android.bp +++ b/cmds/idlcli/Android.bp @@ -24,7 +24,7 @@ package { cc_defaults { name: "idlcli-defaults", shared_libs: [ - "android.hardware.vibrator-V2-ndk", + "android.hardware.vibrator-V3-ndk", "android.hardware.vibrator@1.0", "android.hardware.vibrator@1.1", "android.hardware.vibrator@1.2", diff --git a/cmds/idlcli/vibrator.h b/cmds/idlcli/vibrator.h index e100eacdd5..b9434950f6 100644 --- a/cmds/idlcli/vibrator.h +++ b/cmds/idlcli/vibrator.h @@ -49,7 +49,7 @@ inline ndk::ScopedAStatus NullptrStatus() { template <typename I> inline auto getService(std::string name) { const auto instance = std::string() + I::descriptor + "/" + name; - auto vibBinder = ndk::SpAIBinder(AServiceManager_getService(instance.c_str())); + auto vibBinder = ndk::SpAIBinder(AServiceManager_checkService(instance.c_str())); return I::fromBinder(vibBinder); } diff --git a/data/etc/input/motion_predictor_config.xml b/data/etc/input/motion_predictor_config.xml index c3f2fedc71..f593eda42d 100644 --- a/data/etc/input/motion_predictor_config.xml +++ b/data/etc/input/motion_predictor_config.xml @@ -35,7 +35,11 @@ The jerk thresholds are based on normalized dt = 1 calculations. --> - <low-jerk>1.0</low-jerk> - <high-jerk>1.1</high-jerk> + <low-jerk>1.5</low-jerk> + <high-jerk>2.0</high-jerk> + + <!-- The alpha in the first-order IIR filter for jerk smoothing. An alpha + of 1 results in no smoothing.--> + <jerk-alpha>0.25</jerk-alpha> </motion-predictor> diff --git a/include/android/performance_hint.h b/include/android/performance_hint.h index 20c5d949c1..3486e9b1d7 100644 --- a/include/android/performance_hint.h +++ b/include/android/performance_hint.h @@ -84,7 +84,6 @@ typedef struct AWorkDuration AWorkDuration; /** * An opaque type representing a handle to a performance hint manager. - * It must be released after use. * * To use:<ul> * <li>Obtain the performance hint manager instance by calling diff --git a/include/android/surface_control.h b/include/android/surface_control.h index 82caccaf68..bf9acb37da 100644 --- a/include/android/surface_control.h +++ b/include/android/surface_control.h @@ -145,6 +145,9 @@ typedef struct ASurfaceTransactionStats ASurfaceTransactionStats; * Buffers which are replaced or removed from the scene in the transaction invoking * this callback may be reused after this point. * + * Starting with API level 36, prefer using \a ASurfaceTransaction_OnBufferRelease to listen + * to when a buffer is ready to be reused. + * * \param context Optional context provided by the client that is passed into * the callback. * @@ -157,8 +160,7 @@ typedef struct ASurfaceTransactionStats ASurfaceTransactionStats; * Available since API level 29. */ typedef void (*ASurfaceTransaction_OnComplete)(void* _Null_unspecified context, - ASurfaceTransactionStats* _Nonnull stats) - __INTRODUCED_IN(29); + ASurfaceTransactionStats* _Nonnull stats); /** * The ASurfaceTransaction_OnCommit callback is invoked when transaction is applied and the updates @@ -186,8 +188,36 @@ typedef void (*ASurfaceTransaction_OnComplete)(void* _Null_unspecified context, * Available since API level 31. */ typedef void (*ASurfaceTransaction_OnCommit)(void* _Null_unspecified context, - ASurfaceTransactionStats* _Nonnull stats) - __INTRODUCED_IN(31); + ASurfaceTransactionStats* _Nonnull stats); + +/** + * The ASurfaceTransaction_OnBufferRelease callback is invoked when a buffer that was passed in + * ASurfaceTransaction_setBuffer is ready to be reused. + * + * This callback is guaranteed to be invoked if ASurfaceTransaction_setBuffer is called with a non + * null buffer. If the buffer in the transaction is replaced via another call to + * ASurfaceTransaction_setBuffer, the callback will be invoked immediately. Otherwise the callback + * will be invoked before the ASurfaceTransaction_OnComplete callback after the buffer was + * presented. + * + * If this callback is set, caller should not release the buffer using the + * ASurfaceTransaction_OnComplete. + * + * \param context Optional context provided by the client that is passed into the callback. + * + * \param release_fence_fd Returns the fence file descriptor used to signal the release of buffer + * associated with this callback. If this fence is valid (>=0), the buffer has not yet been released + * and the fence will signal when the buffer has been released. If the fence is -1 , the buffer is + * already released. The recipient of the callback takes ownership of the fence fd and is + * responsible for closing it. + * + * THREADING + * The callback can be invoked on any thread. + * + * Available since API level 36. + */ +typedef void (*ASurfaceTransaction_OnBufferRelease)(void* _Null_unspecified context, + int release_fence_fd); /** * Returns the timestamp of when the frame was latched by the framework. Once a frame is @@ -251,7 +281,7 @@ int64_t ASurfaceTransactionStats_getAcquireTime( /** * The returns the fence used to signal the release of the PREVIOUS buffer set on * this surface. If this fence is valid (>=0), the PREVIOUS buffer has not yet been released and the - * fence will signal when the PREVIOUS buffer has been released. If the fence is -1 , the PREVIOUS + * fence will signal when the PREVIOUS buffer has been released. If the fence is -1, the PREVIOUS * buffer is already released. The recipient of the callback takes ownership of the * previousReleaseFenceFd and is responsible for closing it. * @@ -353,6 +383,9 @@ void ASurfaceTransaction_setZOrder(ASurfaceTransaction* _Nonnull transaction, * Note that the buffer must be allocated with AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE * as the surface control might be composited using the GPU. * + * Starting with API level 36, prefer using \a ASurfaceTransaction_setBufferWithRelease to + * set a buffer and a callback which will be invoked when the buffer is ready to be reused. + * * Available since API level 29. */ void ASurfaceTransaction_setBuffer(ASurfaceTransaction* _Nonnull transaction, @@ -361,6 +394,29 @@ void ASurfaceTransaction_setBuffer(ASurfaceTransaction* _Nonnull transaction, __INTRODUCED_IN(29); /** + * Updates the AHardwareBuffer displayed for \a surface_control. If not -1, the + * acquire_fence_fd should be a file descriptor that is signaled when all pending work + * for the buffer is complete and the buffer can be safely read. + * + * The frameworks takes ownership of the \a acquire_fence_fd passed and is responsible + * for closing it. + * + * Note that the buffer must be allocated with AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE + * as the surface control might be composited using the GPU. + * + * When the buffer is ready to be reused, the ASurfaceTransaction_OnBufferRelease + * callback will be invoked. If the buffer is null, the callback will not be invoked. + * + * Available since API level 36. + */ +void ASurfaceTransaction_setBufferWithRelease(ASurfaceTransaction* _Nonnull transaction, + ASurfaceControl* _Nonnull surface_control, + AHardwareBuffer* _Nonnull buffer, + int acquire_fence_fd, void* _Null_unspecified context, + ASurfaceTransaction_OnBufferRelease _Nonnull func) + __INTRODUCED_IN(36); + +/** * Updates the color for \a surface_control. This will make the background color for the * ASurfaceControl visible in transparent regions of the surface. Colors \a r, \a g, * and \a b must be within the range that is valid for \a dataspace. \a dataspace and \a alpha diff --git a/include/audiomanager/IAudioManager.h b/include/audiomanager/IAudioManager.h index 769670ea99..0b7e16bc7d 100644 --- a/include/audiomanager/IAudioManager.h +++ b/include/audiomanager/IAudioManager.h @@ -27,7 +27,7 @@ namespace android { // ---------------------------------------------------------------------------- - +// TODO(b/309532236) replace this class with AIDL generated parcelable class IAudioManager : public IInterface { public: @@ -43,6 +43,7 @@ public: RELEASE_RECORDER = IBinder::FIRST_CALL_TRANSACTION + 6, PLAYER_SESSION_ID = IBinder::FIRST_CALL_TRANSACTION + 7, PORT_EVENT = IBinder::FIRST_CALL_TRANSACTION + 8, + PERMISSION_UPDATE_BARRIER = IBinder::FIRST_CALL_TRANSACTION + 9, }; DECLARE_META_INTERFACE(AudioManager) @@ -63,6 +64,7 @@ public: /*oneway*/ virtual status_t playerSessionId(audio_unique_id_t piid, audio_session_t sessionId) = 0; /*oneway*/ virtual status_t portEvent(audio_port_handle_t portId, player_state_t event, const std::unique_ptr<os::PersistableBundle>& extras) = 0; + virtual status_t permissionUpdateBarrier() = 0; }; // ---------------------------------------------------------------------------- diff --git a/include/input/Input.h b/include/input/Input.h index ec08cdd163..a8684bd19b 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -25,6 +25,7 @@ #include <android/input.h> #ifdef __linux__ #include <android/os/IInputConstants.h> +#include <android/os/MotionEventFlag.h> #endif #include <android/os/PointerIconType.h> #include <math.h> @@ -69,15 +70,17 @@ enum { * actual intent. */ AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED = - android::os::IInputConstants::MOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED, + static_cast<int32_t>(android::os::MotionEventFlag::WINDOW_IS_PARTIALLY_OBSCURED), + AMOTION_EVENT_FLAG_HOVER_EXIT_PENDING = - android::os::IInputConstants::MOTION_EVENT_FLAG_HOVER_EXIT_PENDING, + static_cast<int32_t>(android::os::MotionEventFlag::HOVER_EXIT_PENDING), + /** * This flag indicates that the event has been generated by a gesture generator. It * provides a hint to the GestureDetector to not apply any touch slop. */ AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE = - android::os::IInputConstants::MOTION_EVENT_FLAG_IS_GENERATED_GESTURE, + static_cast<int32_t>(android::os::MotionEventFlag::IS_GENERATED_GESTURE), /** * This flag indicates that the event will not cause a focus change if it is directed to an @@ -86,27 +89,27 @@ enum { * into focus. */ AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE = - android::os::IInputConstants::MOTION_EVENT_FLAG_NO_FOCUS_CHANGE, + static_cast<int32_t>(android::os::MotionEventFlag::NO_FOCUS_CHANGE), /** * This event was generated or modified by accessibility service. */ AMOTION_EVENT_FLAG_IS_ACCESSIBILITY_EVENT = - android::os::IInputConstants::INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT, + static_cast<int32_t>(android::os::MotionEventFlag::IS_ACCESSIBILITY_EVENT), AMOTION_EVENT_FLAG_TARGET_ACCESSIBILITY_FOCUS = - android::os::IInputConstants::MOTION_EVENT_FLAG_TARGET_ACCESSIBILITY_FOCUS, + static_cast<int32_t>(android::os::MotionEventFlag::TARGET_ACCESSIBILITY_FOCUS), /* Motion event is inconsistent with previously sent motion events. */ - AMOTION_EVENT_FLAG_TAINTED = android::os::IInputConstants::INPUT_EVENT_FLAG_TAINTED, + AMOTION_EVENT_FLAG_TAINTED = static_cast<int32_t>(android::os::MotionEventFlag::TAINTED), /** Private flag, not used in Java. */ AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION = - android::os::IInputConstants::MOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION, + static_cast<int32_t>(android::os::MotionEventFlag::PRIVATE_FLAG_SUPPORTS_ORIENTATION), /** Private flag, not used in Java. */ - AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION = android::os::IInputConstants:: - MOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION, + AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION = static_cast<int32_t>( + android::os::MotionEventFlag::PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION), /** Mask for all private flags that are not used in Java. */ AMOTION_EVENT_PRIVATE_FLAG_MASK = AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION | @@ -193,6 +196,13 @@ static constexpr size_t MAX_POINTERS = 16; #define MAX_POINTER_ID 31 /* + * Number of high resolution scroll units for one detent (scroll wheel click), as defined in + * evdev. This is relevant when an input device is emitting REL_WHEEL_HI_RES or REL_HWHEEL_HI_RES + * events. + */ +constexpr int32_t kEvdevHighResScrollUnitsPerDetent = 120; + +/* * Declare a concrete type for the NDK's input event forward declaration. */ struct AInputEvent { @@ -241,6 +251,8 @@ enum class InputEventType { TOUCH_MODE = AINPUT_EVENT_TYPE_TOUCH_MODE, ftl_first = KEY, ftl_last = TOUCH_MODE, + // Used by LatencyTracker fuzzer + kMaxValue = ftl_last }; std::string inputEventSourceToString(int32_t source); @@ -890,9 +902,7 @@ public: void splitFrom(const MotionEvent& other, std::bitset<MAX_POINTER_ID + 1> splitPointerIds, int32_t newEventId); - void addSample( - nsecs_t eventTime, - const PointerCoords* pointerCoords); + void addSample(nsecs_t eventTime, const PointerCoords* pointerCoords, int32_t eventId); void offsetLocation(float xOffset, float yOffset); diff --git a/include/input/InputConsumerNoResampling.h b/include/input/InputConsumerNoResampling.h index 9e48b0872d..c98b9cf8c1 100644 --- a/include/input/InputConsumerNoResampling.h +++ b/include/input/InputConsumerNoResampling.h @@ -16,15 +16,21 @@ #pragma once +#include <map> +#include <memory> +#include <optional> + +#include <input/Input.h> +#include <input/InputTransport.h> +#include <input/Resampler.h> #include <utils/Looper.h> -#include "InputTransport.h" namespace android { /** * An interface to receive batched input events. Even if you don't want batching, you still have to * use this interface, and some of the events will be batched if your implementation is slow to - * handle the incoming input. + * handle the incoming input. The events received by these callbacks are never null. */ class InputConsumerCallbacks { public: @@ -34,7 +40,7 @@ public: /** * When you receive this callback, you must (eventually) call "consumeBatchedInputEvents". * If you don't want batching, then call "consumeBatchedInputEvents" immediately with - * std::nullopt frameTime to receive the pending motion event(s). + * std::nullopt requestedFrameTime to receive the pending motion event(s). * @param pendingBatchSource the source of the pending batch. */ virtual void onBatchedInputEventPending(int32_t pendingBatchSource) = 0; @@ -47,13 +53,13 @@ public: /** * Consumes input events from an input channel. * - * This is a re-implementation of InputConsumer that does not have resampling at the current moment. - * A lot of the higher-level logic has been folded into this class, to make it easier to use. - * In the legacy class, InputConsumer, the consumption logic was partially handled in the jni layer, - * as well as various actions like adding the fd to the Choreographer. + * This is a re-implementation of InputConsumer. At the moment it only supports resampling for + * single pointer events. A lot of the higher-level logic has been folded into this class, to make + * it easier to use. In the legacy class, InputConsumer, the consumption logic was partially handled + * in the jni layer, as well as various actions like adding the fd to the Choreographer. * * TODO(b/297226446): use this instead of "InputConsumer": - * - Add resampling to this class + * - Add resampling for multiple pointer events. * - Allow various resampling strategies to be specified * - Delete the old "InputConsumer" and use this class instead, renaming it to "InputConsumer". * - Add tracing @@ -64,8 +70,18 @@ public: */ class InputConsumerNoResampling final { public: + /** + * @param callbacks are used to interact with InputConsumerNoResampling. They're called whenever + * the event is ready to consume. + * @param looper needs to be sp and not shared_ptr because it inherits from + * RefBase + * @param resampler the resampling strategy to use. If null, no resampling will be + * performed. + */ explicit InputConsumerNoResampling(const std::shared_ptr<InputChannel>& channel, - sp<Looper> looper, InputConsumerCallbacks& callbacks); + sp<Looper> looper, InputConsumerCallbacks& callbacks, + std::unique_ptr<Resampler> resampler); + ~InputConsumerNoResampling(); /** @@ -74,15 +90,17 @@ public: void finishInputEvent(uint32_t seq, bool handled); void reportTimeline(int32_t inputEventId, nsecs_t gpuCompletedTime, nsecs_t presentTime); /** - * If you want to consume all events immediately (disable batching), the you still must call - * this. For frameTime, use a std::nullopt. - * @param frameTime the time up to which consume the events. When there's double (or triple) - * buffering, you may want to not consume all events currently available, because you could be - * still working on an older frame, but there could already have been events that arrived that - * are more recent. + * If you want to consume all events immediately (disable batching), then you still must call + * this. For requestedFrameTime, use a std::nullopt. It is not guaranteed that the consumption + * will occur at requestedFrameTime. The resampling strategy may modify it. + * @param requestedFrameTime the time up to which consume the events. When there's double (or + * triple) buffering, you may want to not consume all events currently available, because you + * could be still working on an older frame, but there could already have been events that + * arrived that are more recent. * @return whether any events were actually consumed */ - bool consumeBatchedInputEvents(std::optional<nsecs_t> frameTime); + bool consumeBatchedInputEvents(std::optional<nsecs_t> requestedFrameTime); + /** * Returns true when there is *likely* a pending batch or a pending event in the channel. * @@ -99,6 +117,7 @@ private: std::shared_ptr<InputChannel> mChannel; sp<Looper> mLooper; InputConsumerCallbacks& mCallbacks; + std::unique_ptr<Resampler> mResampler; // Looper-related infrastructure /** @@ -177,11 +196,34 @@ private: /** * Batched InputMessages, per deviceId. * For each device, we are storing a queue of batched messages. These will all be collapsed into - * a single MotionEvent (up to a specific frameTime) when the consumer calls + * a single MotionEvent (up to a specific requestedFrameTime) when the consumer calls * `consumeBatchedInputEvents`. */ std::map<DeviceId, std::queue<InputMessage>> mBatches; /** + * Creates a MotionEvent by consuming samples from the provided queue. If one message has + * eventTime > adjustedFrameTime, all subsequent messages in the queue will be skipped. It is + * assumed that messages are queued in chronological order. In other words, only events that + * occurred prior to the adjustedFrameTime will be consumed. + * @param requestedFrameTime the time up to which to consume events. + * @param messages the queue of messages to consume from + */ + std::pair<std::unique_ptr<MotionEvent>, std::optional<uint32_t>> createBatchedMotionEvent( + const nsecs_t requestedFrameTime, std::queue<InputMessage>& messages); + + /** + * Consumes the batched input events, optionally allowing the caller to specify a device id + * and/or requestedFrameTime threshold. It is not guaranteed that consumption will occur at + * requestedFrameTime. + * @param deviceId The device id from which to consume events. If std::nullopt, consumes events + * from any device id. + * @param requestedFrameTime The time up to which consume the events. If std::nullopt, consumes + * input events with any timestamp. + * @return Whether or not any events were consumed. + */ + bool consumeBatchedInputEvents(std::optional<DeviceId> deviceId, + std::optional<nsecs_t> requestedFrameTime); + /** * A map from a single sequence number to several sequence numbers. This is needed because of * batching. When batching is enabled, a single MotionEvent will contain several samples. Each * sample came from an individual InputMessage of Type::Motion, and therefore will have to be diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h index 7d8c19e702..1a482396ee 100644 --- a/include/input/InputDevice.h +++ b/include/input/InputDevice.h @@ -389,6 +389,7 @@ enum class InputDeviceConfigurationFileType : int32_t { CONFIGURATION = 0, /* .idc file */ KEY_LAYOUT = 1, /* .kl file */ KEY_CHARACTER_MAP = 2, /* .kcm file */ + ftl_last = KEY_CHARACTER_MAP, }; /* diff --git a/include/input/InputEventBuilders.h b/include/input/InputEventBuilders.h index 25d35e9fe7..5bd5070488 100644 --- a/include/input/InputEventBuilders.h +++ b/include/input/InputEventBuilders.h @@ -19,6 +19,8 @@ #include <android/input.h> #include <attestation/HmacKeyManager.h> #include <input/Input.h> +#include <input/InputTransport.h> +#include <ui/LogicalDisplayId.h> #include <utils/Timers.h> // for nsecs_t, systemTime #include <vector> @@ -44,6 +46,11 @@ public: PointerBuilder& y(float y) { return axis(AMOTION_EVENT_AXIS_Y, y); } + PointerBuilder& isResampled(bool isResampled) { + mCoords.isResampled = isResampled; + return *this; + } + PointerBuilder& axis(int32_t axis, float value) { mCoords.setAxisValue(axis, value); return *this; @@ -58,6 +65,87 @@ private: PointerCoords mCoords; }; +class InputMessageBuilder { +public: + InputMessageBuilder(InputMessage::Type type, uint32_t seq) : mType{type}, mSeq{seq} {} + + InputMessageBuilder& eventId(int32_t eventId) { + mEventId = eventId; + return *this; + } + + InputMessageBuilder& eventTime(nsecs_t eventTime) { + mEventTime = eventTime; + return *this; + } + + InputMessageBuilder& deviceId(DeviceId deviceId) { + mDeviceId = deviceId; + return *this; + } + + InputMessageBuilder& source(int32_t source) { + mSource = source; + return *this; + } + + InputMessageBuilder& displayId(ui::LogicalDisplayId displayId) { + mDisplayId = displayId; + return *this; + } + + InputMessageBuilder& action(int32_t action) { + mAction = action; + return *this; + } + + InputMessageBuilder& downTime(nsecs_t downTime) { + mDownTime = downTime; + return *this; + } + + InputMessageBuilder& pointer(PointerBuilder pointerBuilder) { + mPointers.push_back(pointerBuilder); + return *this; + } + + InputMessage build() const { + InputMessage message{}; + // Header + message.header.type = mType; + message.header.seq = mSeq; + // Body + message.body.motion.eventId = mEventId; + message.body.motion.pointerCount = mPointers.size(); + message.body.motion.eventTime = mEventTime; + message.body.motion.deviceId = mDeviceId; + message.body.motion.source = mSource; + message.body.motion.displayId = mDisplayId.val(); + message.body.motion.action = mAction; + message.body.motion.downTime = mDownTime; + + for (size_t i = 0; i < mPointers.size(); ++i) { + message.body.motion.pointers[i].properties = mPointers[i].buildProperties(); + message.body.motion.pointers[i].coords = mPointers[i].buildCoords(); + } + return message; + } + +private: + const InputMessage::Type mType; + const uint32_t mSeq; + + int32_t mEventId{InputEvent::nextId()}; + nsecs_t mEventTime{systemTime(SYSTEM_TIME_MONOTONIC)}; + DeviceId mDeviceId{DEFAULT_DEVICE_ID}; + int32_t mSource{AINPUT_SOURCE_TOUCHSCREEN}; + ui::LogicalDisplayId mDisplayId{ui::LogicalDisplayId::DEFAULT}; + int32_t mAction{AMOTION_EVENT_ACTION_MOVE}; + nsecs_t mDownTime{mEventTime}; + + std::vector<PointerBuilder> mPointers; +}; + class MotionEventBuilder { public: MotionEventBuilder(int32_t action, int32_t source) { @@ -127,7 +215,7 @@ public: return *this; } - MotionEvent build() { + MotionEvent build() const { std::vector<PointerProperties> pointerProperties; std::vector<PointerCoords> pointerCoords; for (const PointerBuilder& pointer : mPointers) { @@ -135,20 +223,22 @@ public: pointerCoords.push_back(pointer.buildCoords()); } + auto [xCursorPosition, yCursorPosition] = + std::make_pair(mRawXCursorPosition, mRawYCursorPosition); // Set mouse cursor position for the most common cases to avoid boilerplate. if (mSource == AINPUT_SOURCE_MOUSE && - !MotionEvent::isValidCursorPosition(mRawXCursorPosition, mRawYCursorPosition)) { - mRawXCursorPosition = pointerCoords[0].getX(); - mRawYCursorPosition = pointerCoords[0].getY(); + !MotionEvent::isValidCursorPosition(xCursorPosition, yCursorPosition)) { + xCursorPosition = pointerCoords[0].getX(); + yCursorPosition = pointerCoords[0].getY(); } MotionEvent event; event.initialize(InputEvent::nextId(), mDeviceId, mSource, mDisplayId, INVALID_HMAC, mAction, mActionButton, mFlags, /*edgeFlags=*/0, AMETA_NONE, mButtonState, MotionClassification::NONE, mTransform, - /*xPrecision=*/0, /*yPrecision=*/0, mRawXCursorPosition, - mRawYCursorPosition, mRawTransform, mDownTime, mEventTime, - mPointers.size(), pointerProperties.data(), pointerCoords.data()); + /*xPrecision=*/0, /*yPrecision=*/0, xCursorPosition, yCursorPosition, + mRawTransform, mDownTime, mEventTime, mPointers.size(), + pointerProperties.data(), pointerCoords.data()); return event; } diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h index 6548810ca8..0cd87201fb 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -263,7 +263,7 @@ public: * Return DEAD_OBJECT if the channel's peer has been closed. * Other errors probably indicate that the channel is broken. */ - status_t sendMessage(const InputMessage* msg); + virtual status_t sendMessage(const InputMessage* msg); /* Receive a message sent by the other endpoint. * @@ -275,14 +275,14 @@ public: * Return DEAD_OBJECT if the channel's peer has been closed. * Other errors probably indicate that the channel is broken. */ - status_t receiveMessage(InputMessage* msg); + virtual android::base::Result<InputMessage> receiveMessage(); /* Tells whether there is a message in the channel available to be received. * * This is only a performance hint and may return false negative results. Clients should not * rely on availability of the message based on the return value. */ - bool probablyHasInput() const; + virtual bool probablyHasInput() const; /* Wait until there is a message in the channel. * @@ -323,11 +323,12 @@ public: */ sp<IBinder> getConnectionToken() const; +protected: + InputChannel(const std::string name, android::base::unique_fd fd, sp<IBinder> token); + private: static std::unique_ptr<InputChannel> create(const std::string& name, android::base::unique_fd fd, sp<IBinder> token); - - InputChannel(const std::string name, android::base::unique_fd fd, sp<IBinder> token); }; /* @@ -363,7 +364,8 @@ public: * Returns OK on success. * Returns WOULD_BLOCK if the channel is full. * Returns DEAD_OBJECT if the channel's peer has been closed. - * Returns BAD_VALUE if seq is 0 or if pointerCount is less than 1 or greater than MAX_POINTERS. + * Returns BAD_VALUE if seq is 0 or if pointerCount is less than 1 or greater than MAX_POINTERS, + * or if the verifier is enabled and the event failed verification upon publishing. * Other errors probably indicate that the channel is broken. */ status_t publishMotionEvent(uint32_t seq, int32_t eventId, int32_t deviceId, int32_t source, diff --git a/include/input/KeyCharacterMap.h b/include/input/KeyCharacterMap.h index 92d5ec4d4e..67b37b1213 100644 --- a/include/input/KeyCharacterMap.h +++ b/include/input/KeyCharacterMap.h @@ -126,9 +126,9 @@ public: bool getEvents(int32_t deviceId, const char16_t* chars, size_t numChars, Vector<KeyEvent>& outEvents) const; - /* Maps an Android key code to another Android key code. This mapping is applied after scanCode - * and usageCodes are mapped to corresponding Android Keycode */ - void addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode); + /* Maps some Android key code to another Android key code. This mapping is applied after + * scanCode and usageCodes are mapped to corresponding Android Keycode */ + void setKeyRemapping(const std::map<int32_t, int32_t>& keyRemapping); /* Maps a scan code and usage code to a key code, in case this key map overrides * the mapping in some way. */ diff --git a/include/input/MotionPredictor.h b/include/input/MotionPredictor.h index f71503988f..200c301ffe 100644 --- a/include/input/MotionPredictor.h +++ b/include/input/MotionPredictor.h @@ -43,7 +43,9 @@ static inline bool isMotionPredictionEnabled() { class JerkTracker { public: // Initialize the tracker. If normalizedDt is true, assume that each sample pushed has dt=1. - JerkTracker(bool normalizedDt); + // alpha is the coefficient of the first-order IIR filter for jerk. A factor of 1 results + // in no smoothing. + JerkTracker(bool normalizedDt, float alpha); // Add a position to the tracker and update derivative estimates. void pushSample(int64_t timestamp, float xPos, float yPos); @@ -58,10 +60,13 @@ public: private: const bool mNormalizedDt; + // Coefficient of first-order IIR filter to smooth jerk calculation. + const float mAlpha; RingBuffer<int64_t> mTimestamps{4}; std::array<float, 4> mXDerivatives{}; // [x, x', x'', x'''] std::array<float, 4> mYDerivatives{}; // [y, y', y'', y'''] + float mJerkMagnitude; }; /** @@ -124,15 +129,17 @@ private: std::unique_ptr<TfLiteMotionPredictorBuffers> mBuffers; std::optional<MotionEvent> mLastEvent; - // mJerkTracker assumes normalized dt = 1 between recorded samples because - // the underlying mModel input also assumes fixed-interval samples. - // Normalized dt as 1 is also used to correspond with the similar Jank - // implementation from the JetPack MotionPredictor implementation. - JerkTracker mJerkTracker{true}; - std::optional<MotionPredictorMetricsManager> mMetricsManager; + std::unique_ptr<JerkTracker> mJerkTracker; + + std::unique_ptr<MotionPredictorMetricsManager> mMetricsManager; const ReportAtomFunction mReportAtomFunction; + + // Initialize prediction model and associated objects. + // Called during lazy initialization. + // TODO: b/210158587 Consider removing lazy initialization. + void initializeObjects(); }; } // namespace android diff --git a/include/input/Resampler.h b/include/input/Resampler.h new file mode 100644 index 0000000000..dcb25b729f --- /dev/null +++ b/include/input/Resampler.h @@ -0,0 +1,154 @@ +/** + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <chrono> +#include <optional> +#include <vector> + +#include <input/Input.h> +#include <input/InputTransport.h> +#include <input/RingBuffer.h> +#include <utils/Timers.h> + +namespace android { + +/** + * Resampler is an interface for resampling MotionEvents. Every resampling implementation + * must use this interface to enable resampling inside InputConsumer's logic. + */ +struct Resampler { + virtual ~Resampler() = default; + + /** + * Tries to resample motionEvent at frameTime. The provided frameTime must be greater than + * the latest sample time of motionEvent. It is not guaranteed that resampling occurs at + * frameTime. Interpolation may occur is futureSample is available. Otherwise, motionEvent + * may be resampled by another method, or not resampled at all. Furthermore, it is the + * implementer's responsibility to guarantee the following: + * - If resampling occurs, a single additional sample should be added to motionEvent. That is, + * if motionEvent had N samples before being passed to Resampler, then it will have N + 1 + * samples by the end of the resampling. No other field of motionEvent should be modified. + * - If resampling does not occur, then motionEvent must not be modified in any way. + */ + virtual void resampleMotionEvent(std::chrono::nanoseconds frameTime, MotionEvent& motionEvent, + const InputMessage* futureSample) = 0; + + /** + * Returns resample latency. Resample latency is the time difference between frame time and + * resample time. More precisely, let frameTime and resampleTime be two timestamps, and + * frameTime > resampleTime. Resample latency is defined as frameTime - resampleTime. + */ + virtual std::chrono::nanoseconds getResampleLatency() const = 0; +}; + +class LegacyResampler final : public Resampler { +public: + /** + * Tries to resample `motionEvent` at `frameTime` by adding a resampled sample at the end of + * `motionEvent` with eventTime equal to `resampleTime` and pointer coordinates determined by + * linear interpolation or linear extrapolation. An earlier `resampleTime` will be used if + * extrapolation takes place and `resampleTime` is too far in the future. If `futureSample` is + * not null, interpolation will occur. If `futureSample` is null and there is enough historical + * data, LegacyResampler will extrapolate. Otherwise, no resampling takes place and + * `motionEvent` is unmodified. + */ + void resampleMotionEvent(std::chrono::nanoseconds frameTime, MotionEvent& motionEvent, + const InputMessage* futureSample) override; + + std::chrono::nanoseconds getResampleLatency() const override; + +private: + struct Pointer { + PointerProperties properties; + PointerCoords coords; + }; + + struct Sample { + std::chrono::nanoseconds eventTime; + std::vector<Pointer> pointers; + + std::vector<PointerCoords> asPointerCoords() const { + std::vector<PointerCoords> pointersCoords; + for (const Pointer& pointer : pointers) { + pointersCoords.push_back(pointer.coords); + } + return pointersCoords; + } + }; + + /** + * Keeps track of the previous MotionEvent deviceId to enable comparison between the previous + * and the current deviceId. + */ + std::optional<DeviceId> mPreviousDeviceId; + + /** + * Up to two latest samples from MotionEvent. Updated every time resampleMotionEvent is called. + * Note: We store up to two samples in order to simplify the implementation. Although, + * calculations are possible with only one previous sample. + */ + RingBuffer<Sample> mLatestSamples{/*capacity=*/2}; + + /** + * Adds up to mLatestSamples.capacity() of motionEvent's latest samples to mLatestSamples. If + * motionEvent has fewer samples than mLatestSamples.capacity(), then the available samples are + * added to mLatestSamples. + */ + void updateLatestSamples(const MotionEvent& motionEvent); + + static Sample messageToSample(const InputMessage& message); + + /** + * Checks if auxiliary sample has the same pointer properties of target sample. That is, + * auxiliary pointer IDs must appear in the same order as target pointer IDs, their toolType + * must match and be resampleable. + */ + static bool pointerPropertiesResampleable(const Sample& target, const Sample& auxiliary); + + /** + * Checks if there are necessary conditions to interpolate. For example, interpolation cannot + * take place if samples are too far apart in time. mLatestSamples must have at least one sample + * when canInterpolate is invoked. + */ + bool canInterpolate(const InputMessage& futureSample) const; + + /** + * Returns a sample interpolated between the latest sample of mLatestSamples and futureSample, + * if the conditions from canInterpolate are satisfied. Otherwise, returns nullopt. + * mLatestSamples must have at least one sample when attemptInterpolation is called. + */ + std::optional<Sample> attemptInterpolation(std::chrono::nanoseconds resampleTime, + const InputMessage& futureSample) const; + + /** + * Checks if there are necessary conditions to extrapolate. That is, there are at least two + * samples in mLatestSamples, and delta is bounded within a time interval. + */ + bool canExtrapolate() const; + + /** + * Returns a sample extrapolated from the two samples of mLatestSamples, if the conditions from + * canExtrapolate are satisfied. The returned sample either has eventTime equal to resampleTime, + * or an earlier time if resampleTime is too far in the future. If canExtrapolate returns false, + * this function returns nullopt. + */ + std::optional<Sample> attemptExtrapolation(std::chrono::nanoseconds resampleTime) const; + + inline static void addSampleToMotionEvent(const Sample& sample, MotionEvent& motionEvent); +}; +} // namespace android diff --git a/include/input/TfLiteMotionPredictor.h b/include/input/TfLiteMotionPredictor.h index 728a8e1e39..49e909ea55 100644 --- a/include/input/TfLiteMotionPredictor.h +++ b/include/input/TfLiteMotionPredictor.h @@ -110,6 +110,9 @@ public: // High jerk means more predictions will be pruned, vice versa for low. float lowJerk = 0; float highJerk = 0; + + // Coefficient for the first-order IIR filter for jerk calculation. + float jerkAlpha = 1; }; // Creates a model from an encoded Flatbuffer model. diff --git a/include/input/VirtualInputDevice.h b/include/input/VirtualInputDevice.h index 222dac8557..b6c630529c 100644 --- a/include/input/VirtualInputDevice.h +++ b/include/input/VirtualInputDevice.h @@ -17,14 +17,30 @@ #pragma once #include <android-base/unique_fd.h> +#include <input/Input.h> +#include <map> namespace android { +enum class DeviceType { + KEYBOARD, + MOUSE, + TOUCHSCREEN, + DPAD, + STYLUS, + ROTARY_ENCODER, +}; + +android::base::unique_fd openUinput(const char* readableName, int32_t vendorId, int32_t productId, + const char* phys, DeviceType deviceType, int32_t screenHeight, + int32_t screenWidth); + enum class UinputAction { RELEASE = 0, PRESS = 1, MOVE = 2, CANCEL = 3, + ftl_last = CANCEL, }; class VirtualInputDevice { @@ -77,6 +93,8 @@ public: private: static const std::map<int, int> BUTTON_CODE_MAPPING; + int32_t mAccumulatedHighResScrollX; + int32_t mAccumulatedHighResScrollY; }; class VirtualTouchscreen : public VirtualInputDevice { @@ -122,4 +140,14 @@ private: bool handleStylusUp(uint16_t tool, std::chrono::nanoseconds eventTime); }; +class VirtualRotaryEncoder : public VirtualInputDevice { +public: + VirtualRotaryEncoder(android::base::unique_fd fd); + virtual ~VirtualRotaryEncoder() override; + bool writeScrollEvent(float scrollAmount, std::chrono::nanoseconds eventTime); + +private: + int32_t mAccumulatedHighResScrollAmount; +}; + } // namespace android diff --git a/include/private/performance_hint_private.h b/include/private/performance_hint_private.h index 8c356d0140..e5eee340ca 100644 --- a/include/private/performance_hint_private.h +++ b/include/private/performance_hint_private.h @@ -108,6 +108,10 @@ APerformanceHintSession* APerformanceHint_createSessionInternal(APerformanceHint const int32_t* threadIds, size_t size, int64_t initialTargetWorkDurationNanos, SessionTag tag); +/** + * Forces FMQ to be enabled or disabled, for testing only. + */ +void APerformanceHint_setUseFMQForTesting(bool enabled); __END_DECLS diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp index 4c3f4a6428..d1a56635f5 100644 --- a/libs/graphicsenv/GraphicsEnv.cpp +++ b/libs/graphicsenv/GraphicsEnv.cpp @@ -401,18 +401,10 @@ void GraphicsEnv::setDriverToLoad(GpuStatsInfo::Driver driver) { switch (driver) { case GpuStatsInfo::Driver::GL: case GpuStatsInfo::Driver::GL_UPDATED: - case GpuStatsInfo::Driver::ANGLE: { - if (mGpuStats.glDriverToLoad == GpuStatsInfo::Driver::NONE || - mGpuStats.glDriverToLoad == GpuStatsInfo::Driver::GL) { - mGpuStats.glDriverToLoad = driver; - break; - } - - if (mGpuStats.glDriverFallback == GpuStatsInfo::Driver::NONE) { - mGpuStats.glDriverFallback = driver; - } + case GpuStatsInfo::Driver::ANGLE: + mGpuStats.glDriverToLoad = driver; break; - } + case GpuStatsInfo::Driver::VULKAN: case GpuStatsInfo::Driver::VULKAN_UPDATED: { if (mGpuStats.vkDriverToLoad == GpuStatsInfo::Driver::NONE || @@ -561,8 +553,7 @@ void GraphicsEnv::sendGpuStatsLocked(GpuStatsInfo::Api api, bool isDriverLoaded, bool isIntendedDriverLoaded = false; if (api == GpuStatsInfo::Api::API_GL) { driver = mGpuStats.glDriverToLoad; - isIntendedDriverLoaded = - isDriverLoaded && (mGpuStats.glDriverFallback == GpuStatsInfo::Driver::NONE); + isIntendedDriverLoaded = isDriverLoaded; } else { driver = mGpuStats.vkDriverToLoad; isIntendedDriverLoaded = diff --git a/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h b/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h index 23f583bda0..72f29c6b0b 100644 --- a/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h +++ b/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h @@ -125,6 +125,11 @@ public: VULKAN_DEVICE_EXTENSION = 9, }; + enum GLTelemetryHints { + NO_HINT = 0, + SKIP_TELEMETRY = 1, + }; + GpuStatsInfo() = default; GpuStatsInfo(const GpuStatsInfo&) = default; virtual ~GpuStatsInfo() = default; @@ -136,7 +141,6 @@ public: std::string appPackageName = ""; int32_t vulkanVersion = 0; Driver glDriverToLoad = Driver::NONE; - Driver glDriverFallback = Driver::NONE; Driver vkDriverToLoad = Driver::NONE; Driver vkDriverFallback = Driver::NONE; bool glDriverToSend = false; diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp index 51d2e5305a..1243b214d3 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -255,6 +255,7 @@ filegroup { "BitTube.cpp", "BLASTBufferQueue.cpp", "BufferItemConsumer.cpp", + "BufferReleaseChannel.cpp", "Choreographer.cpp", "CompositorTiming.cpp", "ConsumerBase.cpp", diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index 044170c378..25e6a52ed1 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -20,12 +20,16 @@ #define ATRACE_TAG ATRACE_TAG_GRAPHICS //#define LOG_NDEBUG 0 +#include <com_android_graphics_libgui_flags.h> #include <cutils/atomic.h> +#include <ftl/fake_guard.h> #include <gui/BLASTBufferQueue.h> #include <gui/BufferItemConsumer.h> #include <gui/BufferQueueConsumer.h> #include <gui/BufferQueueCore.h> #include <gui/BufferQueueProducer.h> +#include <sys/epoll.h> +#include <sys/eventfd.h> #include <gui/FrameRateUtils.h> #include <gui/GLConsumer.h> @@ -39,7 +43,6 @@ #include <private/gui/ComposerServiceAIDL.h> #include <android-base/thread_annotations.h> -#include <chrono> #include <com_android_graphics_libgui_flags.h> @@ -74,6 +77,12 @@ namespace android { std::unique_lock _lock{mutex}; \ base::ScopedLockAssertion assumeLocked(mutex); +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) +static ReleaseBufferCallback EMPTY_RELEASE_CALLBACK = + [](const ReleaseCallbackId&, const sp<Fence>& /*releaseFence*/, + std::optional<uint32_t> /*currentMaxAcquiredBufferCount*/) {}; +#endif + void BLASTBufferItemConsumer::onDisconnect() { Mutex::Autolock lock(mMutex); mPreviouslyConnected = mCurrentlyConnected; @@ -175,16 +184,21 @@ BLASTBufferQueue::BLASTBufferQueue(const std::string& name, bool updateDestinati mSyncTransaction(nullptr), mUpdateDestinationFrame(updateDestinationFrame) { createBufferQueue(&mProducer, &mConsumer); - // since the adapter is in the client process, set dequeue timeout - // explicitly so that dequeueBuffer will block - mProducer->setDequeueTimeout(std::numeric_limits<int64_t>::max()); - - // safe default, most producers are expected to override this - mProducer->setMaxDequeuedBufferCount(2); +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) + mBufferItemConsumer = new BLASTBufferItemConsumer(mProducer, mConsumer, + GraphicBuffer::USAGE_HW_COMPOSER | + GraphicBuffer::USAGE_HW_TEXTURE, + 1, false, this); +#else mBufferItemConsumer = new BLASTBufferItemConsumer(mConsumer, GraphicBuffer::USAGE_HW_COMPOSER | GraphicBuffer::USAGE_HW_TEXTURE, 1, false, this); +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) + // since the adapter is in the client process, set dequeue timeout + // explicitly so that dequeueBuffer will block + mProducer->setDequeueTimeout(std::numeric_limits<int64_t>::max()); + static std::atomic<uint32_t> nextId = 0; mProducerId = nextId++; mName = name + "#" + std::to_string(mProducerId); @@ -210,6 +224,12 @@ BLASTBufferQueue::BLASTBufferQueue(const std::string& name, bool updateDestinati }, this); +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) + std::unique_ptr<gui::BufferReleaseChannel::ConsumerEndpoint> bufferReleaseConsumer; + gui::BufferReleaseChannel::open(mName, bufferReleaseConsumer, mBufferReleaseProducer); + mBufferReleaseReader = std::make_shared<BufferReleaseReader>(std::move(bufferReleaseConsumer)); +#endif + BQA_LOGV("BLASTBufferQueue created"); } @@ -236,6 +256,14 @@ BLASTBufferQueue::~BLASTBufferQueue() { } } +void BLASTBufferQueue::onFirstRef() { + // safe default, most producers are expected to override this + mProducer->setMaxDequeuedBufferCount(2); +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) + mBufferReleaseThread.start(sp<BLASTBufferQueue>::fromExisting(this)); +#endif +} + void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height, int32_t format) { LOG_ALWAYS_FATAL_IF(surface == nullptr, "BLASTBufferQueue: mSurfaceControl must not be NULL"); @@ -259,6 +287,9 @@ void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width, if (surfaceControlChanged) { t.setFlags(mSurfaceControl, layer_state_t::eEnableBackpressure, layer_state_t::eEnableBackpressure); +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) + t.setBufferReleaseChannel(mSurfaceControl, mBufferReleaseProducer); +#endif applyTransaction = true; } mTransformHint = mSurfaceControl->getTransformHint(); @@ -296,14 +327,12 @@ static std::optional<SurfaceControlStats> findMatchingStat( return std::nullopt; } -static void transactionCommittedCallbackThunk(void* context, nsecs_t latchTime, - const sp<Fence>& presentFence, - const std::vector<SurfaceControlStats>& stats) { - if (context == nullptr) { - return; - } - sp<BLASTBufferQueue> bq = static_cast<BLASTBufferQueue*>(context); - bq->transactionCommittedCallback(latchTime, presentFence, stats); +TransactionCompletedCallbackTakesContext BLASTBufferQueue::makeTransactionCommittedCallbackThunk() { + return [bbq = sp<BLASTBufferQueue>::fromExisting( + this)](void* /*context*/, nsecs_t latchTime, const sp<Fence>& presentFence, + const std::vector<SurfaceControlStats>& stats) { + bbq->transactionCommittedCallback(latchTime, presentFence, stats); + }; } void BLASTBufferQueue::transactionCommittedCallback(nsecs_t /*latchTime*/, @@ -336,18 +365,15 @@ void BLASTBufferQueue::transactionCommittedCallback(nsecs_t /*latchTime*/, BQA_LOGE("No matching SurfaceControls found: mSurfaceControlsWithPendingCallback was " "empty."); } - decStrong((void*)transactionCommittedCallbackThunk); } } -static void transactionCallbackThunk(void* context, nsecs_t latchTime, - const sp<Fence>& presentFence, - const std::vector<SurfaceControlStats>& stats) { - if (context == nullptr) { - return; - } - sp<BLASTBufferQueue> bq = static_cast<BLASTBufferQueue*>(context); - bq->transactionCallback(latchTime, presentFence, stats); +TransactionCompletedCallbackTakesContext BLASTBufferQueue::makeTransactionCallbackThunk() { + return [bbq = sp<BLASTBufferQueue>::fromExisting( + this)](void* /*context*/, nsecs_t latchTime, const sp<Fence>& presentFence, + const std::vector<SurfaceControlStats>& stats) { + bbq->transactionCallback(latchTime, presentFence, stats); + }; } void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence>& /*presentFence*/, @@ -381,6 +407,7 @@ void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence stat.latchTime, stat.frameEventStats.dequeueReadyTime); } +#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) auto currFrameNumber = stat.frameEventStats.frameNumber; std::vector<ReleaseCallbackId> staleReleases; for (const auto& [key, value]: mSubmitted) { @@ -396,6 +423,7 @@ void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence stat.currentMaxAcquiredBufferCount, true /* fakeRelease */); } +#endif } else { BQA_LOGE("Failed to find matching SurfaceControl in transactionCallback"); } @@ -403,23 +431,6 @@ void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence BQA_LOGE("No matching SurfaceControls found: mSurfaceControlsWithPendingCallback was " "empty."); } - - decStrong((void*)transactionCallbackThunk); - } -} - -// Unlike transactionCallbackThunk the release buffer callback does not extend the life of the -// BBQ. This is because if the BBQ is destroyed, then the buffers will be released by the client. -// So we pass in a weak pointer to the BBQ and if it still alive, then we release the buffer. -// Otherwise, this is a no-op. -static void releaseBufferCallbackThunk(wp<BLASTBufferQueue> context, const ReleaseCallbackId& id, - const sp<Fence>& releaseFence, - std::optional<uint32_t> currentMaxAcquiredBufferCount) { - sp<BLASTBufferQueue> blastBufferQueue = context.promote(); - if (blastBufferQueue) { - blastBufferQueue->releaseBufferCallback(id, releaseFence, currentMaxAcquiredBufferCount); - } else { - ALOGV("releaseBufferCallbackThunk %s blastBufferQueue is dead", id.to_string().c_str()); } } @@ -432,6 +443,23 @@ void BLASTBufferQueue::flushShadowQueue() { } } +// Unlike transactionCallbackThunk the release buffer callback does not extend the life of the +// BBQ. This is because if the BBQ is destroyed, then the buffers will be released by the client. +// So we pass in a weak pointer to the BBQ and if it still alive, then we release the buffer. +// Otherwise, this is a no-op. +ReleaseBufferCallback BLASTBufferQueue::makeReleaseBufferCallbackThunk() { + return [weakBbq = wp<BLASTBufferQueue>::fromExisting( + this)](const ReleaseCallbackId& id, const sp<Fence>& releaseFence, + std::optional<uint32_t> currentMaxAcquiredBufferCount) { + sp<BLASTBufferQueue> bbq = weakBbq.promote(); + if (!bbq) { + ALOGV("releaseBufferCallbackThunk %s blastBufferQueue is dead", id.to_string().c_str()); + return; + } + bbq->releaseBufferCallback(id, releaseFence, currentMaxAcquiredBufferCount); + }; +} + void BLASTBufferQueue::releaseBufferCallback( const ReleaseCallbackId& id, const sp<Fence>& releaseFence, std::optional<uint32_t> currentMaxAcquiredBufferCount) { @@ -594,9 +622,6 @@ status_t BLASTBufferQueue::acquireNextBufferLocked( t->notifyProducerDisconnect(mSurfaceControl); } - // Ensure BLASTBufferQueue stays alive until we receive the transaction complete callback. - incStrong((void*)transactionCallbackThunk); - // Only update mSize for destination bounds if the incoming buffer matches the requested size. // Otherwise, it could cause stretching since the destination bounds will update before the // buffer with the new size is acquired. @@ -609,9 +634,12 @@ status_t BLASTBufferQueue::acquireNextBufferLocked( bufferItem.mGraphicBuffer->getHeight(), bufferItem.mTransform, bufferItem.mScalingMode, crop); - auto releaseBufferCallback = - std::bind(releaseBufferCallbackThunk, wp<BLASTBufferQueue>(this) /* callbackContext */, - std::placeholders::_1, std::placeholders::_2, std::placeholders::_3); +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) + ReleaseBufferCallback releaseBufferCallback = + applyTransaction ? EMPTY_RELEASE_CALLBACK : makeReleaseBufferCallbackThunk(); +#else + auto releaseBufferCallback = makeReleaseBufferCallbackThunk(); +#endif sp<Fence> fence = bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE; nsecs_t dequeueTime = -1; @@ -629,7 +657,7 @@ status_t BLASTBufferQueue::acquireNextBufferLocked( t->setDataspace(mSurfaceControl, static_cast<ui::Dataspace>(bufferItem.mDataSpace)); t->setHdrMetadata(mSurfaceControl, bufferItem.mHdrMetadata); t->setSurfaceDamageRegion(mSurfaceControl, bufferItem.mSurfaceDamage); - t->addTransactionCompletedCallback(transactionCallbackThunk, static_cast<void*>(this)); + t->addTransactionCompletedCallback(makeTransactionCallbackThunk(), nullptr); mSurfaceControlsWithPendingCallback.push(mSurfaceControl); @@ -786,9 +814,9 @@ void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) { // Only need a commit callback when syncing to ensure the buffer that's synced has been // sent to SF - incStrong((void*)transactionCommittedCallbackThunk); - mSyncTransaction->addTransactionCommittedCallback(transactionCommittedCallbackThunk, - static_cast<void*>(this)); + mSyncTransaction + ->addTransactionCommittedCallback(makeTransactionCommittedCallbackThunk(), + nullptr); if (mAcquireSingleBuffer) { prevCallback = mTransactionReadyCallback; prevTransaction = mSyncTransaction; @@ -817,7 +845,7 @@ void BLASTBufferQueue::onFrameDequeued(const uint64_t bufferId) { void BLASTBufferQueue::onFrameCancelled(const uint64_t bufferId) { std::lock_guard _lock{mTimestampMutex}; mDequeueTimestamps.erase(bufferId); -}; +} bool BLASTBufferQueue::syncNextTransaction( std::function<void(SurfaceComposerClient::Transaction*)> callback, @@ -1113,9 +1141,9 @@ public: // can be non-blocking when the producer is in the client process. class BBQBufferQueueProducer : public BufferQueueProducer { public: - BBQBufferQueueProducer(const sp<BufferQueueCore>& core, wp<BLASTBufferQueue> bbq) + BBQBufferQueueProducer(const sp<BufferQueueCore>& core, const wp<BLASTBufferQueue>& bbq) : BufferQueueProducer(core, false /* consumerIsSurfaceFlinger*/), - mBLASTBufferQueue(std::move(bbq)) {} + mBLASTBufferQueue(bbq) {} status_t connect(const sp<IProducerListener>& listener, int api, bool producerControlledByApp, QueueBufferOutput* output) override { @@ -1130,27 +1158,32 @@ public: // We want to resize the frame history when changing the size of the buffer queue status_t setMaxDequeuedBufferCount(int maxDequeuedBufferCount) override { int maxBufferCount; - status_t status = BufferQueueProducer::setMaxDequeuedBufferCount(maxDequeuedBufferCount, - &maxBufferCount); + if (status_t status = BufferQueueProducer::setMaxDequeuedBufferCount(maxDequeuedBufferCount, + &maxBufferCount); + status != OK) { + return status; + } + + sp<BLASTBufferQueue> bbq = mBLASTBufferQueue.promote(); + if (!bbq) { + return OK; + } + // if we can't determine the max buffer count, then just skip growing the history size - if (status == OK) { - size_t newFrameHistorySize = maxBufferCount + 2; // +2 because triple buffer rendering - // optimize away resizing the frame history unless it will grow - if (newFrameHistorySize > FrameEventHistory::INITIAL_MAX_FRAME_HISTORY) { - sp<BLASTBufferQueue> bbq = mBLASTBufferQueue.promote(); - if (bbq != nullptr) { - ALOGV("increasing frame history size to %zu", newFrameHistorySize); - bbq->resizeFrameEventHistory(newFrameHistorySize); - } - } + size_t newFrameHistorySize = maxBufferCount + 2; // +2 because triple buffer rendering + // optimize away resizing the frame history unless it will grow + if (newFrameHistorySize > FrameEventHistory::INITIAL_MAX_FRAME_HISTORY) { + ALOGV("increasing frame history size to %zu", newFrameHistorySize); + bbq->resizeFrameEventHistory(newFrameHistorySize); } - return status; + + return OK; } int query(int what, int* value) override { if (what == NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER) { *value = 1; - return NO_ERROR; + return OK; } return BufferQueueProducer::query(what, value); } @@ -1230,7 +1263,125 @@ bool BLASTBufferQueue::isSameSurfaceControl(const sp<SurfaceControl>& surfaceCon void BLASTBufferQueue::setTransactionHangCallback( std::function<void(const std::string&)> callback) { std::lock_guard _lock{mMutex}; - mTransactionHangCallback = callback; + mTransactionHangCallback = std::move(callback); +} + +void BLASTBufferQueue::setApplyToken(sp<IBinder> applyToken) { + std::lock_guard _lock{mMutex}; + mApplyToken = std::move(applyToken); +} + +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) + +BLASTBufferQueue::BufferReleaseReader::BufferReleaseReader( + std::unique_ptr<gui::BufferReleaseChannel::ConsumerEndpoint> endpoint) + : mEndpoint{std::move(endpoint)} { + mEpollFd = android::base::unique_fd{epoll_create1(0)}; + LOG_ALWAYS_FATAL_IF(!mEpollFd.ok(), + "Failed to create buffer release epoll file descriptor. errno=%d " + "message='%s'", + errno, strerror(errno)); + + epoll_event registerEndpointFd{}; + registerEndpointFd.events = EPOLLIN; + registerEndpointFd.data.fd = mEndpoint->getFd(); + status_t status = + epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, mEndpoint->getFd(), ®isterEndpointFd); + LOG_ALWAYS_FATAL_IF(status == -1, + "Failed to register buffer release consumer file descriptor with epoll. " + "errno=%d message='%s'", + errno, strerror(errno)); + + mEventFd = android::base::unique_fd(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK)); + LOG_ALWAYS_FATAL_IF(!mEventFd.ok(), + "Failed to create buffer release event file descriptor. errno=%d " + "message='%s'", + errno, strerror(errno)); + + epoll_event registerEventFd{}; + registerEventFd.events = EPOLLIN; + registerEventFd.data.fd = mEventFd.get(); + status = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, mEventFd.get(), ®isterEventFd); + LOG_ALWAYS_FATAL_IF(status == -1, + "Failed to register buffer release event file descriptor with epoll. " + "errno=%d message='%s'", + errno, strerror(errno)); +} + +BLASTBufferQueue::BufferReleaseReader& BLASTBufferQueue::BufferReleaseReader::operator=( + BufferReleaseReader&& other) { + if (this != &other) { + ftl::FakeGuard guard{mMutex}; + ftl::FakeGuard otherGuard{other.mMutex}; + mEndpoint = std::move(other.mEndpoint); + mEpollFd = std::move(other.mEpollFd); + mEventFd = std::move(other.mEventFd); + } + return *this; } +status_t BLASTBufferQueue::BufferReleaseReader::readBlocking(ReleaseCallbackId& outId, + sp<Fence>& outFence, + uint32_t& outMaxAcquiredBufferCount) { + epoll_event event{}; + while (true) { + int eventCount = epoll_wait(mEpollFd.get(), &event, 1 /* maxevents */, -1 /* timeout */); + if (eventCount == 1) { + break; + } + if (eventCount == -1 && errno != EINTR) { + ALOGE("epoll_wait error while waiting for buffer release. errno=%d message='%s'", errno, + strerror(errno)); + } + } + + if (event.data.fd == mEventFd.get()) { + uint64_t value; + if (read(mEventFd.get(), &value, sizeof(uint64_t)) == -1 && errno != EWOULDBLOCK) { + ALOGE("error while reading from eventfd. errno=%d message='%s'", errno, + strerror(errno)); + } + return WOULD_BLOCK; + } + + std::lock_guard lock{mMutex}; + return mEndpoint->readReleaseFence(outId, outFence, outMaxAcquiredBufferCount); +} + +void BLASTBufferQueue::BufferReleaseReader::interruptBlockingRead() { + uint64_t value = 1; + if (write(mEventFd.get(), &value, sizeof(uint64_t)) == -1) { + ALOGE("failed to notify dequeue event. errno=%d message='%s'", errno, strerror(errno)); + } +} + +void BLASTBufferQueue::BufferReleaseThread::start(const sp<BLASTBufferQueue>& bbq) { + mRunning = std::make_shared<std::atomic_bool>(true); + mReader = bbq->mBufferReleaseReader; + std::thread([running = mRunning, reader = mReader, weakBbq = wp<BLASTBufferQueue>(bbq)]() { + pthread_setname_np(pthread_self(), "BufferReleaseThread"); + while (*running) { + ReleaseCallbackId id; + sp<Fence> fence; + uint32_t maxAcquiredBufferCount; + if (status_t status = reader->readBlocking(id, fence, maxAcquiredBufferCount); + status != OK) { + continue; + } + sp<BLASTBufferQueue> bbq = weakBbq.promote(); + if (!bbq) { + return; + } + bbq->releaseBufferCallback(id, fence, maxAcquiredBufferCount); + } + }).detach(); +} + +BLASTBufferQueue::BufferReleaseThread::~BufferReleaseThread() { + *mRunning = false; + mReader->interruptBlockingRead(); +} + +#endif + } // namespace android diff --git a/libs/gui/BufferItemConsumer.cpp b/libs/gui/BufferItemConsumer.cpp index e6331e7282..bfe3d6e023 100644 --- a/libs/gui/BufferItemConsumer.cpp +++ b/libs/gui/BufferItemConsumer.cpp @@ -21,8 +21,11 @@ #include <inttypes.h> +#include <com_android_graphics_libgui_flags.h> #include <gui/BufferItem.h> #include <gui/BufferItemConsumer.h> +#include <ui/BufferQueueDefs.h> +#include <ui/GraphicBuffer.h> #define BI_LOGV(x, ...) ALOGV("[%s] " x, mName.c_str(), ##__VA_ARGS__) // #define BI_LOGD(x, ...) ALOGD("[%s] " x, mName.c_str(), ##__VA_ARGS__) @@ -32,18 +35,37 @@ namespace android { +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) +BufferItemConsumer::BufferItemConsumer(uint64_t consumerUsage, int bufferCount, + bool controlledByApp, bool isConsumerSurfaceFlinger) + : ConsumerBase(controlledByApp, isConsumerSurfaceFlinger) { + initialize(consumerUsage, bufferCount); +} + +BufferItemConsumer::BufferItemConsumer(const sp<IGraphicBufferProducer>& producer, + const sp<IGraphicBufferConsumer>& consumer, + uint64_t consumerUsage, int bufferCount, + bool controlledByApp) + : ConsumerBase(producer, consumer, controlledByApp) { + initialize(consumerUsage, bufferCount); +} +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) + BufferItemConsumer::BufferItemConsumer( const sp<IGraphicBufferConsumer>& consumer, uint64_t consumerUsage, int bufferCount, bool controlledByApp) : ConsumerBase(consumer, controlledByApp) { + initialize(consumerUsage, bufferCount); +} + +void BufferItemConsumer::initialize(uint64_t consumerUsage, int bufferCount) { status_t err = mConsumer->setConsumerUsageBits(consumerUsage); - LOG_ALWAYS_FATAL_IF(err != OK, - "Failed to set consumer usage bits to %#" PRIx64, consumerUsage); + LOG_ALWAYS_FATAL_IF(err != OK, "Failed to set consumer usage bits to %#" PRIx64, consumerUsage); if (bufferCount != DEFAULT_MAX_BUFFERS) { err = mConsumer->setMaxAcquiredBufferCount(bufferCount); - LOG_ALWAYS_FATAL_IF(err != OK, - "Failed to set max acquired buffer count to %d", bufferCount); + LOG_ALWAYS_FATAL_IF(err != OK, "Failed to set max acquired buffer count to %d", + bufferCount); } } @@ -87,17 +109,38 @@ status_t BufferItemConsumer::acquireBuffer(BufferItem *item, status_t BufferItemConsumer::releaseBuffer(const BufferItem &item, const sp<Fence>& releaseFence) { - status_t err; + Mutex::Autolock _l(mMutex); + return releaseBufferSlotLocked(item.mSlot, item.mGraphicBuffer, releaseFence); +} +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) +status_t BufferItemConsumer::releaseBuffer(const sp<GraphicBuffer>& buffer, + const sp<Fence>& releaseFence) { Mutex::Autolock _l(mMutex); - err = addReleaseFenceLocked(item.mSlot, item.mGraphicBuffer, releaseFence); + if (buffer == nullptr) { + return BAD_VALUE; + } + + int slotIndex = getSlotForBufferLocked(buffer); + if (slotIndex == INVALID_BUFFER_SLOT) { + return BAD_VALUE; + } + + return releaseBufferSlotLocked(slotIndex, buffer, releaseFence); +} +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + +status_t BufferItemConsumer::releaseBufferSlotLocked(int slotIndex, const sp<GraphicBuffer>& buffer, + const sp<Fence>& releaseFence) { + status_t err; + + err = addReleaseFenceLocked(slotIndex, buffer, releaseFence); if (err != OK) { BI_LOGE("Failed to addReleaseFenceLocked"); } - err = releaseBufferLocked(item.mSlot, item.mGraphicBuffer, EGL_NO_DISPLAY, - EGL_NO_SYNC_KHR); + err = releaseBufferLocked(slotIndex, buffer, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR); if (err != OK && err != IGraphicBufferConsumer::STALE_BUFFER_SLOT) { BI_LOGE("Failed to release buffer: %s (%d)", strerror(-err), err); diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp index ead9f0f014..831b2ee4be 100644 --- a/libs/gui/BufferQueueProducer.cpp +++ b/libs/gui/BufferQueueProducer.cpp @@ -45,7 +45,10 @@ #include <system/window.h> +#include <com_android_graphics_libgui_flags.h> + namespace android { +using namespace com::android::graphics::libgui; // Macros for include BufferQueueCore information in log messages #define BQ_LOGV(x, ...) \ @@ -924,6 +927,7 @@ status_t BufferQueueProducer::queueBuffer(int slot, uint64_t currentFrameNumber = 0; BufferItem item; int connectedApi; + bool enableEglCpuThrottling = true; sp<Fence> lastQueuedFence; { // Autolock scope @@ -1097,6 +1101,9 @@ status_t BufferQueueProducer::queueBuffer(int slot, VALIDATE_CONSISTENCY(); connectedApi = mCore->mConnectedApi; + if (flags::bq_producer_throttles_only_async_mode()) { + enableEglCpuThrottling = mCore->mAsyncMode || mCore->mDequeueBufferCannotBlock; + } lastQueuedFence = std::move(mLastQueueBufferFence); mLastQueueBufferFence = std::move(acquireFence); @@ -1142,7 +1149,7 @@ status_t BufferQueueProducer::queueBuffer(int slot, } // Wait without lock held - if (connectedApi == NATIVE_WINDOW_API_EGL) { + if (connectedApi == NATIVE_WINDOW_API_EGL && enableEglCpuThrottling) { // Waiting here allows for two full buffers to be queued but not a // third. In the event that frames take varying time, this makes a // small trade-off in favor of latency rather than throughput. diff --git a/libs/gui/BufferReleaseChannel.cpp b/libs/gui/BufferReleaseChannel.cpp new file mode 100644 index 0000000000..27367aa83f --- /dev/null +++ b/libs/gui/BufferReleaseChannel.cpp @@ -0,0 +1,358 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "BufferReleaseChannel" + +#include <fcntl.h> +#include <sys/socket.h> +#include <sys/uio.h> + +#include <android-base/result.h> +#include <android/binder_status.h> +#include <binder/Parcel.h> +#include <utils/Flattenable.h> + +#include <gui/BufferReleaseChannel.h> +#include <private/gui/ParcelUtils.h> + +using android::base::Result; + +namespace android::gui { + +namespace { + +template <typename T> +static void readAligned(const void*& buffer, size_t& size, T& value) { + size -= FlattenableUtils::align<alignof(T)>(buffer); + FlattenableUtils::read(buffer, size, value); +} + +template <typename T> +static void writeAligned(void*& buffer, size_t& size, T value) { + size -= FlattenableUtils::align<alignof(T)>(buffer); + FlattenableUtils::write(buffer, size, value); +} + +template <typename T> +static void addAligned(size_t& size, T /* value */) { + size = FlattenableUtils::align<sizeof(T)>(size); + size += sizeof(T); +} + +template <typename T> +static inline constexpr uint32_t low32(const T n) { + return static_cast<uint32_t>(static_cast<uint64_t>(n)); +} + +template <typename T> +static inline constexpr uint32_t high32(const T n) { + return static_cast<uint32_t>(static_cast<uint64_t>(n) >> 32); +} + +template <typename T> +static inline constexpr T to64(const uint32_t lo, const uint32_t hi) { + return static_cast<T>(static_cast<uint64_t>(hi) << 32 | lo); +} + +} // namespace + +size_t BufferReleaseChannel::Message::getPodSize() const { + size_t size = 0; + addAligned(size, low32(releaseCallbackId.bufferId)); + addAligned(size, high32(releaseCallbackId.bufferId)); + addAligned(size, low32(releaseCallbackId.framenumber)); + addAligned(size, high32(releaseCallbackId.framenumber)); + addAligned(size, maxAcquiredBufferCount); + return size; +} + +size_t BufferReleaseChannel::Message::getFlattenedSize() const { + size_t size = releaseFence->getFlattenedSize(); + size = FlattenableUtils::align<4>(size); + size += getPodSize(); + return size; +} + +status_t BufferReleaseChannel::Message::flatten(void*& buffer, size_t& size, int*& fds, + size_t& count) const { + if (status_t err = releaseFence->flatten(buffer, size, fds, count); err != OK) { + return err; + } + size -= FlattenableUtils::align<4>(buffer); + + // Check we still have enough space + if (size < getPodSize()) { + return NO_MEMORY; + } + + writeAligned(buffer, size, low32(releaseCallbackId.bufferId)); + writeAligned(buffer, size, high32(releaseCallbackId.bufferId)); + writeAligned(buffer, size, low32(releaseCallbackId.framenumber)); + writeAligned(buffer, size, high32(releaseCallbackId.framenumber)); + writeAligned(buffer, size, maxAcquiredBufferCount); + return OK; +} + +status_t BufferReleaseChannel::Message::unflatten(void const*& buffer, size_t& size, + int const*& fds, size_t& count) { + releaseFence = new Fence(); + if (status_t err = releaseFence->unflatten(buffer, size, fds, count); err != OK) { + return err; + } + size -= FlattenableUtils::align<4>(buffer); + + // Check we still have enough space + if (size < getPodSize()) { + return OK; + } + + uint32_t bufferIdLo = 0, bufferIdHi = 0; + uint32_t frameNumberLo = 0, frameNumberHi = 0; + + readAligned(buffer, size, bufferIdLo); + readAligned(buffer, size, bufferIdHi); + releaseCallbackId.bufferId = to64<int64_t>(bufferIdLo, bufferIdHi); + readAligned(buffer, size, frameNumberLo); + readAligned(buffer, size, frameNumberHi); + releaseCallbackId.framenumber = to64<uint64_t>(frameNumberLo, frameNumberHi); + readAligned(buffer, size, maxAcquiredBufferCount); + + return OK; +} + +status_t BufferReleaseChannel::ConsumerEndpoint::readReleaseFence( + ReleaseCallbackId& outReleaseCallbackId, sp<Fence>& outReleaseFence, + uint32_t& outMaxAcquiredBufferCount) { + Message message; + mFlattenedBuffer.resize(message.getFlattenedSize()); + std::array<uint8_t, CMSG_SPACE(sizeof(int))> controlMessageBuffer; + + iovec iov{ + .iov_base = mFlattenedBuffer.data(), + .iov_len = mFlattenedBuffer.size(), + }; + + msghdr msg{ + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = controlMessageBuffer.data(), + .msg_controllen = controlMessageBuffer.size(), + }; + + int result; + do { + result = recvmsg(mFd, &msg, 0); + } while (result == -1 && errno == EINTR); + if (result == -1) { + if (errno == EWOULDBLOCK || errno == EAGAIN) { + return WOULD_BLOCK; + } + ALOGE("Error reading release fence from socket: error %#x (%s)", errno, strerror(errno)); + return UNKNOWN_ERROR; + } + + if (msg.msg_iovlen != 1) { + ALOGE("Error reading release fence from socket: bad data length"); + return UNKNOWN_ERROR; + } + + if (msg.msg_controllen % sizeof(int) != 0) { + ALOGE("Error reading release fence from socket: bad fd length"); + return UNKNOWN_ERROR; + } + + size_t dataLen = msg.msg_iov->iov_len; + const void* data = static_cast<const void*>(msg.msg_iov->iov_base); + if (!data) { + ALOGE("Error reading release fence from socket: no buffer data"); + return UNKNOWN_ERROR; + } + + size_t fdCount = 0; + const int* fdData = nullptr; + if (cmsghdr* cmsg = CMSG_FIRSTHDR(&msg)) { + fdData = reinterpret_cast<const int*>(CMSG_DATA(cmsg)); + fdCount = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int); + } + + if (status_t err = message.unflatten(data, dataLen, fdData, fdCount); err != OK) { + return err; + } + + outReleaseCallbackId = message.releaseCallbackId; + outReleaseFence = std::move(message.releaseFence); + outMaxAcquiredBufferCount = message.maxAcquiredBufferCount; + + return OK; +} + +int BufferReleaseChannel::ProducerEndpoint::writeReleaseFence(const ReleaseCallbackId& callbackId, + const sp<Fence>& fence, + uint32_t maxAcquiredBufferCount) { + Message message{callbackId, fence ? fence : Fence::NO_FENCE, maxAcquiredBufferCount}; + mFlattenedBuffer.resize(message.getFlattenedSize()); + int flattenedFd; + { + // Make copies of needed items since flatten modifies them, and we don't + // want to send anything if there's an error during flatten. + void* flattenedBufferPtr = mFlattenedBuffer.data(); + size_t flattenedBufferSize = mFlattenedBuffer.size(); + int* flattenedFdPtr = &flattenedFd; + size_t flattenedFdCount = 1; + if (status_t err = message.flatten(flattenedBufferPtr, flattenedBufferSize, flattenedFdPtr, + flattenedFdCount); + err != OK) { + ALOGE("Failed to flatten BufferReleaseChannel message."); + return err; + } + } + + iovec iov{ + .iov_base = mFlattenedBuffer.data(), + .iov_len = mFlattenedBuffer.size(), + }; + + msghdr msg{ + .msg_iov = &iov, + .msg_iovlen = 1, + }; + + std::array<uint8_t, CMSG_SPACE(sizeof(int))> controlMessageBuffer; + if (fence && fence->isValid()) { + msg.msg_control = controlMessageBuffer.data(); + msg.msg_controllen = controlMessageBuffer.size(); + + cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); + memcpy(CMSG_DATA(cmsg), &flattenedFd, sizeof(int)); + } + + int result; + do { + result = sendmsg(mFd, &msg, 0); + } while (result == -1 && errno == EINTR); + if (result == -1) { + ALOGD("Error writing release fence to socket: error %#x (%s)", errno, strerror(errno)); + return -errno; + } + + return OK; +} + +status_t BufferReleaseChannel::ProducerEndpoint::readFromParcel(const android::Parcel* parcel) { + if (!parcel) return STATUS_BAD_VALUE; + SAFE_PARCEL(parcel->readUtf8FromUtf16, &mName); + SAFE_PARCEL(parcel->readUniqueFileDescriptor, &mFd); + return STATUS_OK; +} + +status_t BufferReleaseChannel::ProducerEndpoint::writeToParcel(android::Parcel* parcel) const { + if (!parcel) return STATUS_BAD_VALUE; + SAFE_PARCEL(parcel->writeUtf8AsUtf16, mName); + SAFE_PARCEL(parcel->writeUniqueFileDescriptor, mFd); + return STATUS_OK; +} + +status_t BufferReleaseChannel::open(std::string name, + std::unique_ptr<ConsumerEndpoint>& outConsumer, + std::shared_ptr<ProducerEndpoint>& outProducer) { + outConsumer.reset(); + outProducer.reset(); + + int sockets[2]; + if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) { + ALOGE("[%s] Failed to create socket pair. errorno=%d message='%s'", name.c_str(), errno, + strerror(errno)); + return -errno; + } + + android::base::unique_fd consumerFd(sockets[0]); + android::base::unique_fd producerFd(sockets[1]); + + // Socket buffer size. The default is typically about 128KB, which is much larger than + // we really need. + size_t bufferSize = 32 * 1024; + if (setsockopt(consumerFd.get(), SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize)) == + -1) { + ALOGE("[%s] Failed to set consumer socket send buffer size. errno=%d message='%s'", + name.c_str(), errno, strerror(errno)); + return -errno; + } + if (setsockopt(consumerFd.get(), SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize)) == + -1) { + ALOGE("[%s] Failed to set consumer socket receive buffer size. errno=%d " + "message='%s'", + name.c_str(), errno, strerror(errno)); + return -errno; + } + if (setsockopt(producerFd.get(), SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize)) == + -1) { + ALOGE("[%s] Failed to set producer socket send buffer size. errno=%d message='%s'", + name.c_str(), errno, strerror(errno)); + return -errno; + } + if (setsockopt(producerFd.get(), SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize)) == + -1) { + ALOGE("[%s] Failed to set producer socket receive buffer size. errno=%d " + "message='%s'", + name.c_str(), errno, strerror(errno)); + return -errno; + } + + // Configure the consumer socket to be non-blocking. + int flags = fcntl(consumerFd.get(), F_GETFL, 0); + if (flags == -1) { + ALOGE("[%s] Failed to get consumer socket flags. errno=%d message='%s'", name.c_str(), + errno, strerror(errno)); + return -errno; + } + if (fcntl(consumerFd.get(), F_SETFL, flags | O_NONBLOCK) == -1) { + ALOGE("[%s] Failed to set consumer socket to non-blocking mode. errno=%d " + "message='%s'", + name.c_str(), errno, strerror(errno)); + return -errno; + } + + // Configure a timeout for the producer socket. + const timeval timeout{.tv_sec = 1, .tv_usec = 0}; + if (setsockopt(producerFd.get(), SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeval)) == -1) { + ALOGE("[%s] Failed to set producer socket timeout. errno=%d message='%s'", name.c_str(), + errno, strerror(errno)); + return -errno; + } + + // Make the consumer read-only + if (shutdown(consumerFd.get(), SHUT_WR) == -1) { + ALOGE("[%s] Failed to shutdown writing on consumer socket. errno=%d message='%s'", + name.c_str(), errno, strerror(errno)); + return -errno; + } + + // Make the producer write-only + if (shutdown(producerFd.get(), SHUT_RD) == -1) { + ALOGE("[%s] Failed to shutdown reading on producer socket. errno=%d message='%s'", + name.c_str(), errno, strerror(errno)); + return -errno; + } + + outConsumer = std::make_unique<ConsumerEndpoint>(name, std::move(consumerFd)); + outProducer = std::make_shared<ProducerEndpoint>(std::move(name), std::move(producerFd)); + return STATUS_OK; +} + +} // namespace android::gui
\ No newline at end of file diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp index b625c3f75e..602bba8dab 100644 --- a/libs/gui/ConsumerBase.cpp +++ b/libs/gui/ConsumerBase.cpp @@ -14,8 +14,6 @@ * limitations under the License. */ -#include <inttypes.h> - #define LOG_TAG "ConsumerBase" #define ATRACE_TAG ATRACE_TAG_GRAPHICS //#define LOG_NDEBUG 0 @@ -29,17 +27,23 @@ #include <cutils/atomic.h> +#include <com_android_graphics_libgui_flags.h> #include <gui/BufferItem.h> +#include <gui/BufferQueue.h> +#include <gui/ConsumerBase.h> #include <gui/ISurfaceComposer.h> +#include <gui/Surface.h> #include <gui/SurfaceComposerClient.h> -#include <gui/ConsumerBase.h> #include <private/gui/ComposerService.h> +#include <log/log.h> #include <utils/Log.h> #include <utils/String8.h> #include <utils/Trace.h> +#include <inttypes.h> + // Macros for including the ConsumerBase name in log messages #define CB_LOGV(x, ...) ALOGV("[%s] " x, mName.c_str(), ##__VA_ARGS__) // #define CB_LOGD(x, ...) ALOGD("[%s] " x, mName.c_str(), ##__VA_ARGS__) @@ -59,6 +63,30 @@ ConsumerBase::ConsumerBase(const sp<IGraphicBufferConsumer>& bufferQueue, bool c mAbandoned(false), mConsumer(bufferQueue), mPrevFinalReleaseFence(Fence::NO_FENCE) { + initialize(controlledByApp); +} + +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) +ConsumerBase::ConsumerBase(bool controlledByApp, bool consumerIsSurfaceFlinger) + : mAbandoned(false), mPrevFinalReleaseFence(Fence::NO_FENCE) { + sp<IGraphicBufferProducer> producer; + BufferQueue::createBufferQueue(&producer, &mConsumer, consumerIsSurfaceFlinger); + mSurface = sp<Surface>::make(producer, controlledByApp); + initialize(controlledByApp); +} + +ConsumerBase::ConsumerBase(const sp<IGraphicBufferProducer>& producer, + const sp<IGraphicBufferConsumer>& consumer, bool controlledByApp) + : mAbandoned(false), + mConsumer(consumer), + mSurface(sp<Surface>::make(producer, controlledByApp)), + mPrevFinalReleaseFence(Fence::NO_FENCE) { + initialize(controlledByApp); +} + +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) + +void ConsumerBase::initialize(bool controlledByApp) { // Choose a name using the PID and a process-unique ID. mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId()); @@ -96,6 +124,35 @@ void ConsumerBase::onLastStrongRef(const void* id __attribute__((unused))) { abandon(); } +int ConsumerBase::getSlotForBufferLocked(const sp<GraphicBuffer>& buffer) { + if (!buffer) { + return BufferQueue::INVALID_BUFFER_SLOT; + } + + uint64_t id = buffer->getId(); + for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; i++) { + auto& slot = mSlots[i]; + if (slot.mGraphicBuffer && slot.mGraphicBuffer->getId() == id) { + return i; + } + } + + return BufferQueue::INVALID_BUFFER_SLOT; +} + +status_t ConsumerBase::detachBufferLocked(int slotIndex) { + status_t result = mConsumer->detachBuffer(slotIndex); + + if (result != NO_ERROR) { + CB_LOGE("Failed to detach buffer: %d", result); + return result; + } + + freeBufferLocked(slotIndex); + + return result; +} + void ConsumerBase::freeBufferLocked(int slotIndex) { CB_LOGV("freeBufferLocked: slotIndex=%d", slotIndex); mSlots[slotIndex].mGraphicBuffer = nullptr; @@ -252,16 +309,30 @@ status_t ConsumerBase::detachBuffer(int slot) { return NO_INIT; } - status_t result = mConsumer->detachBuffer(slot); - if (result != NO_ERROR) { - CB_LOGE("Failed to detach buffer: %d", result); - return result; + return detachBufferLocked(slot); +} + +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) +status_t ConsumerBase::detachBuffer(const sp<GraphicBuffer>& buffer) { + CB_LOGV("detachBuffer"); + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + CB_LOGE("detachBuffer: ConsumerBase is abandoned!"); + return NO_INIT; + } + if (buffer == nullptr) { + return BAD_VALUE; } - freeBufferLocked(slot); + int slotIndex = getSlotForBufferLocked(buffer); + if (slotIndex == BufferQueue::INVALID_BUFFER_SLOT) { + return BAD_VALUE; + } - return result; + return detachBufferLocked(slotIndex); } +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) status_t ConsumerBase::setDefaultBufferSize(uint32_t width, uint32_t height) { Mutex::Autolock _l(mMutex); @@ -309,6 +380,17 @@ status_t ConsumerBase::setTransformHint(uint32_t hint) { return mConsumer->setTransformHint(hint); } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) +status_t ConsumerBase::setMaxBufferCount(int bufferCount) { + Mutex::Autolock lock(mMutex); + if (mAbandoned) { + CB_LOGE("setMaxBufferCount: ConsumerBase is abandoned!"); + return NO_INIT; + } + return mConsumer->setMaxBufferCount(bufferCount); +} +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) + status_t ConsumerBase::setMaxAcquiredBufferCount(int maxAcquiredBuffers) { Mutex::Autolock lock(mMutex); if (mAbandoned) { @@ -318,6 +400,17 @@ status_t ConsumerBase::setMaxAcquiredBufferCount(int maxAcquiredBuffers) { return mConsumer->setMaxAcquiredBufferCount(maxAcquiredBuffers); } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) +status_t ConsumerBase::setConsumerIsProtected(bool isProtected) { + Mutex::Autolock lock(mMutex); + if (mAbandoned) { + CB_LOGE("setConsumerIsProtected: ConsumerBase is abandoned!"); + return NO_INIT; + } + return mConsumer->setConsumerIsProtected(isProtected); +} +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) + sp<NativeHandle> ConsumerBase::getSidebandStream() const { Mutex::Autolock _l(mMutex); if (mAbandoned) { @@ -384,6 +477,19 @@ void ConsumerBase::dumpLocked(String8& result, const char* prefix) const { } } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) +sp<Surface> ConsumerBase::getSurface() const { + LOG_ALWAYS_FATAL_IF(mSurface == nullptr, + "It's illegal to get the surface of a Consumer that does not own it. This " + "should be impossible once the old CTOR is removed."); + return mSurface; +} + +sp<IGraphicBufferConsumer> ConsumerBase::getIGraphicBufferConsumer() const { + return mConsumer; +} +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) + status_t ConsumerBase::acquireBufferLocked(BufferItem *item, nsecs_t presentWhen, uint64_t maxFrameNumber) { if (mAbandoned) { diff --git a/libs/gui/CpuConsumer.cpp b/libs/gui/CpuConsumer.cpp index 3031fa11fc..23b432e1f4 100644 --- a/libs/gui/CpuConsumer.cpp +++ b/libs/gui/CpuConsumer.cpp @@ -18,9 +18,9 @@ #define LOG_TAG "CpuConsumer" //#define ATRACE_TAG ATRACE_TAG_GRAPHICS -#include <gui/CpuConsumer.h> - +#include <com_android_graphics_libgui_flags.h> #include <gui/BufferItem.h> +#include <gui/CpuConsumer.h> #include <utils/Log.h> #define CC_LOGV(x, ...) ALOGV("[%s] " x, mName.c_str(), ##__VA_ARGS__) @@ -31,12 +31,25 @@ namespace android { -CpuConsumer::CpuConsumer(const sp<IGraphicBufferConsumer>& bq, - size_t maxLockedBuffers, bool controlledByApp) : - ConsumerBase(bq, controlledByApp), - mMaxLockedBuffers(maxLockedBuffers), - mCurrentLockedBuffers(0) -{ +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) +CpuConsumer::CpuConsumer(size_t maxLockedBuffers, bool controlledByApp, + bool isConsumerSurfaceFlinger) + : ConsumerBase(controlledByApp, isConsumerSurfaceFlinger), + mMaxLockedBuffers(maxLockedBuffers), + mCurrentLockedBuffers(0) { + // Create tracking entries for locked buffers + mAcquiredBuffers.insertAt(0, maxLockedBuffers); + + mConsumer->setConsumerUsageBits(GRALLOC_USAGE_SW_READ_OFTEN); + mConsumer->setMaxAcquiredBufferCount(static_cast<int32_t>(maxLockedBuffers)); +} +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) + +CpuConsumer::CpuConsumer(const sp<IGraphicBufferConsumer>& bq, size_t maxLockedBuffers, + bool controlledByApp) + : ConsumerBase(bq, controlledByApp), + mMaxLockedBuffers(maxLockedBuffers), + mCurrentLockedBuffers(0) { // Create tracking entries for locked buffers mAcquiredBuffers.insertAt(0, maxLockedBuffers); diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp index d49489c5a8..95cce5c1df 100644 --- a/libs/gui/GLConsumer.cpp +++ b/libs/gui/GLConsumer.cpp @@ -101,6 +101,34 @@ static bool hasEglProtectedContent() { return hasIt; } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) +GLConsumer::GLConsumer(uint32_t tex, uint32_t texTarget, bool useFenceSync, bool isControlledByApp) + : ConsumerBase(isControlledByApp, /* isConsumerSurfaceFlinger */ false), + mCurrentCrop(Rect::EMPTY_RECT), + mCurrentTransform(0), + mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), + mCurrentFence(Fence::NO_FENCE), + mCurrentTimestamp(0), + mCurrentDataSpace(HAL_DATASPACE_UNKNOWN), + mCurrentFrameNumber(0), + mDefaultWidth(1), + mDefaultHeight(1), + mFilteringEnabled(true), + mTexName(tex), + mUseFenceSync(useFenceSync), + mTexTarget(texTarget), + mEglDisplay(EGL_NO_DISPLAY), + mEglContext(EGL_NO_CONTEXT), + mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT), + mAttached(true) { + GLC_LOGV("GLConsumer"); + + memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), sizeof(mCurrentTransformMatrix)); + + mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS); +} +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) + GLConsumer::GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t tex, uint32_t texTarget, bool useFenceSync, bool isControlledByApp) : ConsumerBase(bq, isControlledByApp), @@ -130,27 +158,54 @@ GLConsumer::GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t tex, mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS); } -GLConsumer::GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t texTarget, - bool useFenceSync, bool isControlledByApp) : - ConsumerBase(bq, isControlledByApp), - mCurrentCrop(Rect::EMPTY_RECT), - mCurrentTransform(0), - mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), - mCurrentFence(Fence::NO_FENCE), - mCurrentTimestamp(0), - mCurrentDataSpace(HAL_DATASPACE_UNKNOWN), - mCurrentFrameNumber(0), - mDefaultWidth(1), - mDefaultHeight(1), - mFilteringEnabled(true), - mTexName(0), - mUseFenceSync(useFenceSync), - mTexTarget(texTarget), - mEglDisplay(EGL_NO_DISPLAY), - mEglContext(EGL_NO_CONTEXT), - mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT), - mAttached(false) -{ +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) +GLConsumer::GLConsumer(uint32_t texTarget, bool useFenceSync, bool isControlledByApp) + : ConsumerBase(isControlledByApp, /* isConsumerSurfaceFlinger */ false), + mCurrentCrop(Rect::EMPTY_RECT), + mCurrentTransform(0), + mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), + mCurrentFence(Fence::NO_FENCE), + mCurrentTimestamp(0), + mCurrentDataSpace(HAL_DATASPACE_UNKNOWN), + mCurrentFrameNumber(0), + mDefaultWidth(1), + mDefaultHeight(1), + mFilteringEnabled(true), + mTexName(0), + mUseFenceSync(useFenceSync), + mTexTarget(texTarget), + mEglDisplay(EGL_NO_DISPLAY), + mEglContext(EGL_NO_CONTEXT), + mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT), + mAttached(false) { + GLC_LOGV("GLConsumer"); + + memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), sizeof(mCurrentTransformMatrix)); + + mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS); +} +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) + +GLConsumer::GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t texTarget, bool useFenceSync, + bool isControlledByApp) + : ConsumerBase(bq, isControlledByApp), + mCurrentCrop(Rect::EMPTY_RECT), + mCurrentTransform(0), + mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), + mCurrentFence(Fence::NO_FENCE), + mCurrentTimestamp(0), + mCurrentDataSpace(HAL_DATASPACE_UNKNOWN), + mCurrentFrameNumber(0), + mDefaultWidth(1), + mDefaultHeight(1), + mFilteringEnabled(true), + mTexName(0), + mUseFenceSync(useFenceSync), + mTexTarget(texTarget), + mEglDisplay(EGL_NO_DISPLAY), + mEglContext(EGL_NO_CONTEXT), + mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT), + mAttached(false) { GLC_LOGV("GLConsumer"); memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp index ff6b558d41..269936858a 100644 --- a/libs/gui/ISurfaceComposer.cpp +++ b/libs/gui/ISurfaceComposer.cpp @@ -62,7 +62,7 @@ public: status_t setTransactionState( const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& state, - const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken, + 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, diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp index f5d19aac78..83fc827c5f 100644 --- a/libs/gui/ITransactionCompletedListener.cpp +++ b/libs/gui/ITransactionCompletedListener.cpp @@ -43,12 +43,6 @@ enum class Tag : uint32_t { } // Anonymous namespace -namespace { // Anonymous - -constexpr int32_t kSerializedCallbackTypeOnCompelteWithJankData = 2; - -} // Anonymous namespace - status_t FrameEventHistoryStats::writeToParcel(Parcel* output) const { status_t err = output->writeUint64(frameNumber); if (err != NO_ERROR) return err; @@ -119,23 +113,6 @@ status_t FrameEventHistoryStats::readFromParcel(const Parcel* input) { return err; } -JankData::JankData() - : frameVsyncId(FrameTimelineInfo::INVALID_VSYNC_ID), jankType(JankType::None) {} - -status_t JankData::writeToParcel(Parcel* output) const { - SAFE_PARCEL(output->writeInt64, frameVsyncId); - SAFE_PARCEL(output->writeInt32, jankType); - SAFE_PARCEL(output->writeInt64, frameIntervalNs); - return NO_ERROR; -} - -status_t JankData::readFromParcel(const Parcel* input) { - SAFE_PARCEL(input->readInt64, &frameVsyncId); - SAFE_PARCEL(input->readInt32, &jankType); - SAFE_PARCEL(input->readInt64, &frameIntervalNs); - return NO_ERROR; -} - status_t SurfaceStats::writeToParcel(Parcel* output) const { SAFE_PARCEL(output->writeStrongBinder, surfaceControl); if (const auto* acquireFence = std::get_if<sp<Fence>>(&acquireTimeOrFence)) { @@ -160,10 +137,6 @@ status_t SurfaceStats::writeToParcel(Parcel* output) const { SAFE_PARCEL(output->writeUint32, currentMaxAcquiredBufferCount); SAFE_PARCEL(output->writeParcelable, eventStats); - SAFE_PARCEL(output->writeInt32, static_cast<int32_t>(jankData.size())); - for (const auto& data : jankData) { - SAFE_PARCEL(output->writeParcelable, data); - } SAFE_PARCEL(output->writeParcelable, previousReleaseCallbackId); return NO_ERROR; } @@ -200,13 +173,6 @@ status_t SurfaceStats::readFromParcel(const Parcel* input) { SAFE_PARCEL(input->readUint32, ¤tMaxAcquiredBufferCount); SAFE_PARCEL(input->readParcelable, &eventStats); - int32_t jankData_size = 0; - SAFE_PARCEL_READ_SIZE(input->readInt32, &jankData_size, input->dataSize()); - for (int i = 0; i < jankData_size; i++) { - JankData data; - SAFE_PARCEL(input->readParcelable, &data); - jankData.push_back(data); - } SAFE_PARCEL(input->readParcelable, &previousReleaseCallbackId); return NO_ERROR; } @@ -371,11 +337,7 @@ ListenerCallbacks ListenerCallbacks::filter(CallbackId::Type type) const { status_t CallbackId::writeToParcel(Parcel* output) const { SAFE_PARCEL(output->writeInt64, id); - if (type == Type::ON_COMPLETE && includeJankData) { - SAFE_PARCEL(output->writeInt32, kSerializedCallbackTypeOnCompelteWithJankData); - } else { - SAFE_PARCEL(output->writeInt32, static_cast<int32_t>(type)); - } + SAFE_PARCEL(output->writeInt32, static_cast<int32_t>(type)); return NO_ERROR; } @@ -383,13 +345,7 @@ status_t CallbackId::readFromParcel(const Parcel* input) { SAFE_PARCEL(input->readInt64, &id); int32_t typeAsInt; SAFE_PARCEL(input->readInt32, &typeAsInt); - if (typeAsInt == kSerializedCallbackTypeOnCompelteWithJankData) { - type = Type::ON_COMPLETE; - includeJankData = true; - } else { - type = static_cast<CallbackId::Type>(typeAsInt); - includeJankData = false; - } + type = static_cast<CallbackId::Type>(typeAsInt); return NO_ERROR; } diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index 3745805aa3..b10996951b 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -17,7 +17,6 @@ #define LOG_TAG "LayerState" #include <cinttypes> -#include <cmath> #include <android/gui/ISurfaceComposerClient.h> #include <android/native_window.h> @@ -177,6 +176,7 @@ status_t layer_state_t::write(Parcel& output) const } SAFE_PARCEL(output.write, stretchEffect); + SAFE_PARCEL(output.writeParcelable, edgeExtensionParameters); SAFE_PARCEL(output.write, bufferCrop); SAFE_PARCEL(output.write, destinationFrame); SAFE_PARCEL(output.writeInt32, static_cast<uint32_t>(trustedOverlay)); @@ -193,6 +193,13 @@ status_t layer_state_t::write(Parcel& output) const SAFE_PARCEL(output.writeFloat, currentHdrSdrRatio); SAFE_PARCEL(output.writeFloat, desiredHdrSdrRatio); SAFE_PARCEL(output.writeInt32, static_cast<int32_t>(cachingHint)); + + const bool hasBufferReleaseChannel = (bufferReleaseChannel != nullptr); + SAFE_PARCEL(output.writeBool, hasBufferReleaseChannel); + if (hasBufferReleaseChannel) { + SAFE_PARCEL(output.writeParcelable, *bufferReleaseChannel); + } + return NO_ERROR; } @@ -306,6 +313,7 @@ status_t layer_state_t::read(const Parcel& input) } SAFE_PARCEL(input.read, stretchEffect); + SAFE_PARCEL(input.readParcelable, &edgeExtensionParameters); SAFE_PARCEL(input.read, bufferCrop); SAFE_PARCEL(input.read, destinationFrame); uint32_t trustedOverlayInt; @@ -337,6 +345,13 @@ status_t layer_state_t::read(const Parcel& input) SAFE_PARCEL(input.readInt32, &tmpInt32); cachingHint = static_cast<gui::CachingHint>(tmpInt32); + bool hasBufferReleaseChannel; + SAFE_PARCEL(input.readBool, &hasBufferReleaseChannel); + if (hasBufferReleaseChannel) { + bufferReleaseChannel = std::make_shared<gui::BufferReleaseChannel::ProducerEndpoint>(); + SAFE_PARCEL(input.readParcelable, bufferReleaseChannel.get()); + } + return NO_ERROR; } @@ -682,6 +697,10 @@ void layer_state_t::merge(const layer_state_t& other) { what |= eStretchChanged; stretchEffect = other.stretchEffect; } + if (other.what & eEdgeExtensionChanged) { + what |= eEdgeExtensionChanged; + edgeExtensionParameters = other.edgeExtensionParameters; + } if (other.what & eBufferCropChanged) { what |= eBufferCropChanged; bufferCrop = other.bufferCrop; @@ -712,6 +731,10 @@ void layer_state_t::merge(const layer_state_t& other) { if (other.what & eFlushJankData) { what |= eFlushJankData; } + if (other.what & eBufferReleaseChannelChanged) { + what |= eBufferReleaseChannelChanged; + bufferReleaseChannel = other.bufferReleaseChannel; + } if ((other.what & what) != other.what) { ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? " "other.what=0x%" PRIX64 " what=0x%" PRIX64 " unmerged flags=0x%" PRIX64, @@ -783,6 +806,7 @@ uint64_t layer_state_t::diff(const layer_state_t& other) const { CHECK_DIFF(diff, eAutoRefreshChanged, other, autoRefresh); CHECK_DIFF(diff, eTrustedOverlayChanged, other, trustedOverlay); CHECK_DIFF(diff, eStretchChanged, other, stretchEffect); + CHECK_DIFF(diff, eEdgeExtensionChanged, other, edgeExtensionParameters); CHECK_DIFF(diff, eBufferCropChanged, other, bufferCrop); CHECK_DIFF(diff, eDestinationFrameChanged, other, destinationFrame); if (other.what & eProducerDisconnect) diff |= eProducerDisconnect; @@ -790,6 +814,7 @@ uint64_t layer_state_t::diff(const layer_state_t& other) const { CHECK_DIFF(diff, eColorChanged, other, color.rgb); CHECK_DIFF(diff, eColorSpaceAgnosticChanged, other, colorSpaceAgnostic); CHECK_DIFF(diff, eDimmingEnabledChanged, other, dimmingEnabled); + if (other.what & eBufferReleaseChannelChanged) diff |= eBufferReleaseChannelChanged; return diff; } @@ -867,88 +892,6 @@ status_t InputWindowCommands::read(const Parcel& input) { // ---------------------------------------------------------------------------- -namespace gui { - -status_t CaptureArgs::writeToParcel(Parcel* output) const { - SAFE_PARCEL(output->writeInt32, static_cast<int32_t>(pixelFormat)); - SAFE_PARCEL(output->write, sourceCrop); - SAFE_PARCEL(output->writeFloat, frameScaleX); - SAFE_PARCEL(output->writeFloat, frameScaleY); - SAFE_PARCEL(output->writeBool, captureSecureLayers); - SAFE_PARCEL(output->writeInt32, uid); - SAFE_PARCEL(output->writeInt32, static_cast<int32_t>(dataspace)); - SAFE_PARCEL(output->writeBool, allowProtected); - SAFE_PARCEL(output->writeBool, grayscale); - SAFE_PARCEL(output->writeInt32, excludeHandles.size()); - for (auto& excludeHandle : excludeHandles) { - SAFE_PARCEL(output->writeStrongBinder, excludeHandle); - } - SAFE_PARCEL(output->writeBool, hintForSeamlessTransition); - return NO_ERROR; -} - -status_t CaptureArgs::readFromParcel(const Parcel* input) { - int32_t value = 0; - SAFE_PARCEL(input->readInt32, &value); - pixelFormat = static_cast<ui::PixelFormat>(value); - SAFE_PARCEL(input->read, sourceCrop); - SAFE_PARCEL(input->readFloat, &frameScaleX); - SAFE_PARCEL(input->readFloat, &frameScaleY); - SAFE_PARCEL(input->readBool, &captureSecureLayers); - SAFE_PARCEL(input->readInt32, &uid); - SAFE_PARCEL(input->readInt32, &value); - dataspace = static_cast<ui::Dataspace>(value); - SAFE_PARCEL(input->readBool, &allowProtected); - SAFE_PARCEL(input->readBool, &grayscale); - int32_t numExcludeHandles = 0; - SAFE_PARCEL_READ_SIZE(input->readInt32, &numExcludeHandles, input->dataSize()); - excludeHandles.reserve(numExcludeHandles); - for (int i = 0; i < numExcludeHandles; i++) { - sp<IBinder> binder; - SAFE_PARCEL(input->readStrongBinder, &binder); - excludeHandles.emplace(binder); - } - SAFE_PARCEL(input->readBool, &hintForSeamlessTransition); - return NO_ERROR; -} - -status_t DisplayCaptureArgs::writeToParcel(Parcel* output) const { - SAFE_PARCEL(CaptureArgs::writeToParcel, output); - - SAFE_PARCEL(output->writeStrongBinder, displayToken); - SAFE_PARCEL(output->writeUint32, width); - SAFE_PARCEL(output->writeUint32, height); - return NO_ERROR; -} - -status_t DisplayCaptureArgs::readFromParcel(const Parcel* input) { - SAFE_PARCEL(CaptureArgs::readFromParcel, input); - - SAFE_PARCEL(input->readStrongBinder, &displayToken); - SAFE_PARCEL(input->readUint32, &width); - SAFE_PARCEL(input->readUint32, &height); - return NO_ERROR; -} - -status_t LayerCaptureArgs::writeToParcel(Parcel* output) const { - SAFE_PARCEL(CaptureArgs::writeToParcel, output); - - SAFE_PARCEL(output->writeStrongBinder, layerHandle); - SAFE_PARCEL(output->writeBool, childrenOnly); - return NO_ERROR; -} - -status_t LayerCaptureArgs::readFromParcel(const Parcel* input) { - SAFE_PARCEL(CaptureArgs::readFromParcel, input); - - SAFE_PARCEL(input->readStrongBinder, &layerHandle); - - SAFE_PARCEL(input->readBool, &childrenOnly); - return NO_ERROR; -} - -}; // namespace gui - ReleaseCallbackId BufferData::generateReleaseCallbackId() const { uint64_t bufferId; if (buffer) { diff --git a/libs/gui/ScreenCaptureResults.cpp b/libs/gui/ScreenCaptureResults.cpp index 601a5f9b33..2de023e5b2 100644 --- a/libs/gui/ScreenCaptureResults.cpp +++ b/libs/gui/ScreenCaptureResults.cpp @@ -40,6 +40,13 @@ status_t ScreenCaptureResults::writeToParcel(android::Parcel* parcel) const { SAFE_PARCEL(parcel->writeBool, capturedSecureLayers); SAFE_PARCEL(parcel->writeBool, capturedHdrLayers); SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(capturedDataspace)); + if (optionalGainMap != nullptr) { + SAFE_PARCEL(parcel->writeBool, true); + SAFE_PARCEL(parcel->write, *optionalGainMap); + } else { + SAFE_PARCEL(parcel->writeBool, false); + } + SAFE_PARCEL(parcel->writeFloat, hdrSdrRatio); return NO_ERROR; } @@ -68,6 +75,14 @@ status_t ScreenCaptureResults::readFromParcel(const android::Parcel* parcel) { uint32_t dataspace = 0; SAFE_PARCEL(parcel->readUint32, &dataspace); capturedDataspace = static_cast<ui::Dataspace>(dataspace); + + bool hasGainmap; + SAFE_PARCEL(parcel->readBool, &hasGainmap); + if (hasGainmap) { + optionalGainMap = new GraphicBuffer(); + SAFE_PARCEL(parcel->read, *optionalGainMap); + } + SAFE_PARCEL(parcel->readFloat, &hdrSdrRatio); return NO_ERROR; } diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index 87fd448f0c..66e7ddd915 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -21,6 +21,8 @@ #include <gui/Surface.h> #include <condition_variable> +#include <cstddef> +#include <cstdint> #include <deque> #include <mutex> #include <thread> @@ -41,11 +43,9 @@ #include <ui/GraphicBuffer.h> #include <ui/Region.h> -#include <gui/AidlStatusUtil.h> +#include <gui/AidlUtil.h> #include <gui/BufferItem.h> -#include <gui/IProducerListener.h> - #include <gui/ISurfaceComposer.h> #include <gui/LayerState.h> #include <private/gui/ComposerService.h> @@ -77,9 +77,28 @@ bool isInterceptorRegistrationOp(int op) { } // namespace +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) +Surface::ProducerDeathListenerProxy::ProducerDeathListenerProxy(wp<SurfaceListener> surfaceListener) + : mSurfaceListener(surfaceListener) {} + +void Surface::ProducerDeathListenerProxy::binderDied(const wp<IBinder>&) { + sp<SurfaceListener> surfaceListener = mSurfaceListener.promote(); + if (!surfaceListener) { + return; + } + + if (surfaceListener->needsDeathNotify()) { + surfaceListener->onRemoteDied(); + } +} +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + Surface::Surface(const sp<IGraphicBufferProducer>& bufferProducer, bool controlledByApp, const sp<IBinder>& surfaceControlHandle) : mGraphicBufferProducer(bufferProducer), +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + mSurfaceDeathListener(nullptr), +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) mCrop(Rect::EMPTY_RECT), mBufferAge(0), mGenerationNumber(0), @@ -134,6 +153,12 @@ Surface::~Surface() { if (mConnectedToCpu) { Surface::disconnect(NATIVE_WINDOW_API_CPU); } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + if (mSurfaceDeathListener != nullptr) { + IInterface::asBinder(mGraphicBufferProducer)->unlinkToDeath(mSurfaceDeathListener); + mSurfaceDeathListener = nullptr; + } +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) } sp<ISurfaceComposer> Surface::composerService() const { @@ -163,6 +188,12 @@ void Surface::allocateBuffers() { mReqFormat, mReqUsage); } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) +status_t Surface::allowAllocation(bool allowAllocation) { + return mGraphicBufferProducer->allowAllocation(allowAllocation); +} +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + status_t Surface::setGenerationNumber(uint32_t generation) { status_t result = mGraphicBufferProducer->setGenerationNumber(generation); if (result == NO_ERROR) { @@ -695,6 +726,51 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) { return OK; } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + +status_t Surface::dequeueBuffer(sp<GraphicBuffer>* buffer, sp<Fence>* outFence) { + if (buffer == nullptr || outFence == nullptr) { + return BAD_VALUE; + } + + android_native_buffer_t* anb; + int fd = -1; + status_t res = dequeueBuffer(&anb, &fd); + *buffer = GraphicBuffer::from(anb); + *outFence = sp<Fence>::make(fd); + return res; +} + +status_t Surface::queueBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence>& fd, + SurfaceQueueBufferOutput* output) { + if (buffer == nullptr) { + return BAD_VALUE; + } + return queueBuffer(buffer.get(), fd ? fd->get() : -1, output); +} + +status_t Surface::detachBuffer(const sp<GraphicBuffer>& buffer) { + if (nullptr == buffer) { + return BAD_VALUE; + } + + Mutex::Autolock lock(mMutex); + + uint64_t bufferId = buffer->getId(); + for (int slot = 0; slot < Surface::NUM_BUFFER_SLOTS; ++slot) { + auto& bufferSlot = mSlots[slot]; + if (bufferSlot.buffer != nullptr && bufferSlot.buffer->getId() == bufferId) { + bufferSlot.buffer = nullptr; + bufferSlot.dirtyRegion = Region::INVALID_REGION; + return mGraphicBufferProducer->detachBuffer(slot); + } + } + + return BAD_VALUE; +} + +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + int Surface::dequeueBuffers(std::vector<BatchBuffer>* buffers) { using DequeueBufferInput = IGraphicBufferProducer::DequeueBufferInput; using DequeueBufferOutput = IGraphicBufferProducer::DequeueBufferOutput; @@ -1118,6 +1194,140 @@ void Surface::onBufferQueuedLocked(int slot, sp<Fence> fence, } } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + +int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd, + SurfaceQueueBufferOutput* surfaceOutput) { + ATRACE_CALL(); + ALOGV("Surface::queueBuffer"); + + IGraphicBufferProducer::QueueBufferOutput output; + IGraphicBufferProducer::QueueBufferInput input; + int slot; + sp<Fence> fence; + { + Mutex::Autolock lock(mMutex); + + slot = getSlotFromBufferLocked(buffer); + if (slot < 0) { + if (fenceFd >= 0) { + close(fenceFd); + } + return slot; + } + if (mSharedBufferSlot == slot && mSharedBufferHasBeenQueued) { + if (fenceFd >= 0) { + close(fenceFd); + } + return OK; + } + + getQueueBufferInputLocked(buffer, fenceFd, mTimestamp, &input); + applyGrallocMetadataLocked(buffer, input); + fence = input.fence; + } + nsecs_t now = systemTime(); + // Drop the lock temporarily while we touch the underlying producer. In the case of a local + // BufferQueue, the following should be allowable: + // + // Surface::queueBuffer + // -> IConsumerListener::onFrameAvailable callback triggers automatically + // -> implementation calls IGraphicBufferConsumer::acquire/release immediately + // -> SurfaceListener::onBufferRelesed callback triggers automatically + // -> implementation calls Surface::dequeueBuffer + status_t err = mGraphicBufferProducer->queueBuffer(slot, input, &output); + { + Mutex::Autolock lock(mMutex); + + mLastQueueDuration = systemTime() - now; + if (err != OK) { + ALOGE("queueBuffer: error queuing buffer, %d", err); + } + + onBufferQueuedLocked(slot, fence, output); + } + + if (surfaceOutput != nullptr) { + *surfaceOutput = {.bufferReplaced = output.bufferReplaced}; + } + + return err; +} + +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) +int Surface::queueBuffers(const std::vector<BatchQueuedBuffer>& buffers, + std::vector<SurfaceQueueBufferOutput>* queueBufferOutputs) +#else +int Surface::queueBuffers(const std::vector<BatchQueuedBuffer>& buffers) +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) +{ + ATRACE_CALL(); + ALOGV("Surface::queueBuffers"); + + size_t numBuffers = buffers.size(); + std::vector<IGraphicBufferProducer::QueueBufferInput> igbpQueueBufferInputs(numBuffers); + std::vector<IGraphicBufferProducer::QueueBufferOutput> igbpQueueBufferOutputs; + std::vector<int> bufferSlots(numBuffers, -1); + std::vector<sp<Fence>> bufferFences(numBuffers); + + int err; + { + Mutex::Autolock lock(mMutex); + + if (mSharedBufferMode) { + ALOGE("%s: batched operation is not supported in shared buffer mode", __FUNCTION__); + return INVALID_OPERATION; + } + + for (size_t batchIdx = 0; batchIdx < numBuffers; batchIdx++) { + int i = getSlotFromBufferLocked(buffers[batchIdx].buffer); + if (i < 0) { + if (buffers[batchIdx].fenceFd >= 0) { + close(buffers[batchIdx].fenceFd); + } + return i; + } + bufferSlots[batchIdx] = i; + + IGraphicBufferProducer::QueueBufferInput input; + getQueueBufferInputLocked(buffers[batchIdx].buffer, buffers[batchIdx].fenceFd, + buffers[batchIdx].timestamp, &input); + input.slot = i; + bufferFences[batchIdx] = input.fence; + igbpQueueBufferInputs[batchIdx] = input; + } + } + nsecs_t now = systemTime(); + err = mGraphicBufferProducer->queueBuffers(igbpQueueBufferInputs, &igbpQueueBufferOutputs); + { + Mutex::Autolock lock(mMutex); + mLastQueueDuration = systemTime() - now; + if (err != OK) { + ALOGE("%s: error queuing buffer, %d", __FUNCTION__, err); + } + + for (size_t batchIdx = 0; batchIdx < numBuffers; batchIdx++) { + onBufferQueuedLocked(bufferSlots[batchIdx], bufferFences[batchIdx], + igbpQueueBufferOutputs[batchIdx]); + } + } + +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + if (queueBufferOutputs != nullptr) { + queueBufferOutputs->clear(); + queueBufferOutputs->resize(numBuffers); + for (size_t batchIdx = 0; batchIdx < numBuffers; batchIdx++) { + (*queueBufferOutputs)[batchIdx].bufferReplaced = + igbpQueueBufferOutputs[batchIdx].bufferReplaced; + } + } +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + + return err; +} + +#else + int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) { ATRACE_CALL(); ALOGV("Surface::queueBuffer"); @@ -1205,6 +1415,8 @@ int Surface::queueBuffers(const std::vector<BatchQueuedBuffer>& buffers) { return err; } +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + void Surface::querySupportedTimestampsLocked() const { // mMutex must be locked when calling this method. @@ -1860,30 +2072,23 @@ bool Surface::transformToDisplayInverse() const { } int Surface::connect(int api) { - static sp<IProducerListener> listener = new StubProducerListener(); + static sp<SurfaceListener> listener = new StubSurfaceListener(); return connect(api, listener); } -int Surface::connect(int api, const sp<IProducerListener>& listener) { - return connect(api, listener, false); -} - -int Surface::connect( - int api, bool reportBufferRemoval, const sp<SurfaceListener>& sListener) { - if (sListener != nullptr) { - mListenerProxy = new ProducerListenerProxy(this, sListener); - } - return connect(api, mListenerProxy, reportBufferRemoval); -} - -int Surface::connect( - int api, const sp<IProducerListener>& listener, bool reportBufferRemoval) { +int Surface::connect(int api, const sp<SurfaceListener>& listener, bool reportBufferRemoval) { ATRACE_CALL(); ALOGV("Surface::connect"); Mutex::Autolock lock(mMutex); IGraphicBufferProducer::QueueBufferOutput output; mReportRemovedBuffers = reportBufferRemoval; - int err = mGraphicBufferProducer->connect(listener, api, mProducerControlledByApp, &output); + + if (listener != nullptr) { + mListenerProxy = new ProducerListenerProxy(this, listener); + } + + int err = + mGraphicBufferProducer->connect(mListenerProxy, api, mProducerControlledByApp, &output); if (err == NO_ERROR) { mDefaultWidth = output.width; mDefaultHeight = output.height; @@ -1898,6 +2103,13 @@ int Surface::connect( } mConsumerRunningBehind = (output.numPendingBuffers >= 2); + +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + if (listener && listener->needsDeathNotify()) { + mSurfaceDeathListener = sp<ProducerDeathListenerProxy>::make(listener); + IInterface::asBinder(mGraphicBufferProducer)->linkToDeath(mSurfaceDeathListener); + } +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) } if (!err && api == NATIVE_WINDOW_API_CPU) { mConnectedToCpu = true; @@ -1911,7 +2123,6 @@ int Surface::connect( return err; } - int Surface::disconnect(int api, IGraphicBufferProducer::DisconnectMode mode) { ATRACE_CALL(); ALOGV("Surface::disconnect"); @@ -1939,6 +2150,14 @@ int Surface::disconnect(int api, IGraphicBufferProducer::DisconnectMode mode) { mConnectedToCpu = false; } } + +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + if (mSurfaceDeathListener != nullptr) { + IInterface::asBinder(mGraphicBufferProducer)->unlinkToDeath(mSurfaceDeathListener); + mSurfaceDeathListener = nullptr; + } +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + return err; } diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index af91bb3ae2..df58df43be 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -20,8 +20,11 @@ #include <stdint.h> #include <sys/types.h> +#include <com_android_graphics_libgui_flags.h> + #include <android/gui/BnWindowInfosReportedListener.h> #include <android/gui/DisplayState.h> +#include <android/gui/EdgeExtensionParameters.h> #include <android/gui/ISurfaceComposerClient.h> #include <android/gui/IWindowInfosListener.h> #include <android/gui/TrustedPresentationThresholds.h> @@ -40,7 +43,7 @@ #include <system/graphics.h> -#include <gui/AidlStatusUtil.h> +#include <gui/AidlUtil.h> #include <gui/BufferItemConsumer.h> #include <gui/CpuConsumer.h> #include <gui/IGraphicBufferProducer.h> @@ -86,7 +89,8 @@ int64_t generateId() { return (((int64_t)getpid()) << 32) | ++idCounter; } -void emptyCallback(nsecs_t, const sp<Fence>&, const std::vector<SurfaceControlStats>&) {} +constexpr int64_t INVALID_VSYNC = -1; + } // namespace const std::string SurfaceComposerClient::kEmpty{}; @@ -207,9 +211,168 @@ sp<SurfaceComposerClient> SurfaceComposerClient::getDefault() { return DefaultComposerClient::getComposerClient(); } +// --------------------------------------------------------------------------- + JankDataListener::~JankDataListener() { } +status_t JankDataListener::flushJankData() { + if (mLayerId == -1) { + return INVALID_OPERATION; + } + + binder::Status status = ComposerServiceAIDL::getComposerService()->flushJankData(mLayerId); + return statusTFromBinderStatus(status); +} + +std::mutex JankDataListenerFanOut::sFanoutInstanceMutex; +std::unordered_map<int32_t, sp<JankDataListenerFanOut>> JankDataListenerFanOut::sFanoutInstances; + +binder::Status JankDataListenerFanOut::onJankData(const std::vector<gui::JankData>& jankData) { + // Find the highest VSync ID. + int64_t lastVsync = jankData.empty() + ? 0 + : std::max_element(jankData.begin(), jankData.end(), + [](const gui::JankData& jd1, const gui::JankData& jd2) { + return jd1.frameVsyncId < jd2.frameVsyncId; + }) + ->frameVsyncId; + + // Fan out the jank data callback. + std::vector<wp<JankDataListener>> listenersToRemove; + for (auto listener : getActiveListeners()) { + if (!listener->onJankDataAvailable(jankData) || + (listener->mRemoveAfter >= 0 && listener->mRemoveAfter <= lastVsync)) { + listenersToRemove.push_back(listener); + } + } + + return removeListeners(listenersToRemove) + ? binder::Status::ok() + : binder::Status::fromExceptionCode(binder::Status::EX_NULL_POINTER); +} + +status_t JankDataListenerFanOut::addListener(sp<SurfaceControl> sc, sp<JankDataListener> listener) { + sp<IBinder> layer = sc->getHandle(); + if (layer == nullptr) { + return UNEXPECTED_NULL; + } + int32_t layerId = sc->getLayerId(); + + sFanoutInstanceMutex.lock(); + auto it = sFanoutInstances.find(layerId); + bool registerNeeded = it == sFanoutInstances.end(); + sp<JankDataListenerFanOut> fanout; + if (registerNeeded) { + fanout = sp<JankDataListenerFanOut>::make(layerId); + sFanoutInstances.insert({layerId, fanout}); + } else { + fanout = it->second; + } + + fanout->mMutex.lock(); + fanout->mListeners.insert(listener); + fanout->mMutex.unlock(); + + sFanoutInstanceMutex.unlock(); + + if (registerNeeded) { + binder::Status status = + ComposerServiceAIDL::getComposerService()->addJankListener(layer, fanout); + return statusTFromBinderStatus(status); + } + return OK; +} + +status_t JankDataListenerFanOut::removeListener(sp<JankDataListener> listener) { + int32_t layerId = listener->mLayerId; + if (layerId == -1) { + return INVALID_OPERATION; + } + + int64_t removeAfter = INVALID_VSYNC; + sp<JankDataListenerFanOut> fanout; + + sFanoutInstanceMutex.lock(); + auto it = sFanoutInstances.find(layerId); + if (it != sFanoutInstances.end()) { + fanout = it->second; + removeAfter = fanout->updateAndGetRemovalVSync(); + } + + if (removeAfter != INVALID_VSYNC) { + // Remove this instance from the map, so that no new listeners are added + // while we're scheduled to be removed. + sFanoutInstances.erase(layerId); + } + sFanoutInstanceMutex.unlock(); + + if (removeAfter < 0) { + return OK; + } + + binder::Status status = + ComposerServiceAIDL::getComposerService()->removeJankListener(layerId, fanout, + removeAfter); + return statusTFromBinderStatus(status); +} + +std::vector<sp<JankDataListener>> JankDataListenerFanOut::getActiveListeners() { + std::scoped_lock<std::mutex> lock(mMutex); + + std::vector<sp<JankDataListener>> listeners; + for (auto it = mListeners.begin(); it != mListeners.end();) { + auto listener = it->promote(); + if (!listener) { + it = mListeners.erase(it); + } else { + listeners.push_back(std::move(listener)); + it++; + } + } + return listeners; +} + +bool JankDataListenerFanOut::removeListeners(const std::vector<wp<JankDataListener>>& listeners) { + std::scoped_lock<std::mutex> fanoutLock(sFanoutInstanceMutex); + std::scoped_lock<std::mutex> listenersLock(mMutex); + + for (auto listener : listeners) { + mListeners.erase(listener); + } + + if (mListeners.empty()) { + sFanoutInstances.erase(mLayerId); + return false; + } + return true; +} + +int64_t JankDataListenerFanOut::updateAndGetRemovalVSync() { + std::scoped_lock<std::mutex> lock(mMutex); + if (mRemoveAfter >= 0) { + // We've already been scheduled to be removed. Don't schedule again. + return INVALID_VSYNC; + } + + int64_t removeAfter = 0; + for (auto it = mListeners.begin(); it != mListeners.end();) { + auto listener = it->promote(); + if (!listener) { + it = mListeners.erase(it); + } else if (listener->mRemoveAfter < 0) { + // We have at least one listener that's still interested. Don't remove. + return INVALID_VSYNC; + } else { + removeAfter = std::max(removeAfter, listener->mRemoveAfter); + it++; + } + } + + mRemoveAfter = removeAfter; + return removeAfter; +} + // --------------------------------------------------------------------------- // TransactionCompletedListener does not use ANDROID_SINGLETON_STATIC_INSTANCE because it needs @@ -256,14 +419,6 @@ CallbackId TransactionCompletedListener::addCallbackFunction( surfaceControls, CallbackId::Type callbackType) { std::lock_guard<std::mutex> lock(mMutex); - return addCallbackFunctionLocked(callbackFunction, surfaceControls, callbackType); -} - -CallbackId TransactionCompletedListener::addCallbackFunctionLocked( - const TransactionCompletedCallback& callbackFunction, - const std::unordered_set<sp<SurfaceControl>, SurfaceComposerClient::SCHash>& - surfaceControls, - CallbackId::Type callbackType) { startListeningLocked(); CallbackId callbackId(getNextIdLocked(), callbackType); @@ -272,33 +427,11 @@ CallbackId TransactionCompletedListener::addCallbackFunctionLocked( for (const auto& surfaceControl : surfaceControls) { callbackSurfaceControls[surfaceControl->getHandle()] = surfaceControl; - - if (callbackType == CallbackId::Type::ON_COMPLETE && - mJankListeners.count(surfaceControl->getLayerId()) != 0) { - callbackId.includeJankData = true; - } } return callbackId; } -void TransactionCompletedListener::addJankListener(const sp<JankDataListener>& listener, - sp<SurfaceControl> surfaceControl) { - std::lock_guard<std::mutex> lock(mMutex); - mJankListeners.insert({surfaceControl->getLayerId(), listener}); -} - -void TransactionCompletedListener::removeJankListener(const sp<JankDataListener>& listener) { - std::lock_guard<std::mutex> lock(mMutex); - for (auto it = mJankListeners.begin(); it != mJankListeners.end();) { - if (it->second == listener) { - it = mJankListeners.erase(it); - } else { - it++; - } - } -} - void TransactionCompletedListener::setReleaseBufferCallback(const ReleaseCallbackId& callbackId, ReleaseBufferCallback listener) { std::scoped_lock<std::mutex> lock(mMutex); @@ -325,32 +458,20 @@ void TransactionCompletedListener::removeSurfaceStatsListener(void* context, voi } void TransactionCompletedListener::addSurfaceControlToCallbacks( - SurfaceComposerClient::CallbackInfo& callbackInfo, - const sp<SurfaceControl>& surfaceControl) { + const sp<SurfaceControl>& surfaceControl, + const std::unordered_set<CallbackId, CallbackIdHash>& callbackIds) { std::lock_guard<std::mutex> lock(mMutex); - bool includingJankData = false; - for (auto callbackId : callbackInfo.callbackIds) { + for (auto callbackId : callbackIds) { mCallbacks[callbackId].surfaceControls.emplace(std::piecewise_construct, std::forward_as_tuple( surfaceControl->getHandle()), std::forward_as_tuple(surfaceControl)); - includingJankData = includingJankData || callbackId.includeJankData; - } - - // If no registered callback is requesting jank data, but there is a jank listener registered - // on the new surface control, add a synthetic callback that requests the jank data. - if (!includingJankData && mJankListeners.count(surfaceControl->getLayerId()) != 0) { - CallbackId callbackId = - addCallbackFunctionLocked(&emptyCallback, callbackInfo.surfaceControls, - CallbackId::Type::ON_COMPLETE); - callbackInfo.callbackIds.emplace(callbackId); } } void TransactionCompletedListener::onTransactionCompleted(ListenerStats listenerStats) { std::unordered_map<CallbackId, CallbackTranslation, CallbackIdHash> callbacksMap; - std::multimap<int32_t, sp<JankDataListener>> jankListenersMap; { std::lock_guard<std::mutex> lock(mMutex); @@ -366,7 +487,6 @@ void TransactionCompletedListener::onTransactionCompleted(ListenerStats listener * sp<SurfaceControl> that could possibly exist for the callbacks. */ callbacksMap = mCallbacks; - jankListenersMap = mJankListeners; for (const auto& transactionStats : listenerStats.transactionStats) { for (auto& callbackId : transactionStats.callbackIds) { mCallbacks.erase(callbackId); @@ -486,12 +606,6 @@ void TransactionCompletedListener::onTransactionCompleted(ListenerStats listener transactionStats.presentFence, surfaceStats); } } - - if (surfaceStats.jankData.empty()) continue; - auto jankRange = jankListenersMap.equal_range(layerId); - for (auto it = jankRange.first; it != jankRange.second; it++) { - it->second->onJankDataAvailable(surfaceStats.jankData); - } } } } @@ -713,7 +827,6 @@ SurfaceComposerClient::Transaction::Transaction() { SurfaceComposerClient::Transaction::Transaction(const Transaction& other) : mId(other.mId), - mTransactionNestCount(other.mTransactionNestCount), mAnimation(other.mAnimation), mEarlyWakeupStart(other.mEarlyWakeupStart), mEarlyWakeupEnd(other.mEarlyWakeupEnd), @@ -753,7 +866,6 @@ SurfaceComposerClient::Transaction::createFromParcel(const Parcel* parcel) { status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel) { const uint64_t transactionId = parcel->readUint64(); - const uint32_t transactionNestCount = parcel->readUint32(); const bool animation = parcel->readBool(); const bool earlyWakeupStart = parcel->readBool(); const bool earlyWakeupEnd = parcel->readBool(); @@ -850,7 +962,6 @@ status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel // Parsing was successful. Update the object. mId = transactionId; - mTransactionNestCount = transactionNestCount; mAnimation = animation; mEarlyWakeupStart = earlyWakeupStart; mEarlyWakeupEnd = earlyWakeupEnd; @@ -882,7 +993,6 @@ status_t SurfaceComposerClient::Transaction::writeToParcel(Parcel* parcel) const const_cast<SurfaceComposerClient::Transaction*>(this)->cacheBuffers(); parcel->writeUint64(mId); - parcel->writeUint32(mTransactionNestCount); parcel->writeBool(mAnimation); parcel->writeBool(mEarlyWakeupStart); parcel->writeBool(mEarlyWakeupEnd); @@ -1004,8 +1114,9 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::merge(Tr // register all surface controls for all callbackIds for this listener that is merging for (const auto& surfaceControl : currentProcessCallbackInfo.surfaceControls) { - mTransactionCompletedListener->addSurfaceControlToCallbacks(currentProcessCallbackInfo, - surfaceControl); + mTransactionCompletedListener + ->addSurfaceControlToCallbacks(surfaceControl, + currentProcessCallbackInfo.callbackIds); } } @@ -1033,7 +1144,6 @@ void SurfaceComposerClient::Transaction::clear() { mInputWindowCommands.clear(); mUncacheBuffers.clear(); mMayContainBuffer = false; - mTransactionNestCount = 0; mAnimation = false; mEarlyWakeupStart = false; mEarlyWakeupEnd = false; @@ -1059,7 +1169,8 @@ void SurfaceComposerClient::doUncacheBufferTransaction(uint64_t cacheId) { uncacheBuffer.token = BufferCache::getInstance().getToken(); uncacheBuffer.id = cacheId; Vector<ComposerState> composerStates; - status_t status = sf->setTransactionState(FrameTimelineInfo{}, composerStates, {}, + Vector<DisplayState> displayStates; + status_t status = sf->setTransactionState(FrameTimelineInfo{}, composerStates, displayStates, ISurfaceComposer::eOneWay, Transaction::getDefaultApplyToken(), {}, systemTime(), true, {uncacheBuffer}, false, {}, generateId(), {}); @@ -1361,7 +1472,7 @@ void SurfaceComposerClient::Transaction::registerSurfaceControlForCallback( auto& callbackInfo = mListenerCallbacks[TransactionCompletedListener::getIInstance()]; callbackInfo.surfaceControls.insert(sc); - mTransactionCompletedListener->addSurfaceControlToCallbacks(callbackInfo, sc); + mTransactionCompletedListener->addSurfaceControlToCallbacks(sc, callbackInfo.callbackIds); } SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setPosition( @@ -1940,8 +2051,9 @@ SurfaceComposerClient::Transaction::setFrameRateSelectionPriority(const sp<Surfa SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::addTransactionCallback( TransactionCompletedCallbackTakesContext callback, void* callbackContext, CallbackId::Type callbackType) { - auto callbackWithContext = std::bind(callback, callbackContext, std::placeholders::_1, - std::placeholders::_2, std::placeholders::_3); + auto callbackWithContext = + std::bind(std::move(callback), callbackContext, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3); const auto& surfaceControls = mListenerCallbacks[mTransactionCompletedListener].surfaceControls; CallbackId callbackId = @@ -1955,13 +2067,15 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::addTrans SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::addTransactionCompletedCallback( TransactionCompletedCallbackTakesContext callback, void* callbackContext) { - return addTransactionCallback(callback, callbackContext, CallbackId::Type::ON_COMPLETE); + return addTransactionCallback(std::move(callback), callbackContext, + CallbackId::Type::ON_COMPLETE); } SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::addTransactionCommittedCallback( TransactionCompletedCallbackTakesContext callback, void* callbackContext) { - return addTransactionCallback(callback, callbackContext, CallbackId::Type::ON_COMMIT); + return addTransactionCallback(std::move(callback), callbackContext, + CallbackId::Type::ON_COMMIT); } SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::notifyProducerDisconnect( @@ -2217,6 +2331,23 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setStret return *this; } +bool SurfaceComposerClient::flagEdgeExtensionEffectUseShader() { + return com::android::graphics::libgui::flags::edge_extension_shader(); +} + +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setEdgeExtensionEffect( + const sp<SurfaceControl>& sc, const gui::EdgeExtensionParameters& effect) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + + s->what |= layer_state_t::eEdgeExtensionChanged; + s->edgeExtensionParameters = effect; + return *this; +} + SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBufferCrop( const sp<SurfaceControl>& sc, const Rect& bufferCrop) { layer_state_t* s = getLayerState(sc); @@ -2262,6 +2393,22 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setDropI return *this; } +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBufferReleaseChannel( + const sp<SurfaceControl>& sc, + const std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint>& channel) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + + s->what |= layer_state_t::eBufferReleaseChannelChanged; + s->bufferReleaseChannel = channel; + + registerSurfaceControlForCallback(sc); + return *this; +} + // --------------------------------------------------------------------------- DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp<IBinder>& token) { diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp index c5f9c38ca3..f126c0be2f 100644 --- a/libs/gui/SurfaceControl.cpp +++ b/libs/gui/SurfaceControl.cpp @@ -139,9 +139,9 @@ sp<Surface> SurfaceControl::generateSurfaceLocked() uint32_t ignore; auto flags = mCreateFlags & (ISurfaceComposerClient::eCursorWindow | ISurfaceComposerClient::eOpaque); - mBbqChild = mClient->createSurface(String8("bbq-wrapper"), 0, 0, mFormat, + mBbqChild = mClient->createSurface(String8::format("[BBQ] %s", mName.c_str()), 0, 0, mFormat, flags, mHandle, {}, &ignore); - mBbq = sp<BLASTBufferQueue>::make("bbq-adapter", mBbqChild, mWidth, mHeight, mFormat); + mBbq = sp<BLASTBufferQueue>::make("[BBQ]" + mName, mBbqChild, mWidth, mHeight, mFormat); // This surface is always consumed by SurfaceFlinger, so the // producerControlledByApp value doesn't matter; using false. diff --git a/libs/gui/WindowInfosListenerReporter.cpp b/libs/gui/WindowInfosListenerReporter.cpp index 0929b8e120..91c9a85149 100644 --- a/libs/gui/WindowInfosListenerReporter.cpp +++ b/libs/gui/WindowInfosListenerReporter.cpp @@ -15,7 +15,7 @@ */ #include <android/gui/ISurfaceComposer.h> -#include <gui/AidlStatusUtil.h> +#include <gui/AidlUtil.h> #include <gui/WindowInfosListenerReporter.h> #include "gui/WindowInfosUpdate.h" diff --git a/libs/gui/aidl/Android.bp b/libs/gui/aidl/Android.bp index 8ed08c2644..fd035f60f5 100644 --- a/libs/gui/aidl/Android.bp +++ b/libs/gui/aidl/Android.bp @@ -28,9 +28,6 @@ filegroup { ":libgui_extra_unstructured_aidl_files", "android/gui/BitTube.aidl", - "android/gui/CaptureArgs.aidl", - "android/gui/DisplayCaptureArgs.aidl", - "android/gui/LayerCaptureArgs.aidl", "android/gui/LayerMetadata.aidl", "android/gui/ParcelableVsyncEventData.aidl", "android/gui/ScreenCaptureResults.aidl", diff --git a/libs/gui/aidl/android/gui/CaptureArgs.aidl b/libs/gui/aidl/android/gui/CaptureArgs.aidl index 9f198cae10..4920344e0e 100644 --- a/libs/gui/aidl/android/gui/CaptureArgs.aidl +++ b/libs/gui/aidl/android/gui/CaptureArgs.aidl @@ -16,4 +16,63 @@ package android.gui; -parcelable CaptureArgs cpp_header "gui/DisplayCaptureArgs.h" rust_type "gui_aidl_types_rs::CaptureArgs"; +import android.gui.ARect; + +// Common arguments for capturing content on-screen +parcelable CaptureArgs { + const int UNSET_UID = -1; + + // Desired pixel format of the final screenshotted buffer + int /*ui::PixelFormat*/ pixelFormat = 1; + + // Crop in layer space: all content outside of the crop will not be captured. + ARect sourceCrop; + + // Scale in the x-direction for the screenshotted result. + float frameScaleX = 1.0f; + + // Scale in the y-direction for the screenshotted result. + float frameScaleY = 1.0f; + + // True if capturing secure layers is permitted + boolean captureSecureLayers = false; + + // UID whose content we want to screenshot + int uid = UNSET_UID; + + // Force capture to be in a color space. If the value is ui::Dataspace::UNKNOWN, the captured + // result will be in a colorspace appropriate for capturing the display contents + // The display may use non-RGB dataspace (ex. displayP3) that could cause pixel data could be + // different from SRGB (byte per color), and failed when checking colors in tests. + // NOTE: In normal cases, we want the screen to be captured in display's colorspace. + int /*ui::Dataspace*/ dataspace = 0; + + // The receiver of the capture can handle protected buffer. A protected buffer has + // GRALLOC_USAGE_PROTECTED usage bit and must not be accessed unprotected behaviour. + // Any read/write access from unprotected context will result in undefined behaviour. + // Protected contents are typically DRM contents. This has no direct implication to the + // secure property of the surface, which is specified by the application explicitly to avoid + // the contents being accessed/captured by screenshot or unsecure display. + boolean allowProtected = false; + + // True if the content should be captured in grayscale + boolean grayscale = false; + + // List of layers to exclude capturing from + IBinder[] excludeHandles; + + // Hint that the caller will use the screenshot animation as part of a transition animation. + // The canonical example would be screen rotation - in such a case any color shift in the + // screenshot is a detractor so composition in the display's colorspace is required. + // Otherwise, the system may choose a colorspace that is more appropriate for use-cases + // such as file encoding or for blending HDR content into an ap's UI, where the display's + // exact colorspace is not an appropriate intermediate result. + // Note that if the caller is requesting a specific dataspace, this hint does nothing. + boolean hintForSeamlessTransition = false; + + // Allows the screenshot to attach a gainmap, which allows for a per-pixel + // transformation of the screenshot to another luminance range, typically + // mapping an SDR base image into HDR. + boolean attachGainmap = false; +} + diff --git a/libs/gui/aidl/android/gui/DisplayCaptureArgs.aidl b/libs/gui/aidl/android/gui/DisplayCaptureArgs.aidl index fc97dbf03d..e00a2dfa82 100644 --- a/libs/gui/aidl/android/gui/DisplayCaptureArgs.aidl +++ b/libs/gui/aidl/android/gui/DisplayCaptureArgs.aidl @@ -16,5 +16,18 @@ package android.gui; -parcelable DisplayCaptureArgs cpp_header "gui/DisplayCaptureArgs.h" rust_type "gui_aidl_types_rs::DisplayCaptureArgs"; +import android.gui.CaptureArgs; + +// Arguments for screenshotting an entire display +parcelable DisplayCaptureArgs { + CaptureArgs captureArgs; + + // The display that we want to screenshot + IBinder displayToken; + + // The width of the render area when we screenshot + int width = 0; + // The length of the render area when we screenshot + int height = 0; +} diff --git a/libs/gui/aidl/android/gui/EdgeExtensionParameters.aidl b/libs/gui/aidl/android/gui/EdgeExtensionParameters.aidl new file mode 100644 index 0000000000..44f4259f74 --- /dev/null +++ b/libs/gui/aidl/android/gui/EdgeExtensionParameters.aidl @@ -0,0 +1,27 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.gui; + + +/** @hide */ +parcelable EdgeExtensionParameters { + // These represent the translation of the window as requested by the animation + boolean extendRight; + boolean extendLeft; + boolean extendTop; + boolean extendBottom; +}
\ No newline at end of file diff --git a/libs/gui/aidl/android/gui/IJankListener.aidl b/libs/gui/aidl/android/gui/IJankListener.aidl new file mode 100644 index 0000000000..2bfd1af69d --- /dev/null +++ b/libs/gui/aidl/android/gui/IJankListener.aidl @@ -0,0 +1,29 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.gui; + +import android.gui.JankData; + +/** @hide */ +interface IJankListener { + + /** + * Callback reporting jank data of the most recent frames. + * @See {@link ISurfaceComposer#addJankListener(IBinder, IJankListener)} + */ + void onJankData(in JankData[] data); +} diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl index 6d018ea7ef..ac14138e8c 100644 --- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl +++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl @@ -42,6 +42,7 @@ import android.gui.ISurfaceComposerClient; import android.gui.ITunnelModeEnabledListener; import android.gui.IWindowInfosListener; import android.gui.IWindowInfosPublisher; +import android.gui.IJankListener; import android.gui.LayerCaptureArgs; import android.gui.OverlayProperties; import android.gui.PullAtomData; @@ -580,4 +581,22 @@ interface ISurfaceComposer { * This method should not block the ShutdownThread therefore it's handled asynchronously. */ oneway void notifyShutdown(); + + /** + * Registers the jank listener on the given layer to receive jank data of future frames. + */ + void addJankListener(IBinder layer, IJankListener listener); + + /** + * Flushes any pending jank data on the given layer to any registered listeners on that layer. + */ + oneway void flushJankData(int layerId); + + /** + * Schedules the removal of the jank listener from the given layer after the VSync with the + * specified ID. Use a value <= 0 for afterVsync to remove the listener immediately. The given + * listener will not be removed before the given VSync, but may still receive data for frames + * past the provided VSync. + */ + oneway void removeJankListener(int layerId, IJankListener listener, long afterVsync); } diff --git a/libs/gui/aidl/android/gui/JankData.aidl b/libs/gui/aidl/android/gui/JankData.aidl new file mode 100644 index 0000000000..ec13681182 --- /dev/null +++ b/libs/gui/aidl/android/gui/JankData.aidl @@ -0,0 +1,45 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + package android.gui; + + /** @hide */ +parcelable JankData { + /** + * Identifier for the frame submitted with Transaction.setFrameTimelineVsyncId + */ + long frameVsyncId; + + /** + * Bitmask of jank types that occurred. + */ + int jankType; + + /** + * Time between frames in nanoseconds. + */ + long frameIntervalNs; + + /** + * Time allocated to the application to render this frame. + */ + long scheduledAppFrameTimeNs; + + /** + * Time taken by the application to render this frame. + */ + long actualAppFrameTimeNs; +} diff --git a/libs/gui/aidl/android/gui/LayerCaptureArgs.aidl b/libs/gui/aidl/android/gui/LayerCaptureArgs.aidl index 18d293f211..004c35a5ce 100644 --- a/libs/gui/aidl/android/gui/LayerCaptureArgs.aidl +++ b/libs/gui/aidl/android/gui/LayerCaptureArgs.aidl @@ -16,4 +16,15 @@ package android.gui; -parcelable LayerCaptureArgs cpp_header "gui/LayerCaptureArgs.h" rust_type "gui_aidl_types_rs::LayerCaptureArgs"; +import android.gui.CaptureArgs; + +// Arguments for capturing a layer and/or its children +parcelable LayerCaptureArgs { + CaptureArgs captureArgs; + + // The Layer that we may want to capture. We would also capture its children + IBinder layerHandle; + // True if we don't actually want to capture the layer and want to capture + // its children instead. + boolean childrenOnly = false; +} diff --git a/libs/gui/aidl/android/gui/ScreenCaptureResults.aidl b/libs/gui/aidl/android/gui/ScreenCaptureResults.aidl index 97a903515b..f4ef16dc71 100644 --- a/libs/gui/aidl/android/gui/ScreenCaptureResults.aidl +++ b/libs/gui/aidl/android/gui/ScreenCaptureResults.aidl @@ -16,4 +16,4 @@ package android.gui; -parcelable ScreenCaptureResults cpp_header "gui/ScreenCaptureResults.h" rust_type "gui_aidl_types_rs::ScreenCaptureResults";
\ No newline at end of file +parcelable ScreenCaptureResults cpp_header "gui/ScreenCaptureResults.h" rust_type "gui_aidl_types_rs::ScreenCaptureResults"; diff --git a/libs/gui/include/gui/AidlStatusUtil.h b/libs/gui/include/gui/AidlUtil.h index 55be27bf35..a3ecd84ba1 100644 --- a/libs/gui/include/gui/AidlStatusUtil.h +++ b/libs/gui/include/gui/AidlUtil.h @@ -16,9 +16,11 @@ #pragma once +#include <android/gui/ARect.h> #include <binder/Status.h> +#include <ui/Rect.h> -// Extracted from frameworks/av/media/libaudioclient/include/media/AidlConversionUtil.h +// Originally extracted from frameworks/av/media/libaudioclient/include/media/AidlConversionUtil.h namespace android::gui::aidl_utils { /** @@ -68,7 +70,7 @@ static inline status_t statusTFromExceptionCode(int32_t exceptionCode) { * * return_type method(type0 param0, ...) */ -static inline status_t statusTFromBinderStatus(const ::android::binder::Status &status) { +static inline status_t statusTFromBinderStatus(const ::android::binder::Status& status) { return status.isOk() ? OK // check OK, : status.serviceSpecificErrorCode() // service-side error, not standard Java exception // (fromServiceSpecificError) @@ -84,8 +86,8 @@ static inline status_t statusTFromBinderStatus(const ::android::binder::Status & * where Java callers expect an exception, not an integer return value. */ static inline ::android::binder::Status binderStatusFromStatusT( - status_t status, const char *optionalMessage = nullptr) { - const char *const emptyIfNull = optionalMessage == nullptr ? "" : optionalMessage; + status_t status, const char* optionalMessage = nullptr) { + const char* const emptyIfNull = optionalMessage == nullptr ? "" : optionalMessage; // From binder::Status instructions: // Prefer a generic exception code when possible, then a service specific // code, and finally a status_t for low level failures or legacy support. @@ -111,4 +113,26 @@ static inline ::android::binder::Status binderStatusFromStatusT( return Status::fromServiceSpecificError(status, emptyIfNull); } +static inline Rect fromARect(ARect rect) { + return Rect(rect.left, rect.top, rect.right, rect.bottom); +} + +static inline ARect toARect(Rect rect) { + ARect aRect; + + aRect.left = rect.left; + aRect.top = rect.top; + aRect.right = rect.right; + aRect.bottom = rect.bottom; + return aRect; +} + +static inline ARect toARect(int32_t left, int32_t top, int32_t right, int32_t bottom) { + return toARect(Rect(left, top, right, bottom)); +} + +static inline ARect toARect(int32_t width, int32_t height) { + return toARect(Rect(width, height)); +} + } // namespace android::gui::aidl_utils diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h index 0e1a505c69..8592cffd15 100644 --- a/libs/gui/include/gui/BLASTBufferQueue.h +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -17,9 +17,10 @@ #ifndef ANDROID_GUI_BLAST_BUFFER_QUEUE_H #define ANDROID_GUI_BLAST_BUFFER_QUEUE_H +#include <com_android_graphics_libgui_flags.h> #include <gui/BufferItem.h> #include <gui/BufferItemConsumer.h> - +#include <gui/IGraphicBufferConsumer.h> #include <gui/IGraphicBufferProducer.h> #include <gui/SurfaceComposerClient.h> @@ -28,7 +29,6 @@ #include <utils/RefBase.h> #include <system/window.h> -#include <thread> #include <queue> #include <com_android_graphics_libgui_flags.h> @@ -40,12 +40,20 @@ class BufferItemConsumer; class BLASTBufferItemConsumer : public BufferItemConsumer { public: +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) + BLASTBufferItemConsumer(const sp<IGraphicBufferProducer>& producer, + const sp<IGraphicBufferConsumer>& consumer, uint64_t consumerUsage, + int bufferCount, bool controlledByApp, wp<BLASTBufferQueue> bbq) + : BufferItemConsumer(producer, consumer, consumerUsage, bufferCount, controlledByApp), +#else BLASTBufferItemConsumer(const sp<IGraphicBufferConsumer>& consumer, uint64_t consumerUsage, int bufferCount, bool controlledByApp, wp<BLASTBufferQueue> bbq) : BufferItemConsumer(consumer, consumerUsage, bufferCount, controlledByApp), +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) mBLASTBufferQueue(std::move(bbq)), mCurrentlyConnected(false), - mPreviouslyConnected(false) {} + mPreviouslyConnected(false) { + } void onDisconnect() override EXCLUDES(mMutex); void addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps, @@ -95,15 +103,21 @@ public: void onFrameDequeued(const uint64_t) override; void onFrameCancelled(const uint64_t) override; + TransactionCompletedCallbackTakesContext makeTransactionCommittedCallbackThunk(); void transactionCommittedCallback(nsecs_t latchTime, const sp<Fence>& presentFence, const std::vector<SurfaceControlStats>& stats); + + TransactionCompletedCallbackTakesContext makeTransactionCallbackThunk(); virtual void transactionCallback(nsecs_t latchTime, const sp<Fence>& presentFence, const std::vector<SurfaceControlStats>& stats); + + ReleaseBufferCallback makeReleaseBufferCallbackThunk(); void releaseBufferCallback(const ReleaseCallbackId& id, const sp<Fence>& releaseFence, std::optional<uint32_t> currentMaxAcquiredBufferCount); void releaseBufferCallbackLocked(const ReleaseCallbackId& id, const sp<Fence>& releaseFence, std::optional<uint32_t> currentMaxAcquiredBufferCount, bool fakeRelease) REQUIRES(mMutex); + bool syncNextTransaction(std::function<void(SurfaceComposerClient::Transaction*)> callback, bool acquireSingleBuffer = true); void stopContinuousSyncTransaction(); @@ -128,9 +142,11 @@ public: * indicates the reason for the hang. */ void setTransactionHangCallback(std::function<void(const std::string&)> callback); - + void setApplyToken(sp<IBinder>); virtual ~BLASTBufferQueue(); + void onFirstRef() override; + private: friend class BLASTBufferQueueHelper; friend class BBQBufferQueueProducer; @@ -170,8 +186,7 @@ private: // BufferQueue internally allows 1 more than // the max to be acquired - int32_t mMaxAcquiredBuffers = 1; - + int32_t mMaxAcquiredBuffers GUARDED_BY(mMutex) = 1; int32_t mNumFrameAvailable GUARDED_BY(mMutex) = 0; int32_t mNumAcquired GUARDED_BY(mMutex) = 0; @@ -256,7 +271,7 @@ private: // Queues up transactions using this token in SurfaceFlinger. This prevents queued up // transactions from other parts of the client from blocking this transaction. - const sp<IBinder> mApplyToken GUARDED_BY(mMutex) = sp<BBinder>::make(); + sp<IBinder> mApplyToken GUARDED_BY(mMutex) = sp<BBinder>::make(); // Guards access to mDequeueTimestamps since we cannot hold to mMutex in onFrameDequeued or // we will deadlock. @@ -300,6 +315,51 @@ private: std::function<void(const std::string&)> mTransactionHangCallback; std::unordered_set<uint64_t> mSyncedFrameNumbers GUARDED_BY(mMutex); + +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) + class BufferReleaseReader { + public: + BufferReleaseReader() = default; + BufferReleaseReader(std::unique_ptr<gui::BufferReleaseChannel::ConsumerEndpoint>); + BufferReleaseReader& operator=(BufferReleaseReader&&); + + // Block until we can read a buffer release message. + // + // Returns: + // * OK if a ReleaseCallbackId and Fence were successfully read. + // * WOULD_BLOCK if the blocking read was interrupted by interruptBlockingRead. + // * UNKNOWN_ERROR if something went wrong. + status_t readBlocking(ReleaseCallbackId& outId, sp<Fence>& outReleaseFence, + uint32_t& outMaxAcquiredBufferCount); + + // Signals the reader's eventfd to wake up any threads waiting on readBlocking. + void interruptBlockingRead(); + + private: + std::mutex mMutex; + std::unique_ptr<gui::BufferReleaseChannel::ConsumerEndpoint> mEndpoint GUARDED_BY(mMutex); + android::base::unique_fd mEpollFd; + android::base::unique_fd mEventFd; + }; + + // BufferReleaseChannel is used to communicate buffer releases from SurfaceFlinger to + // the client. See BBQBufferQueueProducer::dequeueBuffer for details. + std::shared_ptr<BufferReleaseReader> mBufferReleaseReader; + std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint> mBufferReleaseProducer; + + class BufferReleaseThread { + public: + BufferReleaseThread() = default; + ~BufferReleaseThread(); + void start(const sp<BLASTBufferQueue>&); + + private: + std::shared_ptr<std::atomic_bool> mRunning; + std::shared_ptr<BufferReleaseReader> mReader; + }; + + BufferReleaseThread mBufferReleaseThread; +#endif }; } // namespace android diff --git a/libs/gui/include/gui/BufferItemConsumer.h b/libs/gui/include/gui/BufferItemConsumer.h index a905610ee2..6810edaf7c 100644 --- a/libs/gui/include/gui/BufferItemConsumer.h +++ b/libs/gui/include/gui/BufferItemConsumer.h @@ -17,13 +17,15 @@ #ifndef ANDROID_GUI_BUFFERITEMCONSUMER_H #define ANDROID_GUI_BUFFERITEMCONSUMER_H -#include <gui/ConsumerBase.h> +#include <com_android_graphics_libgui_flags.h> #include <gui/BufferQueue.h> +#include <gui/ConsumerBase.h> #define ANDROID_GRAPHICS_BUFFERITEMCONSUMER_JNI_ID "mBufferItemConsumer" namespace android { +class GraphicBuffer; class String8; /** @@ -51,9 +53,17 @@ class BufferItemConsumer: public ConsumerBase // access at the same time. // controlledByApp tells whether this consumer is controlled by the // application. +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) + BufferItemConsumer(uint64_t consumerUsage, int bufferCount = DEFAULT_MAX_BUFFERS, + bool controlledByApp = false, bool isConsumerSurfaceFlinger = false); + BufferItemConsumer(const sp<IGraphicBufferConsumer>& consumer, uint64_t consumerUsage, + int bufferCount = DEFAULT_MAX_BUFFERS, bool controlledByApp = false) + __attribute((deprecated("Prefer ctors that create their own surface and consumer."))); +#else BufferItemConsumer(const sp<IGraphicBufferConsumer>& consumer, uint64_t consumerUsage, int bufferCount = DEFAULT_MAX_BUFFERS, bool controlledByApp = false); +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) ~BufferItemConsumer() override; @@ -85,7 +95,25 @@ class BufferItemConsumer: public ConsumerBase status_t releaseBuffer(const BufferItem &item, const sp<Fence>& releaseFence = Fence::NO_FENCE); - private: +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + status_t releaseBuffer(const sp<GraphicBuffer>& buffer, + const sp<Fence>& releaseFence = Fence::NO_FENCE); +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + +protected: +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) + // This should only be used by BLASTBufferQueue: + BufferItemConsumer(const sp<IGraphicBufferProducer>& producer, + const sp<IGraphicBufferConsumer>& consumer, uint64_t consumerUsage, + int bufferCount = DEFAULT_MAX_BUFFERS, bool controlledByApp = false); +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) + +private: + void initialize(uint64_t consumerUsage, int bufferCount); + + status_t releaseBufferSlotLocked(int slotIndex, const sp<GraphicBuffer>& buffer, + const sp<Fence>& releaseFence); + void freeBufferLocked(int slotIndex) override; // mBufferFreedListener is the listener object that will be called when diff --git a/libs/gui/include/gui/BufferReleaseChannel.h b/libs/gui/include/gui/BufferReleaseChannel.h new file mode 100644 index 0000000000..51fe0b6fab --- /dev/null +++ b/libs/gui/include/gui/BufferReleaseChannel.h @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <string> +#include <vector> + +#include <android-base/unique_fd.h> + +#include <binder/Parcelable.h> +#include <gui/ITransactionCompletedListener.h> +#include <ui/Fence.h> +#include <utils/Errors.h> + +namespace android::gui { + +/** + * IPC wrapper to pass release fences from SurfaceFlinger to apps via a local unix domain socket. + */ +class BufferReleaseChannel { +private: + class Endpoint { + public: + Endpoint(std::string name, android::base::unique_fd fd) + : mName(std::move(name)), mFd(std::move(fd)) {} + Endpoint() {} + + Endpoint(Endpoint&&) noexcept = default; + Endpoint& operator=(Endpoint&&) noexcept = default; + + Endpoint(const Endpoint&) = delete; + void operator=(const Endpoint&) = delete; + + const android::base::unique_fd& getFd() const { return mFd; } + + protected: + std::string mName; + android::base::unique_fd mFd; + }; + +public: + class ConsumerEndpoint : public Endpoint { + public: + ConsumerEndpoint(std::string name, android::base::unique_fd fd) + : Endpoint(std::move(name), std::move(fd)) {} + + /** + * Reads a release fence from the BufferReleaseChannel. + * + * Returns OK on success. + * Returns WOULD_BLOCK if there is no fence present. + * Other errors probably indicate that the channel is broken. + */ + status_t readReleaseFence(ReleaseCallbackId& outReleaseCallbackId, + sp<Fence>& outReleaseFence, uint32_t& maxAcquiredBufferCount); + + private: + std::vector<uint8_t> mFlattenedBuffer; + }; + + class ProducerEndpoint : public Endpoint, public Parcelable { + public: + ProducerEndpoint(std::string name, android::base::unique_fd fd) + : Endpoint(std::move(name), std::move(fd)) {} + ProducerEndpoint() {} + + status_t readFromParcel(const android::Parcel* parcel) override; + status_t writeToParcel(android::Parcel* parcel) const override; + + status_t writeReleaseFence(const ReleaseCallbackId&, const sp<Fence>& releaseFence, + uint32_t maxAcquiredBufferCount); + + private: + std::vector<uint8_t> mFlattenedBuffer; + }; + + /** + * Create two endpoints that make up the BufferReleaseChannel. + * + * Return OK on success. + */ + static status_t open(const std::string name, std::unique_ptr<ConsumerEndpoint>& outConsumer, + std::shared_ptr<ProducerEndpoint>& outProducer); + + struct Message : public Flattenable<Message> { + ReleaseCallbackId releaseCallbackId; + sp<Fence> releaseFence = Fence::NO_FENCE; + uint32_t maxAcquiredBufferCount; + + Message() = default; + Message(ReleaseCallbackId releaseCallbackId, sp<Fence> releaseFence, + uint32_t maxAcquiredBufferCount) + : releaseCallbackId{releaseCallbackId}, + releaseFence{std::move(releaseFence)}, + maxAcquiredBufferCount{maxAcquiredBufferCount} {} + + // Flattenable protocol + size_t getFlattenedSize() const; + + size_t getFdCount() const { return releaseFence->getFdCount(); } + + status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const; + + status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count); + + private: + size_t getPodSize() const; + }; +}; + +} // namespace android::gui diff --git a/libs/gui/include/gui/ConsumerBase.h b/libs/gui/include/gui/ConsumerBase.h index 8ff0cd0f6e..e976aa48be 100644 --- a/libs/gui/include/gui/ConsumerBase.h +++ b/libs/gui/include/gui/ConsumerBase.h @@ -17,18 +17,17 @@ #ifndef ANDROID_GUI_CONSUMERBASE_H #define ANDROID_GUI_CONSUMERBASE_H +#include <com_android_graphics_libgui_flags.h> #include <gui/BufferQueueDefs.h> #include <gui/IConsumerListener.h> #include <gui/IGraphicBufferConsumer.h> +#include <gui/IGraphicBufferProducer.h> #include <gui/OccupancyTracker.h> - #include <ui/PixelFormat.h> - #include <utils/String8.h> #include <utils/Vector.h> #include <utils/threads.h> - namespace android { // ---------------------------------------------------------------------------- @@ -76,12 +75,28 @@ public: void dumpState(String8& result) const; void dumpState(String8& result, const char* prefix) const; +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) + // Returns a Surface that can be used as the producer for this consumer. + sp<Surface> getSurface() const; + + // DEPRECATED, DO NOT USE. Returns the underlying IGraphicBufferConsumer + // that backs this ConsumerBase. + sp<IGraphicBufferConsumer> getIGraphicBufferConsumer() const + __attribute((deprecated("DO NOT USE: Temporary hack for refactoring"))); +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) + // setFrameAvailableListener sets the listener object that will be notified // when a new frame becomes available. void setFrameAvailableListener(const wp<FrameAvailableListener>& listener); // See IGraphicBufferConsumer::detachBuffer - status_t detachBuffer(int slot); + status_t detachBuffer(int slot) __attribute(( + deprecated("Please use the GraphicBuffer variant--slots are deprecated."))); + +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + // See IGraphicBufferConsumer::detachBuffer + status_t detachBuffer(const sp<GraphicBuffer>& buffer); +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) // See IGraphicBufferConsumer::setDefaultBufferSize status_t setDefaultBufferSize(uint32_t width, uint32_t height); @@ -98,9 +113,18 @@ public: // See IGraphicBufferConsumer::setTransformHint status_t setTransformHint(uint32_t hint); +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) + // See IGraphicBufferConsumer::setMaxBufferCount + status_t setMaxBufferCount(int bufferCount); +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) + // See IGraphicBufferConsumer::setMaxAcquiredBufferCount status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers); +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) + status_t setConsumerIsProtected(bool isProtected); +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) + // See IGraphicBufferConsumer::getSidebandStream sp<NativeHandle> getSidebandStream() const; @@ -115,12 +139,24 @@ private: ConsumerBase(const ConsumerBase&); void operator=(const ConsumerBase&); + void initialize(bool controlledByApp); + protected: // ConsumerBase constructs a new ConsumerBase object to consume image // buffers from the given IGraphicBufferConsumer. // The controlledByApp flag indicates that this consumer is under the application's // control. +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) + explicit ConsumerBase(bool controlledByApp = false, bool consumerIsSurfaceFlinger = false); + explicit ConsumerBase(const sp<IGraphicBufferProducer>& producer, + const sp<IGraphicBufferConsumer>& consumer, bool controlledByApp = false); + + explicit ConsumerBase(const sp<IGraphicBufferConsumer>& consumer, bool controlledByApp = false) + __attribute((deprecated("ConsumerBase should own its own producer, and constructing it " + "without one is fragile! This method is going away soon."))); +#else explicit ConsumerBase(const sp<IGraphicBufferConsumer>& consumer, bool controlledByApp = false); +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) // onLastStrongRef gets called by RefBase just before the dtor of the most // derived class. It is used to clean up the buffers so that ConsumerBase @@ -150,6 +186,10 @@ protected: virtual void onBuffersReleased() override; virtual void onSidebandStreamChanged() override; + virtual int getSlotForBufferLocked(const sp<GraphicBuffer>& buffer); + + virtual status_t detachBufferLocked(int slotIndex); + // freeBufferLocked frees up the given buffer slot. If the slot has been // initialized this will release the reference to the GraphicBuffer in that // slot. Otherwise it has no effect. @@ -264,10 +304,16 @@ protected: Mutex mFrameAvailableMutex; wp<FrameAvailableListener> mFrameAvailableListener; - // The ConsumerBase has-a BufferQueue and is responsible for creating this object - // if none is supplied + // The ConsumerBase has-a BufferQueue and is responsible for creating these + // objects if not supplied. sp<IGraphicBufferConsumer> mConsumer; +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) + // This Surface wraps the IGraphicBufferConsumer created for this + // ConsumerBase. + sp<Surface> mSurface; +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) + // The final release fence of the most recent buffer released by // releaseBufferLocked. sp<Fence> mPrevFinalReleaseFence; diff --git a/libs/gui/include/gui/CpuConsumer.h b/libs/gui/include/gui/CpuConsumer.h index 806fbe8aa0..2bba61bbe8 100644 --- a/libs/gui/include/gui/CpuConsumer.h +++ b/libs/gui/include/gui/CpuConsumer.h @@ -19,8 +19,9 @@ #include <system/window.h> -#include <gui/ConsumerBase.h> +#include <com_android_graphics_libgui_flags.h> #include <gui/BufferQueue.h> +#include <gui/ConsumerBase.h> #include <utils/Vector.h> @@ -91,8 +92,17 @@ class CpuConsumer : public ConsumerBase // Create a new CPU consumer. The maxLockedBuffers parameter specifies // how many buffers can be locked for user access at the same time. +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) + CpuConsumer(size_t maxLockedBuffers, bool controlledByApp = false, + bool isConsumerSurfaceFlinger = false); + + CpuConsumer(const sp<IGraphicBufferConsumer>& bq, size_t maxLockedBuffers, + bool controlledByApp = false) + __attribute((deprecated("Prefer ctors that create their own surface and consumer."))); +#else CpuConsumer(const sp<IGraphicBufferConsumer>& bq, size_t maxLockedBuffers, bool controlledByApp = false); +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) // Gets the next graphics buffer from the producer and locks it for CPU use, // filling out the passed-in locked buffer structure with the native pointer diff --git a/libs/gui/include/gui/DisplayCaptureArgs.h b/libs/gui/include/gui/DisplayCaptureArgs.h deleted file mode 100644 index e29ce41bd5..0000000000 --- a/libs/gui/include/gui/DisplayCaptureArgs.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (C) 2022 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 <stdint.h> -#include <sys/types.h> - -#include <binder/IBinder.h> -#include <binder/Parcel.h> -#include <binder/Parcelable.h> -#include <gui/SpHash.h> -#include <ui/GraphicTypes.h> -#include <ui/PixelFormat.h> -#include <ui/Rect.h> -#include <unordered_set> - -namespace android::gui { - -struct CaptureArgs : public Parcelable { - const static int32_t UNSET_UID = -1; - virtual ~CaptureArgs() = default; - - ui::PixelFormat pixelFormat{ui::PixelFormat::RGBA_8888}; - Rect sourceCrop; - float frameScaleX{1}; - float frameScaleY{1}; - bool captureSecureLayers{false}; - int32_t uid{UNSET_UID}; - // Force capture to be in a color space. If the value is ui::Dataspace::UNKNOWN, the captured - // result will be in a colorspace appropriate for capturing the display contents - // The display may use non-RGB dataspace (ex. displayP3) that could cause pixel data could be - // different from SRGB (byte per color), and failed when checking colors in tests. - // NOTE: In normal cases, we want the screen to be captured in display's colorspace. - ui::Dataspace dataspace = ui::Dataspace::UNKNOWN; - - // The receiver of the capture can handle protected buffer. A protected buffer has - // GRALLOC_USAGE_PROTECTED usage bit and must not be accessed unprotected behaviour. - // Any read/write access from unprotected context will result in undefined behaviour. - // Protected contents are typically DRM contents. This has no direct implication to the - // secure property of the surface, which is specified by the application explicitly to avoid - // the contents being accessed/captured by screenshot or unsecure display. - bool allowProtected = false; - - bool grayscale = false; - - std::unordered_set<sp<IBinder>, SpHash<IBinder>> excludeHandles; - - // Hint that the caller will use the screenshot animation as part of a transition animation. - // The canonical example would be screen rotation - in such a case any color shift in the - // screenshot is a detractor so composition in the display's colorspace is required. - // Otherwise, the system may choose a colorspace that is more appropriate for use-cases - // such as file encoding or for blending HDR content into an ap's UI, where the display's - // exact colorspace is not an appropriate intermediate result. - // Note that if the caller is requesting a specific dataspace, this hint does nothing. - bool hintForSeamlessTransition = false; - - virtual status_t writeToParcel(Parcel* output) const; - virtual status_t readFromParcel(const Parcel* input); -}; - -struct DisplayCaptureArgs : CaptureArgs { - sp<IBinder> displayToken; - uint32_t width{0}; - uint32_t height{0}; - - status_t writeToParcel(Parcel* output) const override; - status_t readFromParcel(const Parcel* input) override; -}; - -}; // namespace android::gui diff --git a/libs/gui/include/gui/LayerCaptureArgs.h b/libs/gui/include/gui/Flags.h index fae2bcc787..735375a1e7 100644 --- a/libs/gui/include/gui/LayerCaptureArgs.h +++ b/libs/gui/include/gui/Flags.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 The Android Open Source Project + * Copyright 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,19 +16,9 @@ #pragma once -#include <stdint.h> -#include <sys/types.h> +#include <com_android_graphics_libgui_flags.h> -#include <gui/DisplayCaptureArgs.h> - -namespace android::gui { - -struct LayerCaptureArgs : CaptureArgs { - sp<IBinder> layerHandle; - bool childrenOnly{false}; - - status_t writeToParcel(Parcel* output) const override; - status_t readFromParcel(const Parcel* input) override; -}; - -}; // namespace android::gui +#define WB_CAMERA3_AND_PROCESSORS_WITH_DEPENDENCIES \ + (COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CAMERA3_AND_PROCESSORS) && \ + COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) && \ + COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS))
\ No newline at end of file diff --git a/libs/gui/include/gui/FrameTimestamps.h b/libs/gui/include/gui/FrameTimestamps.h index 3d1be4d2eb..462081bfd2 100644 --- a/libs/gui/include/gui/FrameTimestamps.h +++ b/libs/gui/include/gui/FrameTimestamps.h @@ -116,7 +116,7 @@ public: // Public for testing. static nsecs_t snapToNextTick( nsecs_t timestamp, nsecs_t tickPhase, nsecs_t tickInterval); - nsecs_t getReportedCompositeDeadline() const { return mCompositorTiming.deadline; }; + nsecs_t getReportedCompositeDeadline() const { return mCompositorTiming.deadline; } nsecs_t getNextCompositeDeadline(const nsecs_t now) const; nsecs_t getCompositeInterval() const { return mCompositorTiming.interval; } diff --git a/libs/gui/include/gui/GLConsumer.h b/libs/gui/include/gui/GLConsumer.h index ba268ab17a..bfe3eb31e8 100644 --- a/libs/gui/include/gui/GLConsumer.h +++ b/libs/gui/include/gui/GLConsumer.h @@ -20,6 +20,7 @@ #include <EGL/egl.h> #include <EGL/eglext.h> +#include <com_android_graphics_libgui_flags.h> #include <gui/BufferQueueDefs.h> #include <gui/ConsumerBase.h> @@ -82,12 +83,25 @@ public: // If the constructor without the tex parameter is used, the GLConsumer is // created in a detached state, and attachToContext must be called before // calls to updateTexImage. - GLConsumer(const sp<IGraphicBufferConsumer>& bq, - uint32_t tex, uint32_t texureTarget, bool useFenceSync, - bool isControlledByApp); +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) + GLConsumer(uint32_t tex, uint32_t textureTarget, bool useFenceSync, bool isControlledByApp); - GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t texureTarget, - bool useFenceSync, bool isControlledByApp); + GLConsumer(uint32_t textureTarget, bool useFenceSync, bool isControlledByApp); + + GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t tex, uint32_t textureTarget, + bool useFenceSync, bool isControlledByApp) + __attribute((deprecated("Prefer ctors that create their own surface and consumer."))); + + GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t textureTarget, bool useFenceSync, + bool isControlledByApp) + __attribute((deprecated("Prefer ctors that create their own surface and consumer."))); +#else + GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t tex, uint32_t textureTarget, + bool useFenceSync, bool isControlledByApp); + + GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t textureTarget, bool useFenceSync, + bool isControlledByApp); +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) // updateTexImage acquires the most recently queued buffer, and sets the // image contents of the target texture to it. diff --git a/libs/gui/include/gui/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h index 84a1730d7a..197e792367 100644 --- a/libs/gui/include/gui/IGraphicBufferProducer.h +++ b/libs/gui/include/gui/IGraphicBufferProducer.h @@ -867,6 +867,6 @@ class BnGraphicBufferProducer : public IGraphicBufferProducer { #endif // ---------------------------------------------------------------------------- -}; // namespace android +} // namespace android #endif // ANDROID_GUI_IGRAPHICBUFFERPRODUCER_H diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h index eb4a802c17..9a422fd808 100644 --- a/libs/gui/include/gui/ISurfaceComposer.h +++ b/libs/gui/include/gui/ISurfaceComposer.h @@ -18,6 +18,7 @@ #include <android/gui/CachingHint.h> #include <android/gui/DisplayBrightness.h> +#include <android/gui/DisplayCaptureArgs.h> #include <android/gui/FrameTimelineInfo.h> #include <android/gui/IDisplayEventConnection.h> #include <android/gui/IFpsListener.h> @@ -27,6 +28,7 @@ #include <android/gui/ITunnelModeEnabledListener.h> #include <android/gui/IWindowInfosListener.h> #include <android/gui/IWindowInfosPublisher.h> +#include <android/gui/LayerCaptureArgs.h> #include <binder/IBinder.h> #include <binder/IInterface.h> #include <gui/ITransactionCompletedListener.h> @@ -70,13 +72,6 @@ using gui::IRegionSamplingListener; using gui::IScreenCaptureListener; using gui::SpHash; -namespace gui { - -struct DisplayCaptureArgs; -struct LayerCaptureArgs; - -} // namespace gui - namespace ui { struct DisplayMode; @@ -112,7 +107,7 @@ public: /* open/close transactions. requires ACCESS_SURFACE_FLINGER permission */ virtual status_t setTransactionState( const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& state, - const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken, + 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, diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ITransactionCompletedListener.h index bc97cd0828..014029b257 100644 --- a/libs/gui/include/gui/ITransactionCompletedListener.h +++ b/libs/gui/include/gui/ITransactionCompletedListener.h @@ -16,8 +16,6 @@ #pragma once -#include "JankInfo.h" - #include <binder/IInterface.h> #include <binder/Parcel.h> #include <binder/Parcelable.h> @@ -40,15 +38,10 @@ class ListenerCallbacks; class CallbackId : public Parcelable { public: int64_t id; - enum class Type : int32_t { - ON_COMPLETE = 0, - ON_COMMIT = 1, - /*reserved for serialization = 2*/ - } type; - bool includeJankData; // Only respected for ON_COMPLETE callbacks. + enum class Type : int32_t { ON_COMPLETE = 0, ON_COMMIT = 1 } type; CallbackId() {} - CallbackId(int64_t id, Type type) : id(id), type(type), includeJankData(false) {} + CallbackId(int64_t id, Type type) : id(id), type(type) {} status_t writeToParcel(Parcel* output) const override; status_t readFromParcel(const Parcel* input) override; @@ -113,29 +106,6 @@ public: nsecs_t dequeueReadyTime; }; -/** - * Jank information representing SurfaceFlinger's jank classification about frames for a specific - * surface. - */ -class JankData : public Parcelable { -public: - status_t writeToParcel(Parcel* output) const override; - status_t readFromParcel(const Parcel* input) override; - - JankData(); - JankData(int64_t frameVsyncId, int32_t jankType, nsecs_t frameIntervalNs) - : frameVsyncId(frameVsyncId), jankType(jankType), frameIntervalNs(frameIntervalNs) {} - - // Identifier for the frame submitted with Transaction.setFrameTimelineVsyncId - int64_t frameVsyncId; - - // Bitmask of janks that occurred - int32_t jankType; - - // Expected duration of the frame - nsecs_t frameIntervalNs; -}; - class SurfaceStats : public Parcelable { public: status_t writeToParcel(Parcel* output) const override; @@ -145,14 +115,13 @@ public: SurfaceStats(const sp<IBinder>& sc, std::variant<nsecs_t, sp<Fence>> acquireTimeOrFence, const sp<Fence>& prevReleaseFence, std::optional<uint32_t> hint, uint32_t currentMaxAcquiredBuffersCount, FrameEventHistoryStats frameEventStats, - std::vector<JankData> jankData, ReleaseCallbackId previousReleaseCallbackId) + ReleaseCallbackId previousReleaseCallbackId) : surfaceControl(sc), acquireTimeOrFence(std::move(acquireTimeOrFence)), previousReleaseFence(prevReleaseFence), transformHint(hint), currentMaxAcquiredBufferCount(currentMaxAcquiredBuffersCount), eventStats(frameEventStats), - jankData(std::move(jankData)), previousReleaseCallbackId(previousReleaseCallbackId) {} sp<IBinder> surfaceControl; @@ -161,7 +130,6 @@ public: std::optional<uint32_t> transformHint = 0; uint32_t currentMaxAcquiredBufferCount = 0; FrameEventHistoryStats eventStats; - std::vector<JankData> jankData; ReleaseCallbackId previousReleaseCallbackId; }; diff --git a/libs/gui/include/gui/LayerMetadata.h b/libs/gui/include/gui/LayerMetadata.h index 9cf62bc7d6..7ee291df4c 100644 --- a/libs/gui/include/gui/LayerMetadata.h +++ b/libs/gui/include/gui/LayerMetadata.h @@ -74,6 +74,7 @@ enum class GameMode : int32_t { } // namespace android::gui using android::gui::METADATA_ACCESSIBILITY_ID; +using android::gui::METADATA_CALLING_UID; using android::gui::METADATA_DEQUEUE_TIME; using android::gui::METADATA_GAME_MODE; using android::gui::METADATA_MOUSE_CURSOR; diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index 5f2f8dc013..2cdde3255e 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -21,7 +21,9 @@ #include <stdint.h> #include <sys/types.h> +#include <android/gui/DisplayCaptureArgs.h> #include <android/gui/IWindowInfosReportedListener.h> +#include <android/gui/LayerCaptureArgs.h> #include <android/gui/TrustedPresentationThresholds.h> #include <android/native_window.h> #include <gui/IGraphicBufferProducer.h> @@ -29,13 +31,13 @@ #include <math/mat4.h> #include <android/gui/DropInputMode.h> +#include <android/gui/EdgeExtensionParameters.h> #include <android/gui/FocusRequest.h> #include <android/gui/TrustedOverlay.h> #include <ftl/flags.h> -#include <gui/DisplayCaptureArgs.h> +#include <gui/BufferReleaseChannel.h> #include <gui/ISurfaceComposer.h> -#include <gui/LayerCaptureArgs.h> #include <gui/LayerMetadata.h> #include <gui/SpHash.h> #include <gui/SurfaceControl.h> @@ -218,6 +220,8 @@ struct layer_state_t { eTrustedOverlayChanged = 0x4000'00000000, eDropInputModeChanged = 0x8000'00000000, eExtendedRangeBrightnessChanged = 0x10000'00000000, + eEdgeExtensionChanged = 0x20000'00000000, + eBufferReleaseChannelChanged = 0x40000'00000000, }; layer_state_t(); @@ -241,7 +245,7 @@ struct layer_state_t { layer_state_t::eCropChanged | layer_state_t::eDestinationFrameChanged | layer_state_t::eMatrixChanged | layer_state_t::ePositionChanged | layer_state_t::eTransformToDisplayInverseChanged | - layer_state_t::eTransparentRegionChanged; + layer_state_t::eTransparentRegionChanged | layer_state_t::eEdgeExtensionChanged; // Buffer and related updates. static constexpr uint64_t BUFFER_CHANGES = layer_state_t::eApiChanged | @@ -393,6 +397,9 @@ struct layer_state_t { // Stretch effect to be applied to this layer StretchEffect stretchEffect; + // Edge extension effect to be applied to this layer + gui::EdgeExtensionParameters edgeExtensionParameters; + Rect bufferCrop; Rect destinationFrame; @@ -407,6 +414,8 @@ struct layer_state_t { TrustedPresentationThresholds trustedPresentationThresholds; TrustedPresentationListener trustedPresentationListener; + + std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint> bufferReleaseChannel; }; class ComposerState { diff --git a/libs/gui/include/gui/ScreenCaptureResults.h b/libs/gui/include/gui/ScreenCaptureResults.h index 6e17791a29..f176f48fb4 100644 --- a/libs/gui/include/gui/ScreenCaptureResults.h +++ b/libs/gui/include/gui/ScreenCaptureResults.h @@ -36,6 +36,11 @@ public: bool capturedSecureLayers{false}; bool capturedHdrLayers{false}; ui::Dataspace capturedDataspace{ui::Dataspace::V0_SRGB}; + // A gainmap that can be used to "lift" the screenshot into HDR + sp<GraphicBuffer> optionalGainMap; + // HDR/SDR ratio value that fully applies the gainmap. + // Note that we use 1/64 epsilon offsets to eliminate precision issues + float hdrSdrRatio{1.0f}; }; } // namespace android::gui diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h index bdcaaf2866..14a351316d 100644 --- a/libs/gui/include/gui/Surface.h +++ b/libs/gui/include/gui/Surface.h @@ -18,6 +18,7 @@ #define ANDROID_GUI_SURFACE_H #include <android/gui/FrameTimelineInfo.h> +#include <com_android_graphics_libgui_flags.h> #include <gui/BufferQueueDefs.h> #include <gui/HdrMetadata.h> #include <gui/IGraphicBufferProducer.h> @@ -35,6 +36,8 @@ namespace android { +class GraphicBuffer; + namespace gui { class ISurfaceComposer; } // namespace gui @@ -56,8 +59,41 @@ public: virtual bool needsReleaseNotify() = 0; virtual void onBuffersDiscarded(const std::vector<sp<GraphicBuffer>>& buffers) = 0; + virtual void onBufferDetached(int slot) = 0; +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_CONSUMER_ATTACH_CALLBACK) + virtual void onBufferAttached() {} + virtual bool needsAttachNotify() { return false; } +#endif + +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + // Called if this Surface is connected to a remote implementation and it + // dies or becomes unavailable. + virtual void onRemoteDied() {} + + // Clients will overwrite this if they want to receive a notification + // via onRemoteDied. This should return a constant value. + virtual bool needsDeathNotify() { return false; } +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) }; +class StubSurfaceListener : public SurfaceListener { +public: + virtual ~StubSurfaceListener() {} + virtual void onBufferReleased() override {} + virtual bool needsReleaseNotify() { return false; } + virtual void onBuffersDiscarded(const std::vector<sp<GraphicBuffer>>& /*buffers*/) override {} + virtual void onBufferDetached(int /*slot*/) override {} +}; + +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) +// Contains additional data from the queueBuffer operation. +struct SurfaceQueueBufferOutput { + // True if this queueBuffer caused a buffer to be replaced in the queue + // (and therefore not will not be acquired) + bool bufferReplaced = false; +}; +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + /* * An implementation of ANativeWindow that feeds graphics buffers into a * BufferQueue. @@ -154,6 +190,11 @@ public: */ virtual void allocateBuffers(); +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + // See IGraphicBufferProducer::allowAllocation + status_t allowAllocation(bool allowAllocation); +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + /* Sets the generation number on the IGraphicBufferProducer and updates the * generation number on any buffers attached to the Surface after this call. * See IGBP::setGenerationNumber for more information. */ @@ -170,6 +211,14 @@ public: * in <system/window.h>. */ int setScalingMode(int mode); + virtual int setBuffersTimestamp(int64_t timestamp); + virtual int setBuffersDataSpace(ui::Dataspace dataSpace); + virtual int setCrop(Rect const* rect); + virtual int setBuffersTransform(uint32_t transform); + virtual int setBuffersStickyTransform(uint32_t transform); + virtual int setBuffersFormat(PixelFormat format); + virtual int setUsage(uint64_t reqUsage); + // See IGraphicBufferProducer::setDequeueTimeout status_t setDequeueTimeout(nsecs_t timeout); @@ -321,7 +370,12 @@ private: protected: virtual int dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd); virtual int cancelBuffer(ANativeWindowBuffer* buffer, int fenceFd); +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + virtual int queueBuffer(ANativeWindowBuffer* buffer, int fenceFd, + SurfaceQueueBufferOutput* surfaceOutput = nullptr); +#else virtual int queueBuffer(ANativeWindowBuffer* buffer, int fenceFd); +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) virtual int perform(int operation, va_list args); virtual int setSwapInterval(int interval); @@ -330,16 +384,9 @@ protected: virtual int connect(int api); virtual int setBufferCount(int bufferCount); virtual int setBuffersUserDimensions(uint32_t width, uint32_t height); - virtual int setBuffersFormat(PixelFormat format); - virtual int setBuffersTransform(uint32_t transform); - virtual int setBuffersStickyTransform(uint32_t transform); - virtual int setBuffersTimestamp(int64_t timestamp); - virtual int setBuffersDataSpace(ui::Dataspace dataSpace); virtual int setBuffersSmpte2086Metadata(const android_smpte2086_metadata* metadata); virtual int setBuffersCta8613Metadata(const android_cta861_3_metadata* metadata); virtual int setBuffersHdr10PlusMetadata(const size_t size, const uint8_t* metadata); - virtual int setCrop(Rect const* rect); - virtual int setUsage(uint64_t reqUsage); virtual void setSurfaceDamage(android_native_rect_t* rects, size_t numRects); public: @@ -357,22 +404,15 @@ public: virtual int unlockAndPost(); virtual int query(int what, int* value) const; - virtual int connect(int api, const sp<IProducerListener>& listener); - // When reportBufferRemoval is true, clients must call getAndFlushRemovedBuffers to fetch // GraphicBuffers removed from this surface after a dequeueBuffer, detachNextBuffer or // attachBuffer call. This allows clients with their own buffer caches to free up buffers no // longer in use by this surface. - virtual int connect( - int api, const sp<IProducerListener>& listener, - bool reportBufferRemoval); - virtual int detachNextBuffer(sp<GraphicBuffer>* outBuffer, - sp<Fence>* outFence); + virtual int connect(int api, const sp<SurfaceListener>& listener, + bool reportBufferRemoval = false); + virtual int detachNextBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence); virtual int attachBuffer(ANativeWindowBuffer*); - virtual int connect( - int api, bool reportBufferRemoval, - const sp<SurfaceListener>& sListener); virtual void destroy(); // When client connects to Surface with reportBufferRemoval set to true, any buffers removed @@ -387,6 +427,21 @@ public: static status_t attachAndQueueBufferWithDataspace(Surface* surface, sp<GraphicBuffer> buffer, ui::Dataspace dataspace); +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + // Dequeues a buffer and its outFence, which must be signalled before the buffer can be used. + status_t dequeueBuffer(sp<GraphicBuffer>* buffer, sp<Fence>* outFence); + + // Queues a buffer, with an optional fd fence that captures pending work on the buffer. This + // buffer must have been returned by dequeueBuffer or associated with this Surface via an + // attachBuffer operation. + status_t queueBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence>& fd = Fence::NO_FENCE, + SurfaceQueueBufferOutput* output = nullptr); + + // Detaches this buffer, dissociating it from this Surface. This buffer must have been returned + // by queueBuffer or associated with this Surface via an attachBuffer operation. + status_t detachBuffer(const sp<GraphicBuffer>& buffer); +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + // Batch version of dequeueBuffer, cancelBuffer and queueBuffer // Note that these batched operations are not supported when shared buffer mode is being used. struct BatchBuffer { @@ -401,8 +456,13 @@ public: int fenceFd = -1; nsecs_t timestamp = NATIVE_WINDOW_TIMESTAMP_AUTO; }; +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + virtual int queueBuffers(const std::vector<BatchQueuedBuffer>& buffers, + std::vector<SurfaceQueueBufferOutput>* queueBufferOutputs = nullptr); +#else virtual int queueBuffers( const std::vector<BatchQueuedBuffer>& buffers); +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) protected: enum { NUM_BUFFER_SLOTS = BufferQueueDefs::NUM_BUFFER_SLOTS }; @@ -422,12 +482,38 @@ protected: return mSurfaceListener->needsReleaseNotify(); } + virtual void onBufferDetached(int slot) { mSurfaceListener->onBufferDetached(slot); } + virtual void onBuffersDiscarded(const std::vector<int32_t>& slots); +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_CONSUMER_ATTACH_CALLBACK) + virtual void onBufferAttached() { + mSurfaceListener->onBufferAttached(); + } + + virtual bool needsAttachNotify() { + return mSurfaceListener->needsAttachNotify(); + } +#endif private: wp<Surface> mParent; sp<SurfaceListener> mSurfaceListener; }; +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + class ProducerDeathListenerProxy : public IBinder::DeathRecipient { + public: + ProducerDeathListenerProxy(wp<SurfaceListener> surfaceListener); + ProducerDeathListenerProxy(ProducerDeathListenerProxy&) = delete; + + // IBinder::DeathRecipient + virtual void binderDied(const wp<IBinder>&) override; + + private: + wp<SurfaceListener> mSurfaceListener; + }; + friend class ProducerDeathListenerProxy; +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + void querySupportedTimestampsLocked() const; void freeAllBuffers(); @@ -459,6 +545,13 @@ protected: // TODO: rename to mBufferProducer sp<IGraphicBufferProducer> mGraphicBufferProducer; +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + // mSurfaceDeathListener gets registered as mGraphicBufferProducer's + // DeathRecipient when SurfaceListener::needsDeathNotify returns true and + // gets notified when it dies. + sp<ProducerDeathListenerProxy> mSurfaceDeathListener; +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + // mSlots stores the buffers that have been allocated for each buffer slot. // It is initialized to null pointers, and gets filled in with the result of // IGraphicBufferProducer::requestBuffer when the client dequeues a buffer from a diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index 0862e03c44..4f9af16826 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -35,14 +35,17 @@ #include <ui/BlurRegion.h> #include <ui/ConfigStoreTypes.h> #include <ui/DisplayedFrameStats.h> +#include <ui/EdgeExtensionEffect.h> #include <ui/FrameStats.h> #include <ui/GraphicTypes.h> #include <ui/PixelFormat.h> #include <ui/Rotation.h> #include <ui/StaticDisplayInfo.h> +#include <android/gui/BnJankListener.h> #include <android/gui/ISurfaceComposerClient.h> +#include <gui/BufferReleaseChannel.h> #include <gui/CpuConsumer.h> #include <gui/ISurfaceComposer.h> #include <gui/ITransactionCompletedListener.h> @@ -337,6 +340,8 @@ public: static std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport> getDisplayDecorationSupport(const sp<IBinder>& displayToken); + static bool flagEdgeExtensionEffectUseShader(); + // ------------------------------------------------------------------------ // surface creation / destruction @@ -447,7 +452,6 @@ public: uint64_t mId; - uint32_t mTransactionNestCount = 0; bool mAnimation = false; bool mEarlyWakeupStart = false; bool mEarlyWakeupEnd = false; @@ -743,11 +747,26 @@ public: Transaction& setStretchEffect(const sp<SurfaceControl>& sc, const StretchEffect& stretchEffect); + /** + * Provides the edge extension effect configured on a container that the + * surface is rendered within. + * @param sc target surface the edge extension should be applied to + * @param effect the corresponding EdgeExtensionParameters to be applied + * to the surface. + * @return The transaction being constructed + */ + Transaction& setEdgeExtensionEffect(const sp<SurfaceControl>& sc, + const gui::EdgeExtensionParameters& effect); + Transaction& setBufferCrop(const sp<SurfaceControl>& sc, const Rect& bufferCrop); Transaction& setDestinationFrame(const sp<SurfaceControl>& sc, const Rect& destinationFrame); Transaction& setDropInputMode(const sp<SurfaceControl>& sc, gui::DropInputMode mode); + Transaction& setBufferReleaseChannel( + const sp<SurfaceControl>& sc, + const std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint>& channel); + status_t setDisplaySurface(const sp<IBinder>& token, const sp<IGraphicBufferProducer>& bufferProducer); @@ -864,12 +883,82 @@ public: // --------------------------------------------------------------------------- -class JankDataListener : public VirtualLightRefBase { +class JankDataListener; + +// Acts as a representative listener to the composer for a single layer and +// forwards any received jank data to multiple listeners. Will remove itself +// from the composer only once the last listener is removed. +class JankDataListenerFanOut : public gui::BnJankListener { +public: + JankDataListenerFanOut(int32_t layerId) : mLayerId(layerId) {} + + binder::Status onJankData(const std::vector<gui::JankData>& jankData) override; + + static status_t addListener(sp<SurfaceControl> sc, sp<JankDataListener> listener); + static status_t removeListener(sp<JankDataListener> listener); + +private: + std::vector<sp<JankDataListener>> getActiveListeners(); + bool removeListeners(const std::vector<wp<JankDataListener>>& listeners); + int64_t updateAndGetRemovalVSync(); + + struct WpJDLHash { + std::size_t operator()(const wp<JankDataListener>& listener) const { + return std::hash<JankDataListener*>{}(listener.unsafe_get()); + } + }; + + std::mutex mMutex; + std::unordered_set<wp<JankDataListener>, WpJDLHash> mListeners GUARDED_BY(mMutex); + int32_t mLayerId; + int64_t mRemoveAfter = -1; + + static std::mutex sFanoutInstanceMutex; + static std::unordered_map<int32_t, sp<JankDataListenerFanOut>> sFanoutInstances; +}; + +// Base class for client listeners interested in jank classification data from +// the composer. Subclasses should override onJankDataAvailable and call the add +// and removal methods to receive jank data. +class JankDataListener : public virtual RefBase { public: - virtual ~JankDataListener() = 0; - virtual void onJankDataAvailable(const std::vector<JankData>& jankData) = 0; + JankDataListener() {} + virtual ~JankDataListener(); + + virtual bool onJankDataAvailable(const std::vector<gui::JankData>& jankData) = 0; + + status_t addListener(sp<SurfaceControl> sc) { + if (mLayerId != -1) { + removeListener(0); + mLayerId = -1; + } + + int32_t layerId = sc->getLayerId(); + status_t status = + JankDataListenerFanOut::addListener(std::move(sc), + sp<JankDataListener>::fromExisting(this)); + if (status == OK) { + mLayerId = layerId; + } + return status; + } + + status_t removeListener(int64_t afterVsync) { + mRemoveAfter = std::max(static_cast<int64_t>(0), afterVsync); + return JankDataListenerFanOut::removeListener(sp<JankDataListener>::fromExisting(this)); + } + + status_t flushJankData(); + + friend class JankDataListenerFanOut; + +private: + int32_t mLayerId = -1; + int64_t mRemoveAfter = -1; }; +// --------------------------------------------------------------------------- + class TransactionCompletedListener : public BnTransactionCompletedListener { public: TransactionCompletedListener(); @@ -904,7 +993,6 @@ protected: std::unordered_map<CallbackId, CallbackTranslation, CallbackIdHash> mCallbacks GUARDED_BY(mMutex); - std::multimap<int32_t, sp<JankDataListener>> mJankListeners GUARDED_BY(mMutex); std::unordered_map<ReleaseCallbackId, ReleaseBufferCallback, ReleaseBufferCallbackIdHash> mReleaseBufferCallbacks GUARDED_BY(mMutex); @@ -927,14 +1015,10 @@ public: const std::unordered_set<sp<SurfaceControl>, SurfaceComposerClient::SCHash>& surfaceControls, CallbackId::Type callbackType); - CallbackId addCallbackFunctionLocked( - const TransactionCompletedCallback& callbackFunction, - const std::unordered_set<sp<SurfaceControl>, SurfaceComposerClient::SCHash>& - surfaceControls, - CallbackId::Type callbackType) REQUIRES(mMutex); - void addSurfaceControlToCallbacks(SurfaceComposerClient::CallbackInfo& callbackInfo, - const sp<SurfaceControl>& surfaceControl); + void addSurfaceControlToCallbacks( + const sp<SurfaceControl>& surfaceControl, + const std::unordered_set<CallbackId, CallbackIdHash>& callbackIds); void addQueueStallListener(std::function<void(const std::string&)> stallListener, void* id); void removeQueueStallListener(void *id); @@ -943,18 +1027,6 @@ public: TrustedPresentationCallback tpc, int id, void* context); void clearTrustedPresentationCallback(int id); - /* - * Adds a jank listener to be informed about SurfaceFlinger's jank classification for a specific - * surface. Jank classifications arrive as part of the transaction callbacks about previous - * frames submitted to this Surface. - */ - void addJankListener(const sp<JankDataListener>& listener, sp<SurfaceControl> surfaceControl); - - /** - * Removes a jank listener previously added to addJankCallback. - */ - void removeJankListener(const sp<JankDataListener>& listener); - void addSurfaceStatsListener(void* context, void* cookie, sp<SurfaceControl> surfaceControl, SurfaceStatsCallback listener); void removeSurfaceStatsListener(void* context, void* cookie); diff --git a/libs/gui/include/gui/view/Surface.h b/libs/gui/include/gui/view/Surface.h index b7aba2b9dc..7ddac8139a 100644 --- a/libs/gui/include/gui/view/Surface.h +++ b/libs/gui/include/gui/view/Surface.h @@ -59,8 +59,9 @@ class Surface : public Parcelable { // of the full parceling to happen on its native side. status_t readFromParcel(const Parcel* parcel, bool nameAlreadyRead); - private: + std::string toString() const; +private: static String16 readMaybeEmptyString16(const Parcel* parcel); }; diff --git a/libs/gui/libgui_flags.aconfig b/libs/gui/libgui_flags.aconfig index 87cef087db..d3f2899ba3 100644 --- a/libs/gui/libgui_flags.aconfig +++ b/libs/gui/libgui_flags.aconfig @@ -43,3 +43,75 @@ flag { purpose: PURPOSE_BUGFIX } } # trace_frame_rate_override + +flag { + name: "wb_consumer_base_owns_bq" + namespace: "core_graphics" + description: "ConsumerBase-based classes now own their own bufferqueue" + bug: "340933754" + is_fixed_read_only: true +} # wb_consumer_base_owns_bq + +flag { + name: "wb_platform_api_improvements" + namespace: "core_graphics" + description: "Simple improvements to Surface and ConsumerBase classes" + bug: "340933794" + is_fixed_read_only: true +} # wb_platform_api_improvements + +flag { + name: "wb_stream_splitter" + namespace: "core_graphics" + description: "Removes IGBP/IGBCs from Camera3StreamSplitter as part of BufferQueue refactors" + bug: "340933206" + is_fixed_read_only: true +} # wb_stream_splitter + +flag { + name: "edge_extension_shader" + namespace: "windowing_frontend" + description: "Enable edge extension via shader" + bug: "322036393" + is_fixed_read_only: true +} # edge_extension_shader + +flag { + name: "buffer_release_channel" + namespace: "window_surfaces" + description: "Enable BufferReleaseChannel to optimize buffer releases" + bug: "294133380" + is_fixed_read_only: true +} # buffer_release_channel + +flag { + name: "wb_ring_buffer" + namespace: "core_graphics" + description: "Remove slot dependency in the Ring Buffer Consumer." + bug: "342197847" + is_fixed_read_only: true +} # wb_ring_buffer + +flag { + name: "wb_camera3_and_processors" + namespace: "core_graphics" + description: "Remove usage of IGBPs in the *Processor and Camera3*" + bug: "342199002" + is_fixed_read_only: true +} # wb_camera3_and_processors + +flag { + name: "wb_libcameraservice" + namespace: "core_graphics" + description: "Remove usage of IGBPs in the libcameraservice." + bug: "342197849" + is_fixed_read_only: true +} # wb_libcameraservice + +flag { + name: "bq_producer_throttles_only_async_mode" + namespace: "core_graphics" + description: "BufferQueueProducer only CPU throttle on queueBuffer() in async mode." + bug: "359252619" + is_fixed_read_only: true +} # bq_producer_throttles_only_async_mode diff --git a/libs/gui/rust/aidl_types/src/lib.rs b/libs/gui/rust/aidl_types/src/lib.rs index fead018bbf..2351df0318 100644 --- a/libs/gui/rust/aidl_types/src/lib.rs +++ b/libs/gui/rust/aidl_types/src/lib.rs @@ -42,10 +42,7 @@ macro_rules! stub_unstructured_parcelable { } stub_unstructured_parcelable!(BitTube); -stub_unstructured_parcelable!(CaptureArgs); -stub_unstructured_parcelable!(DisplayCaptureArgs); stub_unstructured_parcelable!(DisplayInfo); -stub_unstructured_parcelable!(LayerCaptureArgs); stub_unstructured_parcelable!(LayerDebugInfo); stub_unstructured_parcelable!(LayerMetadata); stub_unstructured_parcelable!(ParcelableVsyncEventData); diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp index ea8acbbb72..f07747f32f 100644 --- a/libs/gui/tests/Android.bp +++ b/libs/gui/tests/Android.bp @@ -12,6 +12,34 @@ package { default_applicable_licenses: ["frameworks_native_license"], } +aidl_interface { + name: "libgui_test_server_aidl", + unstable: true, + srcs: ["testserver/aidl/**/*.aidl"], + local_include_dir: "testserver/aidl", + include_dirs: [ + "frameworks/native/aidl/gui", + ], + backend: { + cpp: { + enabled: true, + additional_shared_libraries: [ + "libgui", + "libui", + ], + }, + java: { + enabled: false, + }, + ndk: { + enabled: false, + }, + rust: { + enabled: false, + }, + }, +} + cc_test { name: "libgui_test", test_suites: ["device-tests"], @@ -25,34 +53,41 @@ cc_test { "-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", ], srcs: [ - "LibGuiMain.cpp", // Custom gtest entrypoint "BLASTBufferQueue_test.cpp", "BufferItemConsumer_test.cpp", "BufferQueue_test.cpp", + "BufferReleaseChannel_test.cpp", "Choreographer_test.cpp", "CompositorTiming_test.cpp", "CpuConsumer_test.cpp", - "EndToEndNativeInputTest.cpp", - "FrameRateUtilsTest.cpp", - "DisplayInfo_test.cpp", "DisplayedContentSampling_test.cpp", + "DisplayInfo_test.cpp", + "EndToEndNativeInputTest.cpp", "FillBuffer.cpp", + "FrameRateUtilsTest.cpp", "GLTest.cpp", "IGraphicBufferProducer_test.cpp", + "LibGuiMain.cpp", // Custom gtest entrypoint "Malicious.cpp", "MultiTextureConsumer_test.cpp", "RegionSampling_test.cpp", "StreamSplitter_test.cpp", + "Surface_test.cpp", "SurfaceTextureClient_test.cpp", "SurfaceTextureFBO_test.cpp", + "SurfaceTextureGL_test.cpp", "SurfaceTextureGLThreadToGL_test.cpp", "SurfaceTextureGLToGL_test.cpp", - "SurfaceTextureGL_test.cpp", "SurfaceTextureMultiContextGL_test.cpp", - "Surface_test.cpp", + "TestServer_test.cpp", + "testserver/TestServer.cpp", + "testserver/TestServerClient.cpp", + "testserver/TestServerHost.cpp", "TextureRenderer.cpp", "VsyncEventData_test.cpp", "WindowInfo_test.cpp", @@ -63,10 +98,17 @@ cc_test { "android.hardware.configstore-utils", "libSurfaceFlingerProp", "libGLESv1_CM", + "libgui_test_server_aidl-cpp", "libinput", "libnativedisplay", ], + // This needs to get copied over for the test since it's not part of the + // platform. + data_libs: [ + "libgui_test_server_aidl-cpp", + ], + static_libs: [ "libgmock", ], diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp index 946ff058cf..53f4a36c42 100644 --- a/libs/gui/tests/BLASTBufferQueue_test.cpp +++ b/libs/gui/tests/BLASTBufferQueue_test.cpp @@ -20,7 +20,7 @@ #include <android-base/thread_annotations.h> #include <android/hardware/graphics/common/1.2/types.h> -#include <gui/AidlStatusUtil.h> +#include <gui/AidlUtil.h> #include <gui/BufferQueueCore.h> #include <gui/BufferQueueProducer.h> #include <gui/FrameTimestamps.h> @@ -186,6 +186,10 @@ public: mBlastBufferQueueAdapter->mergeWithNextTransaction(merge, frameNumber); } + void setApplyToken(sp<IBinder> applyToken) { + mBlastBufferQueueAdapter->setApplyToken(std::move(applyToken)); + } + private: sp<TestBLASTBufferQueue> mBlastBufferQueueAdapter; }; @@ -229,7 +233,8 @@ protected: ISurfaceComposerClient::eFXSurfaceBufferState, /*parent*/ mRootSurfaceControl->getHandle()); - mCaptureArgs.sourceCrop = Rect(ui::Size(mDisplayWidth, mDisplayHeight)); + mCaptureArgs.captureArgs.sourceCrop = + gui::aidl_utils::toARect(mDisplayWidth, mDisplayHeight); mCaptureArgs.layerHandle = mRootSurfaceControl->getHandle(); } @@ -510,6 +515,69 @@ TEST_F(BLASTBufferQueueTest, TripleBuffering) { adapter.waitForCallbacks(); } +class WaitForCommittedCallback { +public: + WaitForCommittedCallback() = default; + ~WaitForCommittedCallback() = default; + + void wait() { + std::unique_lock lock(mMutex); + cv.wait(lock, [this] { return mCallbackReceived; }); + } + + void notify() { + std::unique_lock lock(mMutex); + mCallbackReceived = true; + cv.notify_one(); + mCallbackReceivedTimeStamp = std::chrono::system_clock::now(); + } + auto getCallback() { + return [this](void* /* unused context */, nsecs_t /* latchTime */, + const sp<Fence>& /* presentFence */, + const std::vector<SurfaceControlStats>& /* stats */) { notify(); }; + } + std::chrono::time_point<std::chrono::system_clock> mCallbackReceivedTimeStamp; + +private: + std::mutex mMutex; + std::condition_variable cv; + bool mCallbackReceived = false; +}; + +TEST_F(BLASTBufferQueueTest, setApplyToken) { + sp<IBinder> applyToken = sp<BBinder>::make(); + WaitForCommittedCallback firstTransaction; + WaitForCommittedCallback secondTransaction; + { + BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight); + adapter.setApplyToken(applyToken); + sp<IGraphicBufferProducer> igbProducer; + setUpProducer(adapter, igbProducer); + + Transaction t; + t.addTransactionCommittedCallback(firstTransaction.getCallback(), nullptr); + adapter.mergeWithNextTransaction(&t, 1); + queueBuffer(igbProducer, 127, 127, 127, + /*presentTimeDelay*/ std::chrono::nanoseconds(500ms).count()); + } + { + BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight); + adapter.setApplyToken(applyToken); + sp<IGraphicBufferProducer> igbProducer; + setUpProducer(adapter, igbProducer); + + Transaction t; + t.addTransactionCommittedCallback(secondTransaction.getCallback(), nullptr); + adapter.mergeWithNextTransaction(&t, 1); + queueBuffer(igbProducer, 127, 127, 127, /*presentTimeDelay*/ 0); + } + + firstTransaction.wait(); + secondTransaction.wait(); + EXPECT_GT(secondTransaction.mCallbackReceivedTimeStamp, + firstTransaction.mCallbackReceivedTimeStamp); +} + TEST_F(BLASTBufferQueueTest, SetCrop_Item) { uint8_t r = 255; uint8_t g = 0; @@ -1269,6 +1337,20 @@ public: } }; +class TestSurfaceListener : public SurfaceListener { +public: + sp<IGraphicBufferProducer> mIgbp; + TestSurfaceListener(const sp<IGraphicBufferProducer>& igbp) : mIgbp(igbp) {} + void onBufferReleased() override { + sp<GraphicBuffer> buffer; + sp<Fence> fence; + mIgbp->detachNextBuffer(&buffer, &fence); + } + bool needsReleaseNotify() override { return true; } + void onBuffersDiscarded(const std::vector<sp<GraphicBuffer>>& /*buffers*/) override {} + void onBufferDetached(int /*slot*/) {} +}; + TEST_F(BLASTBufferQueueTest, CustomProducerListener) { BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight); sp<IGraphicBufferProducer> igbProducer = adapter.getIGraphicBufferProducer(); @@ -1327,7 +1409,7 @@ TEST_F(BLASTBufferQueueTest, TransformHint) { ASSERT_EQ(ui::Transform::ROT_0, static_cast<ui::Transform::RotationFlags>(transformHint)); ASSERT_EQ(NO_ERROR, - surface->connect(NATIVE_WINDOW_API_CPU, new TestProducerListener(igbProducer))); + surface->connect(NATIVE_WINDOW_API_CPU, new TestSurfaceListener(igbProducer))); // After connecting to the surface, we should get the correct hint. surface->query(NATIVE_WINDOW_TRANSFORM_HINT, &transformHint); diff --git a/libs/gui/tests/BufferItemConsumer_test.cpp b/libs/gui/tests/BufferItemConsumer_test.cpp index 656453411d..3b6a66efe9 100644 --- a/libs/gui/tests/BufferItemConsumer_test.cpp +++ b/libs/gui/tests/BufferItemConsumer_test.cpp @@ -17,10 +17,12 @@ #define LOG_TAG "BufferItemConsumer_test" //#define LOG_NDEBUG 0 +#include <gmock/gmock.h> #include <gtest/gtest.h> #include <gui/BufferItemConsumer.h> #include <gui/IProducerListener.h> #include <gui/Surface.h> +#include <ui/GraphicBuffer.h> namespace android { @@ -43,15 +45,26 @@ class BufferItemConsumerTest : public ::testing::Test { BufferItemConsumerTest* mTest; }; + struct TrackingProducerListener : public BnProducerListener { + TrackingProducerListener(BufferItemConsumerTest* test) : mTest(test) {} + + virtual void onBufferReleased() override {} + virtual bool needsReleaseNotify() override { return true; } + virtual void onBuffersDiscarded(const std::vector<int32_t>&) override {} + virtual void onBufferDetached(int slot) override { mTest->HandleBufferDetached(slot); } + + BufferItemConsumerTest* mTest; + }; + void SetUp() override { - BufferQueue::createBufferQueue(&mProducer, &mConsumer); - mBIC = new BufferItemConsumer(mConsumer, kUsage, kMaxLockedBuffers, true); + mBIC = new BufferItemConsumer(kUsage, kMaxLockedBuffers, true); String8 name("BufferItemConsumer_Under_Test"); mBIC->setName(name); mBFL = new BufferFreedListener(this); mBIC->setBufferFreedListener(mBFL); - sp<IProducerListener> producerListener = new StubProducerListener(); + sp<IProducerListener> producerListener = new TrackingProducerListener(this); + mProducer = mBIC->getSurface()->getIGraphicBufferProducer(); IGraphicBufferProducer::QueueBufferOutput bufferOutput; ASSERT_EQ(NO_ERROR, mProducer->connect(producerListener, NATIVE_WINDOW_API_CPU, @@ -71,6 +84,13 @@ class BufferItemConsumerTest : public ::testing::Test { ALOGD("HandleBufferFreed, mFreedBufferCount=%d", mFreedBufferCount); } + void HandleBufferDetached(int slot) { + std::lock_guard<std::mutex> lock(mMutex); + mDetachedBufferSlots.push_back(slot); + ALOGD("HandleBufferDetached, slot=%d mDetachedBufferSlots-count=%zu", slot, + mDetachedBufferSlots.size()); + } + void DequeueBuffer(int* outSlot) { ASSERT_NE(outSlot, nullptr); @@ -120,6 +140,7 @@ class BufferItemConsumerTest : public ::testing::Test { std::mutex mMutex; int mFreedBufferCount{0}; + std::vector<int> mDetachedBufferSlots = {}; sp<BufferItemConsumer> mBIC; sp<BufferFreedListener> mBFL; @@ -203,4 +224,19 @@ TEST_F(BufferItemConsumerTest, TriggerBufferFreed_DeleteBufferItemConsumer) { ASSERT_EQ(1, GetFreedBufferCount()); } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) +// Test that delete BufferItemConsumer triggers onBufferFreed. +TEST_F(BufferItemConsumerTest, DetachBufferWithBuffer) { + int slot; + // Let buffer go through the cycle at least once. + DequeueBuffer(&slot); + QueueBuffer(slot); + AcquireBuffer(&slot); + + sp<GraphicBuffer> buffer = mBuffers[slot]; + EXPECT_EQ(OK, mBIC->detachBuffer(buffer)); + EXPECT_THAT(mDetachedBufferSlots, testing::ElementsAre(slot)); +} +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + } // namespace android diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp index 590e2c87c9..2e6ffcb57f 100644 --- a/libs/gui/tests/BufferQueue_test.cpp +++ b/libs/gui/tests/BufferQueue_test.cpp @@ -1430,19 +1430,15 @@ TEST_F(BufferQueueTest, TestBqSetFrameRateFlagBuildTimeIsSet) { } struct BufferItemConsumerSetFrameRateListener : public BufferItemConsumer { - BufferItemConsumerSetFrameRateListener(const sp<IGraphicBufferConsumer>& consumer) - : BufferItemConsumer(consumer, GRALLOC_USAGE_SW_READ_OFTEN, 1) {} + BufferItemConsumerSetFrameRateListener() : BufferItemConsumer(GRALLOC_USAGE_SW_READ_OFTEN, 1) {} MOCK_METHOD(void, onSetFrameRate, (float, int8_t, int8_t), (override)); }; TEST_F(BufferQueueTest, TestSetFrameRate) { - sp<IGraphicBufferProducer> producer; - sp<IGraphicBufferConsumer> consumer; - BufferQueue::createBufferQueue(&producer, &consumer); - sp<BufferItemConsumerSetFrameRateListener> bufferConsumer = - sp<BufferItemConsumerSetFrameRateListener>::make(consumer); + sp<BufferItemConsumerSetFrameRateListener>::make(); + sp<IGraphicBufferProducer> producer = bufferConsumer->getSurface()->getIGraphicBufferProducer(); EXPECT_CALL(*bufferConsumer, onSetFrameRate(12.34f, 1, 0)).Times(1); producer->setFrameRate(12.34f, 1, 0); @@ -1493,14 +1489,10 @@ struct OneshotOnDequeuedListener final : public BufferItemConsumer::FrameAvailab // See b/270004534 TEST(BufferQueueThreading, TestProducerDequeueConsumerDestroy) { - sp<IGraphicBufferProducer> producer; - sp<IGraphicBufferConsumer> consumer; - BufferQueue::createBufferQueue(&producer, &consumer); - sp<BufferItemConsumer> bufferConsumer = - sp<BufferItemConsumer>::make(consumer, GRALLOC_USAGE_SW_READ_OFTEN, 2); + sp<BufferItemConsumer>::make(GRALLOC_USAGE_SW_READ_OFTEN, 2); ASSERT_NE(nullptr, bufferConsumer.get()); - sp<Surface> surface = sp<Surface>::make(producer); + sp<Surface> surface = bufferConsumer->getSurface(); native_window_set_buffers_format(surface.get(), PIXEL_FORMAT_RGBA_8888); native_window_set_buffers_dimensions(surface.get(), 100, 100); @@ -1531,14 +1523,10 @@ TEST(BufferQueueThreading, TestProducerDequeueConsumerDestroy) { } TEST_F(BufferQueueTest, TestAdditionalOptions) { - sp<IGraphicBufferProducer> producer; - sp<IGraphicBufferConsumer> consumer; - BufferQueue::createBufferQueue(&producer, &consumer); - sp<BufferItemConsumer> bufferConsumer = - sp<BufferItemConsumer>::make(consumer, GRALLOC_USAGE_SW_READ_OFTEN, 2); + sp<BufferItemConsumer>::make(GRALLOC_USAGE_SW_READ_OFTEN, 2); ASSERT_NE(nullptr, bufferConsumer.get()); - sp<Surface> surface = sp<Surface>::make(producer); + sp<Surface> surface = bufferConsumer->getSurface(); native_window_set_buffers_format(surface.get(), PIXEL_FORMAT_RGBA_8888); native_window_set_buffers_dimensions(surface.get(), 100, 100); diff --git a/libs/gui/tests/BufferReleaseChannel_test.cpp b/libs/gui/tests/BufferReleaseChannel_test.cpp new file mode 100644 index 0000000000..11d122b525 --- /dev/null +++ b/libs/gui/tests/BufferReleaseChannel_test.cpp @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <string> +#include <vector> + +#include <gtest/gtest.h> +#include <gui/BufferReleaseChannel.h> + +using namespace std::string_literals; +using android::gui::BufferReleaseChannel; + +namespace android { + +namespace { + +// Helper function to check if two file descriptors point to the same file. +bool is_same_file(int fd1, int fd2) { + struct stat stat1; + if (fstat(fd1, &stat1) != 0) { + return false; + } + struct stat stat2; + if (fstat(fd2, &stat2) != 0) { + return false; + } + return (stat1.st_dev == stat2.st_dev) && (stat1.st_ino == stat2.st_ino); +} + +} // namespace + +TEST(BufferReleaseChannelTest, MessageFlattenable) { + ReleaseCallbackId releaseCallbackId{1, 2}; + sp<Fence> releaseFence = sp<Fence>::make(memfd_create("fake-fence-fd", 0)); + uint32_t maxAcquiredBufferCount = 5; + + std::vector<uint8_t> dataBuffer; + std::vector<int> fdBuffer; + + // Verify that we can flatten a message + { + BufferReleaseChannel::Message message{releaseCallbackId, releaseFence, + maxAcquiredBufferCount}; + + dataBuffer.resize(message.getFlattenedSize()); + void* dataPtr = dataBuffer.data(); + size_t dataSize = dataBuffer.size(); + + fdBuffer.resize(message.getFdCount()); + int* fdPtr = fdBuffer.data(); + size_t fdSize = fdBuffer.size(); + + ASSERT_EQ(OK, message.flatten(dataPtr, dataSize, fdPtr, fdSize)); + + // Fence's unique_fd uses fdsan to check ownership of the file descriptor. Normally the file + // descriptor is passed through the Unix socket and duplicated (and sent to another process) + // so there's no problem with duplicate file descriptor ownership. For this unit test, we + // need to set up a duplicate file descriptor to avoid crashing due to duplicate ownership. + ASSERT_EQ(releaseFence->get(), fdBuffer[0]); + fdBuffer[0] = message.releaseFence->dup(); + } + + // Verify that we can unflatten a message + { + BufferReleaseChannel::Message message; + + const void* dataPtr = dataBuffer.data(); + size_t dataSize = dataBuffer.size(); + + const int* fdPtr = fdBuffer.data(); + size_t fdSize = fdBuffer.size(); + + ASSERT_EQ(OK, message.unflatten(dataPtr, dataSize, fdPtr, fdSize)); + ASSERT_EQ(releaseCallbackId, message.releaseCallbackId); + ASSERT_TRUE(is_same_file(releaseFence->get(), message.releaseFence->get())); + ASSERT_EQ(maxAcquiredBufferCount, message.maxAcquiredBufferCount); + } +} + +// Verify that the BufferReleaseChannel consume returns WOULD_BLOCK when there's no message +// available. +TEST(BufferReleaseChannelTest, ConsumerEndpointIsNonBlocking) { + std::unique_ptr<BufferReleaseChannel::ConsumerEndpoint> consumer; + std::shared_ptr<BufferReleaseChannel::ProducerEndpoint> producer; + ASSERT_EQ(OK, BufferReleaseChannel::open("test-channel"s, consumer, producer)); + + ReleaseCallbackId releaseCallbackId; + sp<Fence> releaseFence; + uint32_t maxAcquiredBufferCount; + ASSERT_EQ(WOULD_BLOCK, + consumer->readReleaseFence(releaseCallbackId, releaseFence, maxAcquiredBufferCount)); +} + +// Verify that we can write a message to the BufferReleaseChannel producer and read that message +// using the BufferReleaseChannel consumer. +TEST(BufferReleaseChannelTest, ProduceAndConsume) { + std::unique_ptr<BufferReleaseChannel::ConsumerEndpoint> consumer; + std::shared_ptr<BufferReleaseChannel::ProducerEndpoint> producer; + ASSERT_EQ(OK, BufferReleaseChannel::open("test-channel"s, consumer, producer)); + + sp<Fence> fence = sp<Fence>::make(memfd_create("fake-fence-fd", 0)); + + for (uint64_t i = 0; i < 64; i++) { + ReleaseCallbackId producerId{i, i + 1}; + uint32_t maxAcquiredBufferCount = i + 2; + ASSERT_EQ(OK, producer->writeReleaseFence(producerId, fence, maxAcquiredBufferCount)); + } + + for (uint64_t i = 0; i < 64; i++) { + ReleaseCallbackId expectedId{i, i + 1}; + uint32_t expectedMaxAcquiredBufferCount = i + 2; + + ReleaseCallbackId consumerId; + sp<Fence> consumerFence; + uint32_t maxAcquiredBufferCount; + ASSERT_EQ(OK, + consumer->readReleaseFence(consumerId, consumerFence, maxAcquiredBufferCount)); + + ASSERT_EQ(expectedId, consumerId); + ASSERT_TRUE(is_same_file(fence->get(), consumerFence->get())); + ASSERT_EQ(expectedMaxAcquiredBufferCount, maxAcquiredBufferCount); + } +} + +} // namespace android
\ No newline at end of file diff --git a/libs/gui/tests/Choreographer_test.cpp b/libs/gui/tests/Choreographer_test.cpp index 2ac2550f07..8db48d2eb0 100644 --- a/libs/gui/tests/Choreographer_test.cpp +++ b/libs/gui/tests/Choreographer_test.cpp @@ -52,25 +52,23 @@ TEST_F(ChoreographerTest, InputCallbackBeforeAnimation) { sp<Looper> looper = Looper::prepare(0); Choreographer* choreographer = Choreographer::getForThread(); VsyncCallback animationCb; - VsyncCallback inputCb; - choreographer->postFrameCallbackDelayed(nullptr, nullptr, vsyncCallback, &animationCb, 0, CALLBACK_ANIMATION); + VsyncCallback inputCb; choreographer->postFrameCallbackDelayed(nullptr, nullptr, vsyncCallback, &inputCb, 0, CALLBACK_INPUT); - - nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); - nsecs_t currTime; - int pollResult; + auto startTime = std::chrono::system_clock::now(); do { - pollResult = looper->pollOnce(16); - currTime = systemTime(SYSTEM_TIME_MONOTONIC); - } while (!(inputCb.callbackReceived() && animationCb.callbackReceived()) && - (pollResult != Looper::POLL_TIMEOUT && pollResult != Looper::POLL_ERROR) && - (currTime - startTime < 3000)); - - ASSERT_TRUE(inputCb.callbackReceived()) << "did not receive input callback"; - ASSERT_TRUE(animationCb.callbackReceived()) << "did not receive animation callback"; + static constexpr int32_t timeoutMs = 1000; + int pollResult = looper->pollOnce(timeoutMs); + ASSERT_TRUE((pollResult != Looper::POLL_TIMEOUT) && (pollResult != Looper::POLL_ERROR)) + << "Failed to poll looper. Poll result = " << pollResult; + auto elapsedMs = std::chrono::duration_cast<std::chrono::milliseconds>( + std::chrono::system_clock::now() - startTime); + ASSERT_LE(elapsedMs.count(), timeoutMs) + << "Timed out waiting for callbacks. inputCb=" << inputCb.callbackReceived() + << " animationCb=" << animationCb.callbackReceived(); + } while (!(inputCb.callbackReceived() && animationCb.callbackReceived())); ASSERT_EQ(inputCb.frameTime, animationCb.frameTime) << android::base::StringPrintf("input and animation callback frame times don't match. " diff --git a/libs/gui/tests/CpuConsumer_test.cpp b/libs/gui/tests/CpuConsumer_test.cpp index d80bd9c62a..f4239cb69e 100644 --- a/libs/gui/tests/CpuConsumer_test.cpp +++ b/libs/gui/tests/CpuConsumer_test.cpp @@ -66,13 +66,10 @@ protected: test_info->name(), params.width, params.height, params.maxLockedBuffers, params.format); - sp<IGraphicBufferProducer> producer; - sp<IGraphicBufferConsumer> consumer; - BufferQueue::createBufferQueue(&producer, &consumer); - mCC = new CpuConsumer(consumer, params.maxLockedBuffers); + mCC = new CpuConsumer(params.maxLockedBuffers); String8 name("CpuConsumer_Under_Test"); mCC->setName(name); - mSTC = new Surface(producer); + mSTC = mCC->getSurface(); mANW = mSTC; } diff --git a/libs/gui/tests/LibGuiMain.cpp b/libs/gui/tests/LibGuiMain.cpp index 10f7207588..7c7c2cc30f 100644 --- a/libs/gui/tests/LibGuiMain.cpp +++ b/libs/gui/tests/LibGuiMain.cpp @@ -14,8 +14,15 @@ * limitations under the License. */ -#include "gtest/gtest.h" -#include "log/log.h" +#include <android-base/unique_fd.h> +#include <gtest/gtest.h> +#include <log/log.h> + +#include "testserver/TestServer.h" +#include "testserver/TestServerClient.h" +#include "testserver/TestServerHost.h" + +using namespace android; namespace { @@ -32,7 +39,34 @@ class TestCaseLogger : public ::testing::EmptyTestEventListener { } // namespace int main(int argc, char** argv) { + // There are three modes that we can run in to support the libgui TestServer: + // + // - libgui_test : normal mode, runs tests and fork/execs the testserver host process + // - libgui_test --test-server-host $recvPipeFd $sendPipeFd : TestServerHost mode, listens on + // $recvPipeFd for commands and sends responses over $sendPipeFd + // - libgui_test --test-server $name : TestServer mode, starts a ITestService binder service + // under $name + for (int i = 1; i < argc; i++) { + std::string arg = argv[i]; + if (arg == "--test-server-host") { + LOG_ALWAYS_FATAL_IF(argc < (i + 2), "--test-server-host requires two pipe fds"); + // Note that the send/recv are from our perspective. + base::unique_fd recvPipeFd = base::unique_fd(atoi(argv[i + 1])); + base::unique_fd sendPipeFd = base::unique_fd(atoi(argv[i + 2])); + return TestServerHostMain(argv[0], std::move(sendPipeFd), std::move(recvPipeFd)); + } + if (arg == "--test-server") { + LOG_ALWAYS_FATAL_IF(argc < (i + 1), "--test-server requires a name"); + return TestServerMain(argv[i + 1]); + } + } testing::InitGoogleTest(&argc, argv); testing::UnitTest::GetInstance()->listeners().Append(new TestCaseLogger()); + + // This has to be run *before* any test initialization, because it fork/execs a TestServerHost, + // which will later create new binder service. You can't do that in a forked thread after you've + // initialized any binder stuff, which some tests do. + TestServerClient::InitializeOrDie(argv[0]); + return RUN_ALL_TESTS(); }
\ No newline at end of file diff --git a/libs/gui/tests/MultiTextureConsumer_test.cpp b/libs/gui/tests/MultiTextureConsumer_test.cpp index 7d3d4aa412..2428bb3110 100644 --- a/libs/gui/tests/MultiTextureConsumer_test.cpp +++ b/libs/gui/tests/MultiTextureConsumer_test.cpp @@ -34,12 +34,8 @@ protected: virtual void SetUp() { GLTest::SetUp(); - sp<IGraphicBufferProducer> producer; - sp<IGraphicBufferConsumer> consumer; - BufferQueue::createBufferQueue(&producer, &consumer); - mGlConsumer = new GLConsumer(consumer, TEX_ID, - GLConsumer::TEXTURE_EXTERNAL, true, false); - mSurface = new Surface(producer); + mGlConsumer = new GLConsumer(TEX_ID, GLConsumer::TEXTURE_EXTERNAL, true, false); + mSurface = mGlConsumer->getSurface(); mANW = mSurface.get(); } diff --git a/libs/gui/tests/RegionSampling_test.cpp b/libs/gui/tests/RegionSampling_test.cpp index 223e4b6cbd..a0d8c53385 100644 --- a/libs/gui/tests/RegionSampling_test.cpp +++ b/libs/gui/tests/RegionSampling_test.cpp @@ -19,7 +19,7 @@ #include <android/gui/BnRegionSamplingListener.h> #include <binder/ProcessState.h> -#include <gui/AidlStatusUtil.h> +#include <gui/AidlUtil.h> #include <gui/DisplayEventReceiver.h> #include <gui/ISurfaceComposer.h> #include <gui/Surface.h> diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp index b28dca8ab4..59d05b673c 100644 --- a/libs/gui/tests/SurfaceTextureClient_test.cpp +++ b/libs/gui/tests/SurfaceTextureClient_test.cpp @@ -40,12 +40,8 @@ protected: } virtual void SetUp() { - sp<IGraphicBufferProducer> producer; - sp<IGraphicBufferConsumer> consumer; - BufferQueue::createBufferQueue(&producer, &consumer); - mST = new GLConsumer(consumer, 123, GLConsumer::TEXTURE_EXTERNAL, true, - false); - mSTC = new Surface(producer); + mST = new GLConsumer(123, GLConsumer::TEXTURE_EXTERNAL, true, false); + mSTC = mST->getSurface(); mANW = mSTC; // We need a valid GL context so we can test updateTexImage() @@ -731,12 +727,8 @@ protected: ASSERT_NE(EGL_NO_CONTEXT, mEglContext); for (int i = 0; i < NUM_SURFACE_TEXTURES; i++) { - sp<IGraphicBufferProducer> producer; - sp<IGraphicBufferConsumer> consumer; - BufferQueue::createBufferQueue(&producer, &consumer); - sp<GLConsumer> st(new GLConsumer(consumer, i, - GLConsumer::TEXTURE_EXTERNAL, true, false)); - sp<Surface> stc(new Surface(producer)); + sp<GLConsumer> st(new GLConsumer(i, GLConsumer::TEXTURE_EXTERNAL, true, false)); + sp<Surface> stc = st->getSurface(); mEglSurfaces[i] = eglCreateWindowSurface(mEglDisplay, myConfig, static_cast<ANativeWindow*>(stc.get()), nullptr); ASSERT_EQ(EGL_SUCCESS, eglGetError()); diff --git a/libs/gui/tests/SurfaceTextureGL.h b/libs/gui/tests/SurfaceTextureGL.h index 9d8af5d0f5..1309635afd 100644 --- a/libs/gui/tests/SurfaceTextureGL.h +++ b/libs/gui/tests/SurfaceTextureGL.h @@ -38,11 +38,8 @@ protected: void SetUp() { GLTest::SetUp(); - sp<IGraphicBufferProducer> producer; - BufferQueue::createBufferQueue(&producer, &mConsumer); - mST = new GLConsumer(mConsumer, TEX_ID, GLConsumer::TEXTURE_EXTERNAL, - true, false); - mSTC = new Surface(producer); + mST = new GLConsumer(TEX_ID, GLConsumer::TEXTURE_EXTERNAL, true, false); + mSTC = mST->getSurface(); mANW = mSTC; ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), TEST_PRODUCER_USAGE_BITS)); mTextureRenderer = new TextureRenderer(TEX_ID, mST); @@ -63,7 +60,6 @@ protected: mTextureRenderer->drawTexture(); } - sp<IGraphicBufferConsumer> mConsumer; sp<GLConsumer> mST; sp<Surface> mSTC; sp<ANativeWindow> mANW; diff --git a/libs/gui/tests/SurfaceTextureGL_test.cpp b/libs/gui/tests/SurfaceTextureGL_test.cpp index f76c0be265..449533aa57 100644 --- a/libs/gui/tests/SurfaceTextureGL_test.cpp +++ b/libs/gui/tests/SurfaceTextureGL_test.cpp @@ -480,8 +480,8 @@ TEST_F(SurfaceTextureGLTest, DisconnectStressTest) { }; sp<DisconnectWaiter> dw(new DisconnectWaiter()); - mConsumer->consumerConnect(dw, false); - + sp<IGraphicBufferConsumer> consumer = mST->getIGraphicBufferConsumer(); + consumer->consumerConnect(dw, false); sp<Thread> pt(new ProducerThread(mANW)); pt->run("ProducerThread"); diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index 43cd0f8a7f..88893b64ba 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include "gui/view/Surface.h" #include "Constants.h" #include "MockConsumer.h" @@ -23,28 +24,41 @@ #include <android/gui/IDisplayEventConnection.h> #include <android/gui/ISurfaceComposer.h> #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h> +#include <android/hardware_buffer.h> #include <binder/ProcessState.h> +#include <com_android_graphics_libgui_flags.h> #include <configstore/Utils.h> -#include <gui/AidlStatusUtil.h> +#include <gui/AidlUtil.h> #include <gui/BufferItemConsumer.h> -#include <gui/IProducerListener.h> +#include <gui/BufferQueue.h> +#include <gui/CpuConsumer.h> +#include <gui/IConsumerListener.h> +#include <gui/IGraphicBufferConsumer.h> +#include <gui/IGraphicBufferProducer.h> #include <gui/ISurfaceComposer.h> #include <gui/Surface.h> #include <gui/SurfaceComposerClient.h> #include <gui/SyncScreenCaptureListener.h> -#include <inttypes.h> #include <private/gui/ComposerService.h> #include <private/gui/ComposerServiceAIDL.h> #include <sys/types.h> +#include <system/window.h> #include <ui/BufferQueueDefs.h> #include <ui/DisplayMode.h> +#include <ui/GraphicBuffer.h> #include <ui/Rect.h> #include <utils/Errors.h> #include <utils/String8.h> +#include <chrono> +#include <cstddef> +#include <cstdint> +#include <future> #include <limits> #include <thread> +#include "testserver/TestServerClient.h" + namespace android { using namespace std::chrono_literals; @@ -82,7 +96,7 @@ public: virtual void onBuffersDiscarded(const std::vector<sp<GraphicBuffer>>& buffers) { mDiscardedBuffers.insert(mDiscardedBuffers.end(), buffers.begin(), buffers.end()); } - + virtual void onBufferDetached(int /*slot*/) {} int getReleaseNotifyCount() const { return mBuffersReleased; } @@ -97,6 +111,18 @@ private: std::vector<sp<GraphicBuffer>> mDiscardedBuffers; }; +class DeathWatcherListener : public StubSurfaceListener { +public: + virtual void onRemoteDied() { mDiedPromise.set_value(true); } + + virtual bool needsDeathNotify() { return true; } + + std::future<bool> getDiedFuture() { return mDiedPromise.get_future(); } + +private: + std::promise<bool> mDiedPromise; +}; + class SurfaceTest : public ::testing::Test { protected: SurfaceTest() { @@ -143,10 +169,10 @@ protected: if (hasSurfaceListener) { listener = new FakeSurfaceListener(enableReleasedCb); } - ASSERT_EQ(OK, surface->connect( - NATIVE_WINDOW_API_CPU, - /*reportBufferRemoval*/true, - /*listener*/listener)); + ASSERT_EQ(OK, + surface->connect(NATIVE_WINDOW_API_CPU, + /*listener*/ listener, + /*reportBufferRemoval*/ true)); const int BUFFER_COUNT = 4 + extraDiscardedBuffers; ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(window.get(), BUFFER_COUNT)); ASSERT_EQ(NO_ERROR, native_window_set_usage(window.get(), TEST_PRODUCER_USAGE_BITS)); @@ -264,13 +290,9 @@ TEST_F(SurfaceTest, LayerCountIsOne) { TEST_F(SurfaceTest, QueryConsumerUsage) { const int TEST_USAGE_FLAGS = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER; - sp<IGraphicBufferProducer> producer; - sp<IGraphicBufferConsumer> consumer; - BufferQueue::createBufferQueue(&producer, &consumer); - sp<BufferItemConsumer> c = new BufferItemConsumer(consumer, - TEST_USAGE_FLAGS); - sp<Surface> s = new Surface(producer); + sp<BufferItemConsumer> c = new BufferItemConsumer(TEST_USAGE_FLAGS); + sp<Surface> s = c->getSurface(); sp<ANativeWindow> anw(s); int flags = -1; @@ -282,15 +304,11 @@ TEST_F(SurfaceTest, QueryConsumerUsage) { TEST_F(SurfaceTest, QueryDefaultBuffersDataSpace) { const android_dataspace TEST_DATASPACE = HAL_DATASPACE_V0_SRGB; - sp<IGraphicBufferProducer> producer; - sp<IGraphicBufferConsumer> consumer; - BufferQueue::createBufferQueue(&producer, &consumer); - sp<CpuConsumer> cpuConsumer = new CpuConsumer(consumer, 1); + sp<CpuConsumer> cpuConsumer = new CpuConsumer(1); cpuConsumer->setDefaultBufferDataSpace(TEST_DATASPACE); - sp<Surface> s = new Surface(producer); - + sp<Surface> s = cpuConsumer->getSurface(); sp<ANativeWindow> anw(s); android_dataspace dataSpace; @@ -303,11 +321,8 @@ TEST_F(SurfaceTest, QueryDefaultBuffersDataSpace) { } TEST_F(SurfaceTest, SettingGenerationNumber) { - sp<IGraphicBufferProducer> producer; - sp<IGraphicBufferConsumer> consumer; - BufferQueue::createBufferQueue(&producer, &consumer); - sp<CpuConsumer> cpuConsumer = new CpuConsumer(consumer, 1); - sp<Surface> surface = new Surface(producer); + sp<CpuConsumer> cpuConsumer = new CpuConsumer(1); + sp<Surface> surface = cpuConsumer->getSurface(); sp<ANativeWindow> window(surface); // Allocate a buffer with a generation number of 0 @@ -491,11 +506,11 @@ TEST_F(SurfaceTest, GetAndFlushRemovedBuffers) { sp<Surface> surface = new Surface(producer); sp<ANativeWindow> window(surface); - sp<StubProducerListener> listener = new StubProducerListener(); - ASSERT_EQ(OK, surface->connect( - NATIVE_WINDOW_API_CPU, - /*listener*/listener, - /*reportBufferRemoval*/true)); + sp<StubSurfaceListener> listener = new StubSurfaceListener(); + ASSERT_EQ(OK, + surface->connect(NATIVE_WINDOW_API_CPU, + /*listener*/ listener, + /*reportBufferRemoval*/ true)); const int BUFFER_COUNT = 4; ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(window.get(), BUFFER_COUNT)); ASSERT_EQ(NO_ERROR, native_window_set_usage(window.get(), TEST_PRODUCER_USAGE_BITS)); @@ -636,7 +651,7 @@ public: status_t setTransactionState( const FrameTimelineInfo& /*frameTimelineInfo*/, Vector<ComposerState>& /*state*/, - const Vector<DisplayState>& /*displays*/, uint32_t /*flags*/, + 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*/, @@ -987,6 +1002,19 @@ public: binder::Status notifyShutdown() override { return binder::Status::ok(); } + binder::Status addJankListener(const sp<IBinder>& /*layer*/, + const sp<gui::IJankListener>& /*listener*/) override { + return binder::Status::ok(); + } + + binder::Status flushJankData(int32_t /*layerId*/) override { return binder::Status::ok(); } + + binder::Status removeJankListener(int32_t /*layerId*/, + const sp<gui::IJankListener>& /*listener*/, + int64_t /*afterVsync*/) override { + return binder::Status::ok(); + } + protected: IBinder* onAsBinder() override { return nullptr; } @@ -2134,14 +2162,11 @@ TEST_F(SurfaceTest, DefaultMaxBufferCountSetAndUpdated) { TEST_F(SurfaceTest, BatchOperations) { const int BUFFER_COUNT = 16; const int BATCH_SIZE = 8; - sp<IGraphicBufferProducer> producer; - sp<IGraphicBufferConsumer> consumer; - BufferQueue::createBufferQueue(&producer, &consumer); - sp<CpuConsumer> cpuConsumer = new CpuConsumer(consumer, 1); - sp<Surface> surface = new Surface(producer); + sp<CpuConsumer> cpuConsumer = new CpuConsumer(1); + sp<Surface> surface = cpuConsumer->getSurface(); sp<ANativeWindow> window(surface); - sp<StubProducerListener> listener = new StubProducerListener(); + sp<StubSurfaceListener> listener = new StubSurfaceListener(); ASSERT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, /*listener*/listener, /*reportBufferRemoval*/false)); @@ -2186,14 +2211,11 @@ TEST_F(SurfaceTest, BatchOperations) { TEST_F(SurfaceTest, BatchIllegalOperations) { const int BUFFER_COUNT = 16; const int BATCH_SIZE = 8; - sp<IGraphicBufferProducer> producer; - sp<IGraphicBufferConsumer> consumer; - BufferQueue::createBufferQueue(&producer, &consumer); - sp<CpuConsumer> cpuConsumer = new CpuConsumer(consumer, 1); - sp<Surface> surface = new Surface(producer); + sp<CpuConsumer> cpuConsumer = new CpuConsumer(1); + sp<Surface> surface = cpuConsumer->getSurface(); sp<ANativeWindow> window(surface); - sp<StubProducerListener> listener = new StubProducerListener(); + sp<StubSurfaceListener> listener = new StubSurfaceListener(); ASSERT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, /*listener*/listener, /*reportBufferRemoval*/false)); @@ -2213,4 +2235,288 @@ TEST_F(SurfaceTest, BatchIllegalOperations) { ASSERT_EQ(NO_ERROR, surface->disconnect(NATIVE_WINDOW_API_CPU)); } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + +TEST_F(SurfaceTest, PlatformBufferMethods) { + sp<CpuConsumer> cpuConsumer = sp<CpuConsumer>::make(1); + sp<Surface> surface = cpuConsumer->getSurface(); + sp<StubSurfaceListener> listener = sp<StubSurfaceListener>::make(); + sp<GraphicBuffer> buffer; + sp<Fence> fence; + + EXPECT_EQ(OK, + surface->connect(NATIVE_WINDOW_API_CPU, listener, /* reportBufferRemoval */ false)); + + // + // Verify nullptrs are handled safely: + // + + EXPECT_EQ(BAD_VALUE, surface->dequeueBuffer((sp<GraphicBuffer>*)nullptr, nullptr)); + EXPECT_EQ(BAD_VALUE, surface->dequeueBuffer((sp<GraphicBuffer>*)nullptr, &fence)); + EXPECT_EQ(BAD_VALUE, surface->dequeueBuffer(&buffer, nullptr)); + EXPECT_EQ(BAD_VALUE, surface->queueBuffer(nullptr, nullptr)); + EXPECT_EQ(BAD_VALUE, surface->detachBuffer(nullptr)); + + // + // Verify dequeue/queue: + // + + EXPECT_EQ(OK, surface->dequeueBuffer(&buffer, &fence)); + EXPECT_NE(nullptr, buffer); + EXPECT_EQ(OK, surface->queueBuffer(buffer, fence)); + + // + // Verify dequeue/detach: + // + + wp<GraphicBuffer> weakBuffer; + { + EXPECT_EQ(OK, surface->dequeueBuffer(&buffer, &fence)); + + EXPECT_EQ(OK, surface->detachBuffer(buffer)); + + weakBuffer = buffer; + buffer = nullptr; + } + EXPECT_EQ(nullptr, weakBuffer.promote()) << "Weak buffer still held by Surface."; + + // + // Verify detach without borrowing the buffer does not work: + // + + sp<GraphicBuffer> heldTooLongBuffer; + EXPECT_EQ(OK, surface->dequeueBuffer(&heldTooLongBuffer, &fence)); + EXPECT_EQ(OK, surface->queueBuffer(heldTooLongBuffer)); + EXPECT_EQ(BAD_VALUE, surface->detachBuffer(heldTooLongBuffer)); +} + +TEST_F(SurfaceTest, AllowAllocation) { + // controlledByApp must be true to disable blocking + sp<CpuConsumer> cpuConsumer = sp<CpuConsumer>::make(1, /*controlledByApp*/ true); + sp<Surface> surface = cpuConsumer->getSurface(); + sp<StubSurfaceListener> listener = sp<StubSurfaceListener>::make(); + sp<GraphicBuffer> buffer; + sp<Fence> fence; + + EXPECT_EQ(OK, + surface->connect(NATIVE_WINDOW_API_CPU, listener, /* reportBufferRemoval */ false)); + EXPECT_EQ(OK, surface->allowAllocation(false)); + + EXPECT_EQ(OK, surface->setDequeueTimeout(-1)); + EXPECT_EQ(WOULD_BLOCK, surface->dequeueBuffer(&buffer, &fence)); + + EXPECT_EQ(OK, surface->setDequeueTimeout(10)); + EXPECT_EQ(TIMED_OUT, surface->dequeueBuffer(&buffer, &fence)); + + EXPECT_EQ(OK, surface->allowAllocation(true)); + EXPECT_EQ(OK, surface->dequeueBuffer(&buffer, &fence)); +} + +TEST_F(SurfaceTest, QueueAcquireReleaseDequeue_CalledInStack_DoesNotDeadlock) { + class DequeuingSurfaceListener : public SurfaceListener { + public: + DequeuingSurfaceListener(const wp<Surface>& surface) : mSurface(surface) {} + + virtual void onBufferReleased() override { + sp<Surface> surface = mSurface.promote(); + ASSERT_NE(nullptr, surface); + EXPECT_EQ(OK, surface->dequeueBuffer(&mBuffer, &mFence)); + } + + virtual bool needsReleaseNotify() override { return true; } + virtual void onBuffersDiscarded(const std::vector<sp<GraphicBuffer>>&) override {} + virtual void onBufferDetached(int) override {} + + sp<GraphicBuffer> mBuffer; + sp<Fence> mFence; + + private: + wp<Surface> mSurface; + }; + + class ImmediateReleaseConsumerListener : public BufferItemConsumer::FrameAvailableListener { + public: + ImmediateReleaseConsumerListener(const wp<BufferItemConsumer>& consumer) + : mConsumer(consumer) {} + + virtual void onFrameAvailable(const BufferItem&) override { + sp<BufferItemConsumer> consumer = mConsumer.promote(); + ASSERT_NE(nullptr, consumer); + + mCalls += 1; + + BufferItem buffer; + EXPECT_EQ(OK, consumer->acquireBuffer(&buffer, 0)); + EXPECT_EQ(OK, consumer->releaseBuffer(buffer)); + } + + size_t mCalls = 0; + + private: + wp<BufferItemConsumer> mConsumer; + }; + + sp<IGraphicBufferProducer> bqProducer; + sp<IGraphicBufferConsumer> bqConsumer; + BufferQueue::createBufferQueue(&bqProducer, &bqConsumer); + + sp<BufferItemConsumer> consumer = sp<BufferItemConsumer>::make(bqConsumer, 3); + sp<Surface> surface = sp<Surface>::make(bqProducer); + sp<ImmediateReleaseConsumerListener> consumerListener = + sp<ImmediateReleaseConsumerListener>::make(consumer); + consumer->setFrameAvailableListener(consumerListener); + + sp<DequeuingSurfaceListener> surfaceListener = sp<DequeuingSurfaceListener>::make(surface); + EXPECT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, surfaceListener, false)); + + EXPECT_EQ(OK, surface->setMaxDequeuedBufferCount(2)); + + sp<GraphicBuffer> buffer; + sp<Fence> fence; + EXPECT_EQ(OK, surface->dequeueBuffer(&buffer, &fence)); + EXPECT_EQ(OK, surface->queueBuffer(buffer, fence)); + + EXPECT_EQ(1u, consumerListener->mCalls); + EXPECT_NE(nullptr, surfaceListener->mBuffer); + + EXPECT_EQ(OK, surface->disconnect(NATIVE_WINDOW_API_CPU)); +} + +TEST_F(SurfaceTest, ViewSurface_toString) { + view::Surface surface{}; + EXPECT_EQ("", surface.toString()); + + surface.name = String16("name"); + EXPECT_EQ("name", surface.toString()); +} + +TEST_F(SurfaceTest, TestRemoteSurfaceDied_CallbackCalled) { + sp<TestServerClient> testServer = TestServerClient::Create(); + sp<IGraphicBufferProducer> producer = testServer->CreateProducer(); + EXPECT_NE(nullptr, producer); + + sp<Surface> surface = sp<Surface>::make(producer); + sp<DeathWatcherListener> deathWatcher = sp<DeathWatcherListener>::make(); + EXPECT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, deathWatcher)); + + auto diedFuture = deathWatcher->getDiedFuture(); + EXPECT_EQ(OK, testServer->Kill()); + + diedFuture.wait(); + EXPECT_TRUE(diedFuture.get()); +} + +TEST_F(SurfaceTest, TestRemoteSurfaceDied_Disconnect_CallbackNotCalled) { + sp<TestServerClient> testServer = TestServerClient::Create(); + sp<IGraphicBufferProducer> producer = testServer->CreateProducer(); + EXPECT_NE(nullptr, producer); + + sp<Surface> surface = sp<Surface>::make(producer); + sp<DeathWatcherListener> deathWatcher = sp<DeathWatcherListener>::make(); + EXPECT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, deathWatcher)); + EXPECT_EQ(OK, surface->disconnect(NATIVE_WINDOW_API_CPU)); + + auto watcherDiedFuture = deathWatcher->getDiedFuture(); + EXPECT_EQ(OK, testServer->Kill()); + + std::future_status status = watcherDiedFuture.wait_for(std::chrono::seconds(1)); + EXPECT_EQ(std::future_status::timeout, status); +} + +TEST_F(SurfaceTest, QueueBufferOutput_TracksReplacements) { + sp<BufferItemConsumer> consumer = sp<BufferItemConsumer>::make(GRALLOC_USAGE_SW_READ_OFTEN); + ASSERT_EQ(OK, consumer->setMaxBufferCount(3)); + ASSERT_EQ(OK, consumer->setMaxAcquiredBufferCount(1)); + + sp<Surface> surface = consumer->getSurface(); + sp<StubSurfaceListener> listener = sp<StubSurfaceListener>::make(); + + // Async mode sets up an extra buffer so the surface can queue it without waiting. + ASSERT_EQ(OK, surface->setMaxDequeuedBufferCount(1)); + ASSERT_EQ(OK, surface->setAsyncMode(true)); + ASSERT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, listener)); + + sp<GraphicBuffer> buffer; + sp<Fence> fence; + SurfaceQueueBufferOutput output; + BufferItem item; + + // We can queue directly, without an output arg. + EXPECT_EQ(OK, surface->dequeueBuffer(&buffer, &fence)); + EXPECT_EQ(OK, surface->queueBuffer(buffer, fence)); + EXPECT_EQ(OK, consumer->acquireBuffer(&item, 0)); + EXPECT_EQ(OK, consumer->releaseBuffer(item)); + + // We can queue with an output arg, and that we don't expect to see a replacement. + EXPECT_EQ(OK, surface->dequeueBuffer(&buffer, &fence)); + EXPECT_EQ(OK, surface->queueBuffer(buffer, fence, &output)); + EXPECT_FALSE(output.bufferReplaced); + + // We expect see a replacement when we queue a second buffer in async mode, and the consumer + // hasn't acquired the first one yet. + EXPECT_EQ(OK, surface->dequeueBuffer(&buffer, &fence)); + EXPECT_EQ(OK, surface->queueBuffer(buffer, fence, &output)); + EXPECT_TRUE(output.bufferReplaced); +} + +TEST_F(SurfaceTest, QueueBufferOutput_TracksReplacements_Plural) { + sp<BufferItemConsumer> consumer = sp<BufferItemConsumer>::make(GRALLOC_USAGE_SW_READ_OFTEN); + ASSERT_EQ(OK, consumer->setMaxBufferCount(4)); + ASSERT_EQ(OK, consumer->setMaxAcquiredBufferCount(1)); + + sp<Surface> surface = consumer->getSurface(); + consumer->setName(String8("TRPTest")); + sp<StubSurfaceListener> listener = sp<StubSurfaceListener>::make(); + + // Async mode sets up an extra buffer so the surface can queue it without waiting. + ASSERT_EQ(OK, surface->setMaxDequeuedBufferCount(2)); + ASSERT_EQ(OK, surface->setAsyncMode(true)); + ASSERT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, listener)); + + // dequeueBuffers requires a vector of a certain size: + std::vector<Surface::BatchBuffer> buffers(2); + std::vector<Surface::BatchQueuedBuffer> queuedBuffers; + std::vector<SurfaceQueueBufferOutput> outputs; + BufferItem item; + + auto moveBuffersToQueuedBuffers = [&]() { + EXPECT_EQ(2u, buffers.size()); + EXPECT_NE(nullptr, buffers[0].buffer); + EXPECT_NE(nullptr, buffers[1].buffer); + + queuedBuffers.clear(); + for (auto& buffer : buffers) { + auto& queuedBuffer = queuedBuffers.emplace_back(); + queuedBuffer.buffer = buffer.buffer; + queuedBuffer.fenceFd = buffer.fenceFd; + queuedBuffer.timestamp = NATIVE_WINDOW_TIMESTAMP_AUTO; + } + buffers = {{}, {}}; + }; + + // We can queue directly, without an output arg. + EXPECT_EQ(OK, surface->dequeueBuffers(&buffers)); + moveBuffersToQueuedBuffers(); + EXPECT_EQ(OK, surface->queueBuffers(queuedBuffers)); + EXPECT_EQ(OK, consumer->acquireBuffer(&item, 0)); + EXPECT_EQ(OK, consumer->releaseBuffer(item)); + + // We can queue with an output arg. Only the second one should be replaced. + EXPECT_EQ(OK, surface->dequeueBuffers(&buffers)); + moveBuffersToQueuedBuffers(); + EXPECT_EQ(OK, surface->queueBuffers(queuedBuffers, &outputs)); + EXPECT_EQ(2u, outputs.size()); + EXPECT_FALSE(outputs[0].bufferReplaced); + EXPECT_TRUE(outputs[1].bufferReplaced); + + // Since we haven't acquired anything, both queued buffers will replace the original one. + EXPECT_EQ(OK, surface->dequeueBuffers(&buffers)); + moveBuffersToQueuedBuffers(); + EXPECT_EQ(OK, surface->queueBuffers(queuedBuffers, &outputs)); + EXPECT_EQ(2u, outputs.size()); + EXPECT_TRUE(outputs[0].bufferReplaced); + EXPECT_TRUE(outputs[1].bufferReplaced); +} +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + } // namespace android diff --git a/libs/gui/tests/TestServer_test.cpp b/libs/gui/tests/TestServer_test.cpp new file mode 100644 index 0000000000..d6407820ff --- /dev/null +++ b/libs/gui/tests/TestServer_test.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +#include <SurfaceFlingerProperties.h> +#include <android/gui/IDisplayEventConnection.h> +#include <android/gui/ISurfaceComposer.h> +#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h> +#include <android/hardware_buffer.h> +#include <binder/ProcessState.h> +#include <com_android_graphics_libgui_flags.h> +#include <configstore/Utils.h> +#include <gui/AidlUtil.h> +#include <gui/BufferItemConsumer.h> +#include <gui/BufferQueue.h> +#include <gui/CpuConsumer.h> +#include <gui/IConsumerListener.h> +#include <gui/IGraphicBufferConsumer.h> +#include <gui/IGraphicBufferProducer.h> +#include <gui/ISurfaceComposer.h> +#include <gui/Surface.h> +#include <gui/SurfaceComposerClient.h> +#include <gui/SyncScreenCaptureListener.h> +#include <private/gui/ComposerService.h> +#include <private/gui/ComposerServiceAIDL.h> +#include <sys/types.h> +#include <system/window.h> +#include <ui/BufferQueueDefs.h> +#include <ui/DisplayMode.h> +#include <ui/GraphicBuffer.h> +#include <ui/Rect.h> +#include <utils/Errors.h> +#include <utils/String8.h> + +#include <cstddef> +#include <limits> +#include <thread> + +#include "binder/IInterface.h" +#include "testserver/TestServerClient.h" + +namespace android { + +namespace { + +class TestServerTest : public ::testing::Test { +protected: + TestServerTest() { ProcessState::self()->startThreadPool(); } +}; + +} // namespace + +TEST_F(TestServerTest, Create) { + EXPECT_NE(nullptr, TestServerClient::Create()); +} + +TEST_F(TestServerTest, CreateProducer) { + sp<TestServerClient> client = TestServerClient::Create(); + EXPECT_NE(nullptr, client->CreateProducer()); +} + +TEST_F(TestServerTest, KillServer) { + class DeathWaiter : public IBinder::DeathRecipient { + public: + virtual void binderDied(const wp<IBinder>&) override { mPromise.set_value(true); } + std::future<bool> getFuture() { return mPromise.get_future(); } + + std::promise<bool> mPromise; + }; + + sp<TestServerClient> client = TestServerClient::Create(); + sp<IGraphicBufferProducer> producer = client->CreateProducer(); + EXPECT_NE(nullptr, producer); + + sp<DeathWaiter> deathWaiter = sp<DeathWaiter>::make(); + EXPECT_EQ(OK, IInterface::asBinder(producer)->linkToDeath(deathWaiter)); + + auto deathWaiterFuture = deathWaiter->getFuture(); + EXPECT_EQ(OK, client->Kill()); + EXPECT_EQ(nullptr, client->CreateProducer()); + + EXPECT_TRUE(deathWaiterFuture.get()); +} + +} // namespace android diff --git a/libs/gui/tests/testserver/TestServer.cpp b/libs/gui/tests/testserver/TestServer.cpp new file mode 100644 index 0000000000..cd8824e355 --- /dev/null +++ b/libs/gui/tests/testserver/TestServer.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "TestServer" + +#include <android-base/stringprintf.h> +#include <binder/IInterface.h> +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> +#include <binder/ProcessState.h> +#include <binder/Status.h> +#include <gui/BufferQueue.h> +#include <gui/IConsumerListener.h> +#include <gui/IGraphicBufferConsumer.h> +#include <gui/IGraphicBufferProducer.h> +#include <gui/view/Surface.h> +#include <libgui_test_server/BnTestServer.h> +#include <log/log.h> +#include <utils/Errors.h> + +#include <cstdint> +#include <cstdlib> +#include <memory> +#include <mutex> +#include <vector> + +#include <fcntl.h> +#include <unistd.h> + +#include "TestServer.h" + +namespace android { + +namespace { +class TestConsumerListener : public BnConsumerListener { + virtual void onFrameAvailable(const BufferItem&) override {} + virtual void onBuffersReleased() override {} + virtual void onSidebandStreamChanged() override {} +}; + +class TestServiceImpl : public libgui_test_server::BnTestServer { +public: + TestServiceImpl(const char* name) : mName(name) {} + + virtual binder::Status createProducer(view::Surface* out) override { + std::lock_guard<std::mutex> lock(mMutex); + + BufferQueueHolder bq; + BufferQueue::createBufferQueue(&bq.producer, &bq.consumer); + sp<TestConsumerListener> listener = sp<TestConsumerListener>::make(); + bq.consumer->consumerConnect(listener, /*controlledByApp*/ true); + + uint64_t id = 0; + bq.producer->getUniqueId(&id); + std::string name = base::StringPrintf("%s-%" PRIu64, mName, id); + + out->name = String16(name.c_str()); + out->graphicBufferProducer = bq.producer; + mBqs.push_back(std::move(bq)); + + return binder::Status::ok(); + } + + virtual binder::Status killNow() override { + ALOGE("LibGUI Test Service %s dying in response to killNow", mName); + _exit(0); + // Not reached: + return binder::Status::ok(); + } + +private: + std::mutex mMutex; + const char* mName; + + struct BufferQueueHolder { + sp<IGraphicBufferProducer> producer; + sp<IGraphicBufferConsumer> consumer; + }; + + std::vector<BufferQueueHolder> mBqs; +}; +} // namespace + +int TestServerMain(const char* name) { + ProcessState::self()->startThreadPool(); + + sp<TestServiceImpl> testService = sp<TestServiceImpl>::make(name); + ALOGE("service"); + sp<IServiceManager> serviceManager(defaultServiceManager()); + LOG_ALWAYS_FATAL_IF(OK != serviceManager->addService(String16(name), testService)); + + ALOGD("LibGUI Test Service %s STARTED", name); + + IPCThreadState::self()->joinThreadPool(); + + ALOGW("LibGUI Test Service %s DIED", name); + + return 0; +} + +} // namespace android
\ No newline at end of file diff --git a/libs/gui/tests/testserver/TestServer.h b/libs/gui/tests/testserver/TestServer.h new file mode 100644 index 0000000000..4226f1bb09 --- /dev/null +++ b/libs/gui/tests/testserver/TestServer.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +namespace android { + +/* + * Main method for a libgui ITestServer server. + * + * This must be called without any binder setup having been done, because you can't fork and do + * binder things once ProcessState is set up. + * @param name The service name of the test server to start. + * @return retcode + */ +int TestServerMain(const char* name); + +} // namespace android diff --git a/libs/gui/tests/testserver/TestServerClient.cpp b/libs/gui/tests/testserver/TestServerClient.cpp new file mode 100644 index 0000000000..e388074675 --- /dev/null +++ b/libs/gui/tests/testserver/TestServerClient.cpp @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <sys/wait.h> +#include <cerrno> +#define LOG_TAG "TestServerClient" + +#include <android-base/stringprintf.h> +#include <binder/IServiceManager.h> +#include <binder/ProcessState.h> +#include <libgui_test_server/ITestServer.h> +#include <log/log.h> +#include <utils/Errors.h> + +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <sys/types.h> +#include <unistd.h> + +#include <atomic> +#include <csignal> +#include <cstdlib> +#include <mutex> +#include <string> + +#include "TestServerClient.h" +#include "TestServerCommon.h" + +namespace android { + +namespace { + +std::string GetUniqueServiceName() { + static std::atomic<int> uniqueId = 1; + + pid_t pid = getpid(); + int id = uniqueId++; + return base::StringPrintf("Libgui-TestServer-%d-%d", pid, id); +} + +struct RemoteTestServerHostHolder { + RemoteTestServerHostHolder(pid_t pid, int sendFd, int recvFd) + : mPid(pid), mSendFd(sendFd), mRecvFd(recvFd) {} + ~RemoteTestServerHostHolder() { + std::lock_guard lock(mMutex); + + kill(mPid, SIGKILL); + close(mSendFd); + close(mRecvFd); + } + + pid_t CreateTestServerOrDie(std::string name) { + std::lock_guard lock(mMutex); + + CreateServerRequest request; + strlcpy(request.name, name.c_str(), sizeof(request.name) / sizeof(request.name[0])); + + ssize_t bytes = write(mSendFd, &request, sizeof(request)); + LOG_ALWAYS_FATAL_IF(bytes != sizeof(request)); + + CreateServerResponse response; + bytes = read(mRecvFd, &response, sizeof(response)); + LOG_ALWAYS_FATAL_IF(bytes != sizeof(response)); + + return response.pid; + } + +private: + std::mutex mMutex; + + pid_t mPid; + int mSendFd; + int mRecvFd; +}; + +std::unique_ptr<RemoteTestServerHostHolder> g_remoteTestServerHostHolder = nullptr; + +} // namespace + +void TestServerClient::InitializeOrDie(const char* filename) { + int sendPipeFds[2]; + int ret = pipe(sendPipeFds); + LOG_ALWAYS_FATAL_IF(ret, "Unable to create subprocess send pipe"); + + int recvPipeFds[2]; + ret = pipe(recvPipeFds); + LOG_ALWAYS_FATAL_IF(ret, "Unable to create subprocess recv pipe"); + + pid_t childPid = fork(); + LOG_ALWAYS_FATAL_IF(childPid < 0, "Unable to fork child process"); + + if (childPid == 0) { + // We forked! + close(sendPipeFds[1]); + close(recvPipeFds[0]); + + // We'll be reading from the parent's "send" and writing to the parent's "recv". + std::string sendPipe = std::to_string(sendPipeFds[0]); + std::string recvPipe = std::to_string(recvPipeFds[1]); + char* args[] = { + const_cast<char*>(filename), + const_cast<char*>("--test-server-host"), + const_cast<char*>(sendPipe.c_str()), + const_cast<char*>(recvPipe.c_str()), + nullptr, + }; + + ret = execv(filename, args); + ALOGE("Failed to exec libguiTestServer. ret=%d errno=%d (%s)", ret, errno, strerror(errno)); + status_t status = -errno; + write(recvPipeFds[1], &status, sizeof(status)); + _exit(EXIT_FAILURE); + } + + close(sendPipeFds[0]); + close(recvPipeFds[1]); + + // Check for an OK status that the host started. If so, we're good to go. + status_t status; + ret = read(recvPipeFds[0], &status, sizeof(status)); + LOG_ALWAYS_FATAL_IF(ret != sizeof(status), "Unable to read from pipe: %d", ret); + LOG_ALWAYS_FATAL_IF(OK != status, "Pipe returned failed status: %d", status); + + g_remoteTestServerHostHolder = + std::make_unique<RemoteTestServerHostHolder>(childPid, sendPipeFds[1], recvPipeFds[0]); +} + +sp<TestServerClient> TestServerClient::Create() { + std::string serviceName = GetUniqueServiceName(); + + pid_t childPid = g_remoteTestServerHostHolder->CreateTestServerOrDie(serviceName); + ALOGD("Created child server %s with pid %d", serviceName.c_str(), childPid); + + sp<libgui_test_server::ITestServer> server = + waitForService<libgui_test_server::ITestServer>(String16(serviceName.c_str())); + LOG_ALWAYS_FATAL_IF(server == nullptr); + ALOGD("Created connected to child server %s", serviceName.c_str()); + + return sp<TestServerClient>::make(server); +} + +TestServerClient::TestServerClient(const sp<libgui_test_server::ITestServer>& server) + : mServer(server) {} + +TestServerClient::~TestServerClient() { + Kill(); +} + +sp<IGraphicBufferProducer> TestServerClient::CreateProducer() { + std::lock_guard<std::mutex> lock(mMutex); + + if (!mIsAlive) { + return nullptr; + } + + view::Surface surface; + binder::Status status = mServer->createProducer(&surface); + + if (!status.isOk()) { + ALOGE("Failed to create remote producer. Error: %s", status.exceptionMessage().c_str()); + return nullptr; + } + + if (!surface.graphicBufferProducer) { + ALOGE("Remote producer returned no IGBP."); + return nullptr; + } + + return surface.graphicBufferProducer; +} + +status_t TestServerClient::Kill() { + std::lock_guard<std::mutex> lock(mMutex); + if (!mIsAlive) { + return DEAD_OBJECT; + } + + mServer->killNow(); + mServer = nullptr; + mIsAlive = false; + + return OK; +} + +} // namespace android diff --git a/libs/gui/tests/testserver/TestServerClient.h b/libs/gui/tests/testserver/TestServerClient.h new file mode 100644 index 0000000000..53296344a3 --- /dev/null +++ b/libs/gui/tests/testserver/TestServerClient.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <libgui_test_server/ITestServer.h> +#include <utils/RefBase.h> + +namespace android { + +class TestServerClient : public RefBase { +public: + static void InitializeOrDie(const char* filename); + static sp<TestServerClient> Create(); + + TestServerClient(const sp<libgui_test_server::ITestServer>& server); + virtual ~TestServerClient() override; + + sp<IGraphicBufferProducer> CreateProducer(); + status_t Kill(); + +private: + std::mutex mMutex; + + sp<libgui_test_server::ITestServer> mServer; + bool mIsAlive = true; +}; + +} // namespace android diff --git a/libs/gui/tests/testserver/TestServerCommon.h b/libs/gui/tests/testserver/TestServerCommon.h new file mode 100644 index 0000000000..7370f20ef8 --- /dev/null +++ b/libs/gui/tests/testserver/TestServerCommon.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <fcntl.h> + +namespace android { + +/* + * Test -> TestServerHost Request to create a new ITestServer fork. + */ +struct CreateServerRequest { + /* + * Service name for new ITestServer. + */ + char name[128]; +}; + +/* + * TestServerHost -> Test Response for creating an ITestServer fork. + */ +struct CreateServerResponse { + /* + * pid of new ITestServer. + */ + pid_t pid; +}; + +} // namespace android
\ No newline at end of file diff --git a/libs/gui/tests/testserver/TestServerHost.cpp b/libs/gui/tests/testserver/TestServerHost.cpp new file mode 100644 index 0000000000..696c3b9817 --- /dev/null +++ b/libs/gui/tests/testserver/TestServerHost.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "TestServerHost" + +#include <android-base/unique_fd.h> +#include <binder/IInterface.h> +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> +#include <binder/ProcessState.h> +#include <binder/Status.h> +#include <gui/BufferQueue.h> +#include <gui/IConsumerListener.h> +#include <gui/IGraphicBufferConsumer.h> +#include <gui/IGraphicBufferProducer.h> +#include <libgui_test_server/BnTestServer.h> +#include <log/log.h> +#include <utils/Errors.h> + +#include <memory> +#include <vector> + +#include <fcntl.h> +#include <unistd.h> +#include <cstddef> +#include <cstdlib> + +#include "TestServerCommon.h" +#include "TestServerHost.h" + +namespace android { + +namespace { + +pid_t ForkTestServer(const char* filename, char* name) { + pid_t childPid = fork(); + LOG_ALWAYS_FATAL_IF(childPid == -1); + + if (childPid != 0) { + return childPid; + } + + // We forked! + const char* test_server_flag = "--test-server"; + char* args[] = { + const_cast<char*>(filename), + const_cast<char*>(test_server_flag), + name, + nullptr, + }; + + int ret = execv(filename, args); + ALOGE("Failed to exec libgui_test as a TestServer. ret=%d errno=%d (%s)", ret, errno, + strerror(errno)); + _exit(EXIT_FAILURE); +} + +} // namespace + +int TestServerHostMain(const char* filename, base::unique_fd sendPipeFd, + base::unique_fd recvPipeFd) { + status_t status = OK; + LOG_ALWAYS_FATAL_IF(sizeof(status) != write(sendPipeFd.get(), &status, sizeof(status))); + + ALOGE("Launched TestServerHost"); + + while (true) { + CreateServerRequest request = {}; + ssize_t bytes = read(recvPipeFd.get(), &request, sizeof(request)); + LOG_ALWAYS_FATAL_IF(bytes != sizeof(request)); + pid_t childPid = ForkTestServer(filename, request.name); + + CreateServerResponse response = {}; + response.pid = childPid; + bytes = write(sendPipeFd.get(), &response, sizeof(response)); + LOG_ALWAYS_FATAL_IF(bytes != sizeof(response)); + } + + return 0; +} + +} // namespace android
\ No newline at end of file diff --git a/libs/gui/tests/testserver/TestServerHost.h b/libs/gui/tests/testserver/TestServerHost.h new file mode 100644 index 0000000000..df22c0c3fe --- /dev/null +++ b/libs/gui/tests/testserver/TestServerHost.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <android-base/unique_fd.h> + +#include <string> + +namespace android { + +/* + * Main method for a host process for TestServers. + * + * This must be called without any binder setup having been done, because you can't fork and do + * binder things once ProcessState is set up. + * @param filename File name of this binary / the binary to execve into + * @param sendPipeFd Pipe FD to send data to. + * @param recvPipeFd Pipe FD to receive data from. + * @return retcode + */ +int TestServerHostMain(const char* filename, base::unique_fd sendPipeFd, + base::unique_fd recvPipeFd); + +} // namespace android diff --git a/libs/gui/tests/testserver/aidl/libgui_test_server/ITestServer.aidl b/libs/gui/tests/testserver/aidl/libgui_test_server/ITestServer.aidl new file mode 100644 index 0000000000..c939ea00c1 --- /dev/null +++ b/libs/gui/tests/testserver/aidl/libgui_test_server/ITestServer.aidl @@ -0,0 +1,12 @@ +package libgui_test_server; + +import android.view.Surface; + +// Test server for libgui_test +interface ITestServer { + // Create a new producer. The server will have connected to the consumer. + Surface createProducer(); + + // Kills the server immediately. + void killNow(); +} diff --git a/libs/gui/view/Surface.cpp b/libs/gui/view/Surface.cpp index 7c15e7cf92..84c2a6ac71 100644 --- a/libs/gui/view/Surface.cpp +++ b/libs/gui/view/Surface.cpp @@ -121,5 +121,11 @@ String16 Surface::readMaybeEmptyString16(const Parcel* parcel) { return str.value_or(String16()); } +std::string Surface::toString() const { + std::stringstream out; + out << name; + return out.str(); +} + } // namespace view } // namespace android diff --git a/libs/input/Android.bp b/libs/input/Android.bp index d782f42071..e4e81adf58 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -30,6 +30,7 @@ filegroup { "android/os/InputEventInjectionResult.aidl", "android/os/InputEventInjectionSync.aidl", "android/os/InputConfig.aidl", + "android/os/MotionEventFlag.aidl", "android/os/PointerIconType.aidl", ], } @@ -231,6 +232,7 @@ cc_library { "MotionPredictorMetricsManager.cpp", "PrintTools.cpp", "PropertyMap.cpp", + "Resampler.cpp", "TfLiteMotionPredictor.cpp", "TouchVideoFrame.cpp", "VelocityControl.cpp", @@ -257,6 +259,7 @@ cc_library { ], shared_libs: [ + "android.companion.virtualdevice.flags-aconfig-cc", "libbase", "libbinder", "libbinder_ndk", diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index b09814797f..a2bb3453fe 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -580,7 +580,7 @@ void MotionEvent::initialize(int32_t id, int32_t deviceId, uint32_t source, &pointerProperties[pointerCount]); mSampleEventTimes.clear(); mSamplePointerCoords.clear(); - addSample(eventTime, pointerCoords); + addSample(eventTime, pointerCoords, mId); } void MotionEvent::copyFrom(const MotionEvent* other, bool keepHistory) { @@ -640,9 +640,9 @@ void MotionEvent::splitFrom(const android::MotionEvent& other, mSampleEventTimes = other.mSampleEventTimes; } -void MotionEvent::addSample( - int64_t eventTime, - const PointerCoords* pointerCoords) { +void MotionEvent::addSample(int64_t eventTime, const PointerCoords* pointerCoords, + int32_t eventId) { + mId = eventId; mSampleEventTimes.push_back(eventTime); mSamplePointerCoords.insert(mSamplePointerCoords.end(), &pointerCoords[0], &pointerCoords[getPointerCount()]); diff --git a/libs/input/InputConsumer.cpp b/libs/input/InputConsumer.cpp index fcf490d5f9..1eeb4e678c 100644 --- a/libs/input/InputConsumer.cpp +++ b/libs/input/InputConsumer.cpp @@ -135,7 +135,7 @@ void addSample(MotionEvent& event, const InputMessage& msg) { } event.setMetaState(event.getMetaState() | msg.body.motion.metaState); - event.addSample(msg.body.motion.eventTime, pointerCoords); + event.addSample(msg.body.motion.eventTime, pointerCoords, msg.body.motion.eventId); } void initializeTouchModeEvent(TouchModeEvent& event, const InputMessage& msg) { @@ -235,8 +235,9 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consum mMsgDeferred = false; } else { // Receive a fresh message. - status_t result = mChannel->receiveMessage(&mMsg); - if (result == OK) { + android::base::Result<InputMessage> result = mChannel->receiveMessage(); + if (result.ok()) { + mMsg = std::move(result.value()); const auto [_, inserted] = mConsumeTimes.emplace(mMsg.header.seq, systemTime(SYSTEM_TIME_MONOTONIC)); LOG_ALWAYS_FATAL_IF(!inserted, "Already have a consume time for seq=%" PRIu32, @@ -244,11 +245,11 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consum // Trace the event processing timeline - event was just read from the socket ATRACE_ASYNC_BEGIN(mProcessingTraceTag.c_str(), /*cookie=*/mMsg.header.seq); - } - if (result) { + } else { // Consume the next batched event unless batches are being held for later. - if (consumeBatches || result != WOULD_BLOCK) { - result = consumeBatch(factory, frameTime, outSeq, outEvent); + if (consumeBatches || result.error().code() != WOULD_BLOCK) { + result = android::base::Error( + consumeBatch(factory, frameTime, outSeq, outEvent)); if (*outEvent) { ALOGD_IF(DEBUG_TRANSPORT_CONSUMER, "channel '%s' consumer ~ consumed batch event, seq=%u", @@ -256,7 +257,7 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consum break; } } - return result; + return result.error().code(); } } @@ -696,7 +697,7 @@ void InputConsumer::resampleTouchState(nsecs_t sampleTime, MotionEvent* event, currentCoords.getY(), otherCoords.getX(), otherCoords.getY(), alpha); } - event->addSample(sampleTime, touchState.lastResample.pointers); + event->addSample(sampleTime, touchState.lastResample.pointers, event->getId()); } status_t InputConsumer::sendFinishedSignal(uint32_t seq, bool handled) { diff --git a/libs/input/InputConsumerNoResampling.cpp b/libs/input/InputConsumerNoResampling.cpp index 15d992f9f3..cdbc1869c3 100644 --- a/libs/input/InputConsumerNoResampling.cpp +++ b/libs/input/InputConsumerNoResampling.cpp @@ -14,9 +14,11 @@ * limitations under the License. */ -#define LOG_TAG "InputTransport" +#define LOG_TAG "InputConsumerNoResampling" #define ATRACE_TAG ATRACE_TAG_INPUT +#include <chrono> + #include <inttypes.h> #include <android-base/logging.h> @@ -31,12 +33,12 @@ #include <input/PrintTools.h> #include <input/TraceTools.h> -namespace input_flags = com::android::input::flags; - namespace android { namespace { +using std::chrono::nanoseconds; + /** * Log debug messages relating to the consumer end of the transport channel. * Enable this via "adb shell setprop log.tag.InputTransportConsumer DEBUG" (requires restart) @@ -114,7 +116,7 @@ void addSample(MotionEvent& event, const InputMessage& msg) { // TODO(b/329770983): figure out if it's safe to combine events with mismatching metaState event.setMetaState(event.getMetaState() | msg.body.motion.metaState); - event.addSample(msg.body.motion.eventTime, pointerCoords.data()); + event.addSample(msg.body.motion.eventTime, pointerCoords.data(), msg.body.motion.eventId); } std::unique_ptr<TouchModeEvent> createTouchModeEvent(const InputMessage& msg) { @@ -168,17 +170,24 @@ InputMessage createTimelineMessage(int32_t inputEventId, nsecs_t gpuCompletedTim return msg; } +bool isPointerEvent(const MotionEvent& motionEvent) { + return (motionEvent.getSource() & AINPUT_SOURCE_CLASS_POINTER) == AINPUT_SOURCE_CLASS_POINTER; +} } // namespace using android::base::Result; -using android::base::StringPrintf; // --- InputConsumerNoResampling --- InputConsumerNoResampling::InputConsumerNoResampling(const std::shared_ptr<InputChannel>& channel, sp<Looper> looper, - InputConsumerCallbacks& callbacks) - : mChannel(channel), mLooper(looper), mCallbacks(callbacks), mFdEvents(0) { + InputConsumerCallbacks& callbacks, + std::unique_ptr<Resampler> resampler) + : mChannel{channel}, + mLooper{looper}, + mCallbacks{callbacks}, + mResampler{std::move(resampler)}, + mFdEvents(0) { LOG_ALWAYS_FATAL_IF(mLooper == nullptr); mCallback = sp<LooperEventCallback>::make( std::bind(&InputConsumerNoResampling::handleReceiveCallback, this, @@ -190,7 +199,7 @@ InputConsumerNoResampling::InputConsumerNoResampling(const std::shared_ptr<Input InputConsumerNoResampling::~InputConsumerNoResampling() { ensureCalledOnLooperThread(__func__); - consumeBatchedInputEvents(std::nullopt); + consumeBatchedInputEvents(/*requestedFrameTime=*/std::nullopt); while (!mOutboundQueue.empty()) { processOutboundEvents(); // This is our last chance to ack the events. If we don't ack them here, we will get an ANR, @@ -216,8 +225,7 @@ int InputConsumerNoResampling::handleReceiveCallback(int events) { int handledEvents = 0; if (events & ALOOPER_EVENT_INPUT) { - std::vector<InputMessage> messages = readAllMessages(); - handleMessages(std::move(messages)); + handleMessages(readAllMessages()); handledEvents |= ALOOPER_EVENT_INPUT; } @@ -325,10 +333,8 @@ void InputConsumerNoResampling::handleMessages(std::vector<InputMessage>&& messa // add it to batch mBatches[deviceId].emplace(msg); } else { - // consume all pending batches for this event immediately - // TODO(b/329776327): figure out if this could be smarter by limiting the - // consumption only to the current device. - consumeBatchedInputEvents(std::nullopt); + // consume all pending batches for this device immediately + consumeBatchedInputEvents(deviceId, /*requestedFrameTime=*/std::nullopt); handleMessage(msg); } } else { @@ -362,36 +368,36 @@ void InputConsumerNoResampling::handleMessages(std::vector<InputMessage>&& messa std::vector<InputMessage> InputConsumerNoResampling::readAllMessages() { std::vector<InputMessage> messages; while (true) { - InputMessage msg; - status_t result = mChannel->receiveMessage(&msg); - switch (result) { - case OK: { - const auto [_, inserted] = - mConsumeTimes.emplace(msg.header.seq, systemTime(SYSTEM_TIME_MONOTONIC)); - LOG_ALWAYS_FATAL_IF(!inserted, "Already have a consume time for seq=%" PRIu32, - msg.header.seq); - - // Trace the event processing timeline - event was just read from the socket - // TODO(b/329777420): distinguish between multiple instances of InputConsumer - // in the same process. - ATRACE_ASYNC_BEGIN("InputConsumer processing", /*cookie=*/msg.header.seq); - messages.push_back(msg); - break; - } - case WOULD_BLOCK: { - return messages; - } - case DEAD_OBJECT: { - LOG(FATAL) << "Got a dead object for " << mChannel->getName(); - break; - } - case BAD_VALUE: { - LOG(FATAL) << "Got a bad value for " << mChannel->getName(); - break; - } - default: { - LOG(FATAL) << "Unexpected error: " << result; - break; + android::base::Result<InputMessage> result = mChannel->receiveMessage(); + if (result.ok()) { + const InputMessage& msg = *result; + const auto [_, inserted] = + mConsumeTimes.emplace(msg.header.seq, systemTime(SYSTEM_TIME_MONOTONIC)); + LOG_ALWAYS_FATAL_IF(!inserted, "Already have a consume time for seq=%" PRIu32, + msg.header.seq); + + // Trace the event processing timeline - event was just read from the socket + // TODO(b/329777420): distinguish between multiple instances of InputConsumer + // in the same process. + ATRACE_ASYNC_BEGIN("InputConsumer processing", /*cookie=*/msg.header.seq); + messages.push_back(msg); + } else { // !result.ok() + switch (result.error().code()) { + case WOULD_BLOCK: { + return messages; + } + case DEAD_OBJECT: { + LOG(FATAL) << "Got a dead object for " << mChannel->getName(); + break; + } + case BAD_VALUE: { + LOG(FATAL) << "Got a bad value for " << mChannel->getName(); + break; + } + default: { + LOG(FATAL) << "Unexpected error: " << result.error().message(); + break; + } } } } @@ -445,51 +451,81 @@ void InputConsumerNoResampling::handleMessage(const InputMessage& msg) const { } } +std::pair<std::unique_ptr<MotionEvent>, std::optional<uint32_t>> +InputConsumerNoResampling::createBatchedMotionEvent(const nsecs_t requestedFrameTime, + std::queue<InputMessage>& messages) { + std::unique_ptr<MotionEvent> motionEvent; + std::optional<uint32_t> firstSeqForBatch; + const nanoseconds resampleLatency = + (mResampler != nullptr) ? mResampler->getResampleLatency() : nanoseconds{0}; + const nanoseconds adjustedFrameTime = nanoseconds{requestedFrameTime} - resampleLatency; + + while (!messages.empty() && + (messages.front().body.motion.eventTime <= adjustedFrameTime.count())) { + if (motionEvent == nullptr) { + motionEvent = createMotionEvent(messages.front()); + firstSeqForBatch = messages.front().header.seq; + const auto [_, inserted] = mBatchedSequenceNumbers.insert({*firstSeqForBatch, {}}); + LOG_IF(FATAL, !inserted) + << "The sequence " << messages.front().header.seq << " was already present!"; + } else { + addSample(*motionEvent, messages.front()); + mBatchedSequenceNumbers[*firstSeqForBatch].push_back(messages.front().header.seq); + } + messages.pop(); + } + // Check if resampling should be performed. + if (motionEvent != nullptr && isPointerEvent(*motionEvent) && mResampler != nullptr) { + InputMessage* futureSample = nullptr; + if (!messages.empty()) { + futureSample = &messages.front(); + } + mResampler->resampleMotionEvent(nanoseconds{requestedFrameTime}, *motionEvent, + futureSample); + } + return std::make_pair(std::move(motionEvent), firstSeqForBatch); +} + bool InputConsumerNoResampling::consumeBatchedInputEvents( - std::optional<nsecs_t> requestedFrameTime) { + std::optional<DeviceId> deviceId, std::optional<nsecs_t> requestedFrameTime) { ensureCalledOnLooperThread(__func__); // When batching is not enabled, we want to consume all events. That's equivalent to having an - // infinite frameTime. - const nsecs_t frameTime = requestedFrameTime.value_or(std::numeric_limits<nsecs_t>::max()); + // infinite requestedFrameTime. + requestedFrameTime = requestedFrameTime.value_or(std::numeric_limits<nsecs_t>::max()); bool producedEvents = false; - for (auto& [deviceId, messages] : mBatches) { - std::unique_ptr<MotionEvent> motion; - std::optional<uint32_t> firstSeqForBatch; - std::vector<uint32_t> sequences; - while (!messages.empty()) { - const InputMessage& msg = messages.front(); - if (msg.body.motion.eventTime > frameTime) { - break; - } - if (motion == nullptr) { - motion = createMotionEvent(msg); - firstSeqForBatch = msg.header.seq; - const auto [_, inserted] = mBatchedSequenceNumbers.insert({*firstSeqForBatch, {}}); - if (!inserted) { - LOG(FATAL) << "The sequence " << msg.header.seq << " was already present!"; - } - } else { - addSample(*motion, msg); - mBatchedSequenceNumbers[*firstSeqForBatch].push_back(msg.header.seq); - } - messages.pop(); - } + + for (auto deviceIdIter = (deviceId.has_value()) ? (mBatches.find(*deviceId)) + : (mBatches.begin()); + deviceIdIter != mBatches.cend(); ++deviceIdIter) { + std::queue<InputMessage>& messages = deviceIdIter->second; + auto [motion, firstSeqForBatch] = createBatchedMotionEvent(*requestedFrameTime, messages); if (motion != nullptr) { LOG_ALWAYS_FATAL_IF(!firstSeqForBatch.has_value()); mCallbacks.onMotionEvent(std::move(motion), *firstSeqForBatch); producedEvents = true; } else { - // This is OK, it just means that the frameTime is too old (all events that we have - // pending are in the future of the frametime). Maybe print a - // warning? If there are multiple devices active though, this might be normal and can - // just be ignored, unless none of them resulted in any consumption (in that case, this - // function would already return "false" so we could just leave it up to the caller). + // This is OK, it just means that the requestedFrameTime is too old (all events that we + // have pending are in the future of the requestedFrameTime). Maybe print a warning? If + // there are multiple devices active though, this might be normal and can just be + // ignored, unless none of them resulted in any consumption (in that case, this function + // would already return "false" so we could just leave it up to the caller). + } + + if (deviceId.has_value()) { + // We already consumed events for this device. Break here to prevent iterating over the + // other devices. + break; } } std::erase_if(mBatches, [](const auto& pair) { return pair.second.empty(); }); return producedEvents; } +bool InputConsumerNoResampling::consumeBatchedInputEvents( + std::optional<nsecs_t> requestedFrameTime) { + return consumeBatchedInputEvents(/*deviceId=*/std::nullopt, requestedFrameTime); +} + void InputConsumerNoResampling::ensureCalledOnLooperThread(const char* func) const { sp<Looper> callingThreadLooper = Looper::getForThread(); if (callingThreadLooper != mLooper) { diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp index 9333ab83a6..c9030312f9 100644 --- a/libs/input/InputDevice.cpp +++ b/libs/input/InputDevice.cpp @@ -20,6 +20,7 @@ #include <unistd.h> #include <ctype.h> +#include <android-base/logging.h> #include <android-base/properties.h> #include <android-base/stringprintf.h> #include <ftl/enum.h> @@ -31,6 +32,9 @@ using android::base::StringPrintf; namespace android { +// Set to true to log detailed debugging messages about IDC file probing. +static constexpr bool DEBUG_PROBE = false; + static const char* CONFIGURATION_FILE_DIR[] = { "idc/", "keylayout/", @@ -114,15 +118,18 @@ std::string getInputDeviceConfigurationFilePathByName( for (const auto& prefix : pathPrefixes) { path = prefix; appendInputDeviceConfigurationFileRelativePath(path, name, type); -#if DEBUG_PROBE - ALOGD("Probing for system provided input device configuration file: path='%s'", - path.c_str()); -#endif if (!access(path.c_str(), R_OK)) { -#if DEBUG_PROBE - ALOGD("Found"); -#endif + LOG_IF(INFO, DEBUG_PROBE) + << "Found system-provided input device configuration file at " << path; return path; + } else if (errno != ENOENT) { + LOG(WARNING) << "Couldn't find a system-provided input device configuration file at " + << path << " due to error " << errno << " (" << strerror(errno) + << "); there may be an IDC file there that cannot be loaded."; + } else { + LOG_IF(ERROR, DEBUG_PROBE) + << "Didn't find system-provided input device configuration file at " << path + << ": " << strerror(errno); } } @@ -135,21 +142,22 @@ std::string getInputDeviceConfigurationFilePathByName( } path += "/system/devices/"; appendInputDeviceConfigurationFileRelativePath(path, name, type); -#if DEBUG_PROBE - ALOGD("Probing for system user input device configuration file: path='%s'", path.c_str()); -#endif if (!access(path.c_str(), R_OK)) { -#if DEBUG_PROBE - ALOGD("Found"); -#endif + LOG_IF(INFO, DEBUG_PROBE) << "Found system user input device configuration file at " + << path; return path; + } else if (errno != ENOENT) { + LOG(WARNING) << "Couldn't find a system user input device configuration file at " << path + << " due to error " << errno << " (" << strerror(errno) + << "); there may be an IDC file there that cannot be loaded."; + } else { + LOG_IF(ERROR, DEBUG_PROBE) << "Didn't find system user input device configuration file at " + << path << ": " << strerror(errno); } // Not found. -#if DEBUG_PROBE - ALOGD("Probe failed to find input device configuration file: name='%s', type=%d", - name.c_str(), type); -#endif + LOG_IF(INFO, DEBUG_PROBE) << "Probe failed to find input device configuration file with name '" + << name << "' and type " << ftl::enum_string(type); return ""; } diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index 47b422857e..77dcaa9ef2 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -375,13 +375,11 @@ status_t InputChannel::openInputChannelPair(const std::string& name, sp<IBinder> token = sp<BBinder>::make(); - std::string serverChannelName = name + " (server)"; android::base::unique_fd serverFd(sockets[0]); - outServerChannel = InputChannel::create(serverChannelName, std::move(serverFd), token); + outServerChannel = InputChannel::create(name, std::move(serverFd), token); - std::string clientChannelName = name + " (client)"; android::base::unique_fd clientFd(sockets[1]); - outClientChannel = InputChannel::create(clientChannelName, std::move(clientFd), token); + outClientChannel = InputChannel::create(name, std::move(clientFd), token); return OK; } @@ -424,10 +422,11 @@ status_t InputChannel::sendMessage(const InputMessage* msg) { return OK; } -status_t InputChannel::receiveMessage(InputMessage* msg) { +android::base::Result<InputMessage> InputChannel::receiveMessage() { ssize_t nRead; + InputMessage msg; do { - nRead = ::recv(getFd(), msg, sizeof(InputMessage), MSG_DONTWAIT); + nRead = ::recv(getFd(), &msg, sizeof(InputMessage), MSG_DONTWAIT); } while (nRead == -1 && errno == EINTR); if (nRead < 0) { @@ -435,36 +434,36 @@ status_t InputChannel::receiveMessage(InputMessage* msg) { ALOGD_IF(DEBUG_CHANNEL_MESSAGES, "channel '%s' ~ receive message failed, errno=%d", name.c_str(), errno); if (error == EAGAIN || error == EWOULDBLOCK) { - return WOULD_BLOCK; + return android::base::Error(WOULD_BLOCK); } if (error == EPIPE || error == ENOTCONN || error == ECONNREFUSED) { - return DEAD_OBJECT; + return android::base::Error(DEAD_OBJECT); } - return -error; + return android::base::Error(-error); } if (nRead == 0) { // check for EOF ALOGD_IF(DEBUG_CHANNEL_MESSAGES, "channel '%s' ~ receive message failed because peer was closed", name.c_str()); - return DEAD_OBJECT; + return android::base::Error(DEAD_OBJECT); } - if (!msg->isValid(nRead)) { + if (!msg.isValid(nRead)) { ALOGE("channel '%s' ~ received invalid message of size %zd", name.c_str(), nRead); - return BAD_VALUE; + return android::base::Error(BAD_VALUE); } ALOGD_IF(DEBUG_CHANNEL_MESSAGES, "channel '%s' ~ received message of type %s", name.c_str(), - ftl::enum_string(msg->header.type).c_str()); + ftl::enum_string(msg.header.type).c_str()); if (ATRACE_ENABLED()) { // Add an additional trace point to include data about the received message. std::string message = StringPrintf("receiveMessage(inputChannel=%s, seq=0x%" PRIx32 ", type=%s)", - name.c_str(), msg->header.seq, - ftl::enum_string(msg->header.type).c_str()); + name.c_str(), msg.header.seq, + ftl::enum_string(msg.header.type).c_str()); ATRACE_NAME(message.c_str()); } - return OK; + return msg; } bool InputChannel::probablyHasInput() const { @@ -589,7 +588,8 @@ status_t InputPublisher::publishMotionEvent( mInputVerifier.processMovement(deviceId, source, action, pointerCount, pointerProperties, pointerCoords, flags); if (!result.ok()) { - LOG(FATAL) << "Bad stream: " << result.error(); + LOG(ERROR) << "Bad stream: " << result.error(); + return BAD_VALUE; } } if (debugTransportPublisher()) { @@ -729,15 +729,16 @@ status_t InputPublisher::publishTouchModeEvent(uint32_t seq, int32_t eventId, bo } android::base::Result<InputPublisher::ConsumerResponse> InputPublisher::receiveConsumerResponse() { - InputMessage msg; - status_t result = mChannel->receiveMessage(&msg); - if (result) { - if (debugTransportPublisher() && result != WOULD_BLOCK) { + android::base::Result<InputMessage> result = mChannel->receiveMessage(); + if (!result.ok()) { + if (debugTransportPublisher() && result.error().code() != WOULD_BLOCK) { LOG(INFO) << "channel '" << mChannel->getName() << "' publisher ~ " << __func__ << ": " - << strerror(result); + << result.error().message(); } - return android::base::Error(result); + return result.error(); } + + const InputMessage& msg = *result; if (msg.header.type == InputMessage::Type::FINISHED) { ALOGD_IF(debugTransportPublisher(), "channel '%s' publisher ~ %s: finished: seq=%u, handled=%s", diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp index 1cf5612d45..b0563abaf7 100644 --- a/libs/input/KeyCharacterMap.cpp +++ b/libs/input/KeyCharacterMap.cpp @@ -317,19 +317,8 @@ bool KeyCharacterMap::getEvents(int32_t deviceId, const char16_t* chars, size_t return true; } -void KeyCharacterMap::addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode) { - if (fromKeyCode == toKeyCode) { - mKeyRemapping.erase(fromKeyCode); -#if DEBUG_MAPPING - ALOGD("addKeyRemapping: Cleared remapping forKeyCode=%d ~ Result Successful.", fromKeyCode); -#endif - return; - } - mKeyRemapping.insert_or_assign(fromKeyCode, toKeyCode); -#if DEBUG_MAPPING - ALOGD("addKeyRemapping: fromKeyCode=%d, toKeyCode=%d ~ Result Successful.", fromKeyCode, - toKeyCode); -#endif +void KeyCharacterMap::setKeyRemapping(const std::map<int32_t, int32_t>& keyRemapping) { + mKeyRemapping = keyRemapping; } status_t KeyCharacterMap::mapKey(int32_t scanCode, int32_t usageCode, int32_t* outKeyCode) const { diff --git a/libs/input/KeyLayoutMap.cpp b/libs/input/KeyLayoutMap.cpp index 508818852e..f3241c9c32 100644 --- a/libs/input/KeyLayoutMap.cpp +++ b/libs/input/KeyLayoutMap.cpp @@ -240,8 +240,9 @@ const KeyLayoutMap::Key* KeyLayoutMap::getKey(int32_t scanCode, int32_t usageCod std::vector<int32_t> KeyLayoutMap::findScanCodesForKey(int32_t keyCode) const { std::vector<int32_t> scanCodes; + // b/354333072: Only consider keys without FUNCTION flag for (const auto& [scanCode, key] : mKeysByScanCode) { - if (keyCode == key.keyCode) { + if (keyCode == key.keyCode && !(key.flags & POLICY_FLAG_FUNCTION)) { scanCodes.push_back(scanCode); } } diff --git a/libs/input/MotionPredictor.cpp b/libs/input/MotionPredictor.cpp index 5b61d3953f..c61d3943e0 100644 --- a/libs/input/MotionPredictor.cpp +++ b/libs/input/MotionPredictor.cpp @@ -72,9 +72,13 @@ float normalizeRange(float x, float min, float max) { // --- JerkTracker --- -JerkTracker::JerkTracker(bool normalizedDt) : mNormalizedDt(normalizedDt) {} +JerkTracker::JerkTracker(bool normalizedDt, float alpha) + : mNormalizedDt(normalizedDt), mAlpha(alpha) {} void JerkTracker::pushSample(int64_t timestamp, float xPos, float yPos) { + // If we previously had full samples, we have a previous jerk calculation + // to do weighted smoothing. + const bool applySmoothing = mTimestamps.size() == mTimestamps.capacity(); mTimestamps.pushBack(timestamp); const int numSamples = mTimestamps.size(); @@ -115,6 +119,16 @@ void JerkTracker::pushSample(int64_t timestamp, float xPos, float yPos) { } } + if (numSamples == static_cast<int>(mTimestamps.capacity())) { + float newJerkMagnitude = std::hypot(newXDerivatives[3], newYDerivatives[3]); + ALOGD_IF(isDebug(), "raw jerk: %f", newJerkMagnitude); + if (applySmoothing) { + mJerkMagnitude = mJerkMagnitude + (mAlpha * (newJerkMagnitude - mJerkMagnitude)); + } else { + mJerkMagnitude = newJerkMagnitude; + } + } + std::swap(newXDerivatives, mXDerivatives); std::swap(newYDerivatives, mYDerivatives); } @@ -125,7 +139,7 @@ void JerkTracker::reset() { std::optional<float> JerkTracker::jerkMagnitude() const { if (mTimestamps.size() == mTimestamps.capacity()) { - return std::hypot(mXDerivatives[3], mYDerivatives[3]); + return mJerkMagnitude; } return std::nullopt; } @@ -139,6 +153,24 @@ MotionPredictor::MotionPredictor(nsecs_t predictionTimestampOffsetNanos, mCheckMotionPredictionEnabled(std::move(checkMotionPredictionEnabled)), mReportAtomFunction(reportAtomFunction) {} +void MotionPredictor::initializeObjects() { + mModel = TfLiteMotionPredictorModel::create(); + LOG_ALWAYS_FATAL_IF(!mModel); + + // mJerkTracker assumes normalized dt = 1 between recorded samples because + // the underlying mModel input also assumes fixed-interval samples. + // Normalized dt as 1 is also used to correspond with the similar Jank + // implementation from the JetPack MotionPredictor implementation. + mJerkTracker = std::make_unique<JerkTracker>(/*normalizedDt=*/true, mModel->config().jerkAlpha); + + mBuffers = std::make_unique<TfLiteMotionPredictorBuffers>(mModel->inputLength()); + + mMetricsManager = + std::make_unique<MotionPredictorMetricsManager>(mModel->config().predictionInterval, + mModel->outputLength(), + mReportAtomFunction); +} + android::base::Result<void> MotionPredictor::record(const MotionEvent& event) { if (mLastEvent && mLastEvent->getDeviceId() != event.getDeviceId()) { // We still have an active gesture for another device. The provided MotionEvent is not @@ -155,28 +187,18 @@ android::base::Result<void> MotionPredictor::record(const MotionEvent& event) { return {}; } - // Initialise the model now that it's likely to be used. if (!mModel) { - mModel = TfLiteMotionPredictorModel::create(); - LOG_ALWAYS_FATAL_IF(!mModel); - } - - if (!mBuffers) { - mBuffers = std::make_unique<TfLiteMotionPredictorBuffers>(mModel->inputLength()); + initializeObjects(); } // Pass input event to the MetricsManager. - if (!mMetricsManager) { - mMetricsManager.emplace(mModel->config().predictionInterval, mModel->outputLength(), - mReportAtomFunction); - } mMetricsManager->onRecord(event); const int32_t action = event.getActionMasked(); if (action == AMOTION_EVENT_ACTION_UP || action == AMOTION_EVENT_ACTION_CANCEL) { ALOGD_IF(isDebug(), "End of event stream"); mBuffers->reset(); - mJerkTracker.reset(); + mJerkTracker->reset(); mLastEvent.reset(); return {}; } else if (action != AMOTION_EVENT_ACTION_DOWN && action != AMOTION_EVENT_ACTION_MOVE) { @@ -211,9 +233,9 @@ android::base::Result<void> MotionPredictor::record(const MotionEvent& event) { 0, i), .orientation = event.getHistoricalOrientation(0, i), }); - mJerkTracker.pushSample(event.getHistoricalEventTime(i), - coords->getAxisValue(AMOTION_EVENT_AXIS_X), - coords->getAxisValue(AMOTION_EVENT_AXIS_Y)); + mJerkTracker->pushSample(event.getHistoricalEventTime(i), + coords->getAxisValue(AMOTION_EVENT_AXIS_X), + coords->getAxisValue(AMOTION_EVENT_AXIS_Y)); } if (!mLastEvent) { @@ -261,7 +283,7 @@ std::unique_ptr<MotionEvent> MotionPredictor::predict(nsecs_t timestamp) { int64_t predictionTime = mBuffers->lastTimestamp(); const int64_t futureTime = timestamp + mPredictionTimestampOffsetNanos; - const float jerkMagnitude = mJerkTracker.jerkMagnitude().value_or(0); + const float jerkMagnitude = mJerkTracker->jerkMagnitude().value_or(0); const float fractionKept = 1 - normalizeRange(jerkMagnitude, mModel->config().lowJerk, mModel->config().highJerk); // float to ensure proper division below. @@ -323,7 +345,7 @@ std::unique_ptr<MotionEvent> MotionPredictor::predict(nsecs_t timestamp) { event.getRawTransform(), event.getDownTime(), predictionTime, event.getPointerCount(), event.getPointerProperties(), &coords); } else { - prediction->addSample(predictionTime, &coords); + prediction->addSample(predictionTime, &coords, prediction->getId()); } axisFrom = axisTo; diff --git a/libs/input/Resampler.cpp b/libs/input/Resampler.cpp new file mode 100644 index 0000000000..51fadf8ec1 --- /dev/null +++ b/libs/input/Resampler.cpp @@ -0,0 +1,266 @@ +/** + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "LegacyResampler" + +#include <algorithm> +#include <chrono> + +#include <android-base/logging.h> +#include <android-base/properties.h> +#include <ftl/enum.h> + +#include <input/Resampler.h> +#include <utils/Timers.h> + +using std::chrono::nanoseconds; + +namespace android { + +namespace { + +const bool IS_DEBUGGABLE_BUILD = +#if defined(__ANDROID__) + android::base::GetBoolProperty("ro.debuggable", false); +#else + true; +#endif + +bool debugResampling() { + if (!IS_DEBUGGABLE_BUILD) { + static const bool DEBUG_TRANSPORT_RESAMPLING = + __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Resampling", + ANDROID_LOG_INFO); + return DEBUG_TRANSPORT_RESAMPLING; + } + return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Resampling", ANDROID_LOG_INFO); +} + +constexpr std::chrono::milliseconds RESAMPLE_LATENCY{5}; + +constexpr std::chrono::milliseconds RESAMPLE_MIN_DELTA{2}; + +constexpr std::chrono::milliseconds RESAMPLE_MAX_DELTA{20}; + +constexpr std::chrono::milliseconds RESAMPLE_MAX_PREDICTION{8}; + +bool canResampleTool(ToolType toolType) { + return toolType == ToolType::FINGER || toolType == ToolType::MOUSE || + toolType == ToolType::STYLUS || toolType == ToolType::UNKNOWN; +} + +inline float lerp(float a, float b, float alpha) { + return a + alpha * (b - a); +} + +PointerCoords calculateResampledCoords(const PointerCoords& a, const PointerCoords& b, + float alpha) { + // We use the value of alpha to initialize resampledCoords with the latest sample information. + PointerCoords resampledCoords = (alpha < 1.0f) ? a : b; + resampledCoords.isResampled = true; + resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_X, lerp(a.getX(), b.getX(), alpha)); + resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, lerp(a.getY(), b.getY(), alpha)); + return resampledCoords; +} +} // namespace + +void LegacyResampler::updateLatestSamples(const MotionEvent& motionEvent) { + const size_t numSamples = motionEvent.getHistorySize() + 1; + const size_t latestIndex = numSamples - 1; + const size_t secondToLatestIndex = (latestIndex > 0) ? (latestIndex - 1) : 0; + for (size_t sampleIndex = secondToLatestIndex; sampleIndex < numSamples; ++sampleIndex) { + std::vector<Pointer> pointers; + const size_t numPointers = motionEvent.getPointerCount(); + for (size_t pointerIndex = 0; pointerIndex < numPointers; ++pointerIndex) { + // getSamplePointerCoords is the vector representation of a getHistorySize by + // getPointerCount matrix. + const PointerCoords& pointerCoords = + motionEvent.getSamplePointerCoords()[sampleIndex * numPointers + pointerIndex]; + pointers.push_back( + Pointer{*motionEvent.getPointerProperties(pointerIndex), pointerCoords}); + } + mLatestSamples.pushBack( + Sample{nanoseconds{motionEvent.getHistoricalEventTime(sampleIndex)}, pointers}); + } +} + +LegacyResampler::Sample LegacyResampler::messageToSample(const InputMessage& message) { + std::vector<Pointer> pointers; + for (uint32_t i = 0; i < message.body.motion.pointerCount; ++i) { + pointers.push_back(Pointer{message.body.motion.pointers[i].properties, + message.body.motion.pointers[i].coords}); + } + return Sample{nanoseconds{message.body.motion.eventTime}, pointers}; +} + +bool LegacyResampler::pointerPropertiesResampleable(const Sample& target, const Sample& auxiliary) { + if (target.pointers.size() > auxiliary.pointers.size()) { + LOG_IF(INFO, debugResampling()) + << "Not resampled. Auxiliary sample has fewer pointers than target sample."; + return false; + } + for (size_t i = 0; i < target.pointers.size(); ++i) { + if (target.pointers[i].properties.id != auxiliary.pointers[i].properties.id) { + LOG_IF(INFO, debugResampling()) << "Not resampled. Pointer ID mismatch."; + return false; + } + if (target.pointers[i].properties.toolType != auxiliary.pointers[i].properties.toolType) { + LOG_IF(INFO, debugResampling()) << "Not resampled. Pointer ToolType mismatch."; + return false; + } + if (!canResampleTool(target.pointers[i].properties.toolType)) { + LOG_IF(INFO, debugResampling()) + << "Not resampled. Cannot resample " + << ftl::enum_string(target.pointers[i].properties.toolType) << " ToolType."; + return false; + } + } + return true; +} + +bool LegacyResampler::canInterpolate(const InputMessage& message) const { + LOG_IF(FATAL, mLatestSamples.empty()) + << "Not resampled. mLatestSamples must not be empty to interpolate."; + + const Sample& pastSample = *(mLatestSamples.end() - 1); + const Sample& futureSample = messageToSample(message); + + if (!pointerPropertiesResampleable(pastSample, futureSample)) { + return false; + } + + const nanoseconds delta = futureSample.eventTime - pastSample.eventTime; + if (delta < RESAMPLE_MIN_DELTA) { + LOG_IF(INFO, debugResampling()) << "Not resampled. Delta is too small: " << delta << "ns."; + return false; + } + return true; +} + +std::optional<LegacyResampler::Sample> LegacyResampler::attemptInterpolation( + nanoseconds resampleTime, const InputMessage& futureSample) const { + if (!canInterpolate(futureSample)) { + return std::nullopt; + } + LOG_IF(FATAL, mLatestSamples.empty()) + << "Not resampled. mLatestSamples must not be empty to interpolate."; + + const Sample& pastSample = *(mLatestSamples.end() - 1); + + const nanoseconds delta = + nanoseconds{futureSample.body.motion.eventTime} - pastSample.eventTime; + const float alpha = + std::chrono::duration<float, std::milli>(resampleTime - pastSample.eventTime) / delta; + + std::vector<Pointer> resampledPointers; + for (size_t i = 0; i < pastSample.pointers.size(); ++i) { + const PointerCoords& resampledCoords = + calculateResampledCoords(pastSample.pointers[i].coords, + futureSample.body.motion.pointers[i].coords, alpha); + resampledPointers.push_back(Pointer{pastSample.pointers[i].properties, resampledCoords}); + } + return Sample{resampleTime, resampledPointers}; +} + +bool LegacyResampler::canExtrapolate() const { + if (mLatestSamples.size() < 2) { + LOG_IF(INFO, debugResampling()) << "Not resampled. Not enough data."; + return false; + } + + const Sample& pastSample = *(mLatestSamples.end() - 2); + const Sample& presentSample = *(mLatestSamples.end() - 1); + + if (!pointerPropertiesResampleable(presentSample, pastSample)) { + return false; + } + + const nanoseconds delta = presentSample.eventTime - pastSample.eventTime; + if (delta < RESAMPLE_MIN_DELTA) { + LOG_IF(INFO, debugResampling()) << "Not resampled. Delta is too small: " << delta << "ns."; + return false; + } else if (delta > RESAMPLE_MAX_DELTA) { + LOG_IF(INFO, debugResampling()) << "Not resampled. Delta is too large: " << delta << "ns."; + return false; + } + return true; +} + +std::optional<LegacyResampler::Sample> LegacyResampler::attemptExtrapolation( + nanoseconds resampleTime) const { + if (!canExtrapolate()) { + return std::nullopt; + } + LOG_IF(FATAL, mLatestSamples.size() < 2) + << "Not resampled. mLatestSamples must have at least two samples to extrapolate."; + + const Sample& pastSample = *(mLatestSamples.end() - 2); + const Sample& presentSample = *(mLatestSamples.end() - 1); + + const nanoseconds delta = presentSample.eventTime - pastSample.eventTime; + // The farthest future time to which we can extrapolate. If the given resampleTime exceeds this, + // we use this value as the resample time target. + const nanoseconds farthestPrediction = + presentSample.eventTime + std::min<nanoseconds>(delta / 2, RESAMPLE_MAX_PREDICTION); + const nanoseconds newResampleTime = + (resampleTime > farthestPrediction) ? (farthestPrediction) : (resampleTime); + LOG_IF(INFO, debugResampling() && newResampleTime == farthestPrediction) + << "Resample time is too far in the future. Adjusting prediction from " + << (resampleTime - presentSample.eventTime) << " to " + << (farthestPrediction - presentSample.eventTime) << "ns."; + const float alpha = + std::chrono::duration<float, std::milli>(newResampleTime - pastSample.eventTime) / + delta; + + std::vector<Pointer> resampledPointers; + for (size_t i = 0; i < presentSample.pointers.size(); ++i) { + const PointerCoords& resampledCoords = + calculateResampledCoords(pastSample.pointers[i].coords, + presentSample.pointers[i].coords, alpha); + resampledPointers.push_back(Pointer{presentSample.pointers[i].properties, resampledCoords}); + } + return Sample{newResampleTime, resampledPointers}; +} + +inline void LegacyResampler::addSampleToMotionEvent(const Sample& sample, + MotionEvent& motionEvent) { + motionEvent.addSample(sample.eventTime.count(), sample.asPointerCoords().data(), + motionEvent.getId()); +} + +nanoseconds LegacyResampler::getResampleLatency() const { + return RESAMPLE_LATENCY; +} + +void LegacyResampler::resampleMotionEvent(nanoseconds frameTime, MotionEvent& motionEvent, + const InputMessage* futureSample) { + if (mPreviousDeviceId && *mPreviousDeviceId != motionEvent.getDeviceId()) { + mLatestSamples.clear(); + } + mPreviousDeviceId = motionEvent.getDeviceId(); + + const nanoseconds resampleTime = frameTime - RESAMPLE_LATENCY; + + updateLatestSamples(motionEvent); + + const std::optional<Sample> sample = (futureSample != nullptr) + ? (attemptInterpolation(resampleTime, *futureSample)) + : (attemptExtrapolation(resampleTime)); + if (sample.has_value()) { + addSampleToMotionEvent(*sample, motionEvent); + } +} +} // namespace android diff --git a/libs/input/TfLiteMotionPredictor.cpp b/libs/input/TfLiteMotionPredictor.cpp index b843a4bbf6..5250a9d2db 100644 --- a/libs/input/TfLiteMotionPredictor.cpp +++ b/libs/input/TfLiteMotionPredictor.cpp @@ -283,6 +283,7 @@ std::unique_ptr<TfLiteMotionPredictorModel> TfLiteMotionPredictorModel::create() .distanceNoiseFloor = parseXMLFloat(*configRoot, "distance-noise-floor"), .lowJerk = parseXMLFloat(*configRoot, "low-jerk"), .highJerk = parseXMLFloat(*configRoot, "high-jerk"), + .jerkAlpha = parseXMLFloat(*configRoot, "jerk-alpha"), }; return std::unique_ptr<TfLiteMotionPredictorModel>( diff --git a/libs/input/VirtualInputDevice.cpp b/libs/input/VirtualInputDevice.cpp index eea06f1720..51edbf1533 100644 --- a/libs/input/VirtualInputDevice.cpp +++ b/libs/input/VirtualInputDevice.cpp @@ -16,30 +16,267 @@ #define LOG_TAG "VirtualInputDevice" +#include <android-base/logging.h> #include <android/input.h> #include <android/keycodes.h> +#include <android_companion_virtualdevice_flags.h> #include <fcntl.h> #include <input/Input.h> #include <input/VirtualInputDevice.h> #include <linux/uinput.h> -#include <math.h> -#include <utils/Log.h> -#include <map> #include <string> using android::base::unique_fd; +namespace { + /** * Log debug messages about native virtual input devices. * Enable this via "adb shell setprop log.tag.VirtualInputDevice DEBUG" */ -static bool isDebug() { +bool isDebug() { return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO); } +unique_fd invalidFd() { + return unique_fd(-1); +} + +} // namespace + namespace android { +namespace vd_flags = android::companion::virtualdevice::flags; + +/** Creates a new uinput device and assigns a file descriptor. */ +unique_fd openUinput(const char* readableName, int32_t vendorId, int32_t productId, + const char* phys, DeviceType deviceType, int32_t screenHeight, + int32_t screenWidth) { + unique_fd fd(TEMP_FAILURE_RETRY(::open("/dev/uinput", O_WRONLY | O_NONBLOCK))); + if (fd < 0) { + ALOGE("Error creating uinput device: %s", strerror(errno)); + return invalidFd(); + } + + ioctl(fd, UI_SET_PHYS, phys); + + ioctl(fd, UI_SET_EVBIT, EV_KEY); + ioctl(fd, UI_SET_EVBIT, EV_SYN); + switch (deviceType) { + case DeviceType::DPAD: + for (const auto& [_, keyCode] : VirtualDpad::DPAD_KEY_CODE_MAPPING) { + ioctl(fd, UI_SET_KEYBIT, keyCode); + } + break; + case DeviceType::KEYBOARD: + for (const auto& [_, keyCode] : VirtualKeyboard::KEY_CODE_MAPPING) { + ioctl(fd, UI_SET_KEYBIT, keyCode); + } + break; + case DeviceType::MOUSE: + ioctl(fd, UI_SET_EVBIT, EV_REL); + ioctl(fd, UI_SET_KEYBIT, BTN_LEFT); + ioctl(fd, UI_SET_KEYBIT, BTN_RIGHT); + ioctl(fd, UI_SET_KEYBIT, BTN_MIDDLE); + ioctl(fd, UI_SET_KEYBIT, BTN_BACK); + ioctl(fd, UI_SET_KEYBIT, BTN_FORWARD); + ioctl(fd, UI_SET_RELBIT, REL_X); + ioctl(fd, UI_SET_RELBIT, REL_Y); + ioctl(fd, UI_SET_RELBIT, REL_WHEEL); + ioctl(fd, UI_SET_RELBIT, REL_HWHEEL); + if (vd_flags::high_resolution_scroll()) { + ioctl(fd, UI_SET_RELBIT, REL_WHEEL_HI_RES); + ioctl(fd, UI_SET_RELBIT, REL_HWHEEL_HI_RES); + } + break; + case DeviceType::TOUCHSCREEN: + ioctl(fd, UI_SET_EVBIT, EV_ABS); + ioctl(fd, UI_SET_KEYBIT, BTN_TOUCH); + ioctl(fd, UI_SET_ABSBIT, ABS_MT_SLOT); + ioctl(fd, UI_SET_ABSBIT, ABS_MT_POSITION_X); + ioctl(fd, UI_SET_ABSBIT, ABS_MT_POSITION_Y); + ioctl(fd, UI_SET_ABSBIT, ABS_MT_TRACKING_ID); + ioctl(fd, UI_SET_ABSBIT, ABS_MT_TOOL_TYPE); + ioctl(fd, UI_SET_ABSBIT, ABS_MT_TOUCH_MAJOR); + ioctl(fd, UI_SET_ABSBIT, ABS_MT_PRESSURE); + ioctl(fd, UI_SET_PROPBIT, INPUT_PROP_DIRECT); + break; + case DeviceType::STYLUS: + ioctl(fd, UI_SET_EVBIT, EV_ABS); + ioctl(fd, UI_SET_KEYBIT, BTN_TOUCH); + ioctl(fd, UI_SET_KEYBIT, BTN_STYLUS); + ioctl(fd, UI_SET_KEYBIT, BTN_STYLUS2); + ioctl(fd, UI_SET_KEYBIT, BTN_TOOL_PEN); + ioctl(fd, UI_SET_KEYBIT, BTN_TOOL_RUBBER); + ioctl(fd, UI_SET_ABSBIT, ABS_X); + ioctl(fd, UI_SET_ABSBIT, ABS_Y); + ioctl(fd, UI_SET_ABSBIT, ABS_TILT_X); + ioctl(fd, UI_SET_ABSBIT, ABS_TILT_Y); + ioctl(fd, UI_SET_ABSBIT, ABS_PRESSURE); + ioctl(fd, UI_SET_PROPBIT, INPUT_PROP_DIRECT); + break; + case DeviceType::ROTARY_ENCODER: + ioctl(fd, UI_SET_EVBIT, EV_REL); + ioctl(fd, UI_SET_RELBIT, REL_WHEEL); + if (vd_flags::high_resolution_scroll()) { + ioctl(fd, UI_SET_RELBIT, REL_WHEEL_HI_RES); + } + break; + default: + ALOGE("Invalid input device type %d", static_cast<int32_t>(deviceType)); + return invalidFd(); + } + + int version; + if (ioctl(fd, UI_GET_VERSION, &version) == 0 && version >= 5) { + uinput_setup setup; + memset(&setup, 0, sizeof(setup)); + std::strncpy(setup.name, readableName, UINPUT_MAX_NAME_SIZE); + setup.id.version = 1; + setup.id.bustype = BUS_VIRTUAL; + setup.id.vendor = vendorId; + setup.id.product = productId; + if (deviceType == DeviceType::TOUCHSCREEN) { + uinput_abs_setup xAbsSetup; + xAbsSetup.code = ABS_MT_POSITION_X; + xAbsSetup.absinfo.maximum = screenWidth - 1; + xAbsSetup.absinfo.minimum = 0; + if (ioctl(fd, UI_ABS_SETUP, &xAbsSetup) != 0) { + ALOGE("Error creating touchscreen uinput x axis: %s", strerror(errno)); + return invalidFd(); + } + uinput_abs_setup yAbsSetup; + yAbsSetup.code = ABS_MT_POSITION_Y; + yAbsSetup.absinfo.maximum = screenHeight - 1; + yAbsSetup.absinfo.minimum = 0; + if (ioctl(fd, UI_ABS_SETUP, &yAbsSetup) != 0) { + ALOGE("Error creating touchscreen uinput y axis: %s", strerror(errno)); + return invalidFd(); + } + uinput_abs_setup majorAbsSetup; + majorAbsSetup.code = ABS_MT_TOUCH_MAJOR; + majorAbsSetup.absinfo.maximum = screenWidth - 1; + majorAbsSetup.absinfo.minimum = 0; + if (ioctl(fd, UI_ABS_SETUP, &majorAbsSetup) != 0) { + ALOGE("Error creating touchscreen uinput major axis: %s", strerror(errno)); + return invalidFd(); + } + uinput_abs_setup pressureAbsSetup; + pressureAbsSetup.code = ABS_MT_PRESSURE; + pressureAbsSetup.absinfo.maximum = 255; + pressureAbsSetup.absinfo.minimum = 0; + if (ioctl(fd, UI_ABS_SETUP, &pressureAbsSetup) != 0) { + ALOGE("Error creating touchscreen uinput pressure axis: %s", strerror(errno)); + return invalidFd(); + } + uinput_abs_setup slotAbsSetup; + slotAbsSetup.code = ABS_MT_SLOT; + slotAbsSetup.absinfo.maximum = MAX_POINTERS - 1; + slotAbsSetup.absinfo.minimum = 0; + if (ioctl(fd, UI_ABS_SETUP, &slotAbsSetup) != 0) { + ALOGE("Error creating touchscreen uinput slots: %s", strerror(errno)); + return invalidFd(); + } + uinput_abs_setup trackingIdAbsSetup; + trackingIdAbsSetup.code = ABS_MT_TRACKING_ID; + trackingIdAbsSetup.absinfo.maximum = MAX_POINTERS - 1; + trackingIdAbsSetup.absinfo.minimum = 0; + if (ioctl(fd, UI_ABS_SETUP, &trackingIdAbsSetup) != 0) { + ALOGE("Error creating touchscreen uinput tracking ids: %s", strerror(errno)); + return invalidFd(); + } + } else if (deviceType == DeviceType::STYLUS) { + uinput_abs_setup xAbsSetup; + xAbsSetup.code = ABS_X; + xAbsSetup.absinfo.maximum = screenWidth - 1; + xAbsSetup.absinfo.minimum = 0; + if (ioctl(fd, UI_ABS_SETUP, &xAbsSetup) != 0) { + ALOGE("Error creating stylus uinput x axis: %s", strerror(errno)); + return invalidFd(); + } + uinput_abs_setup yAbsSetup; + yAbsSetup.code = ABS_Y; + yAbsSetup.absinfo.maximum = screenHeight - 1; + yAbsSetup.absinfo.minimum = 0; + if (ioctl(fd, UI_ABS_SETUP, &yAbsSetup) != 0) { + ALOGE("Error creating stylus uinput y axis: %s", strerror(errno)); + return invalidFd(); + } + uinput_abs_setup tiltXAbsSetup; + tiltXAbsSetup.code = ABS_TILT_X; + tiltXAbsSetup.absinfo.maximum = 90; + tiltXAbsSetup.absinfo.minimum = -90; + if (ioctl(fd, UI_ABS_SETUP, &tiltXAbsSetup) != 0) { + ALOGE("Error creating stylus uinput tilt x axis: %s", strerror(errno)); + return invalidFd(); + } + uinput_abs_setup tiltYAbsSetup; + tiltYAbsSetup.code = ABS_TILT_Y; + tiltYAbsSetup.absinfo.maximum = 90; + tiltYAbsSetup.absinfo.minimum = -90; + if (ioctl(fd, UI_ABS_SETUP, &tiltYAbsSetup) != 0) { + ALOGE("Error creating stylus uinput tilt y axis: %s", strerror(errno)); + return invalidFd(); + } + uinput_abs_setup pressureAbsSetup; + pressureAbsSetup.code = ABS_PRESSURE; + pressureAbsSetup.absinfo.maximum = 255; + pressureAbsSetup.absinfo.minimum = 0; + if (ioctl(fd, UI_ABS_SETUP, &pressureAbsSetup) != 0) { + ALOGE("Error creating touchscreen uinput pressure axis: %s", strerror(errno)); + return invalidFd(); + } + } + if (ioctl(fd, UI_DEV_SETUP, &setup) != 0) { + ALOGE("Error creating uinput device: %s", strerror(errno)); + return invalidFd(); + } + } else { + // UI_DEV_SETUP was not introduced until version 5. Try setting up manually. + ALOGI("Falling back to version %d manual setup", version); + uinput_user_dev fallback; + memset(&fallback, 0, sizeof(fallback)); + std::strncpy(fallback.name, readableName, UINPUT_MAX_NAME_SIZE); + fallback.id.version = 1; + fallback.id.bustype = BUS_VIRTUAL; + fallback.id.vendor = vendorId; + fallback.id.product = productId; + if (deviceType == DeviceType::TOUCHSCREEN) { + fallback.absmin[ABS_MT_POSITION_X] = 0; + fallback.absmax[ABS_MT_POSITION_X] = screenWidth - 1; + fallback.absmin[ABS_MT_POSITION_Y] = 0; + fallback.absmax[ABS_MT_POSITION_Y] = screenHeight - 1; + fallback.absmin[ABS_MT_TOUCH_MAJOR] = 0; + fallback.absmax[ABS_MT_TOUCH_MAJOR] = screenWidth - 1; + fallback.absmin[ABS_MT_PRESSURE] = 0; + fallback.absmax[ABS_MT_PRESSURE] = 255; + } else if (deviceType == DeviceType::STYLUS) { + fallback.absmin[ABS_X] = 0; + fallback.absmax[ABS_X] = screenWidth - 1; + fallback.absmin[ABS_Y] = 0; + fallback.absmax[ABS_Y] = screenHeight - 1; + fallback.absmin[ABS_TILT_X] = -90; + fallback.absmax[ABS_TILT_X] = 90; + fallback.absmin[ABS_TILT_Y] = -90; + fallback.absmax[ABS_TILT_Y] = 90; + fallback.absmin[ABS_PRESSURE] = 0; + fallback.absmax[ABS_PRESSURE] = 255; + } + if (TEMP_FAILURE_RETRY(write(fd, &fallback, sizeof(fallback))) != sizeof(fallback)) { + ALOGE("Error creating uinput device: %s", strerror(errno)); + return invalidFd(); + } + } + + if (ioctl(fd, UI_DEV_CREATE) != 0) { + ALOGE("Error creating uinput device: %s", strerror(errno)); + return invalidFd(); + } + + return fd; +} + VirtualInputDevice::VirtualInputDevice(unique_fd fd) : mFd(std::move(fd)) {} VirtualInputDevice::~VirtualInputDevice() { @@ -253,7 +490,10 @@ const std::map<int, int> VirtualMouse::BUTTON_CODE_MAPPING = { // clang-format on }; -VirtualMouse::VirtualMouse(unique_fd fd) : VirtualInputDevice(std::move(fd)) {} +VirtualMouse::VirtualMouse(unique_fd fd) + : VirtualInputDevice(std::move(fd)), + mAccumulatedHighResScrollX(0), + mAccumulatedHighResScrollY(0) {} VirtualMouse::~VirtualMouse() {} @@ -272,9 +512,47 @@ bool VirtualMouse::writeRelativeEvent(float relativeX, float relativeY, bool VirtualMouse::writeScrollEvent(float xAxisMovement, float yAxisMovement, std::chrono::nanoseconds eventTime) { - return writeInputEvent(EV_REL, REL_HWHEEL, xAxisMovement, eventTime) && - writeInputEvent(EV_REL, REL_WHEEL, yAxisMovement, eventTime) && - writeInputEvent(EV_SYN, SYN_REPORT, 0, eventTime); + if (!vd_flags::high_resolution_scroll()) { + return writeInputEvent(EV_REL, REL_HWHEEL, static_cast<int32_t>(xAxisMovement), + eventTime) && + writeInputEvent(EV_REL, REL_WHEEL, static_cast<int32_t>(yAxisMovement), + eventTime) && + writeInputEvent(EV_SYN, SYN_REPORT, 0, eventTime); + } + + const auto highResScrollX = + static_cast<int32_t>(xAxisMovement * kEvdevHighResScrollUnitsPerDetent); + const auto highResScrollY = + static_cast<int32_t>(yAxisMovement * kEvdevHighResScrollUnitsPerDetent); + bool highResScrollResult = + writeInputEvent(EV_REL, REL_HWHEEL_HI_RES, highResScrollX, eventTime) && + writeInputEvent(EV_REL, REL_WHEEL_HI_RES, highResScrollY, eventTime); + if (!highResScrollResult) { + return false; + } + + // According to evdev spec, a high-resolution mouse needs to emit REL_WHEEL / REL_HWHEEL events + // in addition to high-res scroll events. Regular scroll events can approximate high-res scroll + // events, so we send a regular scroll event when the accumulated scroll motion reaches a detent + // (single mouse wheel click). + mAccumulatedHighResScrollX += highResScrollX; + mAccumulatedHighResScrollY += highResScrollY; + const int32_t scrollX = mAccumulatedHighResScrollX / kEvdevHighResScrollUnitsPerDetent; + const int32_t scrollY = mAccumulatedHighResScrollY / kEvdevHighResScrollUnitsPerDetent; + if (scrollX != 0) { + if (!writeInputEvent(EV_REL, REL_HWHEEL, scrollX, eventTime)) { + return false; + } + mAccumulatedHighResScrollX %= kEvdevHighResScrollUnitsPerDetent; + } + if (scrollY != 0) { + if (!writeInputEvent(EV_REL, REL_WHEEL, scrollY, eventTime)) { + return false; + } + mAccumulatedHighResScrollY %= kEvdevHighResScrollUnitsPerDetent; + } + + return writeInputEvent(EV_SYN, SYN_REPORT, 0, eventTime); } // --- VirtualTouchscreen --- @@ -509,4 +787,39 @@ bool VirtualStylus::handleStylusUp(uint16_t tool, std::chrono::nanoseconds event return true; } +// --- VirtualRotaryEncoder --- +VirtualRotaryEncoder::VirtualRotaryEncoder(unique_fd fd) + : VirtualInputDevice(std::move(fd)), mAccumulatedHighResScrollAmount(0) {} + +VirtualRotaryEncoder::~VirtualRotaryEncoder() {} + +bool VirtualRotaryEncoder::writeScrollEvent(float scrollAmount, + std::chrono::nanoseconds eventTime) { + if (!vd_flags::high_resolution_scroll()) { + return writeInputEvent(EV_REL, REL_WHEEL, static_cast<int32_t>(scrollAmount), eventTime) && + writeInputEvent(EV_SYN, SYN_REPORT, 0, eventTime); + } + + const auto highResScrollAmount = + static_cast<int32_t>(scrollAmount * kEvdevHighResScrollUnitsPerDetent); + if (!writeInputEvent(EV_REL, REL_WHEEL_HI_RES, highResScrollAmount, eventTime)) { + return false; + } + + // According to evdev spec, a high-resolution scroll device needs to emit REL_WHEEL / REL_HWHEEL + // events in addition to high-res scroll events. Regular scroll events can approximate high-res + // scroll events, so we send a regular scroll event when the accumulated scroll motion reaches a + // detent (single wheel click). + mAccumulatedHighResScrollAmount += highResScrollAmount; + const int32_t scroll = mAccumulatedHighResScrollAmount / kEvdevHighResScrollUnitsPerDetent; + if (scroll != 0) { + if (!writeInputEvent(EV_REL, REL_WHEEL, scroll, eventTime)) { + return false; + } + mAccumulatedHighResScrollAmount %= kEvdevHighResScrollUnitsPerDetent; + } + + return writeInputEvent(EV_SYN, SYN_REPORT, 0, eventTime); +} + } // namespace android diff --git a/libs/input/android/os/IInputConstants.aidl b/libs/input/android/os/IInputConstants.aidl index a77dfa59fe..e23fc94c5e 100644 --- a/libs/input/android/os/IInputConstants.aidl +++ b/libs/input/android/os/IInputConstants.aidl @@ -49,130 +49,24 @@ interface IInputConstants const int POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY = 0x20000; /** - * This flag indicates that the window that received this motion event is partly - * or wholly obscured by another visible window above it and the event directly passed through - * the obscured area. - * - * A security sensitive application can check this flag to identify situations in which - * a malicious application may have covered up part of its content for the purpose - * of misleading the user or hijacking touches. An appropriate response might be - * to drop the suspect touches or to take additional precautions to confirm the user's - * actual intent. - */ - const int MOTION_EVENT_FLAG_WINDOW_IS_OBSCURED = 0x1; - - /** - * This flag indicates that the window that received this motion event is partly - * or wholly obscured by another visible window above it and the event did not directly pass - * through the obscured area. - * - * A security sensitive application can check this flag to identify situations in which - * a malicious application may have covered up part of its content for the purpose - * of misleading the user or hijacking touches. An appropriate response might be - * to drop the suspect touches or to take additional precautions to confirm the user's - * actual intent. - * - * Unlike FLAG_WINDOW_IS_OBSCURED, this is only true if the window that received this event is - * obstructed in areas other than the touched location. - */ - const int MOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED = 0x2; - - /** - * This private flag is only set on {@link #ACTION_HOVER_MOVE} events and indicates that - * this event will be immediately followed by a {@link #ACTION_HOVER_EXIT}. It is used to - * prevent generating redundant {@link #ACTION_HOVER_ENTER} events. - * @hide - */ - const int MOTION_EVENT_FLAG_HOVER_EXIT_PENDING = 0x4; - - /** - * This flag indicates that the event has been generated by a gesture generator. It - * provides a hint to the GestureDetector to not apply any touch slop. - * - * @hide - */ - const int MOTION_EVENT_FLAG_IS_GENERATED_GESTURE = 0x8; - - /** - * This flag is only set for events with {@link #ACTION_POINTER_UP} and {@link #ACTION_CANCEL}. - * It indicates that the pointer going up was an unintentional user touch. When FLAG_CANCELED - * is set, the typical actions that occur in response for a pointer going up (such as click - * handlers, end of drawing) should be aborted. This flag is typically set when the user was - * accidentally touching the screen, such as by gripping the device, or placing the palm on the - * screen. - * - * @see #ACTION_POINTER_UP - * @see #ACTION_CANCEL + * Common input event flag used for both motion and key events for a gesture or pointer being + * canceled. */ const int INPUT_EVENT_FLAG_CANCELED = 0x20; /** - * This flag indicates that the event will not cause a focus change if it is directed to an - * unfocused window, even if it an {@link #ACTION_DOWN}. This is typically used with pointer - * gestures to allow the user to direct gestures to an unfocused window without bringing the - * window into focus. - * @hide - */ - const int MOTION_EVENT_FLAG_NO_FOCUS_CHANGE = 0x40; - - /** - * This flag indicates that the event has a valid value for AXIS_ORIENTATION. - * - * This is a private flag that is not used in Java. - * @hide - */ - const int MOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION = 0x80; - - /** - * This flag indicates that the pointers' AXIS_ORIENTATION can be used to precisely determine - * the direction in which the tool is pointing. The value of the orientation axis will be in - * the range [-pi, pi], which represents a full circle. This is usually supported by devices - * like styluses. - * - * Conversely, AXIS_ORIENTATION cannot be used to tell which direction the tool is pointing - * when this flag is not set. In this case, the axis value will have a range of [-pi/2, pi/2], - * which represents half a circle. This is usually the case for devices like touchscreens and - * touchpads, for which it is difficult to tell which direction along the major axis of the - * touch ellipse the finger is pointing. - * - * This is a private flag that is not used in Java. - * @hide - */ - const int MOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION = 0x100; - - /** - * The input event was generated or modified by accessibility service. - * Shared by both KeyEvent and MotionEvent flags, so this value should not overlap with either - * set of flags, including in input/Input.h and in android/input.h. + * Common input event flag used for both motion and key events, indicating that the event + * was generated or modified by accessibility service. */ const int INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT = 0x800; /** - * Private flag that indicates when the system has detected that this motion event - * may be inconsistent with respect to the sequence of previously delivered motion events, - * such as when a pointer move event is sent but the pointer is not down. - * - * @hide - * @see #isTainted - * @see #setTainted + * Common input event flag used for both motion and key events, indicating that the system has + * detected this event may be inconsistent with the current event sequence or gesture, such as + * when a pointer move event is sent but the pointer is not down. */ const int INPUT_EVENT_FLAG_TAINTED = 0x80000000; - /** - * Private flag indicating that this event was synthesized by the system and should be delivered - * to the accessibility focused view first. When being dispatched such an event is not handled - * by predecessors of the accessibility focused view and after the event reaches that view the - * flag is cleared and normal event dispatch is performed. This ensures that the platform can - * click on any view that has accessibility focus which is semantically equivalent to asking the - * view to perform a click accessibility action but more generic as views not implementing click - * action correctly can still be activated. - * - * @hide - * @see #isTargetAccessibilityFocus() - * @see #setTargetAccessibilityFocus(boolean) - */ - const int MOTION_EVENT_FLAG_TARGET_ACCESSIBILITY_FOCUS = 0x40000000; - /* The default pointer acceleration value. */ const int DEFAULT_POINTER_ACCELERATION = 3; diff --git a/libs/input/android/os/MotionEventFlag.aidl b/libs/input/android/os/MotionEventFlag.aidl new file mode 100644 index 0000000000..2093b0636a --- /dev/null +++ b/libs/input/android/os/MotionEventFlag.aidl @@ -0,0 +1,152 @@ +/** + * Copyright 2024, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +import android.os.IInputConstants; + +/** + * Flag definitions for MotionEvents. + * @hide + */ +@Backing(type="int") +enum MotionEventFlag { + + /** + * This flag indicates that the window that received this motion event is partly + * or wholly obscured by another visible window above it and the event directly passed through + * the obscured area. + * + * A security sensitive application can check this flag to identify situations in which + * a malicious application may have covered up part of its content for the purpose + * of misleading the user or hijacking touches. An appropriate response might be + * to drop the suspect touches or to take additional precautions to confirm the user's + * actual intent. + */ + WINDOW_IS_OBSCURED = 0x1, + + /** + * This flag indicates that the window that received this motion event is partly + * or wholly obscured by another visible window above it and the event did not directly pass + * through the obscured area. + * + * A security sensitive application can check this flag to identify situations in which + * a malicious application may have covered up part of its content for the purpose + * of misleading the user or hijacking touches. An appropriate response might be + * to drop the suspect touches or to take additional precautions to confirm the user's + * actual intent. + * + * Unlike FLAG_WINDOW_IS_OBSCURED, this is only true if the window that received this event is + * obstructed in areas other than the touched location. + */ + WINDOW_IS_PARTIALLY_OBSCURED = 0x2, + + /** + * This private flag is only set on {@link #ACTION_HOVER_MOVE} events and indicates that + * this event will be immediately followed by a {@link #ACTION_HOVER_EXIT}. It is used to + * prevent generating redundant {@link #ACTION_HOVER_ENTER} events. + * @hide + */ + HOVER_EXIT_PENDING = 0x4, + + /** + * This flag indicates that the event has been generated by a gesture generator. It + * provides a hint to the GestureDetector to not apply any touch slop. + * + * @hide + */ + IS_GENERATED_GESTURE = 0x8, + + /** + * This flag is only set for events with {@link #ACTION_POINTER_UP} and {@link #ACTION_CANCEL}. + * It indicates that the pointer going up was an unintentional user touch. When FLAG_CANCELED + * is set, the typical actions that occur in response for a pointer going up (such as click + * handlers, end of drawing) should be aborted. This flag is typically set when the user was + * accidentally touching the screen, such as by gripping the device, or placing the palm on the + * screen. + * + * @see #ACTION_POINTER_UP + * @see #ACTION_CANCEL + */ + CANCELED = IInputConstants.INPUT_EVENT_FLAG_CANCELED, + + /** + * This flag indicates that the event will not cause a focus change if it is directed to an + * unfocused window, even if it an {@link #ACTION_DOWN}. This is typically used with pointer + * gestures to allow the user to direct gestures to an unfocused window without bringing the + * window into focus. + * @hide + */ + NO_FOCUS_CHANGE = 0x40, + + /** + * This flag indicates that the event has a valid value for AXIS_ORIENTATION. + * + * This is a private flag that is not used in Java. + * @hide + */ + PRIVATE_FLAG_SUPPORTS_ORIENTATION = 0x80, + + /** + * This flag indicates that the pointers' AXIS_ORIENTATION can be used to precisely determine + * the direction in which the tool is pointing. The value of the orientation axis will be in + * the range [-pi, pi], which represents a full circle. This is usually supported by devices + * like styluses. + * + * Conversely, AXIS_ORIENTATION cannot be used to tell which direction the tool is pointing + * when this flag is not set. In this case, the axis value will have a range of [-pi/2, pi/2], + * which represents half a circle. This is usually the case for devices like touchscreens and + * touchpads, for which it is difficult to tell which direction along the major axis of the + * touch ellipse the finger is pointing. + * + * This is a private flag that is not used in Java. + * @hide + */ + PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION = 0x100, + + /** + * The input event was generated or modified by accessibility service. + * Shared by both KeyEvent and MotionEvent flags, so this value should not overlap with either + * set of flags, including in input/Input.h and in android/input.h. + */ + IS_ACCESSIBILITY_EVENT = IInputConstants.INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT, + + /** + * Private flag that indicates when the system has detected that this motion event + * may be inconsistent with respect to the sequence of previously delivered motion events, + * such as when a pointer move event is sent but the pointer is not down. + * + * @hide + * @see #isTainted + * @see #setTainted + */ + TAINTED = IInputConstants.INPUT_EVENT_FLAG_TAINTED, + + /** + * Private flag indicating that this event was synthesized by the system and should be delivered + * to the accessibility focused view first. When being dispatched such an event is not handled + * by predecessors of the accessibility focused view and after the event reaches that view the + * flag is cleared and normal event dispatch is performed. This ensures that the platform can + * click on any view that has accessibility focus which is semantically equivalent to asking the + * view to perform a click accessibility action but more generic as views not implementing click + * action correctly can still be activated. + * + * @hide + * @see #isTargetAccessibilityFocus() + * @see #setTargetAccessibilityFocus(boolean) + */ + TARGET_ACCESSIBILITY_FOCUS = 0x40000000, +} diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig index a2192cbdc4..60fb00e128 100644 --- a/libs/input/input_flags.aconfig +++ b/libs/input/input_flags.aconfig @@ -16,13 +16,13 @@ flag { } flag { - name: "enable_gestures_library_timer_provider" + name: "remove_input_channel_from_windowstate" namespace: "input" - description: "Set to true to enable timer support for the touchpad Gestures library" - bug: "297192727" - } + description: "Do not store a copy of input channel inside WindowState." + bug: "323450804" +} - flag { +flag { name: "enable_input_event_tracing" namespace: "input" description: "Set to true to enable input event tracing, including always-on tracing on non-user builds" @@ -37,6 +37,13 @@ flag { } flag { + name: "split_all_touches" + namespace: "input" + description: "Set FLAG_SPLIT_TOUCHES to true for all windows, regardless of what they specify. This is essentially deprecating this flag by forcefully enabling the split functionality" + bug: "239934827" +} + +flag { name: "a11y_crash_on_inconsistent_event_stream" namespace: "accessibility" description: "Brings back fatal logging for inconsistent event streams originating from accessibility." @@ -87,13 +94,6 @@ flag { } flag { - name: "remove_pointer_event_tracking_in_wm" - namespace: "input" - description: "Remove pointer event tracking in WM after the Pointer Icon Refactor" - bug: "315321016" -} - -flag { name: "enable_new_mouse_pointer_ballistics" namespace: "input" description: "Change the acceleration curves for mouse pointer movements to match the touchpad ones" @@ -157,3 +157,53 @@ flag { description: "Keyboard classifier that classifies all keyboards into alphabetic or non-alphabetic" bug: "263559234" } + +flag { + name: "show_pointers_for_partial_screenshare" + namespace: "input" + description: "Show touch and pointer indicators when mirroring a single task" + bug: "310179437" +} + +flag { + name: "include_relative_axis_values_for_captured_touchpads" + namespace: "input" + description: "Include AXIS_RELATIVE_X and AXIS_RELATIVE_Y values when reporting touches from captured touchpads." + bug: "330522990" +} + +flag { + name: "enable_per_device_input_latency_metrics" + namespace: "input" + description: "Capture input latency metrics on a per device granular level using histograms." + bug: "270049345" +} + +flag { + name: "collect_palm_rejection_quality_metrics" + namespace: "input" + description: "Collect quality metrics on framework palm rejection." + bug: "341717757" +} + +flag { + name: "enable_touchpad_no_focus_change" + namespace: "input" + description: "Prevents touchpad gesture changing window focus." + bug: "364460018" +} + +flag { + name: "enable_input_policy_profile" + namespace: "input" + description: "Apply input policy profile for input threads." + bug: "347122505" + is_fixed_read_only: true +} + +flag { + name: "keyboard_repeat_keys" + namespace: "input" + description: "Allow user to enable key repeats or configure timeout before key repeat and key repeat delay rates." + bug: "336585002" +} diff --git a/libs/input/rust/Android.bp b/libs/input/rust/Android.bp index 018d199ce2..63853f77fa 100644 --- a/libs/input/rust/Android.bp +++ b/libs/input/rust/Android.bp @@ -24,6 +24,8 @@ rust_defaults { "liblogger", "liblog_rust", "inputconstants-rust", + "libserde", + "libserde_json", ], whole_static_libs: [ "libinput_from_rust_to_cpp", diff --git a/libs/input/rust/data_store.rs b/libs/input/rust/data_store.rs new file mode 100644 index 0000000000..6bdcefda36 --- /dev/null +++ b/libs/input/rust/data_store.rs @@ -0,0 +1,232 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//! Contains the DataStore, used to store input related data in a persistent way. + +use crate::input::KeyboardType; +use log::{debug, error}; +use serde::{Deserialize, Serialize}; +use std::fs::File; +use std::io::{Read, Write}; +use std::path::Path; +use std::sync::{Arc, RwLock}; + +/// Data store to be used to store information that persistent across device reboots. +pub struct DataStore { + file_reader_writer: Box<dyn FileReaderWriter>, + inner: Arc<RwLock<DataStoreInner>>, +} + +#[derive(Default)] +struct DataStoreInner { + is_loaded: bool, + data: Data, +} + +#[derive(Default, Serialize, Deserialize)] +struct Data { + // Map storing data for keyboard classification for specific devices. + #[serde(default)] + keyboard_classifications: Vec<KeyboardClassification>, + // NOTE: Important things to consider: + // - Add any data that needs to be persisted here in this struct. + // - Mark all new fields with "#[serde(default)]" for backward compatibility. + // - Also, you can't modify the already added fields. + // - Can add new nested fields to existing structs. e.g. Add another field to the struct + // KeyboardClassification and mark it "#[serde(default)]". +} + +#[derive(Default, Serialize, Deserialize)] +struct KeyboardClassification { + descriptor: String, + keyboard_type: KeyboardType, + is_finalized: bool, +} + +impl DataStore { + /// Creates a new instance of Data store + pub fn new(file_reader_writer: Box<dyn FileReaderWriter>) -> Self { + Self { file_reader_writer, inner: Default::default() } + } + + fn load(&mut self) { + if self.inner.read().unwrap().is_loaded { + return; + } + self.load_internal(); + } + + fn load_internal(&mut self) { + let s = self.file_reader_writer.read(); + let data: Data = if !s.is_empty() { + let deserialize: Data = match serde_json::from_str(&s) { + Ok(deserialize) => deserialize, + Err(msg) => { + error!("Unable to deserialize JSON data into struct: {:?} -> {:?}", msg, s); + Default::default() + } + }; + deserialize + } else { + Default::default() + }; + + let mut inner = self.inner.write().unwrap(); + inner.data = data; + inner.is_loaded = true; + } + + fn save(&mut self) { + let string_to_save; + { + let inner = self.inner.read().unwrap(); + string_to_save = serde_json::to_string(&inner.data).unwrap(); + } + self.file_reader_writer.write(string_to_save); + } + + /// Get keyboard type of the device (as stored in the data store) + pub fn get_keyboard_type(&mut self, descriptor: &String) -> Option<(KeyboardType, bool)> { + self.load(); + let data = &self.inner.read().unwrap().data; + for keyboard_classification in data.keyboard_classifications.iter() { + if keyboard_classification.descriptor == *descriptor { + return Some(( + keyboard_classification.keyboard_type, + keyboard_classification.is_finalized, + )); + } + } + None + } + + /// Save keyboard type of the device in the data store + pub fn set_keyboard_type( + &mut self, + descriptor: &String, + keyboard_type: KeyboardType, + is_finalized: bool, + ) { + { + let data = &mut self.inner.write().unwrap().data; + data.keyboard_classifications + .retain(|classification| classification.descriptor != *descriptor); + data.keyboard_classifications.push(KeyboardClassification { + descriptor: descriptor.to_string(), + keyboard_type, + is_finalized, + }) + } + self.save(); + } +} + +pub trait FileReaderWriter { + fn read(&self) -> String; + fn write(&self, to_write: String); +} + +/// Default file reader writer implementation +pub struct DefaultFileReaderWriter { + filepath: String, +} + +impl DefaultFileReaderWriter { + /// Creates a new instance of Default file reader writer that can read and write string to a + /// particular file in the filesystem + pub fn new(filepath: String) -> Self { + Self { filepath } + } +} + +impl FileReaderWriter for DefaultFileReaderWriter { + fn read(&self) -> String { + let path = Path::new(&self.filepath); + let mut fs_string = String::new(); + match File::open(path) { + Err(e) => error!("couldn't open {:?}: {}", path, e), + Ok(mut file) => match file.read_to_string(&mut fs_string) { + Err(e) => error!("Couldn't read from {:?}: {}", path, e), + Ok(_) => debug!("Successfully read from file {:?}", path), + }, + }; + fs_string + } + + fn write(&self, to_write: String) { + let path = Path::new(&self.filepath); + match File::create(path) { + Err(e) => error!("couldn't create {:?}: {}", path, e), + Ok(mut file) => match file.write_all(to_write.as_bytes()) { + Err(e) => error!("Couldn't write to {:?}: {}", path, e), + Ok(_) => debug!("Successfully saved to file {:?}", path), + }, + }; + } +} + +#[cfg(test)] +mod tests { + use crate::data_store::{ + test_file_reader_writer::TestFileReaderWriter, DataStore, FileReaderWriter, + }; + use crate::input::KeyboardType; + + #[test] + fn test_backward_compatibility_version_1() { + // This test tests JSON string that will be created by the first version of data store + // This test SHOULD NOT be modified + let test_reader_writer = TestFileReaderWriter::new(); + test_reader_writer.write(r#"{"keyboard_classifications":[{"descriptor":"descriptor","keyboard_type":{"type":"Alphabetic"},"is_finalized":true}]}"#.to_string()); + + let mut data_store = DataStore::new(Box::new(test_reader_writer)); + let (keyboard_type, is_finalized) = + data_store.get_keyboard_type(&"descriptor".to_string()).unwrap(); + assert_eq!(keyboard_type, KeyboardType::Alphabetic); + assert!(is_finalized); + } +} + +#[cfg(test)] +pub mod test_file_reader_writer { + + use crate::data_store::FileReaderWriter; + use std::sync::{Arc, RwLock}; + + #[derive(Default)] + struct TestFileReaderWriterInner { + fs_string: String, + } + + #[derive(Default, Clone)] + pub struct TestFileReaderWriter(Arc<RwLock<TestFileReaderWriterInner>>); + + impl TestFileReaderWriter { + pub fn new() -> Self { + Default::default() + } + } + + impl FileReaderWriter for TestFileReaderWriter { + fn read(&self) -> String { + self.0.read().unwrap().fs_string.clone() + } + + fn write(&self, fs_string: String) { + self.0.write().unwrap().fs_string = fs_string; + } + } +} diff --git a/libs/input/rust/input.rs b/libs/input/rust/input.rs index 564d94dbb4..90f509d97f 100644 --- a/libs/input/rust/input.rs +++ b/libs/input/rust/input.rs @@ -19,6 +19,8 @@ use crate::ffi::RustInputDeviceIdentifier; use bitflags::bitflags; use inputconstants::aidl::android::os::IInputConstants; +use inputconstants::aidl::android::os::MotionEventFlag::MotionEventFlag; +use serde::{Deserialize, Serialize}; use std::fmt; /// The InputDevice ID. @@ -193,31 +195,34 @@ impl MotionAction { bitflags! { /// MotionEvent flags. + /// The source of truth for the flag definitions are the MotionEventFlag AIDL enum. + /// The flag values are redefined here as a bitflags API. #[derive(Debug)] pub struct MotionFlags: u32 { /// FLAG_WINDOW_IS_OBSCURED - const WINDOW_IS_OBSCURED = IInputConstants::MOTION_EVENT_FLAG_WINDOW_IS_OBSCURED as u32; + const WINDOW_IS_OBSCURED = MotionEventFlag::WINDOW_IS_OBSCURED.0 as u32; /// FLAG_WINDOW_IS_PARTIALLY_OBSCURED - const WINDOW_IS_PARTIALLY_OBSCURED = IInputConstants::MOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED as u32; + const WINDOW_IS_PARTIALLY_OBSCURED = MotionEventFlag::WINDOW_IS_PARTIALLY_OBSCURED.0 as u32; /// FLAG_HOVER_EXIT_PENDING - const HOVER_EXIT_PENDING = IInputConstants::MOTION_EVENT_FLAG_HOVER_EXIT_PENDING as u32; + const HOVER_EXIT_PENDING = MotionEventFlag::HOVER_EXIT_PENDING.0 as u32; /// FLAG_IS_GENERATED_GESTURE - const IS_GENERATED_GESTURE = IInputConstants::MOTION_EVENT_FLAG_IS_GENERATED_GESTURE as u32; + const IS_GENERATED_GESTURE = MotionEventFlag::IS_GENERATED_GESTURE.0 as u32; /// FLAG_CANCELED - const CANCELED = IInputConstants::INPUT_EVENT_FLAG_CANCELED as u32; + const CANCELED = MotionEventFlag::CANCELED.0 as u32; /// FLAG_NO_FOCUS_CHANGE - const NO_FOCUS_CHANGE = IInputConstants::MOTION_EVENT_FLAG_NO_FOCUS_CHANGE as u32; + const NO_FOCUS_CHANGE = MotionEventFlag::NO_FOCUS_CHANGE.0 as u32; /// PRIVATE_FLAG_SUPPORTS_ORIENTATION - const PRIVATE_SUPPORTS_ORIENTATION = IInputConstants::MOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION as u32; + const PRIVATE_FLAG_SUPPORTS_ORIENTATION = + MotionEventFlag::PRIVATE_FLAG_SUPPORTS_ORIENTATION.0 as u32; /// PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION - const PRIVATE_SUPPORTS_DIRECTIONAL_ORIENTATION = - IInputConstants::MOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION as u32; + const PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION = + MotionEventFlag::PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION.0 as u32; /// FLAG_IS_ACCESSIBILITY_EVENT - const IS_ACCESSIBILITY_EVENT = IInputConstants::INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT as u32; + const IS_ACCESSIBILITY_EVENT = MotionEventFlag::IS_ACCESSIBILITY_EVENT.0 as u32; /// FLAG_TAINTED - const TAINTED = IInputConstants::INPUT_EVENT_FLAG_TAINTED as u32; + const TAINTED = MotionEventFlag::TAINTED.0 as u32; /// FLAG_TARGET_ACCESSIBILITY_FOCUS - const TARGET_ACCESSIBILITY_FOCUS = IInputConstants::MOTION_EVENT_FLAG_TARGET_ACCESSIBILITY_FOCUS as u32; + const TARGET_ACCESSIBILITY_FOCUS = MotionEventFlag::TARGET_ACCESSIBILITY_FOCUS.0 as u32; } } @@ -320,9 +325,11 @@ bitflags! { /// A rust enum representation of a Keyboard type. #[repr(u32)] -#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Serialize, Deserialize)] +#[serde(tag = "type")] pub enum KeyboardType { /// KEYBOARD_TYPE_NONE + #[default] None = input_bindgen::AINPUT_KEYBOARD_TYPE_NONE, /// KEYBOARD_TYPE_NON_ALPHABETIC NonAlphabetic = input_bindgen::AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, @@ -333,10 +340,24 @@ pub enum KeyboardType { #[cfg(test)] mod tests { use crate::input::SourceClass; + use crate::MotionFlags; use crate::Source; + use inputconstants::aidl::android::os::MotionEventFlag::MotionEventFlag; + #[test] fn convert_source_class_pointer() { let source = Source::from_bits(input_bindgen::AINPUT_SOURCE_CLASS_POINTER).unwrap(); assert!(source.is_from_class(SourceClass::Pointer)); } + + /// Ensure all MotionEventFlag constants are re-defined in rust. + #[test] + fn all_motion_event_flags_defined() { + for flag in MotionEventFlag::enum_values() { + assert!( + MotionFlags::from_bits(flag.0 as u32).is_some(), + "MotionEventFlag value {flag:?} is not redefined as a rust MotionFlags" + ); + } + } } diff --git a/libs/input/rust/keyboard_classification_config.rs b/libs/input/rust/keyboard_classification_config.rs new file mode 100644 index 0000000000..ab74efb674 --- /dev/null +++ b/libs/input/rust/keyboard_classification_config.rs @@ -0,0 +1,127 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use crate::input::KeyboardType; + +// TODO(b/263559234): Categorize some of these to KeyboardType::None based on ability to produce +// key events at all. (Requires setup allowing InputDevice to dynamically add/remove +// KeyboardInputMapper based on blocklist and KeyEvents in case a KeyboardType::None device ends +// up producing a key event) +pub static CLASSIFIED_DEVICES: &[( + /* vendorId */ u16, + /* productId */ u16, + KeyboardType, + /* is_finalized */ bool, +)] = &[ + // HP X4000 Wireless Mouse + (0x03f0, 0xa407, KeyboardType::NonAlphabetic, true), + // Microsoft Wireless Mobile Mouse 6000 + (0x045e, 0x0745, KeyboardType::NonAlphabetic, true), + // Microsoft Surface Precision Mouse + (0x045e, 0x0821, KeyboardType::NonAlphabetic, true), + // Microsoft Pro IntelliMouse + (0x045e, 0x082a, KeyboardType::NonAlphabetic, true), + // Microsoft Bluetooth Mouse + (0x045e, 0x082f, KeyboardType::NonAlphabetic, true), + // Xbox One Elite Series 2 gamepad + (0x045e, 0x0b05, KeyboardType::NonAlphabetic, true), + // Logitech T400 + (0x046d, 0x4026, KeyboardType::NonAlphabetic, true), + // Logitech M720 Triathlon (Unifying) + (0x046d, 0x405e, KeyboardType::NonAlphabetic, true), + // Logitech MX Master 2S (Unifying) + (0x046d, 0x4069, KeyboardType::NonAlphabetic, true), + // Logitech M585 (Unifying) + (0x046d, 0x406b, KeyboardType::NonAlphabetic, true), + // Logitech MX Anywhere 2 (Unifying) + (0x046d, 0x4072, KeyboardType::NonAlphabetic, true), + // Logitech Pebble M350 + (0x046d, 0x4080, KeyboardType::NonAlphabetic, true), + // Logitech T630 Ultrathin + (0x046d, 0xb00d, KeyboardType::NonAlphabetic, true), + // Logitech M558 + (0x046d, 0xb011, KeyboardType::NonAlphabetic, true), + // Logitech MX Master (Bluetooth) + (0x046d, 0xb012, KeyboardType::NonAlphabetic, true), + // Logitech MX Anywhere 2 (Bluetooth) + (0x046d, 0xb013, KeyboardType::NonAlphabetic, true), + // Logitech M720 Triathlon (Bluetooth) + (0x046d, 0xb015, KeyboardType::NonAlphabetic, true), + // Logitech M535 + (0x046d, 0xb016, KeyboardType::NonAlphabetic, true), + // Logitech MX Master / Anywhere 2 (Bluetooth) + (0x046d, 0xb017, KeyboardType::NonAlphabetic, true), + // Logitech MX Master 2S (Bluetooth) + (0x046d, 0xb019, KeyboardType::NonAlphabetic, true), + // Logitech MX Anywhere 2S (Bluetooth) + (0x046d, 0xb01a, KeyboardType::NonAlphabetic, true), + // Logitech M585/M590 (Bluetooth) + (0x046d, 0xb01b, KeyboardType::NonAlphabetic, true), + // Logitech G603 Lightspeed Gaming Mouse (Bluetooth) + (0x046d, 0xb01c, KeyboardType::NonAlphabetic, true), + // Logitech MX Master (Bluetooth) + (0x046d, 0xb01e, KeyboardType::NonAlphabetic, true), + // Logitech MX Anywhere 2 (Bluetooth) + (0x046d, 0xb01f, KeyboardType::NonAlphabetic, true), + // Logitech MX Master 3 (Bluetooth) + (0x046d, 0xb023, KeyboardType::NonAlphabetic, true), + // Logitech G604 Lightspeed Gaming Mouse (Bluetooth) + (0x046d, 0xb024, KeyboardType::NonAlphabetic, true), + // Logitech Spotlight Presentation Remote (Bluetooth) + (0x046d, 0xb503, KeyboardType::NonAlphabetic, true), + // Logitech R500 (Bluetooth) + (0x046d, 0xb505, KeyboardType::NonAlphabetic, true), + // Logitech M500s + (0x046d, 0xc093, KeyboardType::NonAlphabetic, true), + // Logitech Spotlight Presentation Remote (USB dongle) + (0x046d, 0xc53e, KeyboardType::NonAlphabetic, true), + // Elecom Enelo IR LED Mouse 350 + (0x056e, 0x0134, KeyboardType::NonAlphabetic, true), + // Elecom EPRIM Blue LED 5 button mouse 228 + (0x056e, 0x0141, KeyboardType::NonAlphabetic, true), + // Elecom Blue LED Mouse 203 + (0x056e, 0x0159, KeyboardType::NonAlphabetic, true), + // Zebra LS2208 barcode scanner + (0x05e0, 0x1200, KeyboardType::NonAlphabetic, true), + // RDing FootSwitch1F1 + (0x0c45, 0x7403, KeyboardType::NonAlphabetic, true), + // SteelSeries Sensei RAW Frost Blue + (0x1038, 0x1369, KeyboardType::NonAlphabetic, true), + // SteelSeries Rival 3 Wired + (0x1038, 0x1824, KeyboardType::NonAlphabetic, true), + // SteelSeries Rival 3 Wireless (USB dongle) + (0x1038, 0x1830, KeyboardType::NonAlphabetic, true), + // Yubico.com Yubikey + (0x1050, 0x0010, KeyboardType::NonAlphabetic, true), + // Yubico.com Yubikey 4 OTP+U2F+CCID + (0x1050, 0x0407, KeyboardType::NonAlphabetic, true), + // Lenovo USB-C Wired Compact Mouse + (0x17ef, 0x6123, KeyboardType::NonAlphabetic, true), + // Corsair Katar Pro Wireless (USB dongle) + (0x1b1c, 0x1b94, KeyboardType::NonAlphabetic, true), + // Corsair Katar Pro Wireless (Bluetooth) + (0x1bae, 0x1b1c, KeyboardType::NonAlphabetic, true), + // Kensington Pro Fit Full-size + (0x1bcf, 0x08a0, KeyboardType::NonAlphabetic, true), + // Huion HS64 + (0x256c, 0x006d, KeyboardType::NonAlphabetic, true), + // XP-Pen Star G640 + (0x28bd, 0x0914, KeyboardType::NonAlphabetic, true), + // XP-Pen Artist 12 Pro + (0x28bd, 0x091f, KeyboardType::NonAlphabetic, true), + // XP-Pen Deco mini7W + (0x28bd, 0x0928, KeyboardType::NonAlphabetic, true), +]; diff --git a/libs/input/rust/keyboard_classifier.rs b/libs/input/rust/keyboard_classifier.rs index 1063fac664..3c789b41e2 100644 --- a/libs/input/rust/keyboard_classifier.rs +++ b/libs/input/rust/keyboard_classifier.rs @@ -31,39 +31,37 @@ //! across multiple device connections in a time period, then change type to //! KeyboardType::NonAlphabetic. Once changed, it can still change back to Alphabetic //! (i.e. verified = false). -//! -//! TODO(b/263559234): Data store implementation to store information about past classification +use crate::data_store::DataStore; use crate::input::{DeviceId, InputDevice, KeyboardType}; +use crate::keyboard_classification_config::CLASSIFIED_DEVICES; use crate::{DeviceClass, ModifierState}; use std::collections::HashMap; /// The KeyboardClassifier is used to classify a keyboard device into non-keyboard, alphabetic /// keyboard or non-alphabetic keyboard -#[derive(Default)] pub struct KeyboardClassifier { device_map: HashMap<DeviceId, KeyboardInfo>, + data_store: DataStore, } struct KeyboardInfo { - _device: InputDevice, + device: InputDevice, keyboard_type: KeyboardType, is_finalized: bool, } impl KeyboardClassifier { /// Create a new KeyboardClassifier - pub fn new() -> Self { - Default::default() + pub fn new(data_store: DataStore) -> Self { + Self { device_map: HashMap::new(), data_store } } /// Adds keyboard to KeyboardClassifier pub fn notify_keyboard_changed(&mut self, device: InputDevice) { let (keyboard_type, is_finalized) = self.classify_keyboard(&device); - self.device_map.insert( - device.device_id, - KeyboardInfo { _device: device, keyboard_type, is_finalized }, - ); + self.device_map + .insert(device.device_id, KeyboardInfo { device, keyboard_type, is_finalized }); } /// Get keyboard type for a tracked keyboard in KeyboardClassifier @@ -106,11 +104,16 @@ impl KeyboardClassifier { if Self::is_alphabetic_key(&evdev_code) { keyboard.keyboard_type = KeyboardType::Alphabetic; keyboard.is_finalized = true; + self.data_store.set_keyboard_type( + &keyboard.device.identifier.descriptor, + keyboard.keyboard_type, + keyboard.is_finalized, + ); } } } - fn classify_keyboard(&self, device: &InputDevice) -> (KeyboardType, bool) { + fn classify_keyboard(&mut self, device: &InputDevice) -> (KeyboardType, bool) { // This should never happen but having keyboard device class is necessary to be classified // as any type of keyboard. if !device.classes.contains(DeviceClass::Keyboard) { @@ -126,6 +129,21 @@ impl KeyboardClassifier { (KeyboardType::NonAlphabetic, true) }; } + + // Check in data store + if let Some((keyboard_type, is_finalized)) = + self.data_store.get_keyboard_type(&device.identifier.descriptor) + { + return (keyboard_type, is_finalized); + } + + // Check in known device list for classification + for (vendor, product, keyboard_type, is_finalized) in CLASSIFIED_DEVICES.iter() { + if device.identifier.vendor == *vendor && device.identifier.product == *product { + return (*keyboard_type, *is_finalized); + } + } + // Any composite device with multiple device classes should be categorized as non-alphabetic // keyboard initially if device.classes.contains(DeviceClass::Touch) @@ -168,17 +186,20 @@ impl KeyboardClassifier { #[cfg(test)] mod tests { + use crate::data_store::{test_file_reader_writer::TestFileReaderWriter, DataStore}; use crate::input::{DeviceId, InputDevice, KeyboardType}; + use crate::keyboard_classification_config::CLASSIFIED_DEVICES; use crate::keyboard_classifier::KeyboardClassifier; use crate::{DeviceClass, ModifierState, RustInputDeviceIdentifier}; static DEVICE_ID: DeviceId = DeviceId(1); + static SECOND_DEVICE_ID: DeviceId = DeviceId(2); static KEY_A: i32 = 30; static KEY_1: i32 = 2; #[test] fn classify_external_alphabetic_keyboard() { - let mut classifier = KeyboardClassifier::new(); + let mut classifier = create_classifier(); classifier.notify_keyboard_changed(create_device( DeviceClass::Keyboard | DeviceClass::AlphabeticKey | DeviceClass::External, )); @@ -188,7 +209,7 @@ mod tests { #[test] fn classify_external_non_alphabetic_keyboard() { - let mut classifier = KeyboardClassifier::new(); + let mut classifier = create_classifier(); classifier .notify_keyboard_changed(create_device(DeviceClass::Keyboard | DeviceClass::External)); assert_eq!(classifier.get_keyboard_type(DEVICE_ID), KeyboardType::NonAlphabetic); @@ -197,7 +218,7 @@ mod tests { #[test] fn classify_mouse_pretending_as_keyboard() { - let mut classifier = KeyboardClassifier::new(); + let mut classifier = create_classifier(); classifier.notify_keyboard_changed(create_device( DeviceClass::Keyboard | DeviceClass::Cursor @@ -210,7 +231,7 @@ mod tests { #[test] fn classify_touchpad_pretending_as_keyboard() { - let mut classifier = KeyboardClassifier::new(); + let mut classifier = create_classifier(); classifier.notify_keyboard_changed(create_device( DeviceClass::Keyboard | DeviceClass::Touchpad @@ -223,7 +244,7 @@ mod tests { #[test] fn classify_stylus_pretending_as_keyboard() { - let mut classifier = KeyboardClassifier::new(); + let mut classifier = create_classifier(); classifier.notify_keyboard_changed(create_device( DeviceClass::Keyboard | DeviceClass::ExternalStylus @@ -236,7 +257,7 @@ mod tests { #[test] fn classify_dpad_pretending_as_keyboard() { - let mut classifier = KeyboardClassifier::new(); + let mut classifier = create_classifier(); classifier.notify_keyboard_changed(create_device( DeviceClass::Keyboard | DeviceClass::Dpad @@ -249,7 +270,7 @@ mod tests { #[test] fn classify_joystick_pretending_as_keyboard() { - let mut classifier = KeyboardClassifier::new(); + let mut classifier = create_classifier(); classifier.notify_keyboard_changed(create_device( DeviceClass::Keyboard | DeviceClass::Joystick @@ -262,7 +283,7 @@ mod tests { #[test] fn classify_gamepad_pretending_as_keyboard() { - let mut classifier = KeyboardClassifier::new(); + let mut classifier = create_classifier(); classifier.notify_keyboard_changed(create_device( DeviceClass::Keyboard | DeviceClass::Gamepad @@ -275,7 +296,7 @@ mod tests { #[test] fn reclassify_keyboard_on_alphabetic_key_event() { - let mut classifier = KeyboardClassifier::new(); + let mut classifier = create_classifier(); classifier.notify_keyboard_changed(create_device( DeviceClass::Keyboard | DeviceClass::Dpad @@ -293,7 +314,7 @@ mod tests { #[test] fn dont_reclassify_keyboard_on_non_alphabetic_key_event() { - let mut classifier = KeyboardClassifier::new(); + let mut classifier = create_classifier(); classifier.notify_keyboard_changed(create_device( DeviceClass::Keyboard | DeviceClass::Dpad @@ -311,7 +332,7 @@ mod tests { #[test] fn dont_reclassify_keyboard_on_alphabetic_key_event_with_modifiers() { - let mut classifier = KeyboardClassifier::new(); + let mut classifier = create_classifier(); classifier.notify_keyboard_changed(create_device( DeviceClass::Keyboard | DeviceClass::Dpad @@ -326,20 +347,82 @@ mod tests { assert!(!classifier.is_finalized(DEVICE_ID)); } + #[test] + fn classify_known_devices() { + let mut classifier = create_classifier(); + for (vendor, product, keyboard_type, is_finalized) in CLASSIFIED_DEVICES.iter() { + classifier + .notify_keyboard_changed(create_device_with_vendor_product_ids(*vendor, *product)); + assert_eq!(classifier.get_keyboard_type(DEVICE_ID), *keyboard_type); + assert_eq!(classifier.is_finalized(DEVICE_ID), *is_finalized); + } + } + + #[test] + fn classify_previously_reclassified_devices() { + let test_reader_writer = TestFileReaderWriter::new(); + { + let mut classifier = + KeyboardClassifier::new(DataStore::new(Box::new(test_reader_writer.clone()))); + let device = create_device( + DeviceClass::Keyboard + | DeviceClass::Dpad + | DeviceClass::AlphabeticKey + | DeviceClass::External, + ); + classifier.notify_keyboard_changed(device); + classifier.process_key(DEVICE_ID, KEY_A, ModifierState::None); + } + + // Re-create classifier and data store to mimic a reboot (but use the same file system + // reader writer) + { + let mut classifier = + KeyboardClassifier::new(DataStore::new(Box::new(test_reader_writer.clone()))); + let device = InputDevice { + device_id: SECOND_DEVICE_ID, + identifier: create_identifier(/* vendor= */ 234, /* product= */ 345), + classes: DeviceClass::Keyboard + | DeviceClass::Dpad + | DeviceClass::AlphabeticKey + | DeviceClass::External, + }; + classifier.notify_keyboard_changed(device); + assert_eq!(classifier.get_keyboard_type(SECOND_DEVICE_ID), KeyboardType::Alphabetic); + assert!(classifier.is_finalized(SECOND_DEVICE_ID)); + } + } + + fn create_classifier() -> KeyboardClassifier { + KeyboardClassifier::new(DataStore::new(Box::new(TestFileReaderWriter::new()))) + } + + fn create_identifier(vendor: u16, product: u16) -> RustInputDeviceIdentifier { + RustInputDeviceIdentifier { + name: "test_device".to_string(), + location: "location".to_string(), + unique_id: "unique_id".to_string(), + bus: 123, + vendor, + product, + version: 567, + descriptor: "descriptor".to_string(), + } + } + fn create_device(classes: DeviceClass) -> InputDevice { InputDevice { device_id: DEVICE_ID, - identifier: RustInputDeviceIdentifier { - name: "test_device".to_string(), - location: "location".to_string(), - unique_id: "unique_id".to_string(), - bus: 123, - vendor: 234, - product: 345, - version: 567, - descriptor: "descriptor".to_string(), - }, + identifier: create_identifier(/* vendor= */ 234, /* product= */ 345), classes, } } + + fn create_device_with_vendor_product_ids(vendor: u16, product: u16) -> InputDevice { + InputDevice { + device_id: DEVICE_ID, + identifier: create_identifier(vendor, product), + classes: DeviceClass::Keyboard | DeviceClass::AlphabeticKey | DeviceClass::External, + } + } } diff --git a/libs/input/rust/lib.rs b/libs/input/rust/lib.rs index 88374695f1..4f4ea8568b 100644 --- a/libs/input/rust/lib.rs +++ b/libs/input/rust/lib.rs @@ -16,12 +16,16 @@ //! The rust component of libinput. +mod data_store; mod input; mod input_verifier; +mod keyboard_classification_config; mod keyboard_classifier; +pub use data_store::{DataStore, DefaultFileReaderWriter}; pub use input::{ - DeviceClass, DeviceId, InputDevice, ModifierState, MotionAction, MotionFlags, Source, + DeviceClass, DeviceId, InputDevice, KeyboardType, ModifierState, MotionAction, MotionFlags, + Source, }; pub use input_verifier::InputVerifier; pub use keyboard_classifier::KeyboardClassifier; @@ -149,7 +153,14 @@ fn reset_device(verifier: &mut InputVerifier, device_id: i32) { } fn create_keyboard_classifier() -> Box<KeyboardClassifier> { - Box::new(KeyboardClassifier::new()) + // Future design: Make this data store singleton by passing it to C++ side and making it global + // and pass by reference to components that need to store persistent data. + // + // Currently only used by rust keyboard classifier so keeping it here. + let data_store = DataStore::new(Box::new(DefaultFileReaderWriter::new( + "/data/system/inputflinger-data.json".to_string(), + ))); + Box::new(KeyboardClassifier::new(data_store)) } fn notify_keyboard_changed( diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index 9c0a41eafe..81c6175805 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -16,6 +16,7 @@ cc_test { "BlockingQueue_test.cpp", "IdGenerator_test.cpp", "InputChannel_test.cpp", + "InputConsumer_test.cpp", "InputDevice_test.cpp", "InputEvent_test.cpp", "InputPublisherAndConsumer_test.cpp", @@ -23,7 +24,9 @@ cc_test { "InputVerifier_test.cpp", "MotionPredictor_test.cpp", "MotionPredictorMetricsManager_test.cpp", + "Resampler_test.cpp", "RingBuffer_test.cpp", + "TestInputChannel.cpp", "TfLiteMotionPredictor_test.cpp", "TouchResampling_test.cpp", "TouchVideoFrame_test.cpp", diff --git a/libs/input/tests/InputChannel_test.cpp b/libs/input/tests/InputChannel_test.cpp index 02d4c07bfa..25356cfcf0 100644 --- a/libs/input/tests/InputChannel_test.cpp +++ b/libs/input/tests/InputChannel_test.cpp @@ -65,11 +65,7 @@ TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) { ASSERT_EQ(OK, result) << "should have successfully opened a channel pair"; - // Name - EXPECT_STREQ("channel name (server)", serverChannel->getName().c_str()) - << "server channel should have suffixed name"; - EXPECT_STREQ("channel name (client)", clientChannel->getName().c_str()) - << "client channel should have suffixed name"; + EXPECT_EQ(serverChannel->getName(), clientChannel->getName()); // Server->Client communication InputMessage serverMsg = {}; @@ -78,9 +74,10 @@ TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) { EXPECT_EQ(OK, serverChannel->sendMessage(&serverMsg)) << "server channel should be able to send message to client channel"; - InputMessage clientMsg; - EXPECT_EQ(OK, clientChannel->receiveMessage(&clientMsg)) + android::base::Result<InputMessage> clientMsgResult = clientChannel->receiveMessage(); + ASSERT_TRUE(clientMsgResult.ok()) << "client channel should be able to receive message from server channel"; + const InputMessage& clientMsg = *clientMsgResult; EXPECT_EQ(serverMsg.header.type, clientMsg.header.type) << "client channel should receive the correct message from server channel"; EXPECT_EQ(serverMsg.body.key.action, clientMsg.body.key.action) @@ -94,9 +91,10 @@ TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) { EXPECT_EQ(OK, clientChannel->sendMessage(&clientReply)) << "client channel should be able to send message to server channel"; - InputMessage serverReply; - EXPECT_EQ(OK, serverChannel->receiveMessage(&serverReply)) + android::base::Result<InputMessage> serverReplyResult = serverChannel->receiveMessage(); + ASSERT_TRUE(serverReplyResult.ok()) << "server channel should be able to receive message from client channel"; + const InputMessage& serverReply = *serverReplyResult; EXPECT_EQ(clientReply.header.type, serverReply.header.type) << "server channel should receive the correct message from client channel"; EXPECT_EQ(clientReply.header.seq, serverReply.header.seq) @@ -134,9 +132,10 @@ TEST_F(InputChannelTest, ProbablyHasInput) { << "client channel should observe that message is available before receiving it"; // Receive (consume) the message. - InputMessage clientMsg; - EXPECT_EQ(OK, receiverChannel->receiveMessage(&clientMsg)) + android::base::Result<InputMessage> clientMsgResult = receiverChannel->receiveMessage(); + ASSERT_TRUE(clientMsgResult.ok()) << "client channel should be able to receive message from server channel"; + const InputMessage& clientMsg = *clientMsgResult; EXPECT_EQ(serverMsg.header.type, clientMsg.header.type) << "client channel should receive the correct message from server channel"; EXPECT_EQ(serverMsg.body.key.action, clientMsg.body.key.action) @@ -156,8 +155,8 @@ TEST_F(InputChannelTest, ReceiveSignal_WhenNoSignalPresent_ReturnsAnError) { ASSERT_EQ(OK, result) << "should have successfully opened a channel pair"; - InputMessage msg; - EXPECT_EQ(WOULD_BLOCK, clientChannel->receiveMessage(&msg)) + android::base::Result<InputMessage> msgResult = clientChannel->receiveMessage(); + EXPECT_EQ(WOULD_BLOCK, msgResult.error().code()) << "receiveMessage should have returned WOULD_BLOCK"; } @@ -172,8 +171,8 @@ TEST_F(InputChannelTest, ReceiveSignal_WhenPeerClosed_ReturnsAnError) { serverChannel.reset(); // close server channel - InputMessage msg; - EXPECT_EQ(DEAD_OBJECT, clientChannel->receiveMessage(&msg)) + android::base::Result<InputMessage> msgResult = clientChannel->receiveMessage(); + EXPECT_EQ(DEAD_OBJECT, msgResult.error().code()) << "receiveMessage should have returned DEAD_OBJECT"; } @@ -207,7 +206,7 @@ TEST_F(InputChannelTest, SendAndReceive_MotionClassification) { MotionClassification::DEEP_PRESS, }; - InputMessage serverMsg = {}, clientMsg; + InputMessage serverMsg = {}; serverMsg.header.type = InputMessage::Type::MOTION; serverMsg.header.seq = 1; serverMsg.body.motion.pointerCount = 1; @@ -218,11 +217,13 @@ TEST_F(InputChannelTest, SendAndReceive_MotionClassification) { EXPECT_EQ(OK, serverChannel->sendMessage(&serverMsg)) << "server channel should be able to send message to client channel"; - EXPECT_EQ(OK, clientChannel->receiveMessage(&clientMsg)) + android::base::Result<InputMessage> clientMsgResult = clientChannel->receiveMessage(); + ASSERT_TRUE(clientMsgResult.ok()) << "client channel should be able to receive message from server channel"; + const InputMessage& clientMsg = *clientMsgResult; EXPECT_EQ(serverMsg.header.type, clientMsg.header.type); - EXPECT_EQ(classification, clientMsg.body.motion.classification) << - "Expected to receive " << motionClassificationToString(classification); + EXPECT_EQ(classification, clientMsg.body.motion.classification) + << "Expected to receive " << motionClassificationToString(classification); } } diff --git a/libs/input/tests/InputConsumer_test.cpp b/libs/input/tests/InputConsumer_test.cpp new file mode 100644 index 0000000000..d708316236 --- /dev/null +++ b/libs/input/tests/InputConsumer_test.cpp @@ -0,0 +1,247 @@ +/** + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <input/InputConsumerNoResampling.h> + +#include <memory> +#include <optional> + +#include <TestEventMatchers.h> +#include <TestInputChannel.h> +#include <android-base/logging.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <input/BlockingQueue.h> +#include <input/InputEventBuilders.h> +#include <utils/Looper.h> +#include <utils/StrongPointer.h> + +namespace android { + +namespace { + +using std::chrono::nanoseconds; + +using ::testing::AllOf; +using ::testing::Matcher; +using ::testing::Not; + +} // namespace + +class InputConsumerTest : public testing::Test, public InputConsumerCallbacks { +protected: + InputConsumerTest() + : mClientTestChannel{std::make_shared<TestInputChannel>("TestChannel")}, + mLooper{sp<Looper>::make(/*allowNonCallbacks=*/false)} { + Looper::setForThread(mLooper); + mConsumer = + std::make_unique<InputConsumerNoResampling>(mClientTestChannel, mLooper, *this, + std::make_unique<LegacyResampler>()); + } + + void invokeLooperCallback() const { + sp<LooperCallback> callback; + ASSERT_TRUE(mLooper->getFdStateDebug(mClientTestChannel->getFd(), /*ident=*/nullptr, + /*events=*/nullptr, &callback, /*data=*/nullptr)); + callback->handleEvent(mClientTestChannel->getFd(), ALOOPER_EVENT_INPUT, /*data=*/nullptr); + } + + void assertOnBatchedInputEventPendingWasCalled() { + ASSERT_GT(mOnBatchedInputEventPendingInvocationCount, 0UL) + << "onBatchedInputEventPending has not been called."; + --mOnBatchedInputEventPendingInvocationCount; + } + + void assertReceivedMotionEvent(const Matcher<MotionEvent>& matcher) { + std::unique_ptr<MotionEvent> motionEvent = mMotionEvents.pop(); + ASSERT_NE(motionEvent, nullptr); + EXPECT_THAT(*motionEvent, matcher); + } + + std::shared_ptr<TestInputChannel> mClientTestChannel; + sp<Looper> mLooper; + std::unique_ptr<InputConsumerNoResampling> mConsumer; + + BlockingQueue<std::unique_ptr<KeyEvent>> mKeyEvents; + BlockingQueue<std::unique_ptr<MotionEvent>> mMotionEvents; + BlockingQueue<std::unique_ptr<FocusEvent>> mFocusEvents; + BlockingQueue<std::unique_ptr<CaptureEvent>> mCaptureEvents; + BlockingQueue<std::unique_ptr<DragEvent>> mDragEvents; + BlockingQueue<std::unique_ptr<TouchModeEvent>> mTouchModeEvents; + +private: + size_t mOnBatchedInputEventPendingInvocationCount{0}; + + // InputConsumerCallbacks interface + void onKeyEvent(std::unique_ptr<KeyEvent> event, uint32_t seq) override { + mKeyEvents.push(std::move(event)); + mConsumer->finishInputEvent(seq, true); + } + void onMotionEvent(std::unique_ptr<MotionEvent> event, uint32_t seq) override { + mMotionEvents.push(std::move(event)); + mConsumer->finishInputEvent(seq, true); + } + void onBatchedInputEventPending(int32_t pendingBatchSource) override { + if (!mConsumer->probablyHasInput()) { + ADD_FAILURE() << "should deterministically have input because there is a batch"; + } + ++mOnBatchedInputEventPendingInvocationCount; + }; + void onFocusEvent(std::unique_ptr<FocusEvent> event, uint32_t seq) override { + mFocusEvents.push(std::move(event)); + mConsumer->finishInputEvent(seq, true); + }; + void onCaptureEvent(std::unique_ptr<CaptureEvent> event, uint32_t seq) override { + mCaptureEvents.push(std::move(event)); + mConsumer->finishInputEvent(seq, true); + }; + void onDragEvent(std::unique_ptr<DragEvent> event, uint32_t seq) override { + mDragEvents.push(std::move(event)); + mConsumer->finishInputEvent(seq, true); + } + void onTouchModeEvent(std::unique_ptr<TouchModeEvent> event, uint32_t seq) override { + mTouchModeEvents.push(std::move(event)); + mConsumer->finishInputEvent(seq, true); + }; +}; + +TEST_F(InputConsumerTest, MessageStreamBatchedInMotionEvent) { + mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/0} + .eventTime(nanoseconds{0ms}.count()) + .action(AMOTION_EVENT_ACTION_DOWN) + .build()); + mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/1} + .eventTime(nanoseconds{5ms}.count()) + .action(AMOTION_EVENT_ACTION_MOVE) + .build()); + mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/2} + .eventTime(nanoseconds{10ms}.count()) + .action(AMOTION_EVENT_ACTION_MOVE) + .build()); + + mClientTestChannel->assertNoSentMessages(); + + invokeLooperCallback(); + + assertOnBatchedInputEventPendingWasCalled(); + + mConsumer->consumeBatchedInputEvents(/*frameTime=*/std::nullopt); + + std::unique_ptr<MotionEvent> downMotionEvent = mMotionEvents.pop(); + ASSERT_NE(downMotionEvent, nullptr); + + std::unique_ptr<MotionEvent> moveMotionEvent = mMotionEvents.pop(); + ASSERT_NE(moveMotionEvent, nullptr); + EXPECT_EQ(moveMotionEvent->getHistorySize() + 1, 3UL); + + mClientTestChannel->assertFinishMessage(/*seq=*/0, /*handled=*/true); + mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true); + mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true); +} + +TEST_F(InputConsumerTest, LastBatchedSampleIsLessThanResampleTime) { + mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/0} + .eventTime(nanoseconds{0ms}.count()) + .action(AMOTION_EVENT_ACTION_DOWN) + .build()); + mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/1} + .eventTime(nanoseconds{5ms}.count()) + .action(AMOTION_EVENT_ACTION_MOVE) + .build()); + mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/2} + .eventTime(nanoseconds{10ms}.count()) + .action(AMOTION_EVENT_ACTION_MOVE) + .build()); + mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/3} + .eventTime(nanoseconds{15ms}.count()) + .action(AMOTION_EVENT_ACTION_MOVE) + .build()); + + mClientTestChannel->assertNoSentMessages(); + + invokeLooperCallback(); + + assertOnBatchedInputEventPendingWasCalled(); + + mConsumer->consumeBatchedInputEvents(16'000'000 /*ns*/); + + std::unique_ptr<MotionEvent> downMotionEvent = mMotionEvents.pop(); + ASSERT_NE(downMotionEvent, nullptr); + + std::unique_ptr<MotionEvent> moveMotionEvent = mMotionEvents.pop(); + ASSERT_NE(moveMotionEvent, nullptr); + const size_t numSamples = moveMotionEvent->getHistorySize() + 1; + EXPECT_LT(moveMotionEvent->getHistoricalEventTime(numSamples - 2), + moveMotionEvent->getEventTime()); + + // Consume all remaining events before ending the test. Otherwise, the smart pointer that owns + // consumer is set to null before destroying consumer. This leads to a member function call on a + // null object. + // TODO(b/332613662): Remove this workaround. + mConsumer->consumeBatchedInputEvents(std::nullopt); + + mClientTestChannel->assertFinishMessage(/*seq=*/0, true); + mClientTestChannel->assertFinishMessage(/*seq=*/1, true); + mClientTestChannel->assertFinishMessage(/*seq=*/2, true); + mClientTestChannel->assertFinishMessage(/*seq=*/3, true); +} + +TEST_F(InputConsumerTest, BatchedEventsMultiDeviceConsumption) { + mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/0} + .deviceId(0) + .action(AMOTION_EVENT_ACTION_DOWN) + .build()); + + invokeLooperCallback(); + assertReceivedMotionEvent(AllOf(WithDeviceId(0), WithMotionAction(AMOTION_EVENT_ACTION_DOWN))); + + mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/1} + .deviceId(0) + .action(AMOTION_EVENT_ACTION_MOVE) + .build()); + mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/2} + .deviceId(0) + .action(AMOTION_EVENT_ACTION_MOVE) + .build()); + mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/3} + .deviceId(0) + .action(AMOTION_EVENT_ACTION_MOVE) + .build()); + + mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/4} + .deviceId(1) + .action(AMOTION_EVENT_ACTION_DOWN) + .build()); + + invokeLooperCallback(); + assertReceivedMotionEvent(AllOf(WithDeviceId(1), WithMotionAction(AMOTION_EVENT_ACTION_DOWN))); + + mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/5} + .deviceId(0) + .action(AMOTION_EVENT_ACTION_UP) + .build()); + + invokeLooperCallback(); + assertReceivedMotionEvent(AllOf(WithDeviceId(0), WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + Not(MotionEventIsResampled()))); + + mClientTestChannel->assertFinishMessage(/*seq=*/0, /*handled=*/true); + mClientTestChannel->assertFinishMessage(/*seq=*/4, /*handled=*/true); + mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true); + mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true); + mClientTestChannel->assertFinishMessage(/*seq=*/3, /*handled=*/true); +} +} // namespace android diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp index 3717f49fef..a67e1ef472 100644 --- a/libs/input/tests/InputEvent_test.cpp +++ b/libs/input/tests/InputEvent_test.cpp @@ -371,8 +371,8 @@ void MotionEventTest::initializeEventWithHistory(MotionEvent* event) { mTransform, 2.0f, 2.1f, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, mRawTransform, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 2, mPointerProperties, mSamples[0].pointerCoords); - event->addSample(ARBITRARY_EVENT_TIME + 1, mSamples[1].pointerCoords); - event->addSample(ARBITRARY_EVENT_TIME + 2, mSamples[2].pointerCoords); + event->addSample(ARBITRARY_EVENT_TIME + 1, mSamples[1].pointerCoords, event->getId()); + event->addSample(ARBITRARY_EVENT_TIME + 2, mSamples[2].pointerCoords, event->getId()); } void MotionEventTest::assertEqualsEventWithHistory(const MotionEvent* event) { @@ -591,6 +591,22 @@ TEST_F(MotionEventTest, CopyFrom_DoNotKeepHistory) { ASSERT_EQ(event.getX(0), copy.getX(0)); } +TEST_F(MotionEventTest, CheckEventIdWithHistoryIsIncremented) { + MotionEvent event; + constexpr int32_t ARBITRARY_ID = 42; + event.initialize(ARBITRARY_ID, 2, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, INVALID_HMAC, + AMOTION_EVENT_ACTION_MOVE, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, + AMOTION_EVENT_BUTTON_PRIMARY, MotionClassification::NONE, mTransform, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + mRawTransform, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 2, + mPointerProperties, mSamples[0].pointerCoords); + ASSERT_EQ(event.getId(), ARBITRARY_ID); + event.addSample(ARBITRARY_EVENT_TIME + 1, mSamples[1].pointerCoords, ARBITRARY_ID + 1); + ASSERT_EQ(event.getId(), ARBITRARY_ID + 1); + event.addSample(ARBITRARY_EVENT_TIME + 2, mSamples[2].pointerCoords, ARBITRARY_ID + 2); + ASSERT_EQ(event.getId(), ARBITRARY_ID + 2); +} + TEST_F(MotionEventTest, SplitPointerDown) { MotionEvent event = MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN) .downTime(ARBITRARY_DOWN_TIME) diff --git a/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp b/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp index f49469ccca..1210f711de 100644 --- a/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp @@ -52,7 +52,7 @@ struct PublishMotionArgs { const int32_t action; const nsecs_t downTime; const uint32_t seq; - const int32_t eventId; + int32_t eventId; const int32_t deviceId = 1; const uint32_t source = AINPUT_SOURCE_TOUCHSCREEN; const ui::LogicalDisplayId displayId = ui::LogicalDisplayId::DEFAULT; @@ -291,6 +291,7 @@ protected: void publishAndConsumeKeyEvent(); void publishAndConsumeMotionStream(); void publishAndConsumeMotionDown(nsecs_t downTime); + void publishAndConsumeSinglePointerMultipleSamples(const size_t nSamples); void publishAndConsumeBatchedMotionMove(nsecs_t downTime); void publishAndConsumeFocusEvent(); void publishAndConsumeCaptureEvent(); @@ -298,6 +299,7 @@ protected: void publishAndConsumeTouchModeEvent(); void publishAndConsumeMotionEvent(int32_t action, nsecs_t downTime, const std::vector<Pointer>& pointers); + void TearDown() override { // Destroy the consumer, flushing any of the pending ack's. sendMessage(LooperMessage::DESTROY_CONSUMER); @@ -362,7 +364,7 @@ private: if (!mConsumer->probablyHasInput()) { ADD_FAILURE() << "should deterministically have input because there is a batch"; } - mConsumer->consumeBatchedInputEvents(std::nullopt); + mConsumer->consumeBatchedInputEvents(/*frameTime=*/std::nullopt); }; void onFocusEvent(std::unique_ptr<FocusEvent> event, uint32_t seq) override { mFocusEvents.push(std::move(event)); @@ -394,8 +396,9 @@ void InputPublisherAndConsumerNoResamplingTest::handleMessage(const Message& mes break; } case LooperMessage::CREATE_CONSUMER: { - mConsumer = std::make_unique<InputConsumerNoResampling>(std::move(mClientChannel), - mLooper, *this); + mConsumer = + std::make_unique<InputConsumerNoResampling>(std::move(mClientChannel), mLooper, + *this, /*resampler=*/nullptr); break; } case LooperMessage::DESTROY_CONSUMER: { @@ -519,6 +522,123 @@ void InputPublisherAndConsumerNoResamplingTest::publishAndConsumeMotionDown(nsec {Pointer{.id = 0, .x = 20, .y = 30}}); } +/* + * Decompose a potential multi-sampled MotionEvent into multiple MotionEvents + * with a single sample. + */ +std::vector<MotionEvent> splitBatchedMotionEvent(const MotionEvent& batchedMotionEvent) { + std::vector<MotionEvent> singleMotionEvents; + const size_t batchSize = batchedMotionEvent.getHistorySize() + 1; + for (size_t i = 0; i < batchSize; ++i) { + MotionEvent singleMotionEvent; + singleMotionEvent + .initialize(batchedMotionEvent.getId(), batchedMotionEvent.getDeviceId(), + batchedMotionEvent.getSource(), batchedMotionEvent.getDisplayId(), + batchedMotionEvent.getHmac(), batchedMotionEvent.getAction(), + batchedMotionEvent.getActionButton(), batchedMotionEvent.getFlags(), + batchedMotionEvent.getEdgeFlags(), batchedMotionEvent.getMetaState(), + batchedMotionEvent.getButtonState(), + batchedMotionEvent.getClassification(), + batchedMotionEvent.getTransform(), batchedMotionEvent.getXPrecision(), + batchedMotionEvent.getYPrecision(), + batchedMotionEvent.getRawXCursorPosition(), + batchedMotionEvent.getRawYCursorPosition(), + batchedMotionEvent.getRawTransform(), batchedMotionEvent.getDownTime(), + batchedMotionEvent.getHistoricalEventTime(/*historicalIndex=*/i), + batchedMotionEvent.getPointerCount(), + batchedMotionEvent.getPointerProperties(), + (batchedMotionEvent.getSamplePointerCoords() + i)); + singleMotionEvents.push_back(singleMotionEvent); + } + return singleMotionEvents; +} + +/* + * Simulates a single pointer touching the screen and leaving it there for a period of time. + * Publishes a DOWN event and consumes it right away. Then, publishes a sequence of MOVE + * samples for the same pointer, and waits until it has been consumed. Splits batched MotionEvents + * into individual samples. Checks the consumed MotionEvents against the published ones. + * This test is non-deterministic because it depends on the timing of arrival of events to the + * socket. + * + * @param nSamples The number of MOVE samples to publish before attempting consumption. + */ +void InputPublisherAndConsumerNoResamplingTest::publishAndConsumeSinglePointerMultipleSamples( + const size_t nSamples) { + const nsecs_t downTime = systemTime(SYSTEM_TIME_MONOTONIC); + const Pointer pointer(0, 20, 30); + + const PublishMotionArgs argsDown(AMOTION_EVENT_ACTION_DOWN, downTime, {pointer}, mSeq); + const nsecs_t publishTimeOfDown = systemTime(SYSTEM_TIME_MONOTONIC); + publishMotionEvent(*mPublisher, argsDown); + + // Consume the DOWN event. + ASSERT_TRUE(mMotionEvents.popWithTimeout(TIMEOUT).has_value()); + + verifyFinishedSignal(*mPublisher, mSeq, publishTimeOfDown); + + std::vector<nsecs_t> publishTimes; + std::vector<PublishMotionArgs> argsMoves; + std::queue<uint32_t> publishedSequenceNumbers; + + // Block Looper to increase the chance of batching events + { + std::scoped_lock l(mLock); + mLooperMayProceed = false; + } + sendMessage(LooperMessage::BLOCK_LOOPER); + { + std::unique_lock l(mLock); + mNotifyLooperWaiting.wait(l, [this] { return mLooperIsBlocked; }); + } + + uint32_t firstSampleId; + for (size_t i = 0; i < nSamples; ++i) { + publishedSequenceNumbers.push(++mSeq); + PublishMotionArgs argsMove(AMOTION_EVENT_ACTION_MOVE, downTime, {pointer}, mSeq); + // A batched MotionEvent only has a single event id, currently determined when the + // MotionEvent is initialized. Therefore, to pass the eventId comparisons inside + // verifyArgsEqualToEvent, we need to override the event id of the published args to match + // the event id of the first sample inside the MotionEvent. + if (i == 0) { + firstSampleId = argsMove.eventId; + } + argsMove.eventId = firstSampleId; + publishTimes.push_back(systemTime(SYSTEM_TIME_MONOTONIC)); + argsMoves.push_back(argsMove); + publishMotionEvent(*mPublisher, argsMove); + } + + std::vector<MotionEvent> singleSampledMotionEvents; + + // Unblock Looper + { + std::scoped_lock l(mLock); + mLooperMayProceed = true; + } + mNotifyLooperMayProceed.notify_all(); + + // We have no control over the socket behavior, so the consumer can receive + // the motion as a batched event, or as a sequence of multiple single-sample MotionEvents (or a + // mix of those) + while (singleSampledMotionEvents.size() != nSamples) { + const std::optional<std::unique_ptr<MotionEvent>> batchedMotionEvent = + mMotionEvents.popWithTimeout(TIMEOUT); + // The events received by these calls are never null + std::vector<MotionEvent> splitMotionEvents = splitBatchedMotionEvent(**batchedMotionEvent); + singleSampledMotionEvents.insert(singleSampledMotionEvents.end(), splitMotionEvents.begin(), + splitMotionEvents.end()); + } + + // Consumer can choose to finish events in any order. For simplicity, + // we verify the events in sequence (since that is how the test is implemented). + for (size_t i = 0; i < nSamples; ++i) { + verifyArgsEqualToEvent(argsMoves[i], singleSampledMotionEvents[i]); + verifyFinishedSignal(*mPublisher, publishedSequenceNumbers.front(), publishTimes[i]); + publishedSequenceNumbers.pop(); + } +} + void InputPublisherAndConsumerNoResamplingTest::publishAndConsumeBatchedMotionMove( nsecs_t downTime) { uint32_t seq = mSeq++; @@ -814,4 +934,8 @@ TEST_F(InputPublisherAndConsumerNoResamplingTest, PublishMultipleEvents_EndToEnd ASSERT_NO_FATAL_FAILURE(publishAndConsumeTouchModeEvent()); } +TEST_F(InputPublisherAndConsumerNoResamplingTest, PublishAndConsumeSinglePointer) { + publishAndConsumeSinglePointerMultipleSamples(3); +} + } // namespace android diff --git a/libs/input/tests/MotionPredictorMetricsManager_test.cpp b/libs/input/tests/MotionPredictorMetricsManager_test.cpp index cc41eeb5e7..0542f39f6d 100644 --- a/libs/input/tests/MotionPredictorMetricsManager_test.cpp +++ b/libs/input/tests/MotionPredictorMetricsManager_test.cpp @@ -167,7 +167,8 @@ MotionEvent makeMotionEvent(const std::vector<PredictionPoint>& predictionPoints .y(predictionPoints[i].position[0]) .axis(AMOTION_EVENT_AXIS_PRESSURE, predictionPoints[i].pressure) .buildCoords(); - predictionEvent.addSample(predictionPoints[i].targetTimestamp, &coords); + predictionEvent.addSample(predictionPoints[i].targetTimestamp, &coords, + predictionEvent.getId()); } return predictionEvent; } diff --git a/libs/input/tests/MotionPredictor_test.cpp b/libs/input/tests/MotionPredictor_test.cpp index d077760757..106e686a81 100644 --- a/libs/input/tests/MotionPredictor_test.cpp +++ b/libs/input/tests/MotionPredictor_test.cpp @@ -70,7 +70,7 @@ static MotionEvent getMotionEvent(int32_t action, float x, float y, } TEST(JerkTrackerTest, JerkReadiness) { - JerkTracker jerkTracker(true); + JerkTracker jerkTracker(/*normalizedDt=*/true, /*alpha=*/1); EXPECT_FALSE(jerkTracker.jerkMagnitude()); jerkTracker.pushSample(/*timestamp=*/0, 20, 50); EXPECT_FALSE(jerkTracker.jerkMagnitude()); @@ -87,7 +87,8 @@ TEST(JerkTrackerTest, JerkReadiness) { } TEST(JerkTrackerTest, JerkCalculationNormalizedDtTrue) { - JerkTracker jerkTracker(true); + const float alpha = .5; + JerkTracker jerkTracker(/*normalizedDt=*/true, alpha); jerkTracker.pushSample(/*timestamp=*/0, 20, 50); jerkTracker.pushSample(/*timestamp=*/1, 25, 53); jerkTracker.pushSample(/*timestamp=*/2, 30, 60); @@ -118,11 +119,13 @@ TEST(JerkTrackerTest, JerkCalculationNormalizedDtTrue) { * y'': 3 -> -15 * y''': -18 */ - EXPECT_FLOAT_EQ(jerkTracker.jerkMagnitude().value(), std::hypot(-50, -18)); + const float newJerk = (1 - alpha) * std::hypot(10, -1) + alpha * std::hypot(-50, -18); + EXPECT_FLOAT_EQ(jerkTracker.jerkMagnitude().value(), newJerk); } TEST(JerkTrackerTest, JerkCalculationNormalizedDtFalse) { - JerkTracker jerkTracker(false); + const float alpha = .5; + JerkTracker jerkTracker(/*normalizedDt=*/false, alpha); jerkTracker.pushSample(/*timestamp=*/0, 20, 50); jerkTracker.pushSample(/*timestamp=*/10, 25, 53); jerkTracker.pushSample(/*timestamp=*/20, 30, 60); @@ -153,11 +156,12 @@ TEST(JerkTrackerTest, JerkCalculationNormalizedDtFalse) { * y'': .03 -> -.125 (delta above, divide by 10) * y''': -.0155 (delta above, divide by 10) */ - EXPECT_FLOAT_EQ(jerkTracker.jerkMagnitude().value(), std::hypot(-.0375, -.0155)); + const float newJerk = (1 - alpha) * std::hypot(.01, -.001) + alpha * std::hypot(-.0375, -.0155); + EXPECT_FLOAT_EQ(jerkTracker.jerkMagnitude().value(), newJerk); } TEST(JerkTrackerTest, JerkCalculationAfterReset) { - JerkTracker jerkTracker(true); + JerkTracker jerkTracker(/*normalizedDt=*/true, /*alpha=*/1); jerkTracker.pushSample(/*timestamp=*/0, 20, 50); jerkTracker.pushSample(/*timestamp=*/1, 25, 53); jerkTracker.pushSample(/*timestamp=*/2, 30, 60); @@ -291,15 +295,22 @@ TEST_WITH_FLAGS( MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/0, []() { return true /*enable prediction*/; }); - // Jerk is medium (1.05 normalized, which is halfway between LOW_JANK and HIGH_JANK) - predictor.record(getMotionEvent(DOWN, 0, 5.2, 20ms)); - predictor.record(getMotionEvent(MOVE, 0, 11.5, 30ms)); - predictor.record(getMotionEvent(MOVE, 0, 22, 40ms)); - predictor.record(getMotionEvent(MOVE, 0, 37.75, 50ms)); - predictor.record(getMotionEvent(MOVE, 0, 59.8, 60ms)); + // Create another instance of TfLiteMotionPredictorModel to read config details. + std::unique_ptr<TfLiteMotionPredictorModel> testTfLiteModel = + TfLiteMotionPredictorModel::create(); + const float mediumJerk = + (testTfLiteModel->config().lowJerk + testTfLiteModel->config().highJerk) / 2; + const float a = 3; // initial acceleration + const float b = 4; // initial velocity + const float c = 5; // initial position + predictor.record(getMotionEvent(DOWN, 0, c, 20ms)); + predictor.record(getMotionEvent(MOVE, 0, c + b, 30ms)); + predictor.record(getMotionEvent(MOVE, 0, c + 2 * b + a, 40ms)); + predictor.record(getMotionEvent(MOVE, 0, c + 3 * b + 3 * a + mediumJerk, 50ms)); + predictor.record(getMotionEvent(MOVE, 0, c + 4 * b + 6 * a + 4 * mediumJerk, 60ms)); std::unique_ptr<MotionEvent> predicted = predictor.predict(82 * NSEC_PER_MSEC); EXPECT_NE(nullptr, predicted); - // Halfway between LOW_JANK and HIGH_JANK means that half of the predictions + // Halfway between LOW_JERK and HIGH_JERK means that half of the predictions // will be pruned. If model prediction window is close enough to predict() // call time window, then half of the model predictions (5/2 -> 2) will be // ouputted. diff --git a/libs/input/tests/Resampler_test.cpp b/libs/input/tests/Resampler_test.cpp new file mode 100644 index 0000000000..26dee393c1 --- /dev/null +++ b/libs/input/tests/Resampler_test.cpp @@ -0,0 +1,873 @@ +/** + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <input/Resampler.h> + +#include <gtest/gtest.h> + +#include <chrono> +#include <memory> +#include <vector> + +#include <input/Input.h> +#include <input/InputEventBuilders.h> +#include <input/InputTransport.h> +#include <utils/Timers.h> + +namespace android { + +namespace { + +using namespace std::literals::chrono_literals; + +constexpr float EPSILON = MotionEvent::ROUNDING_PRECISION; + +struct Pointer { + int32_t id{0}; + ToolType toolType{ToolType::FINGER}; + float x{0.0f}; + float y{0.0f}; + bool isResampled{false}; + /** + * Converts from Pointer to PointerCoords. Enables calling LegacyResampler methods and + * assertions only with the relevant data for tests. + */ + operator PointerCoords() const; +}; + +Pointer::operator PointerCoords() const { + PointerCoords pointerCoords; + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y); + pointerCoords.isResampled = isResampled; + return pointerCoords; +} + +struct InputSample { + std::chrono::milliseconds eventTime{0}; + std::vector<Pointer> pointers{}; + + explicit InputSample(std::chrono::milliseconds eventTime, const std::vector<Pointer>& pointers) + : eventTime{eventTime}, pointers{pointers} {} + /** + * Converts from InputSample to InputMessage. Enables calling LegacyResampler methods only with + * the relevant data for tests. + */ + operator InputMessage() const; +}; + +InputSample::operator InputMessage() const { + InputMessageBuilder messageBuilder = + InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/0} + .eventTime(std::chrono::nanoseconds{eventTime}.count()) + .source(AINPUT_SOURCE_TOUCHSCREEN) + .downTime(0); + + for (const Pointer& pointer : pointers) { + messageBuilder.pointer( + PointerBuilder{pointer.id, pointer.toolType}.x(pointer.x).y(pointer.y).isResampled( + pointer.isResampled)); + } + return messageBuilder.build(); +} + +struct InputStream { + std::vector<InputSample> samples{}; + int32_t action{0}; + DeviceId deviceId{0}; + /** + * Converts from InputStream to MotionEvent. Enables calling LegacyResampler methods only with + * the relevant data for tests. + */ + operator MotionEvent() const; +}; + +InputStream::operator MotionEvent() const { + const InputSample& firstSample{*samples.begin()}; + MotionEventBuilder motionEventBuilder = + MotionEventBuilder(action, AINPUT_SOURCE_CLASS_POINTER) + .downTime(0) + .eventTime(static_cast<std::chrono::nanoseconds>(firstSample.eventTime).count()) + .deviceId(deviceId); + for (const Pointer& pointer : firstSample.pointers) { + const PointerBuilder pointerBuilder = + PointerBuilder(pointer.id, pointer.toolType).x(pointer.x).y(pointer.y); + motionEventBuilder.pointer(pointerBuilder); + } + MotionEvent motionEvent = motionEventBuilder.build(); + const size_t numSamples = samples.size(); + for (size_t i = 1; i < numSamples; ++i) { + std::vector<PointerCoords> pointersCoords{samples[i].pointers.begin(), + samples[i].pointers.end()}; + motionEvent.addSample(static_cast<std::chrono::nanoseconds>(samples[i].eventTime).count(), + pointersCoords.data(), motionEvent.getId()); + } + return motionEvent; +} + +} // namespace + +/** + * The testing setup assumes an input rate of 200 Hz and a display rate of 60 Hz. This implies that + * input events are received every 5 milliseconds, while the display consumes batched events every + * ~16 milliseconds. The resampler's RESAMPLE_LATENCY constant determines the resample time, which + * is calculated as frameTime - RESAMPLE_LATENCY. resampleTime specifies the time used for + * resampling. For example, if the desired frame time consumption is ~16 milliseconds, the resample + * time would be ~11 milliseconds. Consequenly, the last added sample to the motion event has an + * event time of ~11 milliseconds. Note that there are specific scenarios where resampleMotionEvent + * is not called with a multiple of ~16 milliseconds. These cases are primarily for data addition + * or to test other functionalities of the resampler. + * + * Coordinates are calculated using linear interpolation (lerp) based on the last two available + * samples. Linear interpolation is defined as (a + alpha*(b - a)). Let t_b and t_a be the + * timestamps of samples a and b, respectively. The interpolation factor alpha is calculated as + * (resampleTime - t_a) / (t_b - t_a). The value of alpha determines whether the resampled + * coordinates are interpolated or extrapolated. If alpha falls within the semi-closed interval [0, + * 1), the coordinates are interpolated. If alpha is greater than or equal to 1, the coordinates are + * extrapolated. + * + * The timeline below depics an interpolation scenario + * -----------------------------------|---------|---------|---------|---------- + * 10ms 11ms 15ms 16ms + * MOVE | MOVE | + * resampleTime frameTime + * Based on the timeline alpha is (11 - 10)/(15 - 10) = 1/5. Thus, coordinates are interpolated. + * + * The following timeline portrays an extrapolation scenario + * -------------------------|---------|---------|-------------------|---------- + * 5ms 10ms 11ms 16ms + * MOVE MOVE | | + * resampleTime frameTime + * Likewise, alpha = (11 - 5)/(10 - 5) = 6/5. Hence, coordinates are extrapolated. + * + * If a motion event was resampled, the tests will check that the following conditions are satisfied + * to guarantee resampling correctness: + * - The motion event metadata must not change. + * - The number of samples in the motion event must only increment by 1. + * - The resampled values must be at the end of motion event coordinates. + * - The rasamples values must be near the hand calculations. + * - The resampled time must be the most recent one in motion event. + */ +class ResamplerTest : public testing::Test { +protected: + ResamplerTest() : mResampler(std::make_unique<LegacyResampler>()) {} + + ~ResamplerTest() override {} + + void SetUp() override {} + + void TearDown() override {} + + std::unique_ptr<Resampler> mResampler; + + /** + * Checks that beforeCall and afterCall are equal except for the mutated attributes by addSample + * member function. + * @param beforeCall MotionEvent before passing it to resampleMotionEvent + * @param afterCall MotionEvent after passing it to resampleMotionEvent + */ + void assertMotionEventMetaDataDidNotMutate(const MotionEvent& beforeCall, + const MotionEvent& afterCall); + + /** + * Asserts the MotionEvent is resampled by checking an increment in history size and that the + * resampled coordinates are near the expected ones. + */ + void assertMotionEventIsResampledAndCoordsNear( + const MotionEvent& original, const MotionEvent& resampled, + const std::vector<PointerCoords>& expectedCoords); + + void assertMotionEventIsNotResampled(const MotionEvent& original, + const MotionEvent& notResampled); +}; + +void ResamplerTest::assertMotionEventMetaDataDidNotMutate(const MotionEvent& beforeCall, + const MotionEvent& afterCall) { + EXPECT_EQ(beforeCall.getDeviceId(), afterCall.getDeviceId()); + EXPECT_EQ(beforeCall.getAction(), afterCall.getAction()); + EXPECT_EQ(beforeCall.getActionButton(), afterCall.getActionButton()); + EXPECT_EQ(beforeCall.getButtonState(), afterCall.getButtonState()); + EXPECT_EQ(beforeCall.getFlags(), afterCall.getFlags()); + EXPECT_EQ(beforeCall.getEdgeFlags(), afterCall.getEdgeFlags()); + EXPECT_EQ(beforeCall.getClassification(), afterCall.getClassification()); + EXPECT_EQ(beforeCall.getPointerCount(), afterCall.getPointerCount()); + EXPECT_EQ(beforeCall.getMetaState(), afterCall.getMetaState()); + EXPECT_EQ(beforeCall.getSource(), afterCall.getSource()); + EXPECT_EQ(beforeCall.getXPrecision(), afterCall.getXPrecision()); + EXPECT_EQ(beforeCall.getYPrecision(), afterCall.getYPrecision()); + EXPECT_EQ(beforeCall.getDownTime(), afterCall.getDownTime()); + EXPECT_EQ(beforeCall.getDisplayId(), afterCall.getDisplayId()); +} + +void ResamplerTest::assertMotionEventIsResampledAndCoordsNear( + const MotionEvent& original, const MotionEvent& resampled, + const std::vector<PointerCoords>& expectedCoords) { + assertMotionEventMetaDataDidNotMutate(original, resampled); + + const size_t originalSampleSize = original.getHistorySize() + 1; + const size_t resampledSampleSize = resampled.getHistorySize() + 1; + EXPECT_EQ(originalSampleSize + 1, resampledSampleSize); + + const size_t numPointers = resampled.getPointerCount(); + const size_t beginLatestSample = resampledSampleSize - 1; + for (size_t i = 0; i < numPointers; ++i) { + SCOPED_TRACE(i); + EXPECT_EQ(original.getPointerId(i), resampled.getPointerId(i)); + EXPECT_EQ(original.getToolType(i), resampled.getToolType(i)); + + const PointerCoords& resampledCoords = + resampled.getSamplePointerCoords()[beginLatestSample * numPointers + i]; + + EXPECT_TRUE(resampledCoords.isResampled); + EXPECT_NEAR(expectedCoords[i].getX(), resampledCoords.getX(), EPSILON); + EXPECT_NEAR(expectedCoords[i].getY(), resampledCoords.getY(), EPSILON); + } +} + +void ResamplerTest::assertMotionEventIsNotResampled(const MotionEvent& original, + const MotionEvent& notResampled) { + assertMotionEventMetaDataDidNotMutate(original, notResampled); + const size_t originalSampleSize = original.getHistorySize() + 1; + const size_t notResampledSampleSize = notResampled.getHistorySize() + 1; + EXPECT_EQ(originalSampleSize, notResampledSampleSize); +} + +TEST_F(ResamplerTest, NonResampledAxesArePreserved) { + constexpr float TOUCH_MAJOR_VALUE = 1.0f; + + MotionEvent motionEvent = + InputStream{{InputSample{5ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + constexpr std::chrono::nanoseconds eventTime{10ms}; + PointerCoords pointerCoords{}; + pointerCoords.isResampled = false; + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, 2.0f); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, 2.0f); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, TOUCH_MAJOR_VALUE); + + motionEvent.addSample(eventTime.count(), &pointerCoords, motionEvent.getId()); + + const InputMessage futureSample = + InputSample{15ms, {{.id = 0, .x = 3.0f, .y = 4.0f, .isResampled = false}}}; + + const MotionEvent originalMotionEvent = motionEvent; + + mResampler->resampleMotionEvent(16ms, motionEvent, &futureSample); + + EXPECT_EQ(motionEvent.getTouchMajor(0), TOUCH_MAJOR_VALUE); + + assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent, + {Pointer{.id = 0, + .x = 2.2f, + .y = 2.4f, + .isResampled = true}}); +} + +TEST_F(ResamplerTest, SinglePointerNotEnoughDataToResample) { + MotionEvent motionEvent = + InputStream{{InputSample{5ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + const MotionEvent originalMotionEvent = motionEvent; + + mResampler->resampleMotionEvent(16ms, motionEvent, /*futureSample=*/nullptr); + + assertMotionEventIsNotResampled(originalMotionEvent, motionEvent); +} + +TEST_F(ResamplerTest, SinglePointerDifferentDeviceIdBetweenMotionEvents) { + MotionEvent motionFromFirstDevice = + InputStream{{InputSample{4ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}, + InputSample{8ms, {{.id = 0, .x = 2.0f, .y = 2.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE, + .deviceId = 0}; + + mResampler->resampleMotionEvent(10ms, motionFromFirstDevice, nullptr); + + MotionEvent motionFromSecondDevice = + InputStream{{InputSample{11ms, + {{.id = 0, .x = 3.0f, .y = 3.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE, + .deviceId = 1}; + const MotionEvent originalMotionEvent = motionFromSecondDevice; + + mResampler->resampleMotionEvent(12ms, motionFromSecondDevice, nullptr); + // The MotionEvent should not be resampled because the second event came from a different device + // than the previous event. + assertMotionEventIsNotResampled(originalMotionEvent, motionFromSecondDevice); +} + +TEST_F(ResamplerTest, SinglePointerSingleSampleInterpolation) { + MotionEvent motionEvent = + InputStream{{InputSample{10ms, + {{.id = 0, .x = 1.0f, .y = 2.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + const InputMessage futureSample = + InputSample{15ms, {{.id = 0, .x = 2.0f, .y = 4.0f, .isResampled = false}}}; + + const MotionEvent originalMotionEvent = motionEvent; + + mResampler->resampleMotionEvent(16ms, motionEvent, &futureSample); + + assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent, + {Pointer{.id = 0, + .x = 1.2f, + .y = 2.4f, + .isResampled = true}}); +} + +TEST_F(ResamplerTest, SinglePointerDeltaTooSmallInterpolation) { + MotionEvent motionEvent = + InputStream{{InputSample{10ms, + {{.id = 0, .x = 1.0f, .y = 2.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + const InputMessage futureSample = + InputSample{11ms, {{.id = 0, .x = 2.0f, .y = 4.0f, .isResampled = false}}}; + + const MotionEvent originalMotionEvent = motionEvent; + + mResampler->resampleMotionEvent(10'500'000ns, motionEvent, &futureSample); + + assertMotionEventIsNotResampled(originalMotionEvent, motionEvent); +} + +/** + * Tests extrapolation given two MotionEvents with a single sample. + */ +TEST_F(ResamplerTest, SinglePointerSingleSampleExtrapolation) { + MotionEvent firstMotionEvent = + InputStream{{InputSample{5ms, {{.id = 0, .x = 1.0f, .y = 2.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + mResampler->resampleMotionEvent(9ms, firstMotionEvent, nullptr); + + MotionEvent secondMotionEvent = + InputStream{{InputSample{10ms, + {{.id = 0, .x = 2.0f, .y = 4.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + const MotionEvent originalMotionEvent = secondMotionEvent; + + mResampler->resampleMotionEvent(16ms, secondMotionEvent, nullptr); + + assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, secondMotionEvent, + {Pointer{.id = 0, + .x = 2.2f, + .y = 4.4f, + .isResampled = true}}); +} + +TEST_F(ResamplerTest, SinglePointerMultipleSampleInterpolation) { + MotionEvent motionEvent = + InputStream{{InputSample{5ms, {{.id = 0, .x = 1.0f, .y = 2.0f, .isResampled = false}}}, + InputSample{10ms, + {{.id = 0, .x = 2.0f, .y = 3.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + const InputMessage futureSample = + InputSample{15ms, {{.id = 0, .x = 3.0f, .y = 5.0f, .isResampled = false}}}; + + const MotionEvent originalMotionEvent = motionEvent; + + mResampler->resampleMotionEvent(16ms, motionEvent, &futureSample); + + assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent, + {Pointer{.id = 0, + .x = 2.2f, + .y = 3.4f, + .isResampled = true}}); +} + +TEST_F(ResamplerTest, SinglePointerMultipleSampleExtrapolation) { + MotionEvent motionEvent = + InputStream{{InputSample{5ms, {{.id = 0, .x = 1.0f, .y = 2.0f, .isResampled = false}}}, + InputSample{10ms, + {{.id = 0, .x = 2.0f, .y = 4.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + const MotionEvent originalMotionEvent = motionEvent; + + mResampler->resampleMotionEvent(16ms, motionEvent, nullptr); + + assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent, + {Pointer{.id = 0, + .x = 2.2f, + .y = 4.4f, + .isResampled = true}}); +} + +TEST_F(ResamplerTest, SinglePointerDeltaTooSmallExtrapolation) { + MotionEvent motionEvent = + InputStream{{InputSample{9ms, {{.id = 0, .x = 1.0f, .y = 2.0f, .isResampled = false}}}, + InputSample{10ms, + {{.id = 0, .x = 2.0f, .y = 4.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + const MotionEvent originalMotionEvent = motionEvent; + + mResampler->resampleMotionEvent(16ms, motionEvent, nullptr); + + assertMotionEventIsNotResampled(originalMotionEvent, motionEvent); +} + +TEST_F(ResamplerTest, SinglePointerDeltaTooLargeExtrapolation) { + MotionEvent motionEvent = + InputStream{{InputSample{5ms, {{.id = 0, .x = 1.0f, .y = 2.0f, .isResampled = false}}}, + InputSample{26ms, + {{.id = 0, .x = 2.0f, .y = 4.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + const MotionEvent originalMotionEvent = motionEvent; + + mResampler->resampleMotionEvent(32ms, motionEvent, nullptr); + + assertMotionEventIsNotResampled(originalMotionEvent, motionEvent); +} + +TEST_F(ResamplerTest, SinglePointerResampleTimeTooFarExtrapolation) { + MotionEvent motionEvent = + InputStream{{InputSample{5ms, {{.id = 0, .x = 1.0f, .y = 2.0f, .isResampled = false}}}, + InputSample{25ms, + {{.id = 0, .x = 2.0f, .y = 4.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + const MotionEvent originalMotionEvent = motionEvent; + + mResampler->resampleMotionEvent(48ms, motionEvent, nullptr); + + assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent, + {Pointer{.id = 0, + .x = 2.4f, + .y = 4.8f, + .isResampled = true}}); +} + +TEST_F(ResamplerTest, MultiplePointerSingleSampleInterpolation) { + MotionEvent motionEvent = + InputStream{{InputSample{5ms, + {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}, + {.id = 1, .x = 2.0f, .y = 2.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + const InputMessage futureSample = + InputSample{15ms, + {{.id = 0, .x = 3.0f, .y = 3.0f, .isResampled = false}, + {.id = 1, .x = 4.0f, .y = 4.0f, .isResampled = false}}}; + + const MotionEvent originalMotionEvent = motionEvent; + + mResampler->resampleMotionEvent(16ms, motionEvent, &futureSample); + + assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent, + {Pointer{.x = 2.2f, .y = 2.2f, .isResampled = true}, + Pointer{.x = 3.2f, .y = 3.2f, .isResampled = true}}); +} + +TEST_F(ResamplerTest, MultiplePointerSingleSampleExtrapolation) { + MotionEvent firstMotionEvent = + InputStream{{InputSample{5ms, + {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}, + {.id = 1, .x = 2.0f, .y = 2.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + mResampler->resampleMotionEvent(9ms, firstMotionEvent, /*futureSample=*/nullptr); + + MotionEvent secondMotionEvent = + InputStream{{InputSample{10ms, + {{.id = 0, .x = 3.0f, .y = 3.0f, .isResampled = false}, + {.id = 1, .x = 4.0f, .y = 4.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + const MotionEvent originalMotionEvent = secondMotionEvent; + + mResampler->resampleMotionEvent(16ms, secondMotionEvent, /*futureSample=*/nullptr); + + assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, secondMotionEvent, + {Pointer{.x = 3.4f, .y = 3.4f, .isResampled = true}, + Pointer{.x = 4.4f, .y = 4.4f, .isResampled = true}}); +} + +TEST_F(ResamplerTest, MultiplePointerMultipleSampleInterpolation) { + MotionEvent motionEvent = + InputStream{{InputSample{5ms, + {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}, + {.id = 1, .x = 2.0f, .y = 2.0f, .isResampled = false}}}, + InputSample{10ms, + {{.id = 0, .x = 3.0f, .y = 3.0f, .isResampled = false}, + {.id = 1, .x = 4.0f, .y = 4.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + const InputMessage futureSample = + InputSample{15ms, + {{.id = 0, .x = 5.0f, .y = 5.0f, .isResampled = false}, + {.id = 1, .x = 6.0f, .y = 6.0f, .isResampled = false}}}; + + const MotionEvent originalMotionEvent = motionEvent; + + mResampler->resampleMotionEvent(16ms, motionEvent, &futureSample); + + assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent, + {Pointer{.x = 3.4f, .y = 3.4f, .isResampled = true}, + Pointer{.x = 4.4f, .y = 4.4f, .isResampled = true}}); +} + +TEST_F(ResamplerTest, MultiplePointerMultipleSampleExtrapolation) { + MotionEvent motionEvent = + InputStream{{InputSample{5ms, + {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}, + {.id = 1, .x = 2.0f, .y = 2.0f, .isResampled = false}}}, + InputSample{10ms, + {{.id = 0, .x = 3.0f, .y = 3.0f, .isResampled = false}, + {.id = 1, .x = 4.0f, .y = 4.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + const MotionEvent originalMotionEvent = motionEvent; + + mResampler->resampleMotionEvent(16ms, motionEvent, /*futureSample=*/nullptr); + + assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent, + {Pointer{.x = 3.4f, .y = 3.4f, .isResampled = true}, + Pointer{.x = 4.4f, .y = 4.4f, .isResampled = true}}); +} + +TEST_F(ResamplerTest, MultiplePointerIncreaseNumPointersInterpolation) { + MotionEvent motionEvent = + InputStream{{InputSample{10ms, + {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}, + {.id = 1, .x = 2.0f, .y = 2.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + const InputMessage futureSample = + InputSample{15ms, + {{.id = 0, .x = 3.0f, .y = 3.0f, .isResampled = false}, + {.id = 1, .x = 4.0f, .y = 4.0f, .isResampled = false}, + {.id = 2, .x = 5.0f, .y = 5.0f, .isResampled = false}}}; + + const MotionEvent originalMotionEvent = motionEvent; + + mResampler->resampleMotionEvent(16ms, motionEvent, &futureSample); + + assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent, + {Pointer{.x = 1.4f, .y = 1.4f, .isResampled = true}, + Pointer{.x = 2.4f, .y = 2.4f, .isResampled = true}}); + + MotionEvent secondMotionEvent = + InputStream{{InputSample{25ms, + {{.id = 0, .x = 3.0f, .y = 3.0f, .isResampled = false}, + {.id = 1, .x = 4.0f, .y = 4.0f, .isResampled = false}, + {.id = 2, .x = 5.0f, .y = 5.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + const InputMessage secondFutureSample = + InputSample{30ms, + {{.id = 0, .x = 5.0f, .y = 5.0f, .isResampled = false}, + {.id = 1, .x = 6.0f, .y = 6.0f, .isResampled = false}, + {.id = 2, .x = 7.0f, .y = 7.0f, .isResampled = false}}}; + + const MotionEvent originalSecondMotionEvent = secondMotionEvent; + + mResampler->resampleMotionEvent(32ms, secondMotionEvent, &secondFutureSample); + + assertMotionEventIsResampledAndCoordsNear(originalSecondMotionEvent, secondMotionEvent, + {Pointer{.x = 3.8f, .y = 3.8f, .isResampled = true}, + Pointer{.x = 4.8f, .y = 4.8f, .isResampled = true}, + Pointer{.x = 5.8f, .y = 5.8f, .isResampled = true}}); +} + +TEST_F(ResamplerTest, MultiplePointerIncreaseNumPointersExtrapolation) { + MotionEvent firstMotionEvent = + InputStream{{InputSample{5ms, + {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}, + {.id = 1, .x = 2.0f, .y = 2.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + mResampler->resampleMotionEvent(9ms, firstMotionEvent, /*futureSample=*/nullptr); + + MotionEvent secondMotionEvent = + InputStream{{InputSample{10ms, + {{.id = 0, .x = 3.0f, .y = 3.0f, .isResampled = false}, + {.id = 1, .x = 4.0f, .y = 4.0f, .isResampled = false}, + {.id = 2, .x = 5.0f, .y = 5.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + const MotionEvent secondOriginalMotionEvent = secondMotionEvent; + + mResampler->resampleMotionEvent(16ms, secondMotionEvent, /*futureSample=*/nullptr); + + assertMotionEventIsNotResampled(secondOriginalMotionEvent, secondMotionEvent); +} + +TEST_F(ResamplerTest, MultiplePointerDecreaseNumPointersInterpolation) { + MotionEvent motionEvent = + InputStream{{InputSample{10ms, + {{.id = 0, .x = 3.0f, .y = 3.0f, .isResampled = false}, + {.id = 1, .x = 4.0f, .y = 4.0f, .isResampled = false}, + {.id = 2, .x = 5.0f, .y = 5.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + const InputMessage futureSample = + InputSample{15ms, + {{.id = 0, .x = 4.0f, .y = 4.0f, .isResampled = false}, + {.id = 1, .x = 5.0f, .y = 5.0f, .isResampled = false}}}; + + const MotionEvent originalMotionEvent = motionEvent; + + mResampler->resampleMotionEvent(16ms, motionEvent, &futureSample); + + assertMotionEventIsNotResampled(originalMotionEvent, motionEvent); +} + +TEST_F(ResamplerTest, MultiplePointerDecreaseNumPointersExtrapolation) { + MotionEvent firstMotionEvent = + InputStream{{InputSample{5ms, + {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}, + {.id = 1, .x = 2.0f, .y = 2.0f, .isResampled = false}, + {.id = 2, .x = 3.0f, .y = 3.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + mResampler->resampleMotionEvent(9ms, firstMotionEvent, /*futureSample=*/nullptr); + + MotionEvent secondMotionEvent = + InputStream{{InputSample{10ms, + {{.id = 0, .x = 3.0f, .y = 3.0f, .isResampled = false}, + {.id = 1, .x = 4.0f, .y = 4.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + const MotionEvent secondOriginalMotionEvent = secondMotionEvent; + + mResampler->resampleMotionEvent(16ms, secondMotionEvent, /*futureSample=*/nullptr); + + assertMotionEventIsResampledAndCoordsNear(secondOriginalMotionEvent, secondMotionEvent, + {Pointer{.x = 3.4f, .y = 3.4f, .isResampled = true}, + Pointer{.x = 4.4f, .y = 4.4f, .isResampled = true}}); +} + +TEST_F(ResamplerTest, MultiplePointerDifferentIdOrderInterpolation) { + MotionEvent motionEvent = + InputStream{{InputSample{10ms, + {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}, + {.id = 1, .x = 2.0f, .y = 2.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + const InputMessage futureSample = + InputSample{15ms, + {{.id = 1, .x = 4.0f, .y = 4.0f, .isResampled = false}, + {.id = 0, .x = 3.0f, .y = 3.0f, .isResampled = false}}}; + + const MotionEvent originalMotionEvent = motionEvent; + + mResampler->resampleMotionEvent(16ms, motionEvent, &futureSample); + + assertMotionEventIsNotResampled(originalMotionEvent, motionEvent); +} + +TEST_F(ResamplerTest, MultiplePointerDifferentIdOrderExtrapolation) { + MotionEvent firstMotionEvent = + InputStream{{InputSample{5ms, + {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}, + {.id = 1, .x = 2.0f, .y = 2.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + mResampler->resampleMotionEvent(9ms, firstMotionEvent, /*futureSample=*/nullptr); + + MotionEvent secondMotionEvent = + InputStream{{InputSample{10ms, + {{.id = 1, .x = 4.0f, .y = 4.0f, .isResampled = false}, + {.id = 0, .x = 3.0f, .y = 3.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + const MotionEvent secondOriginalMotionEvent = secondMotionEvent; + + mResampler->resampleMotionEvent(16ms, secondMotionEvent, /*futureSample=*/nullptr); + + assertMotionEventIsNotResampled(secondOriginalMotionEvent, secondMotionEvent); +} + +TEST_F(ResamplerTest, MultiplePointerDifferentIdsInterpolation) { + MotionEvent motionEvent = + InputStream{{InputSample{10ms, + {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}, + {.id = 1, .x = 2.0f, .y = 2.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + const InputMessage futureSample = + InputSample{15ms, + {{.id = 1, .x = 4.0f, .y = 4.0f, .isResampled = false}, + {.id = 2, .x = 3.0f, .y = 3.0f, .isResampled = false}}}; + + const MotionEvent originalMotionEvent = motionEvent; + + mResampler->resampleMotionEvent(16ms, motionEvent, &futureSample); + + assertMotionEventIsNotResampled(originalMotionEvent, motionEvent); +} + +TEST_F(ResamplerTest, MultiplePointerDifferentIdsExtrapolation) { + MotionEvent firstMotionEvent = + InputStream{{InputSample{5ms, + {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}, + {.id = 1, .x = 2.0f, .y = 2.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + mResampler->resampleMotionEvent(9ms, firstMotionEvent, /*futureSample=*/nullptr); + + MotionEvent secondMotionEvent = + InputStream{{InputSample{10ms, + {{.id = 1, .x = 4.0f, .y = 4.0f, .isResampled = false}, + {.id = 2, .x = 3.0f, .y = 3.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + const MotionEvent secondOriginalMotionEvent = secondMotionEvent; + + mResampler->resampleMotionEvent(16ms, secondMotionEvent, /*futureSample=*/nullptr); + + assertMotionEventIsNotResampled(secondOriginalMotionEvent, secondMotionEvent); +} + +TEST_F(ResamplerTest, MultiplePointerDifferentToolTypeInterpolation) { + MotionEvent motionEvent = InputStream{{InputSample{10ms, + {{.id = 0, + .toolType = ToolType::FINGER, + .x = 1.0f, + .y = 1.0f, + .isResampled = false}, + {.id = 1, + .toolType = ToolType::FINGER, + .x = 2.0f, + .y = 2.0f, + .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + const InputMessage futureSample = InputSample{15ms, + {{.id = 0, + .toolType = ToolType::FINGER, + .x = 3.0, + .y = 3.0, + .isResampled = false}, + {.id = 1, + .toolType = ToolType::STYLUS, + .x = 4.0, + .y = 4.0, + .isResampled = false}}}; + + const MotionEvent originalMotionEvent = motionEvent; + + mResampler->resampleMotionEvent(16ms, motionEvent, &futureSample); + + assertMotionEventIsNotResampled(originalMotionEvent, motionEvent); +} + +TEST_F(ResamplerTest, MultiplePointerDifferentToolTypeExtrapolation) { + MotionEvent firstMotionEvent = InputStream{{InputSample{5ms, + {{.id = 0, + .toolType = ToolType::FINGER, + .x = 1.0f, + .y = 1.0f, + .isResampled = false}, + {.id = 1, + .toolType = ToolType::FINGER, + .x = 2.0f, + .y = 2.0f, + .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + mResampler->resampleMotionEvent(9ms, firstMotionEvent, /*futureSample=*/nullptr); + + MotionEvent secondMotionEvent = InputStream{{InputSample{10ms, + {{.id = 0, + .toolType = ToolType::FINGER, + .x = 1.0f, + .y = 1.0f, + .isResampled = false}, + {.id = 1, + .toolType = ToolType::STYLUS, + .x = 2.0f, + .y = 2.0f, + .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + const MotionEvent secondOriginalMotionEvent = secondMotionEvent; + + mResampler->resampleMotionEvent(16ms, secondMotionEvent, /*futureSample=*/nullptr); + + assertMotionEventIsNotResampled(secondOriginalMotionEvent, secondMotionEvent); +} + +TEST_F(ResamplerTest, MultiplePointerShouldNotResampleToolTypeInterpolation) { + MotionEvent motionEvent = InputStream{{InputSample{10ms, + {{.id = 0, + .toolType = ToolType::PALM, + .x = 1.0f, + .y = 1.0f, + .isResampled = false}, + {.id = 1, + .toolType = ToolType::PALM, + .x = 2.0f, + .y = 2.0f, + .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + const InputMessage futureSample = InputSample{15ms, + {{.id = 0, + .toolType = ToolType::PALM, + .x = 3.0, + .y = 3.0, + .isResampled = false}, + {.id = 1, + .toolType = ToolType::PALM, + .x = 4.0, + .y = 4.0, + .isResampled = false}}}; + + const MotionEvent originalMotionEvent = motionEvent; + + mResampler->resampleMotionEvent(16ms, motionEvent, /*futureSample=*/nullptr); + + assertMotionEventIsNotResampled(originalMotionEvent, motionEvent); +} + +TEST_F(ResamplerTest, MultiplePointerShouldNotResampleToolTypeExtrapolation) { + MotionEvent motionEvent = InputStream{{InputSample{5ms, + {{.id = 0, + .toolType = ToolType::PALM, + .x = 1.0f, + .y = 1.0f, + .isResampled = false}, + {.id = 1, + .toolType = ToolType::PALM, + .x = 2.0f, + .y = 2.0f, + .isResampled = false}}}, + InputSample{10ms, + {{.id = 0, + .toolType = ToolType::PALM, + .x = 3.0f, + .y = 3.0f, + .isResampled = false}, + {.id = 1, + .toolType = ToolType::PALM, + .x = 4.0f, + .y = 4.0f, + .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + const MotionEvent originalMotionEvent = motionEvent; + + mResampler->resampleMotionEvent(16ms, motionEvent, /*futureSample=*/nullptr); + + assertMotionEventIsNotResampled(originalMotionEvent, motionEvent); +} +} // namespace android diff --git a/libs/input/tests/TestEventMatchers.h b/libs/input/tests/TestEventMatchers.h new file mode 100644 index 0000000000..dd2e40c025 --- /dev/null +++ b/libs/input/tests/TestEventMatchers.h @@ -0,0 +1,110 @@ +/** + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <ostream> + +#include <input/Input.h> + +namespace android { + +/** + * This file contains a copy of Matchers from .../inputflinger/tests/TestEventMatchers.h. Ideally, + * implementations must not be duplicated. + * TODO(b/365606513): Find a way to share TestEventMatchers.h between inputflinger and libinput. + */ + +class WithDeviceIdMatcher { +public: + using is_gtest_matcher = void; + explicit WithDeviceIdMatcher(DeviceId deviceId) : mDeviceId(deviceId) {} + + bool MatchAndExplain(const InputEvent& event, std::ostream*) const { + return mDeviceId == event.getDeviceId(); + } + + void DescribeTo(std::ostream* os) const { *os << "with device id " << mDeviceId; } + + void DescribeNegationTo(std::ostream* os) const { *os << "wrong device id"; } + +private: + const DeviceId mDeviceId; +}; + +inline WithDeviceIdMatcher WithDeviceId(int32_t deviceId) { + return WithDeviceIdMatcher(deviceId); +} + +class WithMotionActionMatcher { +public: + using is_gtest_matcher = void; + explicit WithMotionActionMatcher(int32_t action) : mAction(action) {} + + bool MatchAndExplain(const MotionEvent& event, std::ostream*) const { + bool matches = mAction == event.getAction(); + if (event.getAction() == AMOTION_EVENT_ACTION_CANCEL) { + matches &= (event.getFlags() & AMOTION_EVENT_FLAG_CANCELED) != 0; + } + return matches; + } + + void DescribeTo(std::ostream* os) const { + *os << "with motion action " << MotionEvent::actionToString(mAction); + if (mAction == AMOTION_EVENT_ACTION_CANCEL) { + *os << " and FLAG_CANCELED"; + } + } + + void DescribeNegationTo(std::ostream* os) const { *os << "wrong action"; } + +private: + const int32_t mAction; +}; + +inline WithMotionActionMatcher WithMotionAction(int32_t action) { + return WithMotionActionMatcher(action); +} + +class MotionEventIsResampledMatcher { +public: + using is_gtest_matcher = void; + + bool MatchAndExplain(const MotionEvent& motionEvent, std::ostream*) const { + const size_t numSamples = motionEvent.getHistorySize() + 1; + const size_t numPointers = motionEvent.getPointerCount(); + if (numPointers <= 0 || numSamples <= 0) { + return false; + } + for (size_t i = 0; i < numPointers; ++i) { + const PointerCoords& pointerCoords = + motionEvent.getSamplePointerCoords()[numSamples * numPointers + i]; + if (!pointerCoords.isResampled) { + return false; + } + } + return true; + } + + void DescribeTo(std::ostream* os) const { *os << "MotionEvent is resampled."; } + + void DescribeNegationTo(std::ostream* os) const { *os << "MotionEvent is not resampled."; } +}; + +inline MotionEventIsResampledMatcher MotionEventIsResampled() { + return MotionEventIsResampledMatcher(); +} +} // namespace android diff --git a/libs/input/tests/TestInputChannel.cpp b/libs/input/tests/TestInputChannel.cpp new file mode 100644 index 0000000000..26a0ca2106 --- /dev/null +++ b/libs/input/tests/TestInputChannel.cpp @@ -0,0 +1,102 @@ +/** + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "TestInputChannel" +#define ATRACE_TAG ATRACE_TAG_INPUT + +#include <TestInputChannel.h> + +#include <sys/socket.h> +#include <unistd.h> + +#include <array> + +#include <android-base/logging.h> +#include <android-base/unique_fd.h> +#include <binder/IBinder.h> +#include <utils/StrongPointer.h> + +namespace android { + +namespace { + +/** + * Returns a stub file descriptor by opening a socket pair and closing one of the fds. The returned + * fd can be used to construct an InputChannel. + */ +base::unique_fd generateFileDescriptor() { + std::array<int, 2> kFileDescriptors; + LOG_IF(FATAL, ::socketpair(AF_UNIX, SOCK_SEQPACKET, 0, kFileDescriptors.data()) != 0) + << "TestInputChannel. Failed to create socket pair."; + LOG_IF(FATAL, ::close(kFileDescriptors[1]) != 0) + << "TestInputChannel. Failed to close file descriptor."; + return base::unique_fd{kFileDescriptors[0]}; +} +} // namespace + +// --- TestInputChannel --- + +TestInputChannel::TestInputChannel(const std::string& name) + : InputChannel{name, generateFileDescriptor(), sp<BBinder>::make()} {} + +void TestInputChannel::enqueueMessage(const InputMessage& message) { + mReceivedMessages.push(message); +} + +status_t TestInputChannel::sendMessage(const InputMessage* message) { + LOG_IF(FATAL, message == nullptr) + << "TestInputChannel " << getName() << ". No message was passed to sendMessage."; + + mSentMessages.push(*message); + return OK; +} + +base::Result<InputMessage> TestInputChannel::receiveMessage() { + if (mReceivedMessages.empty()) { + return base::Error(WOULD_BLOCK); + } + InputMessage message = mReceivedMessages.front(); + mReceivedMessages.pop(); + return message; +} + +bool TestInputChannel::probablyHasInput() const { + return !mReceivedMessages.empty(); +} + +void TestInputChannel::assertFinishMessage(uint32_t seq, bool handled) { + ASSERT_FALSE(mSentMessages.empty()) + << "TestInputChannel " << getName() << ". Cannot assert. mSentMessages is empty."; + + const InputMessage& finishMessage = mSentMessages.front(); + + EXPECT_EQ(finishMessage.header.seq, seq) + << "TestInputChannel " << getName() + << ". Sequence mismatch. Message seq: " << finishMessage.header.seq + << " Expected seq: " << seq; + + EXPECT_EQ(finishMessage.body.finished.handled, handled) + << "TestInputChannel " << getName() + << ". Handled value mismatch. Message val: " << std::boolalpha + << finishMessage.body.finished.handled << "Expected val: " << handled + << std::noboolalpha; + mSentMessages.pop(); +} + +void TestInputChannel::assertNoSentMessages() const { + ASSERT_TRUE(mSentMessages.empty()); +} +} // namespace android
\ No newline at end of file diff --git a/libs/input/tests/TestInputChannel.h b/libs/input/tests/TestInputChannel.h new file mode 100644 index 0000000000..43253ec0ef --- /dev/null +++ b/libs/input/tests/TestInputChannel.h @@ -0,0 +1,66 @@ +/** + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <queue> +#include <string> + +#include <android-base/result.h> +#include <gtest/gtest.h> +#include <input/InputTransport.h> +#include <utils/Errors.h> + +namespace android { + +class TestInputChannel final : public InputChannel { +public: + explicit TestInputChannel(const std::string& name); + + /** + * Enqueues a message in mReceivedMessages. + */ + void enqueueMessage(const InputMessage& message); + + /** + * Pushes message to mSentMessages. In the default implementation, InputChannel sends messages + * through a file descriptor. TestInputChannel, on the contrary, stores sent messages in + * mSentMessages for assertion reasons. + */ + status_t sendMessage(const InputMessage* message) override; + + /** + * Returns an InputMessage from mReceivedMessages. This is done instead of retrieving data + * directly from fd. + */ + base::Result<InputMessage> receiveMessage() override; + + /** + * Returns if mReceivedMessages is not empty. + */ + bool probablyHasInput() const override; + + void assertFinishMessage(uint32_t seq, bool handled); + + void assertNoSentMessages() const; + +private: + // InputMessages received by the endpoint. + std::queue<InputMessage> mReceivedMessages; + // InputMessages sent by the endpoint. + std::queue<InputMessage> mSentMessages; +}; +} // namespace android diff --git a/libs/nativedisplay/ADisplay.cpp b/libs/nativedisplay/ADisplay.cpp index e3be3bc8f8..d0ca78e658 100644 --- a/libs/nativedisplay/ADisplay.cpp +++ b/libs/nativedisplay/ADisplay.cpp @@ -129,7 +129,7 @@ int ADisplay_acquirePhysicalDisplays(ADisplay*** outDisplays) { std::vector<DisplayConfigImpl> modesPerDisplay[size]; ui::DisplayConnectionType displayConnectionTypes[size]; int numModes = 0; - for (int i = 0; i < size; ++i) { + for (size_t i = 0; i < size; ++i) { ui::StaticDisplayInfo staticInfo; if (const status_t status = SurfaceComposerClient::getStaticDisplayInfo(ids[i].value, &staticInfo); @@ -151,7 +151,7 @@ int ADisplay_acquirePhysicalDisplays(ADisplay*** outDisplays) { numModes += modes.size(); modesPerDisplay[i].reserve(modes.size()); - for (int j = 0; j < modes.size(); ++j) { + for (size_t j = 0; j < modes.size(); ++j) { const ui::DisplayMode& mode = modes[j]; modesPerDisplay[i].emplace_back( DisplayConfigImpl{static_cast<size_t>(mode.id), mode.resolution.getWidth(), @@ -224,7 +224,7 @@ float ADisplay_getMaxSupportedFps(ADisplay* display) { CHECK_NOT_NULL(display); DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display); float maxFps = 0.0; - for (int i = 0; i < impl->numConfigs; ++i) { + for (size_t i = 0; i < impl->numConfigs; ++i) { maxFps = std::max(maxFps, impl->configs[i].fps); } return maxFps; @@ -261,7 +261,7 @@ int ADisplay_getCurrentConfig(ADisplay* display, ADisplayConfig** outConfig) { for (size_t i = 0; i < impl->numConfigs; i++) { auto* config = impl->configs + i; - if (config->id == info.activeDisplayModeId) { + if (info.activeDisplayModeId >= 0 && config->id == (size_t)info.activeDisplayModeId) { *outConfig = reinterpret_cast<ADisplayConfig*>(config); return OK; } diff --git a/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h b/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h index 099f47dbe1..f1453bd64d 100644 --- a/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h +++ b/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h @@ -98,11 +98,25 @@ public: * is created in a detached state, and attachToContext must be called before * calls to updateTexImage. */ +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) + SurfaceTexture(uint32_t tex, uint32_t textureTarget, bool useFenceSync, bool isControlledByApp); + + SurfaceTexture(uint32_t textureTarget, bool useFenceSync, bool isControlledByApp); + + SurfaceTexture(const sp<IGraphicBufferConsumer>& bq, uint32_t tex, uint32_t textureTarget, + bool useFenceSync, bool isControlledByApp) + __attribute((deprecated("Prefer ctors that create their own surface and consumer."))); + + SurfaceTexture(const sp<IGraphicBufferConsumer>& bq, uint32_t textureTarget, bool useFenceSync, + bool isControlledByApp) + __attribute((deprecated("Prefer ctors that create their own surface and consumer."))); +#else SurfaceTexture(const sp<IGraphicBufferConsumer>& bq, uint32_t tex, uint32_t textureTarget, bool useFenceSync, bool isControlledByApp); SurfaceTexture(const sp<IGraphicBufferConsumer>& bq, uint32_t textureTarget, bool useFenceSync, bool isControlledByApp); +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) /** * updateTexImage acquires the most recently queued buffer, and sets the @@ -499,6 +513,8 @@ protected: friend class EGLConsumer; private: + void initialize(); + // Proxy listener to avoid having SurfaceTexture directly implement FrameAvailableListener as it // is extending ConsumerBase which also implements FrameAvailableListener. class FrameAvailableListenerProxy : public ConsumerBase::FrameAvailableListener { diff --git a/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp b/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp index 3a09204878..ce232cc4c7 100644 --- a/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp +++ b/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp @@ -35,6 +35,49 @@ namespace android { static const mat4 mtxIdentity; +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) +SurfaceTexture::SurfaceTexture(uint32_t tex, uint32_t texTarget, bool useFenceSync, + bool isControlledByApp) + : ConsumerBase(isControlledByApp), + mCurrentCrop(Rect::EMPTY_RECT), + mCurrentTransform(0), + mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), + mCurrentFence(Fence::NO_FENCE), + mCurrentTimestamp(0), + mCurrentDataSpace(HAL_DATASPACE_UNKNOWN), + mCurrentFrameNumber(0), + mDefaultWidth(1), + mDefaultHeight(1), + mFilteringEnabled(true), + mTexName(tex), + mUseFenceSync(useFenceSync), + mTexTarget(texTarget), + mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT), + mOpMode(OpMode::attachedToGL) { + initialize(); +} + +SurfaceTexture::SurfaceTexture(uint32_t texTarget, bool useFenceSync, bool isControlledByApp) + : ConsumerBase(isControlledByApp), + mCurrentCrop(Rect::EMPTY_RECT), + mCurrentTransform(0), + mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), + mCurrentFence(Fence::NO_FENCE), + mCurrentTimestamp(0), + mCurrentDataSpace(HAL_DATASPACE_UNKNOWN), + mCurrentFrameNumber(0), + mDefaultWidth(1), + mDefaultHeight(1), + mFilteringEnabled(true), + mTexName(0), + mUseFenceSync(useFenceSync), + mTexTarget(texTarget), + mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT), + mOpMode(OpMode::detached) { + initialize(); +} +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) + SurfaceTexture::SurfaceTexture(const sp<IGraphicBufferConsumer>& bq, uint32_t tex, uint32_t texTarget, bool useFenceSync, bool isControlledByApp) : ConsumerBase(bq, isControlledByApp), @@ -53,11 +96,7 @@ SurfaceTexture::SurfaceTexture(const sp<IGraphicBufferConsumer>& bq, uint32_t te mTexTarget(texTarget), mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT), mOpMode(OpMode::attachedToGL) { - SFT_LOGV("SurfaceTexture"); - - memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), sizeof(mCurrentTransformMatrix)); - - mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS); + initialize(); } SurfaceTexture::SurfaceTexture(const sp<IGraphicBufferConsumer>& bq, uint32_t texTarget, @@ -78,11 +117,7 @@ SurfaceTexture::SurfaceTexture(const sp<IGraphicBufferConsumer>& bq, uint32_t te mTexTarget(texTarget), mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT), mOpMode(OpMode::detached) { - SFT_LOGV("SurfaceTexture"); - - memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), sizeof(mCurrentTransformMatrix)); - - mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS); + initialize(); } status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h) { @@ -531,4 +566,12 @@ void SurfaceTexture::onSetFrameRate(float frameRate, int8_t compatibility, } #endif +void SurfaceTexture::initialize() { + SFT_LOGV("SurfaceTexture"); + + memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), sizeof(mCurrentTransformMatrix)); + + mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS); +} + } // namespace android diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp index dd78049b16..ca41346d46 100644 --- a/libs/nativewindow/AHardwareBuffer.cpp +++ b/libs/nativewindow/AHardwareBuffer.cpp @@ -196,10 +196,10 @@ int AHardwareBuffer_lockAndGetInfo(AHardwareBuffer* buffer, uint64_t usage, return BAD_VALUE; } - if (usage & ~(AHARDWAREBUFFER_USAGE_CPU_READ_MASK | - AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK)) { + if (usage & ~(AHARDWAREBUFFER_USAGE_CPU_READ_MASK | AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK) || + usage == 0) { ALOGE("Invalid usage flags passed to AHardwareBuffer_lock; only " - "AHARDWAREBUFFER_USAGE_CPU_* flags are allowed"); + "AHARDWAREBUFFER_USAGE_CPU_* flags are allowed"); return BAD_VALUE; } @@ -248,10 +248,10 @@ int AHardwareBuffer_lock(AHardwareBuffer* buffer, uint64_t usage, if (!buffer) return BAD_VALUE; - if (usage & ~(AHARDWAREBUFFER_USAGE_CPU_READ_MASK | - AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK)) { + if (usage & ~(AHARDWAREBUFFER_USAGE_CPU_READ_MASK | AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK) || + usage == 0) { ALOGE("Invalid usage flags passed to AHardwareBuffer_lock; only " - "AHARDWAREBUFFER_USAGE_CPU_* flags are allowed"); + "AHARDWAREBUFFER_USAGE_CPU_* flags are allowed"); return BAD_VALUE; } @@ -277,10 +277,10 @@ int AHardwareBuffer_lockPlanes(AHardwareBuffer* buffer, uint64_t usage, int32_t fence, const ARect* rect, AHardwareBuffer_Planes* outPlanes) { if (!buffer || !outPlanes) return BAD_VALUE; - if (usage & ~(AHARDWAREBUFFER_USAGE_CPU_READ_MASK | - AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK)) { + if (usage & ~(AHARDWAREBUFFER_USAGE_CPU_READ_MASK | AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK) || + usage == 0) { ALOGE("Invalid usage flags passed to AHardwareBuffer_lock; only " - " AHARDWAREBUFFER_USAGE_CPU_* flags are allowed"); + " AHARDWAREBUFFER_USAGE_CPU_* flags are allowed"); return BAD_VALUE; } diff --git a/libs/nativewindow/tests/ANativeWindowTest.cpp b/libs/nativewindow/tests/ANativeWindowTest.cpp index 6cf8291da2..937ff02241 100644 --- a/libs/nativewindow/tests/ANativeWindowTest.cpp +++ b/libs/nativewindow/tests/ANativeWindowTest.cpp @@ -50,9 +50,14 @@ protected: const ::testing::TestInfo* const test_info = ::testing::UnitTest::GetInstance()->current_test_info(); ALOGV("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) + mItemConsumer = new BufferItemConsumer(GRALLOC_USAGE_SW_READ_OFTEN); + mWindow = new TestableSurface(mItemConsumer->getSurface()->getIGraphicBufferProducer()); +#else BufferQueue::createBufferQueue(&mProducer, &mConsumer); mItemConsumer = new BufferItemConsumer(mConsumer, GRALLOC_USAGE_SW_READ_OFTEN); mWindow = new TestableSurface(mProducer); +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) const int success = native_window_api_connect(mWindow.get(), NATIVE_WINDOW_API_CPU); EXPECT_EQ(0, success); } @@ -64,10 +69,12 @@ protected: const int success = native_window_api_disconnect(mWindow.get(), NATIVE_WINDOW_API_CPU); EXPECT_EQ(0, success); } + +#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) sp<IGraphicBufferProducer> mProducer; sp<IGraphicBufferConsumer> mConsumer; +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) sp<BufferItemConsumer> mItemConsumer; - sp<TestableSurface> mWindow; }; diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp index 4a04467308..d248ea0b84 100644 --- a/libs/renderengine/Android.bp +++ b/libs/renderengine/Android.bp @@ -100,11 +100,14 @@ filegroup { "skia/debug/SkiaCapture.cpp", "skia/debug/SkiaMemoryReporter.cpp", "skia/filters/BlurFilter.cpp", + "skia/filters/GainmapFactory.cpp", "skia/filters/GaussianBlurFilter.cpp", + "skia/filters/KawaseBlurDualFilter.cpp", "skia/filters/KawaseBlurFilter.cpp", "skia/filters/LinearEffect.cpp", "skia/filters/MouriMap.cpp", "skia/filters/StretchShaderFactory.cpp", + "skia/filters/EdgeExtensionShaderFactory.cpp", ], } diff --git a/libs/renderengine/ExternalTexture.cpp b/libs/renderengine/ExternalTexture.cpp index 6f2a96a87b..8d0fbba2e0 100644 --- a/libs/renderengine/ExternalTexture.cpp +++ b/libs/renderengine/ExternalTexture.cpp @@ -14,11 +14,11 @@ * limitations under the License. */ +#include <common/trace.h> #include <log/log.h> #include <renderengine/RenderEngine.h> #include <renderengine/impl/ExternalTexture.h> #include <ui/GraphicBuffer.h> -#include <utils/Trace.h> namespace android::renderengine::impl { @@ -35,7 +35,7 @@ ExternalTexture::~ExternalTexture() { } void ExternalTexture::remapBuffer() { - ATRACE_CALL(); + SFTRACE_CALL(); { auto buf = mBuffer; mRenderEngine.unmapExternalTextureBuffer(std::move(buf)); diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp index bc3976d9f1..907590a236 100644 --- a/libs/renderengine/RenderEngine.cpp +++ b/libs/renderengine/RenderEngine.cpp @@ -21,6 +21,7 @@ #include "skia/GraphiteVkRenderEngine.h" #include "skia/SkiaGLRenderEngine.h" #include "threaded/RenderEngineThreaded.h" +#include "ui/GraphicTypes.h" #include <com_android_graphics_surfaceflinger_flags.h> #include <cutils/properties.h> @@ -101,17 +102,34 @@ ftl::Future<FenceResult> RenderEngine::drawLayers(const DisplaySettings& display base::unique_fd&& bufferFence) { const auto resultPromise = std::make_shared<std::promise<FenceResult>>(); std::future<FenceResult> resultFuture = resultPromise->get_future(); - updateProtectedContext(layers, buffer); + updateProtectedContext(layers, {buffer.get()}); drawLayersInternal(std::move(resultPromise), display, layers, buffer, std::move(bufferFence)); return resultFuture; } +ftl::Future<FenceResult> RenderEngine::drawGainmap( + const std::shared_ptr<ExternalTexture>& sdr, base::borrowed_fd&& sdrFence, + const std::shared_ptr<ExternalTexture>& hdr, base::borrowed_fd&& hdrFence, + float hdrSdrRatio, ui::Dataspace dataspace, + const std::shared_ptr<ExternalTexture>& gainmap) { + const auto resultPromise = std::make_shared<std::promise<FenceResult>>(); + std::future<FenceResult> resultFuture = resultPromise->get_future(); + updateProtectedContext({}, {sdr.get(), hdr.get(), gainmap.get()}); + drawGainmapInternal(std::move(resultPromise), sdr, std::move(sdrFence), hdr, + std::move(hdrFence), hdrSdrRatio, dataspace, gainmap); + return resultFuture; +} + void RenderEngine::updateProtectedContext(const std::vector<LayerSettings>& layers, - const std::shared_ptr<ExternalTexture>& buffer) { + vector<const ExternalTexture*> buffers) { const bool needsProtectedContext = - (buffer && (buffer->getUsage() & GRALLOC_USAGE_PROTECTED)) || - std::any_of(layers.begin(), layers.end(), [](const LayerSettings& layer) { - const std::shared_ptr<ExternalTexture>& buffer = layer.source.buffer.buffer; + std::any_of(layers.begin(), layers.end(), + [](const LayerSettings& layer) { + const std::shared_ptr<ExternalTexture>& buffer = + layer.source.buffer.buffer; + return buffer && (buffer->getUsage() & GRALLOC_USAGE_PROTECTED); + }) || + std::any_of(buffers.begin(), buffers.end(), [](const ExternalTexture* buffer) { return buffer && (buffer->getUsage() & GRALLOC_USAGE_PROTECTED); }); useProtectedContext(needsProtectedContext); diff --git a/libs/renderengine/benchmark/Android.bp b/libs/renderengine/benchmark/Android.bp index e1a6f6af0d..f84db0b04c 100644 --- a/libs/renderengine/benchmark/Android.bp +++ b/libs/renderengine/benchmark/Android.bp @@ -57,6 +57,7 @@ cc_benchmark { "libui", "libutils", "server_configurable_flags", + "libtracing_perfetto", ], data: ["resources/*"], diff --git a/libs/renderengine/benchmark/AndroidTest.xml b/libs/renderengine/benchmark/AndroidTest.xml new file mode 100644 index 0000000000..3b923cb3a8 --- /dev/null +++ b/libs/renderengine/benchmark/AndroidTest.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<configuration description="Config for librenderengine_bench."> + <option name="test-suite-tag" value="apct" /> + <option name="test-suite-tag" value="apct-native-metric" /> + + <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> + <option name="cleanup" value="true" /> + <option name="push" value="librenderengine_bench->/data/local/tmp/librenderengine_bench" /> + </target_preparer> + <test class="com.android.tradefed.testtype.GoogleBenchmarkTest" > + <option name="native-benchmark-device-path" value="/data/local/tmp" /> + <option name="benchmark-module-name" value="librenderengine_bench" /> + <option name="file-exclusion-filter-regex" value=".*\.config$" /> + <option name="file-exclusion-filter-regex" value=".*/resources/.*" /> + </test> +</configuration> diff --git a/libs/renderengine/benchmark/RenderEngineBench.cpp b/libs/renderengine/benchmark/RenderEngineBench.cpp index 05a2063423..a9264b3914 100644 --- a/libs/renderengine/benchmark/RenderEngineBench.cpp +++ b/libs/renderengine/benchmark/RenderEngineBench.cpp @@ -29,6 +29,16 @@ using namespace android; using namespace android::renderengine; +// To run tests: +/** + * mmm frameworks/native/libs/renderengine/benchmark;\ + * adb push $OUT/data/benchmarktest/librenderengine_bench/librenderengine_bench + * /data/benchmarktest/librenderengine_bench/librenderengine_bench;\ + * adb shell /data/benchmarktest/librenderengine_bench/librenderengine_bench + * + * (64-bit devices: out directory contains benchmarktest64 instead of benchmarktest) + */ + /////////////////////////////////////////////////////////////////////////////// // Helpers for calling drawLayers /////////////////////////////////////////////////////////////////////////////// @@ -64,14 +74,15 @@ std::pair<uint32_t, uint32_t> getDisplaySize() { return std::pair<uint32_t, uint32_t>(width, height); } -static std::unique_ptr<RenderEngine> createRenderEngine(RenderEngine::Threaded threaded, - RenderEngine::GraphicsApi graphicsApi) { +static std::unique_ptr<RenderEngine> createRenderEngine( + RenderEngine::Threaded threaded, RenderEngine::GraphicsApi graphicsApi, + RenderEngine::BlurAlgorithm blurAlgorithm = RenderEngine::BlurAlgorithm::KAWASE) { auto args = RenderEngineCreationArgs::Builder() .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888)) .setImageCacheSize(1) .setEnableProtectedContext(true) .setPrecacheToneMapperShaderOnly(false) - .setBlurAlgorithm(renderengine::RenderEngine::BlurAlgorithm::KAWASE) + .setBlurAlgorithm(blurAlgorithm) .setContextPriority(RenderEngine::ContextPriority::REALTIME) .setThreaded(threaded) .setGraphicsApi(graphicsApi) @@ -172,28 +183,67 @@ static void benchDrawLayers(RenderEngine& re, const std::vector<LayerSettings>& } } +/** + * Return a buffer with the image in the provided path, relative to the executable directory + */ +static std::shared_ptr<ExternalTexture> createTexture(RenderEngine& re, const char* relPathImg) { + // Initially use cpu access so we can decode into it with AImageDecoder. + auto [width, height] = getDisplaySize(); + auto srcBuffer = + allocateBuffer(re, width, height, GRALLOC_USAGE_SW_WRITE_OFTEN, "decoded_source"); + std::string fileName = base::GetExecutableDirectory().append(relPathImg); + renderenginebench::decode(fileName.c_str(), srcBuffer->getBuffer()); + // Now copy into GPU-only buffer for more realistic timing. + srcBuffer = copyBuffer(re, srcBuffer, 0, "source"); + return srcBuffer; +} + /////////////////////////////////////////////////////////////////////////////// // Benchmarks /////////////////////////////////////////////////////////////////////////////// +constexpr char kHomescreenPath[] = "/resources/homescreen.png"; + +/** + * Draw a layer with texture and no additional shaders as a baseline to evaluate a shader's impact + * on performance + */ template <class... Args> -void BM_blur(benchmark::State& benchState, Args&&... args) { +void BM_homescreen(benchmark::State& benchState, Args&&... args) { auto args_tuple = std::make_tuple(std::move(args)...); auto re = createRenderEngine(static_cast<RenderEngine::Threaded>(std::get<0>(args_tuple)), static_cast<RenderEngine::GraphicsApi>(std::get<1>(args_tuple))); - // Initially use cpu access so we can decode into it with AImageDecoder. auto [width, height] = getDisplaySize(); - auto srcBuffer = - allocateBuffer(*re, width, height, GRALLOC_USAGE_SW_WRITE_OFTEN, "decoded_source"); - { - std::string srcImage = base::GetExecutableDirectory(); - srcImage.append("/resources/homescreen.png"); - renderenginebench::decode(srcImage.c_str(), srcBuffer->getBuffer()); - - // Now copy into GPU-only buffer for more realistic timing. - srcBuffer = copyBuffer(*re, srcBuffer, 0, "source"); - } + auto srcBuffer = createTexture(*re, kHomescreenPath); + + const FloatRect layerRect(0, 0, width, height); + LayerSettings layer{ + .geometry = + Geometry{ + .boundaries = layerRect, + }, + .source = + PixelSource{ + .buffer = + Buffer{ + .buffer = srcBuffer, + }, + }, + .alpha = half(1.0f), + }; + auto layers = std::vector<LayerSettings>{layer}; + benchDrawLayers(*re, layers, benchState, "homescreen"); +} + +template <class... Args> +void BM_homescreen_blur(benchmark::State& benchState, Args&&... args) { + auto args_tuple = std::make_tuple(std::move(args)...); + auto re = createRenderEngine(static_cast<RenderEngine::Threaded>(std::get<0>(args_tuple)), + static_cast<RenderEngine::GraphicsApi>(std::get<1>(args_tuple))); + + auto [width, height] = getDisplaySize(); + auto srcBuffer = createTexture(*re, kHomescreenPath); const FloatRect layerRect(0, 0, width, height); LayerSettings layer{ @@ -221,8 +271,55 @@ void BM_blur(benchmark::State& benchState, Args&&... args) { }; auto layers = std::vector<LayerSettings>{layer, blurLayer}; - benchDrawLayers(*re, layers, benchState, "blurred"); + benchDrawLayers(*re, layers, benchState, "homescreen_blurred"); } -BENCHMARK_CAPTURE(BM_blur, SkiaGLThreaded, RenderEngine::Threaded::YES, +template <class... Args> +void BM_homescreen_edgeExtension(benchmark::State& benchState, Args&&... args) { + auto args_tuple = std::make_tuple(std::move(args)...); + auto re = createRenderEngine(static_cast<RenderEngine::Threaded>(std::get<0>(args_tuple)), + static_cast<RenderEngine::GraphicsApi>(std::get<1>(args_tuple))); + + auto [width, height] = getDisplaySize(); + auto srcBuffer = createTexture(*re, kHomescreenPath); + + LayerSettings layer{ + .geometry = + Geometry{ + .boundaries = FloatRect(0, 0, width, height), + }, + .source = + PixelSource{ + .buffer = + Buffer{ + .buffer = srcBuffer, + // Part of the screen is not covered by the texture but + // will be filled in by the shader + .textureTransform = + mat4(mat3(), + vec3(width * 0.3f, height * 0.3f, 0.0f)), + }, + }, + .alpha = half(1.0f), + .edgeExtensionEffect = + EdgeExtensionEffect(/* left */ true, + /* right */ false, /* top */ true, /* bottom */ false), + }; + auto layers = std::vector<LayerSettings>{layer}; + benchDrawLayers(*re, layers, benchState, "homescreen_edge_extension"); +} + +BENCHMARK_CAPTURE(BM_homescreen_blur, gaussian, RenderEngine::Threaded::YES, + RenderEngine::GraphicsApi::GL, RenderEngine::BlurAlgorithm::GAUSSIAN); + +BENCHMARK_CAPTURE(BM_homescreen_blur, kawase, RenderEngine::Threaded::YES, + RenderEngine::GraphicsApi::GL, RenderEngine::BlurAlgorithm::KAWASE); + +BENCHMARK_CAPTURE(BM_homescreen_blur, kawase_dual_filter, RenderEngine::Threaded::YES, + RenderEngine::GraphicsApi::GL, RenderEngine::BlurAlgorithm::KAWASE_DUAL_FILTER); + +BENCHMARK_CAPTURE(BM_homescreen, SkiaGLThreaded, RenderEngine::Threaded::YES, + RenderEngine::GraphicsApi::GL); + +BENCHMARK_CAPTURE(BM_homescreen_edgeExtension, SkiaGLThreaded, RenderEngine::Threaded::YES, RenderEngine::GraphicsApi::GL); diff --git a/libs/renderengine/include/renderengine/DisplaySettings.h b/libs/renderengine/include/renderengine/DisplaySettings.h index b640983a55..280ec19a4c 100644 --- a/libs/renderengine/include/renderengine/DisplaySettings.h +++ b/libs/renderengine/include/renderengine/DisplaySettings.h @@ -102,6 +102,9 @@ struct DisplaySettings { Local, }; TonemapStrategy tonemapStrategy = TonemapStrategy::Libtonemap; + + // For now, meaningful primarily when the TonemappingStrategy is Local + float targetHdrSdrRatio = 1.f; }; static inline bool operator==(const DisplaySettings& lhs, const DisplaySettings& rhs) { diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h index 8ac0af47c6..859ae8b6e2 100644 --- a/libs/renderengine/include/renderengine/LayerSettings.h +++ b/libs/renderengine/include/renderengine/LayerSettings.h @@ -31,6 +31,7 @@ #include <ui/ShadowSettings.h> #include <ui/StretchEffect.h> #include <ui/Transform.h> +#include "ui/EdgeExtensionEffect.h" #include <iosfwd> @@ -134,6 +135,7 @@ struct LayerSettings { mat4 blurRegionTransform = mat4(); StretchEffect stretchEffect; + EdgeExtensionEffect edgeExtensionEffect; // Name associated with the layer for debugging purposes. std::string name; @@ -183,7 +185,9 @@ static inline bool operator==(const LayerSettings& lhs, const LayerSettings& rhs lhs.skipContentDraw == rhs.skipContentDraw && lhs.shadow == rhs.shadow && lhs.backgroundBlurRadius == rhs.backgroundBlurRadius && lhs.blurRegionTransform == rhs.blurRegionTransform && - lhs.stretchEffect == rhs.stretchEffect && lhs.whitePointNits == rhs.whitePointNits; + lhs.stretchEffect == rhs.stretchEffect && + lhs.edgeExtensionEffect == rhs.edgeExtensionEffect && + lhs.whitePointNits == rhs.whitePointNits; } static inline void PrintTo(const Buffer& settings, ::std::ostream* os) { @@ -254,6 +258,10 @@ static inline void PrintTo(const StretchEffect& effect, ::std::ostream* os) { *os << "\n}"; } +static inline void PrintTo(const EdgeExtensionEffect& effect, ::std::ostream* os) { + *os << effect; +} + static inline void PrintTo(const LayerSettings& settings, ::std::ostream* os) { *os << "LayerSettings for '" << settings.name.c_str() << "' {"; *os << "\n .geometry = "; @@ -285,6 +293,10 @@ static inline void PrintTo(const LayerSettings& settings, ::std::ostream* os) { *os << "\n .stretchEffect = "; PrintTo(settings.stretchEffect, os); } + + if (settings.edgeExtensionEffect.hasEffect()) { + *os << "\n .edgeExtensionEffect = " << settings.edgeExtensionEffect; + } *os << "\n .whitePointNits = " << settings.whitePointNits; *os << "\n}"; } diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h index 7207394356..0fd982e812 100644 --- a/libs/renderengine/include/renderengine/RenderEngine.h +++ b/libs/renderengine/include/renderengine/RenderEngine.h @@ -97,6 +97,7 @@ struct PrimeCacheConfig { bool cacheImageDimmedLayers = true; bool cacheClippedLayers = true; bool cacheShadowLayers = true; + bool cacheEdgeExtension = true; bool cachePIPImageLayers = true; bool cacheTransparentImageDimmedLayers = true; bool cacheClippedDimmedImageLayers = true; @@ -131,6 +132,7 @@ public: NONE, GAUSSIAN, KAWASE, + KAWASE_DUAL_FILTER, }; static std::unique_ptr<RenderEngine> create(const RenderEngineCreationArgs& args); @@ -207,6 +209,13 @@ public: const std::shared_ptr<ExternalTexture>& buffer, base::unique_fd&& bufferFence); + virtual ftl::Future<FenceResult> drawGainmap(const std::shared_ptr<ExternalTexture>& sdr, + base::borrowed_fd&& sdrFence, + const std::shared_ptr<ExternalTexture>& hdr, + base::borrowed_fd&& hdrFence, float hdrSdrRatio, + ui::Dataspace dataspace, + const std::shared_ptr<ExternalTexture>& gainmap); + // Clean-up method that should be called on the main thread after the // drawFence returned by drawLayers fires. This method will free up // resources used by the most recently drawn frame. If the frame is still @@ -284,8 +293,7 @@ protected: // Update protectedContext mode depending on whether or not any layer has a protected buffer. void updateProtectedContext(const std::vector<LayerSettings>&, - const std::shared_ptr<ExternalTexture>&); - + std::vector<const ExternalTexture*>); // Attempt to switch RenderEngine into and out of protectedContext mode virtual void useProtectedContext(bool useProtectedContext) = 0; @@ -293,6 +301,13 @@ protected: const std::shared_ptr<std::promise<FenceResult>>&& resultPromise, const DisplaySettings& display, const std::vector<LayerSettings>& layers, const std::shared_ptr<ExternalTexture>& buffer, base::unique_fd&& bufferFence) = 0; + + virtual void drawGainmapInternal( + const std::shared_ptr<std::promise<FenceResult>>&& resultPromise, + const std::shared_ptr<ExternalTexture>& sdr, base::borrowed_fd&& sdrFence, + const std::shared_ptr<ExternalTexture>& hdr, base::borrowed_fd&& hdrFence, + float hdrSdrRatio, ui::Dataspace dataspace, + const std::shared_ptr<ExternalTexture>& gainmap) = 0; }; struct RenderEngineCreationArgs { diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h index a8c242a86f..fb8331d870 100644 --- a/libs/renderengine/include/renderengine/mock/RenderEngine.h +++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h @@ -46,6 +46,17 @@ public: ftl::Future<FenceResult>(const DisplaySettings&, const std::vector<LayerSettings>&, const std::shared_ptr<ExternalTexture>&, base::unique_fd&&)); + MOCK_METHOD7(drawGainmap, + ftl::Future<FenceResult>(const std::shared_ptr<ExternalTexture>&, + base::borrowed_fd&&, + const std::shared_ptr<ExternalTexture>&, + base::borrowed_fd&&, float, ui::Dataspace, + const std::shared_ptr<ExternalTexture>&)); + MOCK_METHOD8(drawGainmapInternal, + void(const std::shared_ptr<std::promise<FenceResult>>&&, + const std::shared_ptr<ExternalTexture>&, base::borrowed_fd&&, + const std::shared_ptr<ExternalTexture>&, base::borrowed_fd&&, float, + ui::Dataspace, const std::shared_ptr<ExternalTexture>&)); MOCK_METHOD5(drawLayersInternal, void(const std::shared_ptr<std::promise<FenceResult>>&&, const DisplaySettings&, const std::vector<LayerSettings>&, const std::shared_ptr<ExternalTexture>&, diff --git a/libs/renderengine/skia/AutoBackendTexture.cpp b/libs/renderengine/skia/AutoBackendTexture.cpp index 8aeef9f4bc..b7b7a4d086 100644 --- a/libs/renderengine/skia/AutoBackendTexture.cpp +++ b/libs/renderengine/skia/AutoBackendTexture.cpp @@ -25,8 +25,8 @@ #include "compat/SkiaBackendTexture.h" +#include <common/trace.h> #include <log/log_main.h> -#include <utils/Trace.h> namespace android { namespace renderengine { @@ -63,7 +63,7 @@ void AutoBackendTexture::releaseImageProc(SkImages::ReleaseContext releaseContex } sk_sp<SkImage> AutoBackendTexture::makeImage(ui::Dataspace dataspace, SkAlphaType alphaType) { - ATRACE_CALL(); + SFTRACE_CALL(); sk_sp<SkImage> image = mBackendTexture->makeImage(alphaType, dataspace, releaseImageProc, this); // The following ref will be counteracted by releaseProc, when SkImage is discarded. @@ -75,7 +75,7 @@ sk_sp<SkImage> AutoBackendTexture::makeImage(ui::Dataspace dataspace, SkAlphaTyp } sk_sp<SkSurface> AutoBackendTexture::getOrCreateSurface(ui::Dataspace dataspace) { - ATRACE_CALL(); + SFTRACE_CALL(); LOG_ALWAYS_FATAL_IF(!mBackendTexture->isOutputBuffer(), "You can't generate an SkSurface for a read-only texture"); if (!mSurface.get() || mDataspace != dataspace) { diff --git a/libs/renderengine/skia/AutoBackendTexture.h b/libs/renderengine/skia/AutoBackendTexture.h index 74daf471fa..a570ad01a2 100644 --- a/libs/renderengine/skia/AutoBackendTexture.h +++ b/libs/renderengine/skia/AutoBackendTexture.h @@ -16,9 +16,9 @@ #pragma once -#include <GrDirectContext.h> #include <SkImage.h> #include <SkSurface.h> +#include <include/gpu/ganesh/GrDirectContext.h> #include <sys/types.h> #include <ui/GraphicTypes.h> diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp index 59b06568a0..57041ee6a1 100644 --- a/libs/renderengine/skia/Cache.cpp +++ b/libs/renderengine/skia/Cache.cpp @@ -27,6 +27,8 @@ #include "ui/Rect.h" #include "utils/Timers.h" +#include <com_android_graphics_libgui_flags.h> + namespace android::renderengine::skia { namespace { @@ -619,6 +621,32 @@ static void drawP3ImageLayers(SkiaRenderEngine* renderengine, const DisplaySetti } } +static void drawEdgeExtensionLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display, + const std::shared_ptr<ExternalTexture>& dstTexture, + const std::shared_ptr<ExternalTexture>& srcTexture) { + const Rect& displayRect = display.physicalDisplay; + // Make the layer + LayerSettings layer{ + // Make the layer bigger than the texture + .geometry = Geometry{.boundaries = FloatRect(0, 0, displayRect.width(), + displayRect.height())}, + .source = PixelSource{.buffer = + Buffer{ + .buffer = srcTexture, + .isOpaque = 1, + }}, + // The type of effect does not affect the shader's uniforms, but the layer must have a + // valid EdgeExtensionEffect to apply the shader + .edgeExtensionEffect = + EdgeExtensionEffect(true /* left */, false, false, true /* bottom */), + }; + for (float alpha : {0.5, 0.0, 1.0}) { + layer.alpha = alpha; + auto layers = std::vector<LayerSettings>{layer}; + renderengine->drawLayers(display, layers, dstTexture, base::unique_fd()); + } +} + // // The collection of shaders cached here were found by using perfetto to record shader compiles // during actions that involve RenderEngine, logging the layer settings, and the shader code @@ -761,6 +789,12 @@ void Cache::primeShaderCache(SkiaRenderEngine* renderengine, PrimeCacheConfig co // Draw layers for b/185569240. drawClippedLayers(renderengine, display, dstTexture, texture); } + + if (com::android::graphics::libgui::flags::edge_extension_shader() && + config.cacheEdgeExtension) { + drawEdgeExtensionLayers(renderengine, display, dstTexture, texture); + drawEdgeExtensionLayers(renderengine, p3Display, dstTexture, texture); + } } if (config.cachePIPImageLayers) { diff --git a/libs/renderengine/skia/GaneshVkRenderEngine.cpp b/libs/renderengine/skia/GaneshVkRenderEngine.cpp index 68798bf8b4..a3a43e20be 100644 --- a/libs/renderengine/skia/GaneshVkRenderEngine.cpp +++ b/libs/renderengine/skia/GaneshVkRenderEngine.cpp @@ -21,9 +21,9 @@ #include <include/gpu/ganesh/vk/GrVkBackendSemaphore.h> +#include <common/trace.h> #include <log/log_main.h> #include <sync/sync.h> -#include <utils/Trace.h> namespace android::renderengine::skia { @@ -78,7 +78,7 @@ base::unique_fd GaneshVkRenderEngine::flushAndSubmit(SkiaGpuContext* context, sk_sp<SkSurface> dstSurface) { sk_sp<GrDirectContext> grContext = context->grDirectContext(); { - ATRACE_NAME("flush surface"); + SFTRACE_NAME("flush surface"); // TODO: Investigate feasibility of combining this "surface flush" into the "context flush" // below. context->grDirectContext()->flush(dstSurface.get()); diff --git a/libs/renderengine/skia/GraphiteVkRenderEngine.cpp b/libs/renderengine/skia/GraphiteVkRenderEngine.cpp index b5cb21b35d..390ad6efd1 100644 --- a/libs/renderengine/skia/GraphiteVkRenderEngine.cpp +++ b/libs/renderengine/skia/GraphiteVkRenderEngine.cpp @@ -23,6 +23,7 @@ #include <include/gpu/graphite/BackendSemaphore.h> #include <include/gpu/graphite/Context.h> #include <include/gpu/graphite/Recording.h> +#include <include/gpu/graphite/vk/VulkanGraphiteTypes.h> #include <log/log_main.h> #include <sync/sync.h> @@ -77,7 +78,7 @@ void GraphiteVkRenderEngine::waitFence(SkiaGpuContext*, base::borrowed_fd fenceF base::unique_fd fenceDup(dupedFd); VkSemaphore waitSemaphore = getVulkanInterface(isProtected()).importSemaphoreFromSyncFd(fenceDup.release()); - graphite::BackendSemaphore beSemaphore(waitSemaphore); + auto beSemaphore = graphite::BackendSemaphores::MakeVulkan(waitSemaphore); mStagedWaitSemaphores.push_back(beSemaphore); } @@ -92,7 +93,7 @@ base::unique_fd GraphiteVkRenderEngine::flushAndSubmit(SkiaGpuContext* context, // This "signal" semaphore is called after rendering, but it is cleaned up in the same mechanism // as "wait" semaphores from waitFence. VkSemaphore vkSignalSemaphore = vulkanInterface.createExportableSemaphore(); - graphite::BackendSemaphore backendSignalSemaphore(vkSignalSemaphore); + auto backendSignalSemaphore = graphite::BackendSemaphores::MakeVulkan(vkSignalSemaphore); // Collect all Vk semaphores that DestroySemaphoreInfo needs to own and delete after GPU work. std::vector<VkSemaphore> vkSemaphoresToCleanUp; @@ -100,7 +101,8 @@ base::unique_fd GraphiteVkRenderEngine::flushAndSubmit(SkiaGpuContext* context, vkSemaphoresToCleanUp.push_back(vkSignalSemaphore); } for (auto backendWaitSemaphore : mStagedWaitSemaphores) { - vkSemaphoresToCleanUp.push_back(backendWaitSemaphore.getVkSemaphore()); + vkSemaphoresToCleanUp.push_back( + graphite::BackendSemaphores::GetVkSemaphore(backendWaitSemaphore)); } DestroySemaphoreInfo* destroySemaphoreInfo = nullptr; diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp index 48270e1d2b..4ef7d5bccb 100644 --- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp @@ -21,19 +21,17 @@ #include "SkiaGLRenderEngine.h" -#include "compat/SkiaGpuContext.h" - #include <EGL/egl.h> #include <EGL/eglext.h> -#include <GrContextOptions.h> -#include <GrTypes.h> #include <android-base/stringprintf.h> -#include <gl/GrGLInterface.h> +#include <common/trace.h> +#include <include/gpu/ganesh/GrContextOptions.h> +#include <include/gpu/ganesh/GrTypes.h> #include <include/gpu/ganesh/gl/GrGLDirectContext.h> -#include <gui/TraceUtils.h> +#include <include/gpu/ganesh/gl/GrGLInterface.h> +#include <log/log_main.h> #include <sync/sync.h> #include <ui/DebugUtils.h> -#include <utils/Trace.h> #include <cmath> #include <cstdint> @@ -41,7 +39,7 @@ #include <numeric> #include "GLExtensions.h" -#include "log/log_main.h" +#include "compat/SkiaGpuContext.h" namespace android { namespace renderengine { @@ -332,7 +330,7 @@ bool SkiaGLRenderEngine::useProtectedContextImpl(GrProtected isProtected) { void SkiaGLRenderEngine::waitFence(SkiaGpuContext*, base::borrowed_fd fenceFd) { if (fenceFd.get() >= 0 && !waitGpuFence(fenceFd)) { - ATRACE_NAME("SkiaGLRenderEngine::waitFence"); + SFTRACE_NAME("SkiaGLRenderEngine::waitFence"); sync_wait(fenceFd.get(), -1); } } @@ -341,19 +339,19 @@ base::unique_fd SkiaGLRenderEngine::flushAndSubmit(SkiaGpuContext* context, sk_sp<SkSurface> dstSurface) { sk_sp<GrDirectContext> grContext = context->grDirectContext(); { - ATRACE_NAME("flush surface"); + SFTRACE_NAME("flush surface"); grContext->flush(dstSurface.get()); } base::unique_fd drawFence = flushGL(); bool requireSync = drawFence.get() < 0; if (requireSync) { - ATRACE_BEGIN("Submit(sync=true)"); + SFTRACE_BEGIN("Submit(sync=true)"); } else { - ATRACE_BEGIN("Submit(sync=false)"); + SFTRACE_BEGIN("Submit(sync=false)"); } bool success = grContext->submit(requireSync ? GrSyncCpu::kYes : GrSyncCpu::kNo); - ATRACE_END(); + SFTRACE_END(); if (!success) { ALOGE("Failed to flush RenderEngine commands"); // Chances are, something illegal happened (Skia's internal GPU object @@ -400,7 +398,7 @@ bool SkiaGLRenderEngine::waitGpuFence(base::borrowed_fd fenceFd) { } base::unique_fd SkiaGLRenderEngine::flushGL() { - ATRACE_CALL(); + SFTRACE_CALL(); if (!GLExtensions::getInstance().hasNativeFenceSync()) { return base::unique_fd(); } diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h index bd177e60ba..765103889e 100644 --- a/libs/renderengine/skia/SkiaGLRenderEngine.h +++ b/libs/renderengine/skia/SkiaGLRenderEngine.h @@ -20,9 +20,10 @@ #include <EGL/egl.h> #include <EGL/eglext.h> #include <GLES2/gl2.h> -#include <GrDirectContext.h> #include <SkSurface.h> #include <android-base/thread_annotations.h> +#include <include/gpu/ganesh/GrContextOptions.h> +#include <include/gpu/ganesh/GrDirectContext.h> #include <renderengine/ExternalTexture.h> #include <renderengine/RenderEngine.h> #include <sys/types.h> @@ -32,7 +33,6 @@ #include "AutoBackendTexture.h" #include "EGL/egl.h" -#include "GrContextOptions.h" #include "SkImageInfo.h" #include "SkiaRenderEngine.h" #include "android-base/macros.h" diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp index e62640eb85..ec9d3efb88 100644 --- a/libs/renderengine/skia/SkiaRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaRenderEngine.cpp @@ -20,9 +20,6 @@ #include "SkiaRenderEngine.h" -#include <GrBackendSemaphore.h> -#include <GrContextOptions.h> -#include <GrTypes.h> #include <SkBlendMode.h> #include <SkCanvas.h> #include <SkColor.h> @@ -54,8 +51,11 @@ #include <SkTileMode.h> #include <android-base/stringprintf.h> #include <common/FlagManager.h> +#include <common/trace.h> #include <gui/FenceMonitor.h> -#include <gui/TraceUtils.h> +#include <include/gpu/ganesh/GrBackendSemaphore.h> +#include <include/gpu/ganesh/GrContextOptions.h> +#include <include/gpu/ganesh/GrTypes.h> #include <include/gpu/ganesh/SkSurfaceGanesh.h> #include <pthread.h> #include <src/core/SkTraceEventCommon.h> @@ -64,7 +64,6 @@ #include <ui/DebugUtils.h> #include <ui/GraphicBuffer.h> #include <ui/HdrRenderTypeUtils.h> -#include <utils/Trace.h> #include <cmath> #include <cstdint> @@ -76,7 +75,9 @@ #include "ColorSpaces.h" #include "compat/SkiaGpuContext.h" #include "filters/BlurFilter.h" +#include "filters/GainmapFactory.h" #include "filters/GaussianBlurFilter.h" +#include "filters/KawaseBlurDualFilter.h" #include "filters/KawaseBlurFilter.h" #include "filters/LinearEffect.h" #include "filters/MouriMap.h" @@ -238,12 +239,22 @@ static inline SkM44 getSkM44(const android::mat4& matrix) { static inline SkPoint3 getSkPoint3(const android::vec3& vector) { return SkPoint3::Make(vector.x, vector.y, vector.z); } + } // namespace namespace android { namespace renderengine { namespace skia { +namespace { +void trace(sp<Fence> fence) { + if (SFTRACE_ENABLED()) { + static gui::FenceMonitor sMonitor("RE Completion"); + sMonitor.queueFence(std::move(fence)); + } +} +} // namespace + using base::StringAppendF; std::future<void> SkiaRenderEngine::primeCache(PrimeCacheConfig config) { @@ -261,7 +272,7 @@ void SkiaRenderEngine::SkSLCacheMonitor::store(const SkData& key, const SkData& const SkString& description) { mShadersCachedSinceLastCall++; mTotalShadersCompiled++; - ATRACE_FORMAT("SF cache: %i shaders", mTotalShadersCompiled); + SFTRACE_FORMAT("SF cache: %i shaders", mTotalShadersCompiled); } int SkiaRenderEngine::reportShadersCompiled() { @@ -286,6 +297,11 @@ SkiaRenderEngine::SkiaRenderEngine(Threaded threaded, PixelFormat pixelFormat, mBlurFilter = new KawaseBlurFilter(); break; } + case BlurAlgorithm::KAWASE_DUAL_FILTER: { + ALOGD("Background Blurs Enabled (Kawase dual-filtering algorithm)"); + mBlurFilter = new KawaseBlurDualFilter(); + break; + } default: { mBlurFilter = nullptr; break; @@ -416,7 +432,7 @@ void SkiaRenderEngine::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, if (isProtectedBuffer || isProtected() || !isGpuSampleable) { return; } - ATRACE_CALL(); + SFTRACE_CALL(); // If we were to support caching protected buffers then we will need to switch the // currently bound context if we are not already using the protected context (and subsequently @@ -441,7 +457,7 @@ void SkiaRenderEngine::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, } void SkiaRenderEngine::unmapExternalTextureBuffer(sp<GraphicBuffer>&& buffer) { - ATRACE_CALL(); + SFTRACE_CALL(); std::lock_guard<std::mutex> lock(mRenderingMutex); if (const auto& iter = mGraphicBufferExternalRefs.find(buffer->getId()); iter != mGraphicBufferExternalRefs.end()) { @@ -498,7 +514,7 @@ bool SkiaRenderEngine::canSkipPostRenderCleanup() const { } void SkiaRenderEngine::cleanupPostRender() { - ATRACE_CALL(); + SFTRACE_CALL(); std::lock_guard<std::mutex> lock(mRenderingMutex); mTextureCleanupMgr.cleanup(); } @@ -507,16 +523,24 @@ sk_sp<SkShader> SkiaRenderEngine::createRuntimeEffectShader( const RuntimeEffectShaderParameters& parameters) { // The given surface will be stretched by HWUI via matrix transformation // which gets similar results for most surfaces - // Determine later on if we need to leverage the stertch shader within + // Determine later on if we need to leverage the stretch shader within // surface flinger const auto& stretchEffect = parameters.layer.stretchEffect; const auto& targetBuffer = parameters.layer.source.buffer.buffer; + const auto graphicBuffer = targetBuffer ? targetBuffer->getBuffer() : nullptr; + auto shader = parameters.shader; - if (stretchEffect.hasEffect()) { - const auto graphicBuffer = targetBuffer ? targetBuffer->getBuffer() : nullptr; - if (graphicBuffer && parameters.shader) { + if (graphicBuffer && parameters.shader) { + if (stretchEffect.hasEffect()) { shader = mStretchShaderFactory.createSkShader(shader, stretchEffect); } + // The given surface requires to be filled outside of its buffer bounds if the edge + // extension is required + const auto& edgeExtensionEffect = parameters.layer.edgeExtensionEffect; + if (edgeExtensionEffect.hasEffect()) { + shader = mEdgeExtensionShaderFactory.createSkShader(shader, parameters.layer, + parameters.imageBounds); + } } if (parameters.requiresLinearEffect) { @@ -525,21 +549,28 @@ sk_sp<SkShader> SkiaRenderEngine::createRuntimeEffectShader( static_cast<ui::PixelFormat>(targetBuffer->getPixelFormat())) : std::nullopt; - if (parameters.display.tonemapStrategy == DisplaySettings::TonemapStrategy::Local) { - // TODO: Handle color matrix transforms in linear space. - SkImage* image = parameters.shader->isAImage((SkMatrix*)nullptr, (SkTileMode*)nullptr); - if (image) { - static MouriMap kMapper; - const float ratio = getHdrRenderType(parameters.layer.sourceDataspace, format) == - HdrRenderType::GENERIC_HDR - ? 1.0f - : parameters.layerDimmingRatio; - return kMapper.mouriMap(getActiveContext(), parameters.shader, ratio); - } + const auto hdrType = getHdrRenderType(parameters.layer.sourceDataspace, format, + parameters.layerDimmingRatio); + + const auto usingLocalTonemap = + parameters.display.tonemapStrategy == DisplaySettings::TonemapStrategy::Local && + hdrType != HdrRenderType::SDR && + shader->isAImage((SkMatrix*)nullptr, (SkTileMode*)nullptr) && + (hdrType != HdrRenderType::DISPLAY_HDR || + parameters.display.targetHdrSdrRatio < parameters.layerDimmingRatio); + if (usingLocalTonemap) { + const float inputRatio = + hdrType == HdrRenderType::GENERIC_HDR ? 1.0f : parameters.layerDimmingRatio; + static MouriMap kMapper; + shader = kMapper.mouriMap(getActiveContext(), shader, inputRatio, + parameters.display.targetHdrSdrRatio); } + // disable tonemapping if we already locally tonemapped + auto inputDataspace = + usingLocalTonemap ? parameters.outputDataSpace : parameters.layer.sourceDataspace; auto effect = - shaders::LinearEffect{.inputDataspace = parameters.layer.sourceDataspace, + shaders::LinearEffect{.inputDataspace = inputDataspace, .outputDataspace = parameters.outputDataSpace, .undoPremultipliedAlpha = parameters.undoPremultipliedAlpha, .fakeOutputDataspace = parameters.fakeOutputDataspace}; @@ -555,20 +586,20 @@ sk_sp<SkShader> SkiaRenderEngine::createRuntimeEffectShader( mat4 colorTransform = parameters.layer.colorTransform; - colorTransform *= - mat4::scale(vec4(parameters.layerDimmingRatio, parameters.layerDimmingRatio, - parameters.layerDimmingRatio, 1.f)); + if (!usingLocalTonemap) { + colorTransform *= + mat4::scale(vec4(parameters.layerDimmingRatio, parameters.layerDimmingRatio, + parameters.layerDimmingRatio, 1.f)); + } - const auto targetBuffer = parameters.layer.source.buffer.buffer; - const auto graphicBuffer = targetBuffer ? targetBuffer->getBuffer() : nullptr; const auto hardwareBuffer = graphicBuffer ? graphicBuffer->toAHardwareBuffer() : nullptr; - return createLinearEffectShader(parameters.shader, effect, runtimeEffect, - std::move(colorTransform), parameters.display.maxLuminance, + return createLinearEffectShader(shader, effect, runtimeEffect, std::move(colorTransform), + parameters.display.maxLuminance, parameters.display.currentLuminanceNits, parameters.layer.source.buffer.maxLuminanceNits, hardwareBuffer, parameters.display.renderIntent); } - return parameters.shader; + return shader; } void SkiaRenderEngine::initCanvas(SkCanvas* canvas, const DisplaySettings& display) { @@ -683,7 +714,7 @@ void SkiaRenderEngine::drawLayersInternal( const std::shared_ptr<std::promise<FenceResult>>&& resultPromise, const DisplaySettings& display, const std::vector<LayerSettings>& layers, const std::shared_ptr<ExternalTexture>& buffer, base::unique_fd&& bufferFence) { - ATRACE_FORMAT("%s for %s", __func__, display.namePlusId.c_str()); + SFTRACE_FORMAT("%s for %s", __func__, display.namePlusId.c_str()); std::lock_guard<std::mutex> lock(mRenderingMutex); @@ -775,7 +806,7 @@ void SkiaRenderEngine::drawLayersInternal( logSettings(display); } for (const auto& layer : layers) { - ATRACE_FORMAT("DrawLayer: %s", layer.name.c_str()); + SFTRACE_FORMAT("DrawLayer: %s", layer.name.c_str()); if (kPrintLayerSettings) { logSettings(layer); @@ -869,7 +900,7 @@ void SkiaRenderEngine::drawLayersInternal( // TODO(b/182216890): Filter out empty layers earlier if (blurRect.width() > 0 && blurRect.height() > 0) { if (layer.backgroundBlurRadius > 0) { - ATRACE_NAME("BackgroundBlur"); + SFTRACE_NAME("BackgroundBlur"); auto blurredImage = mBlurFilter->generate(context, layer.backgroundBlurRadius, blurInput, blurRect); @@ -882,7 +913,7 @@ void SkiaRenderEngine::drawLayersInternal( canvas->concat(getSkM44(layer.blurRegionTransform).asM33()); for (auto region : layer.blurRegions) { if (cachedBlurs[region.blurRadius] == nullptr) { - ATRACE_NAME("BlurRegion"); + SFTRACE_NAME("BlurRegion"); cachedBlurs[region.blurRadius] = mBlurFilter->generate(context, region.blurRadius, blurInput, blurRect); @@ -965,7 +996,7 @@ void SkiaRenderEngine::drawLayersInternal( SkPaint paint; if (layer.source.buffer.buffer) { - ATRACE_NAME("DrawImage"); + SFTRACE_NAME("DrawImage"); validateInputBufferUsage(layer.source.buffer.buffer->getBuffer()); const auto& item = layer.source.buffer; auto imageTextureRef = getOrCreateBackendTexture(item.buffer->getBuffer(), false); @@ -1032,18 +1063,20 @@ void SkiaRenderEngine::drawLayersInternal( toSkColorSpace(layerDataspace))); } - paint.setShader(createRuntimeEffectShader( - RuntimeEffectShaderParameters{.shader = shader, - .layer = layer, - .display = display, - .undoPremultipliedAlpha = !item.isOpaque && - item.usePremultipliedAlpha, - .requiresLinearEffect = requiresLinearEffect, - .layerDimmingRatio = dimInLinearSpace - ? layerDimmingRatio - : 1.f, - .outputDataSpace = display.outputDataspace, - .fakeOutputDataspace = fakeDataspace})); + SkRect imageBounds; + matrix.mapRect(&imageBounds, SkRect::Make(image->bounds())); + + paint.setShader(createRuntimeEffectShader(RuntimeEffectShaderParameters{ + .shader = shader, + .layer = layer, + .display = display, + .undoPremultipliedAlpha = !item.isOpaque && item.usePremultipliedAlpha, + .requiresLinearEffect = requiresLinearEffect, + .layerDimmingRatio = dimInLinearSpace ? layerDimmingRatio : 1.f, + .outputDataSpace = display.outputDataspace, + .fakeOutputDataspace = fakeDataspace, + .imageBounds = imageBounds, + })); // Turn on dithering when dimming beyond this (arbitrary) threshold... static constexpr float kDimmingThreshold = 0.9f; @@ -1096,7 +1129,7 @@ void SkiaRenderEngine::drawLayersInternal( paint.setColorFilter(SkColorFilters::Matrix(colorMatrix)); } } else { - ATRACE_NAME("DrawColor"); + SFTRACE_NAME("DrawColor"); const auto color = layer.source.solidColor; sk_sp<SkShader> shader = SkShaders::Color(SkColor4f{.fR = color.r, .fG = color.g, @@ -1111,7 +1144,8 @@ void SkiaRenderEngine::drawLayersInternal( .requiresLinearEffect = requiresLinearEffect, .layerDimmingRatio = layerDimmingRatio, .outputDataSpace = display.outputDataspace, - .fakeOutputDataspace = fakeDataspace})); + .fakeOutputDataspace = fakeDataspace, + .imageBounds = SkRect::MakeEmpty()})); } if (layer.disableBlending) { @@ -1152,7 +1186,7 @@ void SkiaRenderEngine::drawLayersInternal( canvas->drawRect(bounds.rect(), paint); } if (kGaneshFlushAfterEveryLayer) { - ATRACE_NAME("flush surface"); + SFTRACE_NAME("flush surface"); // No-op in Graphite. If "flushing" Skia's drawing commands after each layer is desired // in Graphite, then a graphite::Recording would need to be snapped and tracked for each // layer, which is likely possible but adds non-trivial complexity (in both bookkeeping @@ -1166,11 +1200,48 @@ void SkiaRenderEngine::drawLayersInternal( LOG_ALWAYS_FATAL_IF(activeSurface != dstSurface); auto drawFence = sp<Fence>::make(flushAndSubmit(context, dstSurface)); + trace(drawFence); + resultPromise->set_value(std::move(drawFence)); +} - if (ATRACE_ENABLED()) { - static gui::FenceMonitor sMonitor("RE Completion"); - sMonitor.queueFence(drawFence); - } +void SkiaRenderEngine::drawGainmapInternal( + const std::shared_ptr<std::promise<FenceResult>>&& resultPromise, + const std::shared_ptr<ExternalTexture>& sdr, base::borrowed_fd&& sdrFence, + const std::shared_ptr<ExternalTexture>& hdr, base::borrowed_fd&& hdrFence, + float hdrSdrRatio, ui::Dataspace dataspace, + const std::shared_ptr<ExternalTexture>& gainmap) { + std::lock_guard<std::mutex> lock(mRenderingMutex); + auto context = getActiveContext(); + auto surfaceTextureRef = getOrCreateBackendTexture(gainmap->getBuffer(), true); + sk_sp<SkSurface> dstSurface = + surfaceTextureRef->getOrCreateSurface(ui::Dataspace::V0_SRGB_LINEAR); + + waitFence(context, sdrFence); + const auto sdrTextureRef = getOrCreateBackendTexture(sdr->getBuffer(), false); + const auto sdrImage = sdrTextureRef->makeImage(dataspace, kPremul_SkAlphaType); + const auto sdrShader = + sdrImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, + SkSamplingOptions({SkFilterMode::kLinear, SkMipmapMode::kNone}), + nullptr); + waitFence(context, hdrFence); + const auto hdrTextureRef = getOrCreateBackendTexture(hdr->getBuffer(), false); + const auto hdrImage = hdrTextureRef->makeImage(dataspace, kPremul_SkAlphaType); + const auto hdrShader = + hdrImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, + SkSamplingOptions({SkFilterMode::kLinear, SkMipmapMode::kNone}), + nullptr); + + static GainmapFactory kGainmapFactory; + const auto gainmapShader = kGainmapFactory.createSkShader(sdrShader, hdrShader, hdrSdrRatio); + + const auto canvas = dstSurface->getCanvas(); + SkPaint paint; + paint.setShader(gainmapShader); + paint.setBlendMode(SkBlendMode::kSrc); + canvas->drawPaint(paint); + + auto drawFence = sp<Fence>::make(flushAndSubmit(context, dstSurface)); + trace(drawFence); resultPromise->set_value(std::move(drawFence)); } @@ -1185,7 +1256,7 @@ size_t SkiaRenderEngine::getMaxViewportDims() const { void SkiaRenderEngine::drawShadow(SkCanvas* canvas, const SkRRect& casterRRect, const ShadowSettings& settings) { - ATRACE_CALL(); + SFTRACE_CALL(); const float casterZ = settings.length / 2.0f; const auto flags = settings.casterIsTranslucent ? kTransparentOccluder_ShadowFlag : kNone_ShadowFlag; diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h index c8f9241257..b5f8898263 100644 --- a/libs/renderengine/skia/SkiaRenderEngine.h +++ b/libs/renderengine/skia/SkiaRenderEngine.h @@ -18,11 +18,12 @@ #define SF_SKIARENDERENGINE_H_ #include <renderengine/RenderEngine.h> -#include <sys/types.h> -#include <GrBackendSemaphore.h> -#include <SkSurface.h> #include <android-base/thread_annotations.h> +#include <include/core/SkImageInfo.h> +#include <include/core/SkSurface.h> +#include <include/gpu/ganesh/GrBackendSemaphore.h> +#include <include/gpu/ganesh/GrContextOptions.h> #include <renderengine/ExternalTexture.h> #include <renderengine/RenderEngine.h> #include <sys/types.h> @@ -32,12 +33,11 @@ #include <unordered_map> #include "AutoBackendTexture.h" -#include "GrContextOptions.h" -#include "SkImageInfo.h" #include "android-base/macros.h" #include "compat/SkiaGpuContext.h" #include "debug/SkiaCapture.h" #include "filters/BlurFilter.h" +#include "filters/EdgeExtensionShaderFactory.h" #include "filters/LinearEffect.h" #include "filters/StretchShaderFactory.h" @@ -142,6 +142,13 @@ private: const std::vector<LayerSettings>& layers, const std::shared_ptr<ExternalTexture>& buffer, base::unique_fd&& bufferFence) override final; + void drawGainmapInternal(const std::shared_ptr<std::promise<FenceResult>>&& resultPromise, + const std::shared_ptr<ExternalTexture>& sdr, + base::borrowed_fd&& sdrFence, + const std::shared_ptr<ExternalTexture>& hdr, + base::borrowed_fd&& hdrFence, float hdrSdrRatio, + ui::Dataspace dataspace, + const std::shared_ptr<ExternalTexture>& gainmap) override final; void dump(std::string& result) override final; @@ -156,6 +163,7 @@ private: float layerDimmingRatio; const ui::Dataspace outputDataSpace; const ui::Dataspace fakeOutputDataspace; + const SkRect& imageBounds; }; sk_sp<SkShader> createRuntimeEffectShader(const RuntimeEffectShaderParameters&); @@ -175,6 +183,7 @@ private: AutoBackendTexture::CleanupManager mTextureCleanupMgr GUARDED_BY(mRenderingMutex); StretchShaderFactory mStretchShaderFactory; + EdgeExtensionShaderFactory mEdgeExtensionShaderFactory; sp<Fence> mLastDrawFence; BlurFilter* mBlurFilter = nullptr; diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.cpp b/libs/renderengine/skia/SkiaVkRenderEngine.cpp index bd501073d7..677a2b63b2 100644 --- a/libs/renderengine/skia/SkiaVkRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaVkRenderEngine.cpp @@ -24,18 +24,16 @@ #include "GaneshVkRenderEngine.h" #include "compat/SkiaGpuContext.h" -#include <GrBackendSemaphore.h> -#include <GrContextOptions.h> -#include <GrDirectContext.h> +#include <include/gpu/ganesh/GrBackendSemaphore.h> +#include <include/gpu/ganesh/GrContextOptions.h> +#include <include/gpu/ganesh/GrDirectContext.h> #include <include/gpu/ganesh/vk/GrVkBackendSemaphore.h> #include <include/gpu/ganesh/vk/GrVkDirectContext.h> -#include <vk/GrVkExtensions.h> -#include <vk/GrVkTypes.h> +#include <include/gpu/ganesh/vk/GrVkTypes.h> #include <android-base/stringprintf.h> -#include <gui/TraceUtils.h> +#include <common/trace.h> #include <sync/sync.h> -#include <utils/Trace.h> #include <memory> #include <string> diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.h b/libs/renderengine/skia/SkiaVkRenderEngine.h index 0a2f9b2228..d2bb3d53cf 100644 --- a/libs/renderengine/skia/SkiaVkRenderEngine.h +++ b/libs/renderengine/skia/SkiaVkRenderEngine.h @@ -17,8 +17,6 @@ #ifndef SF_SKIAVKRENDERENGINE_H_ #define SF_SKIAVKRENDERENGINE_H_ -#include <vk/GrVkBackendContext.h> - #include "SkiaRenderEngine.h" #include "VulkanInterface.h" #include "compat/SkiaGpuContext.h" diff --git a/libs/renderengine/skia/VulkanInterface.cpp b/libs/renderengine/skia/VulkanInterface.cpp index 5e756b03ed..37b69f6590 100644 --- a/libs/renderengine/skia/VulkanInterface.cpp +++ b/libs/renderengine/skia/VulkanInterface.cpp @@ -32,21 +32,8 @@ namespace android { namespace renderengine { namespace skia { -GrVkBackendContext VulkanInterface::getGaneshBackendContext() { - GrVkBackendContext backendContext; - backendContext.fInstance = mInstance; - backendContext.fPhysicalDevice = mPhysicalDevice; - backendContext.fDevice = mDevice; - backendContext.fQueue = mQueue; - backendContext.fGraphicsQueueIndex = mQueueIndex; - backendContext.fMaxAPIVersion = mApiVersion; - backendContext.fVkExtensions = &mGrExtensions; - backendContext.fDeviceFeatures2 = mPhysicalDeviceFeatures2; - backendContext.fGetProc = mGrGetProc; - backendContext.fProtectedContext = mIsProtected ? Protected::kYes : Protected::kNo; - backendContext.fDeviceLostContext = this; // VulkanInterface is long-lived - backendContext.fDeviceLostProc = onVkDeviceFault; - return backendContext; +VulkanBackendContext VulkanInterface::getGaneshBackendContext() { + return this->getGraphiteBackendContext(); }; VulkanBackendContext VulkanInterface::getGraphiteBackendContext() { @@ -57,7 +44,7 @@ VulkanBackendContext VulkanInterface::getGraphiteBackendContext() { backendContext.fQueue = mQueue; backendContext.fGraphicsQueueIndex = mQueueIndex; backendContext.fMaxAPIVersion = mApiVersion; - backendContext.fVkExtensions = &mGrExtensions; + backendContext.fVkExtensions = &mVulkanExtensions; backendContext.fDeviceFeatures2 = mPhysicalDeviceFeatures2; backendContext.fGetProc = mGrGetProc; backendContext.fProtectedContext = mIsProtected ? Protected::kYes : Protected::kNo; @@ -429,11 +416,11 @@ void VulkanInterface::init(bool protectedContent) { mDeviceExtensionNames.push_back(devExt.extensionName); } - mGrExtensions.init(sGetProc, instance, physicalDevice, enabledInstanceExtensionNames.size(), - enabledInstanceExtensionNames.data(), enabledDeviceExtensionNames.size(), - enabledDeviceExtensionNames.data()); + mVulkanExtensions.init(sGetProc, instance, physicalDevice, enabledInstanceExtensionNames.size(), + enabledInstanceExtensionNames.data(), enabledDeviceExtensionNames.size(), + enabledDeviceExtensionNames.data()); - if (!mGrExtensions.hasExtension(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, 1)) { + if (!mVulkanExtensions.hasExtension(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, 1)) { BAIL("Vulkan driver doesn't support external semaphore fd"); } @@ -458,7 +445,7 @@ void VulkanInterface::init(bool protectedContent) { tailPnext = &mProtectedMemoryFeatures->pNext; } - if (mGrExtensions.hasExtension(VK_EXT_DEVICE_FAULT_EXTENSION_NAME, 1)) { + if (mVulkanExtensions.hasExtension(VK_EXT_DEVICE_FAULT_EXTENSION_NAME, 1)) { mDeviceFaultFeatures = new VkPhysicalDeviceFaultFeaturesEXT; mDeviceFaultFeatures->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FAULT_FEATURES_EXT; mDeviceFaultFeatures->pNext = nullptr; @@ -484,7 +471,7 @@ void VulkanInterface::init(bool protectedContent) { queuePriority, }; - if (mGrExtensions.hasExtension(VK_EXT_GLOBAL_PRIORITY_EXTENSION_NAME, 2)) { + if (mVulkanExtensions.hasExtension(VK_EXT_GLOBAL_PRIORITY_EXTENSION_NAME, 2)) { queueNextPtr = &queuePriorityCreateInfo; } @@ -606,7 +593,7 @@ void VulkanInterface::teardown() { mQueue = VK_NULL_HANDLE; // Implicitly destroyed by destroying mDevice. mQueueIndex = 0; mApiVersion = 0; - mGrExtensions = skgpu::VulkanExtensions(); + mVulkanExtensions = skgpu::VulkanExtensions(); mGrGetProc = nullptr; mIsProtected = false; mIsRealtimePriority = false; diff --git a/libs/renderengine/skia/VulkanInterface.h b/libs/renderengine/skia/VulkanInterface.h index f20b00251b..d0fe4d1544 100644 --- a/libs/renderengine/skia/VulkanInterface.h +++ b/libs/renderengine/skia/VulkanInterface.h @@ -16,7 +16,7 @@ #pragma once -#include <include/gpu/vk/GrVkBackendContext.h> +#include <include/gpu/vk/VulkanBackendContext.h> #include <include/gpu/vk/VulkanExtensions.h> #include <include/gpu/vk/VulkanTypes.h> @@ -24,10 +24,6 @@ using namespace skgpu; -namespace skgpu { -struct VulkanBackendContext; -} // namespace skgpu - namespace android { namespace renderengine { namespace skia { @@ -48,7 +44,8 @@ public: bool takeOwnership(); void teardown(); - GrVkBackendContext getGaneshBackendContext(); + // TODO(b/309785258) Combine these into one now that they are the same implementation. + VulkanBackendContext getGaneshBackendContext(); VulkanBackendContext getGraphiteBackendContext(); VkSemaphore createExportableSemaphore(); VkSemaphore importSemaphoreFromSyncFd(int syncFd); @@ -86,7 +83,7 @@ private: VkQueue mQueue = VK_NULL_HANDLE; int mQueueIndex = 0; uint32_t mApiVersion = 0; - skgpu::VulkanExtensions mGrExtensions; + skgpu::VulkanExtensions mVulkanExtensions; VkPhysicalDeviceFeatures2* mPhysicalDeviceFeatures2 = nullptr; VkPhysicalDeviceSamplerYcbcrConversionFeatures* mSamplerYcbcrConversionFeatures = nullptr; VkPhysicalDeviceProtectedMemoryFeatures* mProtectedMemoryFeatures = nullptr; diff --git a/libs/renderengine/skia/compat/GaneshBackendTexture.cpp b/libs/renderengine/skia/compat/GaneshBackendTexture.cpp index d246466965..88282e7f5a 100644 --- a/libs/renderengine/skia/compat/GaneshBackendTexture.cpp +++ b/libs/renderengine/skia/compat/GaneshBackendTexture.cpp @@ -21,26 +21,26 @@ #define ATRACE_TAG ATRACE_TAG_GRAPHICS #include <include/core/SkImage.h> -#include <include/gpu/GrDirectContext.h> +#include <include/gpu/ganesh/GrDirectContext.h> #include <include/gpu/ganesh/SkImageGanesh.h> #include <include/gpu/ganesh/SkSurfaceGanesh.h> #include <include/gpu/ganesh/gl/GrGLBackendSurface.h> #include <include/gpu/ganesh/vk/GrVkBackendSurface.h> -#include <include/gpu/vk/GrVkTypes.h> +#include <include/gpu/ganesh/vk/GrVkTypes.h> #include "skia/ColorSpaces.h" #include "skia/compat/SkiaBackendTexture.h" #include <android/hardware_buffer.h> +#include <common/trace.h> #include <log/log_main.h> -#include <utils/Trace.h> namespace android::renderengine::skia { GaneshBackendTexture::GaneshBackendTexture(sk_sp<GrDirectContext> grContext, AHardwareBuffer* buffer, bool isOutputBuffer) : SkiaBackendTexture(buffer, isOutputBuffer), mGrContext(grContext) { - ATRACE_CALL(); + SFTRACE_CALL(); AHardwareBuffer_Desc desc; AHardwareBuffer_describe(buffer, &desc); const bool createProtectedImage = 0 != (desc.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT); diff --git a/libs/renderengine/skia/compat/GaneshBackendTexture.h b/libs/renderengine/skia/compat/GaneshBackendTexture.h index 5cf8647801..4337df18d4 100644 --- a/libs/renderengine/skia/compat/GaneshBackendTexture.h +++ b/libs/renderengine/skia/compat/GaneshBackendTexture.h @@ -21,7 +21,7 @@ #include <include/android/GrAHardwareBufferUtils.h> #include <include/core/SkColorSpace.h> -#include <include/gpu/GrDirectContext.h> +#include <include/gpu/ganesh/GrDirectContext.h> #include <android-base/macros.h> diff --git a/libs/renderengine/skia/compat/GaneshGpuContext.cpp b/libs/renderengine/skia/compat/GaneshGpuContext.cpp index b2eae009ed..931f8433da 100644 --- a/libs/renderengine/skia/compat/GaneshGpuContext.cpp +++ b/libs/renderengine/skia/compat/GaneshGpuContext.cpp @@ -19,13 +19,13 @@ #include <include/core/SkImageInfo.h> #include <include/core/SkSurface.h> #include <include/core/SkTraceMemoryDump.h> -#include <include/gpu/GrDirectContext.h> -#include <include/gpu/GrTypes.h> +#include <include/gpu/ganesh/GrDirectContext.h> +#include <include/gpu/ganesh/GrTypes.h> #include <include/gpu/ganesh/SkSurfaceGanesh.h> #include <include/gpu/ganesh/gl/GrGLDirectContext.h> +#include <include/gpu/ganesh/gl/GrGLInterface.h> #include <include/gpu/ganesh/vk/GrVkDirectContext.h> -#include <include/gpu/gl/GrGLInterface.h> -#include <include/gpu/vk/GrVkBackendContext.h> +#include <include/gpu/vk/VulkanBackendContext.h> #include "../AutoBackendTexture.h" #include "GaneshBackendTexture.h" @@ -56,10 +56,10 @@ std::unique_ptr<SkiaGpuContext> SkiaGpuContext::MakeGL_Ganesh( } std::unique_ptr<SkiaGpuContext> SkiaGpuContext::MakeVulkan_Ganesh( - const GrVkBackendContext& grVkBackendContext, + const skgpu::VulkanBackendContext& vkBackendContext, GrContextOptions::PersistentCache& skSLCacheMonitor) { return std::make_unique<GaneshGpuContext>( - GrDirectContexts::MakeVulkan(grVkBackendContext, ganeshOptions(skSLCacheMonitor))); + GrDirectContexts::MakeVulkan(vkBackendContext, ganeshOptions(skSLCacheMonitor))); } GaneshGpuContext::GaneshGpuContext(sk_sp<GrDirectContext> grContext) : mGrContext(grContext) { diff --git a/libs/renderengine/skia/compat/GraphiteBackendTexture.cpp b/libs/renderengine/skia/compat/GraphiteBackendTexture.cpp index 3dd9ed242e..a6e93ba7e0 100644 --- a/libs/renderengine/skia/compat/GraphiteBackendTexture.cpp +++ b/libs/renderengine/skia/compat/GraphiteBackendTexture.cpp @@ -28,16 +28,16 @@ #include "skia/ColorSpaces.h" #include <android/hardware_buffer.h> +#include <common/trace.h> #include <inttypes.h> #include <log/log_main.h> -#include <utils/Trace.h> namespace android::renderengine::skia { GraphiteBackendTexture::GraphiteBackendTexture(std::shared_ptr<skgpu::graphite::Recorder> recorder, AHardwareBuffer* buffer, bool isOutputBuffer) : SkiaBackendTexture(buffer, isOutputBuffer), mRecorder(std::move(recorder)) { - ATRACE_CALL(); + SFTRACE_CALL(); AHardwareBuffer_Desc desc; AHardwareBuffer_describe(buffer, &desc); const bool createProtectedImage = 0 != (desc.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT); diff --git a/libs/renderengine/skia/compat/SkiaBackendTexture.h b/libs/renderengine/skia/compat/SkiaBackendTexture.h index 09877a5ede..fa12624ff7 100644 --- a/libs/renderengine/skia/compat/SkiaBackendTexture.h +++ b/libs/renderengine/skia/compat/SkiaBackendTexture.h @@ -18,7 +18,7 @@ #include <include/android/GrAHardwareBufferUtils.h> #include <include/core/SkColorSpace.h> -#include <include/gpu/GrDirectContext.h> +#include <include/gpu/ganesh/GrDirectContext.h> #include <android/hardware_buffer.h> #include <ui/GraphicTypes.h> diff --git a/libs/renderengine/skia/compat/SkiaGpuContext.h b/libs/renderengine/skia/compat/SkiaGpuContext.h index 282dfe7abe..0bd8283987 100644 --- a/libs/renderengine/skia/compat/SkiaGpuContext.h +++ b/libs/renderengine/skia/compat/SkiaGpuContext.h @@ -20,11 +20,10 @@ #define LOG_TAG "RenderEngine" #include <include/core/SkSurface.h> -#include <include/gpu/GrDirectContext.h> -#include <include/gpu/gl/GrGLInterface.h> +#include <include/gpu/ganesh/GrDirectContext.h> +#include <include/gpu/ganesh/gl/GrGLInterface.h> #include <include/gpu/graphite/Context.h> -#include <include/gpu/vk/GrVkBackendContext.h> -#include "include/gpu/vk/VulkanBackendContext.h" +#include <include/gpu/vk/VulkanBackendContext.h> #include "SkiaBackendTexture.h" @@ -52,10 +51,10 @@ public: GrContextOptions::PersistentCache& skSLCacheMonitor); /** - * grVkBackendContext must remain valid until after SkiaGpuContext is destroyed. + * vkBackendContext must remain valid until after SkiaGpuContext is destroyed. */ static std::unique_ptr<SkiaGpuContext> MakeVulkan_Ganesh( - const GrVkBackendContext& grVkBackendContext, + const skgpu::VulkanBackendContext& vkBackendContext, GrContextOptions::PersistentCache& skSLCacheMonitor); // TODO: b/293371537 - Need shader / pipeline monitoring support in Graphite. diff --git a/libs/renderengine/skia/debug/CommonPool.cpp b/libs/renderengine/skia/debug/CommonPool.cpp index bf15300227..9d7c69b019 100644 --- a/libs/renderengine/skia/debug/CommonPool.cpp +++ b/libs/renderengine/skia/debug/CommonPool.cpp @@ -20,8 +20,8 @@ #define LOG_TAG "RenderEngine" #define ATRACE_TAG ATRACE_TAG_GRAPHICS +#include <common/trace.h> #include <sys/resource.h> -#include <utils/Trace.h> #include <system/thread_defs.h> #include <array> @@ -31,7 +31,7 @@ namespace renderengine { namespace skia { CommonPool::CommonPool() { - ATRACE_CALL(); + SFTRACE_CALL(); CommonPool* pool = this; // Create 2 workers diff --git a/libs/renderengine/skia/debug/SkiaCapture.cpp b/libs/renderengine/skia/debug/SkiaCapture.cpp index e778884629..e6a0e22dcf 100644 --- a/libs/renderengine/skia/debug/SkiaCapture.cpp +++ b/libs/renderengine/skia/debug/SkiaCapture.cpp @@ -22,9 +22,9 @@ #include <android-base/properties.h> #include <android-base/stringprintf.h> +#include <common/trace.h> #include <log/log.h> #include <renderengine/RenderEngine.h> -#include <utils/Trace.h> #include "CommonPool.h" #include "SkCanvas.h" @@ -48,7 +48,7 @@ SkiaCapture::~SkiaCapture() { } SkCanvas* SkiaCapture::tryCapture(SkSurface* surface) NO_THREAD_SAFETY_ANALYSIS { - ATRACE_CALL(); + SFTRACE_CALL(); // If we are not running yet, set up. if (CC_LIKELY(!mCaptureRunning)) { @@ -86,7 +86,7 @@ SkCanvas* SkiaCapture::tryCapture(SkSurface* surface) NO_THREAD_SAFETY_ANALYSIS } void SkiaCapture::endCapture() NO_THREAD_SAFETY_ANALYSIS { - ATRACE_CALL(); + SFTRACE_CALL(); // Don't end anything if we are not running. if (CC_LIKELY(!mCaptureRunning)) { return; @@ -102,7 +102,7 @@ void SkiaCapture::endCapture() NO_THREAD_SAFETY_ANALYSIS { } SkCanvas* SkiaCapture::tryOffscreenCapture(SkSurface* surface, OffscreenState* state) { - ATRACE_CALL(); + SFTRACE_CALL(); // Don't start anything if we are not running. if (CC_LIKELY(!mCaptureRunning)) { return surface->getCanvas(); @@ -122,7 +122,7 @@ SkCanvas* SkiaCapture::tryOffscreenCapture(SkSurface* surface, OffscreenState* s } uint64_t SkiaCapture::endOffscreenCapture(OffscreenState* state) { - ATRACE_CALL(); + SFTRACE_CALL(); // Don't end anything if we are not running. if (CC_LIKELY(!mCaptureRunning)) { return 0; @@ -151,7 +151,7 @@ uint64_t SkiaCapture::endOffscreenCapture(OffscreenState* state) { } void SkiaCapture::writeToFile() { - ATRACE_CALL(); + SFTRACE_CALL(); // Pass mMultiPic and mOpenMultiPicStream to a background thread, which will // handle the heavyweight serialization work and destroy them. // mOpenMultiPicStream is released to a bare pointer because keeping it in @@ -169,7 +169,7 @@ void SkiaCapture::writeToFile() { } bool SkiaCapture::setupMultiFrameCapture() { - ATRACE_CALL(); + SFTRACE_CALL(); ALOGD("Set up multi-frame capture, ms = %llu", mTimerInterval.count()); base::SetProperty(PROPERTY_DEBUG_RENDERENGINE_CAPTURE_FILENAME, ""); diff --git a/libs/renderengine/skia/filters/BlurFilter.cpp b/libs/renderengine/skia/filters/BlurFilter.cpp index 1e0c4cf9d0..cd1bd71807 100644 --- a/libs/renderengine/skia/filters/BlurFilter.cpp +++ b/libs/renderengine/skia/filters/BlurFilter.cpp @@ -25,8 +25,8 @@ #include <SkString.h> #include <SkSurface.h> #include <SkTileMode.h> +#include <common/trace.h> #include <log/log.h> -#include <utils/Trace.h> namespace android { namespace renderengine { @@ -79,7 +79,7 @@ void BlurFilter::drawBlurRegion(SkCanvas* canvas, const SkRRect& effectRegion, const uint32_t blurRadius, const float blurAlpha, const SkRect& blurRect, sk_sp<SkImage> blurredImage, sk_sp<SkImage> input) { - ATRACE_CALL(); + SFTRACE_CALL(); SkPaint paint; paint.setAlphaf(blurAlpha); diff --git a/libs/renderengine/skia/filters/EdgeExtensionShaderFactory.cpp b/libs/renderengine/skia/filters/EdgeExtensionShaderFactory.cpp new file mode 100644 index 0000000000..4164c4b4c9 --- /dev/null +++ b/libs/renderengine/skia/filters/EdgeExtensionShaderFactory.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "EdgeExtensionShaderFactory.h" +#include <SkPoint.h> +#include <SkRuntimeEffect.h> +#include <SkStream.h> +#include <SkString.h> +#include <com_android_graphics_libgui_flags.h> +#include "log/log_main.h" + +namespace android::renderengine::skia { + +static const SkString edgeShader = SkString(R"( + uniform shader uContentTexture; + uniform vec2 uImgSize; + + // TODO(b/214232209) oobTolerance is temporary and will be removed when the scrollbar will be + // hidden during the animation + const float oobTolerance = 15; + const int blurRadius = 3; + const float blurArea = float((2 * blurRadius + 1) * (2 * blurRadius + 1)); + + vec4 boxBlur(vec2 p) { + vec4 sumColors = vec4(0); + + for (int i = -blurRadius; i <= blurRadius; i++) { + for (int j = -blurRadius; j <= blurRadius; j++) { + sumColors += uContentTexture.eval(p + vec2(i, j)); + } + } + return sumColors / blurArea; + } + + vec4 main(vec2 coord) { + vec2 nearestTexturePoint = clamp(coord, vec2(0, 0), uImgSize); + if (coord == nearestTexturePoint) { + return uContentTexture.eval(coord); + } else { + vec2 samplePoint = nearestTexturePoint + oobTolerance * normalize( + nearestTexturePoint - coord); + return boxBlur(samplePoint); + } + } +)"); + +EdgeExtensionShaderFactory::EdgeExtensionShaderFactory() { + if (!com::android::graphics::libgui::flags::edge_extension_shader()) { + return; + } + mResult = std::make_unique<SkRuntimeEffect::Result>(SkRuntimeEffect::MakeForShader(edgeShader)); + LOG_ALWAYS_FATAL_IF(!mResult->errorText.isEmpty(), + "EdgeExtensionShaderFactory compilation " + "failed with an unexpected error: %s", + mResult->errorText.c_str()); +} + +sk_sp<SkShader> EdgeExtensionShaderFactory::createSkShader(const sk_sp<SkShader>& inputShader, + const LayerSettings& layer, + const SkRect& imageBounds) const { + LOG_ALWAYS_FATAL_IF(mResult == nullptr, + "EdgeExtensionShaderFactory did not initialize mResult. " + "This means that we unexpectedly applied the edge extension shader"); + + SkRuntimeShaderBuilder builder = SkRuntimeShaderBuilder(mResult->effect); + + builder.child("uContentTexture") = inputShader; + if (imageBounds.isEmpty()) { + builder.uniform("uImgSize") = SkPoint{layer.geometry.boundaries.getWidth(), + layer.geometry.boundaries.getHeight()}; + } else { + builder.uniform("uImgSize") = SkPoint{imageBounds.width(), imageBounds.height()}; + } + return builder.makeShader(); +} +} // namespace android::renderengine::skia
\ No newline at end of file diff --git a/libs/renderengine/skia/filters/EdgeExtensionShaderFactory.h b/libs/renderengine/skia/filters/EdgeExtensionShaderFactory.h new file mode 100644 index 0000000000..17c6b9139f --- /dev/null +++ b/libs/renderengine/skia/filters/EdgeExtensionShaderFactory.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <SkImage.h> +#include <SkRect.h> +#include <SkRuntimeEffect.h> +#include <SkShader.h> +#include <renderengine/LayerSettings.h> +#include <ui/EdgeExtensionEffect.h> + +namespace android::renderengine::skia { + +/** + * This shader is designed to prolong the texture of a surface whose bounds have been extended over + * the size of the texture. This shader is similar to the default clamp, but adds a blur effect and + * samples from close to the edge (compared to on the edge) to avoid weird artifacts when elements + * (in particular, scrollbars) touch the edge. + */ +class EdgeExtensionShaderFactory { +public: + EdgeExtensionShaderFactory(); + + sk_sp<SkShader> createSkShader(const sk_sp<SkShader>& inputShader, const LayerSettings& layer, + const SkRect& imageBounds) const; + +private: + std::unique_ptr<const SkRuntimeEffect::Result> mResult; +}; +} // namespace android::renderengine::skia diff --git a/libs/renderengine/skia/filters/GainmapFactory.cpp b/libs/renderengine/skia/filters/GainmapFactory.cpp new file mode 100644 index 0000000000..e4d4fe96f8 --- /dev/null +++ b/libs/renderengine/skia/filters/GainmapFactory.cpp @@ -0,0 +1,72 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "GainmapFactory.h" + +#include <log/log.h> + +namespace android { +namespace renderengine { +namespace skia { +namespace { + +sk_sp<SkRuntimeEffect> makeEffect(const SkString& sksl) { + auto [effect, error] = SkRuntimeEffect::MakeForShader(sksl); + LOG_ALWAYS_FATAL_IF(!effect, "RuntimeShader error: %s", error.c_str()); + return effect; +} + +// Please refer to https://developer.android.com/media/platform/hdr-image-format#gain_map-generation +static const SkString kGainmapShader = SkString(R"( + uniform shader sdr; + uniform shader hdr; + uniform float mapMaxLog2; + + const float mapMinLog2 = 0.0; + const float mapGamma = 1.0; + const float offsetSdr = 0.015625; + const float offsetHdr = 0.015625; + + float luminance(vec3 linearColor) { + return 0.2126 * linearColor.r + 0.7152 * linearColor.g + 0.0722 * linearColor.b; + } + + vec4 main(vec2 xy) { + float sdrY = luminance(toLinearSrgb(sdr.eval(xy).rgb)); + float hdrY = luminance(toLinearSrgb(hdr.eval(xy).rgb)); + float pixelGain = (hdrY + offsetHdr) / (sdrY + offsetSdr); + float logRecovery = (log2(pixelGain) - mapMinLog2) / (mapMaxLog2 - mapMinLog2); + return vec4(pow(clamp(logRecovery, 0.0, 1.0), mapGamma)); + } +)"); +} // namespace + +const float INTERPOLATION_STRENGTH_VALUE = 0.7f; + +GainmapFactory::GainmapFactory() : mEffect(makeEffect(kGainmapShader)) {} + +sk_sp<SkShader> GainmapFactory::createSkShader(const sk_sp<SkShader>& sdr, + const sk_sp<SkShader>& hdr, float hdrSdrRatio) { + SkRuntimeShaderBuilder shaderBuilder(mEffect); + shaderBuilder.child("sdr") = sdr; + shaderBuilder.child("hdr") = hdr; + shaderBuilder.uniform("mapMaxLog2") = std::log2(hdrSdrRatio); + return shaderBuilder.makeShader(); +} + +} // namespace skia +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/skia/filters/GainmapFactory.h b/libs/renderengine/skia/filters/GainmapFactory.h new file mode 100644 index 0000000000..7aea5e2195 --- /dev/null +++ b/libs/renderengine/skia/filters/GainmapFactory.h @@ -0,0 +1,44 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include <SkRuntimeEffect.h> +#include <SkShader.h> + +namespace android { +namespace renderengine { +namespace skia { + +/** + * Generates a shader for computing a gainmap, given an SDR base image and its idealized HDR + * rendition. The shader follows the procedure in the UltraHDR spec: + * https://developer.android.com/media/platform/hdr-image-format#gain_map-generation, but makes some + * simplifying assumptions about metadata typical for RenderEngine's usage. + */ +class GainmapFactory { +public: + GainmapFactory(); + // Generates the gainmap shader. The hdrSdrRatio is the max_content_boost in the UltraHDR + // specification. + sk_sp<SkShader> createSkShader(const sk_sp<SkShader>& sdr, const sk_sp<SkShader>& hdr, + float hdrSdrRatio); + +private: + sk_sp<SkRuntimeEffect> mEffect; +}; +} // namespace skia +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/skia/filters/GaussianBlurFilter.cpp b/libs/renderengine/skia/filters/GaussianBlurFilter.cpp index c9499cbc24..8c52c571a9 100644 --- a/libs/renderengine/skia/filters/GaussianBlurFilter.cpp +++ b/libs/renderengine/skia/filters/GaussianBlurFilter.cpp @@ -19,18 +19,18 @@ #include "GaussianBlurFilter.h" #include <SkBlendMode.h> #include <SkCanvas.h> +#include <SkImageFilters.h> #include <SkPaint.h> #include <SkRRect.h> #include <SkRuntimeEffect.h> -#include <SkImageFilters.h> #include <SkSize.h> #include <SkString.h> #include <SkSurface.h> #include <SkTileMode.h> +#include <common/trace.h> #include <include/gpu/ganesh/SkSurfaceGanesh.h> -#include "include/gpu/GpuTypes.h" // from Skia #include <log/log.h> -#include <utils/Trace.h> +#include "include/gpu/GpuTypes.h" // from Skia namespace android { namespace renderengine { diff --git a/libs/renderengine/skia/filters/KawaseBlurDualFilter.cpp b/libs/renderengine/skia/filters/KawaseBlurDualFilter.cpp new file mode 100644 index 0000000000..db0b133a26 --- /dev/null +++ b/libs/renderengine/skia/filters/KawaseBlurDualFilter.cpp @@ -0,0 +1,173 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include "KawaseBlurDualFilter.h" +#include <SkAlphaType.h> +#include <SkBlendMode.h> +#include <SkCanvas.h> +#include <SkData.h> +#include <SkPaint.h> +#include <SkRRect.h> +#include <SkRuntimeEffect.h> +#include <SkShader.h> +#include <SkSize.h> +#include <SkString.h> +#include <SkSurface.h> +#include <SkTileMode.h> +#include <include/gpu/GpuTypes.h> +#include <include/gpu/ganesh/SkSurfaceGanesh.h> +#include <log/log.h> +#include <utils/Trace.h> + +namespace android { +namespace renderengine { +namespace skia { + +KawaseBlurDualFilter::KawaseBlurDualFilter() : BlurFilter() { + // A shader to sample each vertex of a unit regular heptagon + // plus the original fragment coordinate. + SkString blurString(R"( + uniform shader child; + uniform float in_blurOffset; + uniform float in_crossFade; + + const float2 STEP_0 = float2( 1.0, 0.0); + const float2 STEP_1 = float2( 0.623489802, 0.781831482); + const float2 STEP_2 = float2(-0.222520934, 0.974927912); + const float2 STEP_3 = float2(-0.900968868, 0.433883739); + const float2 STEP_4 = float2( 0.900968868, -0.433883739); + const float2 STEP_5 = float2(-0.222520934, -0.974927912); + const float2 STEP_6 = float2(-0.623489802, -0.781831482); + + half4 main(float2 xy) { + half3 c = child.eval(xy).rgb; + + c += child.eval(xy + STEP_0 * in_blurOffset).rgb; + c += child.eval(xy + STEP_1 * in_blurOffset).rgb; + c += child.eval(xy + STEP_2 * in_blurOffset).rgb; + c += child.eval(xy + STEP_3 * in_blurOffset).rgb; + c += child.eval(xy + STEP_4 * in_blurOffset).rgb; + c += child.eval(xy + STEP_5 * in_blurOffset).rgb; + c += child.eval(xy + STEP_6 * in_blurOffset).rgb; + + return half4(c * 0.125 * in_crossFade, in_crossFade); + } + )"); + + auto [blurEffect, error] = SkRuntimeEffect::MakeForShader(blurString); + LOG_ALWAYS_FATAL_IF(!blurEffect, "RuntimeShader error: %s", error.c_str()); + mBlurEffect = std::move(blurEffect); +} + +static sk_sp<SkSurface> makeSurface(SkiaGpuContext* context, const SkRect& origRect, int scale) { + SkImageInfo scaledInfo = + SkImageInfo::MakeN32Premul(ceil(static_cast<float>(origRect.width()) / scale), + ceil(static_cast<float>(origRect.height()) / scale)); + return context->createRenderTarget(scaledInfo); +} + +void KawaseBlurDualFilter::blurInto(const sk_sp<SkSurface>& drawSurface, + const sk_sp<SkImage>& readImage, const float radius, + const float alpha) const { + const float scale = static_cast<float>(drawSurface->width()) / readImage->width(); + SkMatrix blurMatrix = SkMatrix::Scale(scale, scale); + blurInto(drawSurface, + readImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, + SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone), + blurMatrix), + readImage->width() / static_cast<float>(drawSurface->width()), radius, alpha); +} + +void KawaseBlurDualFilter::blurInto(const sk_sp<SkSurface>& drawSurface, sk_sp<SkShader> input, + const float inverseScale, const float radius, + const float alpha) const { + SkRuntimeShaderBuilder blurBuilder(mBlurEffect); + blurBuilder.child("child") = std::move(input); + blurBuilder.uniform("in_inverseScale") = inverseScale; + blurBuilder.uniform("in_blurOffset") = radius; + blurBuilder.uniform("in_crossFade") = alpha; + SkPaint paint; + paint.setShader(blurBuilder.makeShader(nullptr)); + paint.setBlendMode(alpha == 1.0f ? SkBlendMode::kSrc : SkBlendMode::kSrcOver); + drawSurface->getCanvas()->drawPaint(paint); +} + +sk_sp<SkImage> KawaseBlurDualFilter::generate(SkiaGpuContext* context, const uint32_t blurRadius, + const sk_sp<SkImage> input, + const SkRect& blurRect) const { + // Apply a conversion factor of (1 / sqrt(3)) to match Skia's built-in blur as used by + // RenderEffect. See the comment in SkBlurMask.cpp for reasoning behind this. + const float radius = blurRadius * 0.57735f; + + // Use a variable number of blur passes depending on the radius. The non-integer part of this + // calculation is used to mix the final pass into the second-last with an alpha blend. + constexpr int kMaxSurfaces = 4; + const float filterDepth = + std::min(kMaxSurfaces - 1.0f, 1.0f + std::max(0.0f, log2f(radius * kInputScale))); + const int filterPasses = std::min(kMaxSurfaces - 1, static_cast<int>(ceil(filterDepth))); + + // Render into surfaces downscaled by 1x, 1x, 2x, and 4x from the initial downscale. + sk_sp<SkSurface> surfaces[kMaxSurfaces] = + {filterPasses >= 0 ? makeSurface(context, blurRect, 1 * kInverseInputScale) : nullptr, + filterPasses >= 1 ? makeSurface(context, blurRect, 1 * kInverseInputScale) : nullptr, + filterPasses >= 2 ? makeSurface(context, blurRect, 2 * kInverseInputScale) : nullptr, + filterPasses >= 3 ? makeSurface(context, blurRect, 4 * kInverseInputScale) : nullptr}; + + // These weights for scaling offsets per-pass are handpicked to look good at 1 <= radius <= 600. + static const float kWeights[7] = {1.0f, 2.0f, 3.5f, 1.0f, 2.0f, 2.0f, 2.0f}; + + // Kawase is an approximation of Gaussian, but behaves differently because it is made up of many + // simpler blurs. A transformation is required to approximate the same effect as Gaussian. + float sumSquaredR = powf(kWeights[0] * powf(2.0f, 1), 2.0f); + for (int i = 0; i < filterPasses; i++) { + const float alpha = std::min(1.0f, filterDepth - i); + sumSquaredR += powf(powf(2.0f, i + 1) * alpha * kWeights[1 + i], 2.0f); + sumSquaredR += powf(powf(2.0f, i + 1) * alpha * kWeights[6 - i], 2.0f); + } + // Solve for R = sqrt(sum(r_i^2)). Divide R by hypot(1,1) to find some (x,y) offsets. + const float step = M_SQRT1_2 * + sqrtf(max(0.0f, (powf(radius, 2.0f) - powf(kInverseInputScale, 2.0f)) / sumSquaredR)); + + // Start by downscaling and doing the first blur pass. + { + // For sampling Skia's API expects the inverse of what logically seems appropriate. In this + // case one may expect Translate(blurRect.fLeft, blurRect.fTop) * Scale(kInverseInputScale) + // but instead we must do the inverse. + SkMatrix blurMatrix = SkMatrix::Translate(-blurRect.fLeft, -blurRect.fTop); + blurMatrix.postScale(kInputScale, kInputScale); + const auto sourceShader = + input->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, + SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone), + blurMatrix); + blurInto(surfaces[0], std::move(sourceShader), kInputScale, kWeights[0] * step, 1.0f); + } + // Next the remaining downscale blur passes. + for (int i = 0; i < filterPasses; i++) { + blurInto(surfaces[i + 1], surfaces[i]->makeImageSnapshot(), kWeights[1 + i] * step, 1.0f); + } + // Finally blur+upscale back to our original size. + for (int i = filterPasses - 1; i >= 0; i--) { + blurInto(surfaces[i], surfaces[i + 1]->makeImageSnapshot(), kWeights[6 - i] * step, + std::min(1.0f, filterDepth - i)); + } + return surfaces[0]->makeImageSnapshot(); +} + +} // namespace skia +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/skia/filters/KawaseBlurDualFilter.h b/libs/renderengine/skia/filters/KawaseBlurDualFilter.h new file mode 100644 index 0000000000..6f4adbf34c --- /dev/null +++ b/libs/renderengine/skia/filters/KawaseBlurDualFilter.h @@ -0,0 +1,55 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <SkCanvas.h> +#include <SkImage.h> +#include <SkRuntimeEffect.h> +#include <SkSurface.h> +#include "BlurFilter.h" + +namespace android { +namespace renderengine { +namespace skia { + +/** + * This is an implementation of a Kawase blur with dual-filtering passes, as described in here: + * https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/00-00-00-20-66/siggraph2015_2D00_mmg_2D00_marius_2D00_slides.pdf + * https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/00-00-00-20-66/siggraph2015_2D00_mmg_2D00_marius_2D00_notes.pdf + */ +class KawaseBlurDualFilter : public BlurFilter { +public: + explicit KawaseBlurDualFilter(); + virtual ~KawaseBlurDualFilter() {} + + // Execute blur, saving it to a texture + sk_sp<SkImage> generate(SkiaGpuContext* context, const uint32_t radius, + const sk_sp<SkImage> blurInput, const SkRect& blurRect) const override; + +private: + sk_sp<SkRuntimeEffect> mBlurEffect; + + void blurInto(const sk_sp<SkSurface>& drawSurface, const sk_sp<SkImage>& readImage, + const float radius, const float alpha) const; + + void blurInto(const sk_sp<SkSurface>& drawSurface, const sk_sp<SkShader> input, + const float inverseScale, const float radius, const float alpha) const; +}; + +} // namespace skia +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/skia/filters/KawaseBlurFilter.cpp b/libs/renderengine/skia/filters/KawaseBlurFilter.cpp index 7a070d7024..defaf6e476 100644 --- a/libs/renderengine/skia/filters/KawaseBlurFilter.cpp +++ b/libs/renderengine/skia/filters/KawaseBlurFilter.cpp @@ -29,10 +29,10 @@ #include <SkString.h> #include <SkSurface.h> #include <SkTileMode.h> +#include <common/trace.h> #include <include/gpu/GpuTypes.h> #include <include/gpu/ganesh/SkSurfaceGanesh.h> #include <log/log.h> -#include <utils/Trace.h> namespace android { namespace renderengine { diff --git a/libs/renderengine/skia/filters/LinearEffect.cpp b/libs/renderengine/skia/filters/LinearEffect.cpp index f7dcd3a6e1..3bc3564f4c 100644 --- a/libs/renderengine/skia/filters/LinearEffect.cpp +++ b/libs/renderengine/skia/filters/LinearEffect.cpp @@ -19,9 +19,9 @@ #define ATRACE_TAG ATRACE_TAG_GRAPHICS #include <SkString.h> +#include <common/trace.h> #include <log/log.h> #include <shaders/shaders.h> -#include <utils/Trace.h> #include <math/mat4.h> @@ -30,7 +30,7 @@ namespace renderengine { namespace skia { sk_sp<SkRuntimeEffect> buildRuntimeEffect(const shaders::LinearEffect& linearEffect) { - ATRACE_CALL(); + SFTRACE_CALL(); SkString shaderString = SkString(shaders::buildLinearEffectSkSL(linearEffect)); auto [shader, error] = SkRuntimeEffect::MakeForShader(shaderString); @@ -45,7 +45,7 @@ sk_sp<SkShader> createLinearEffectShader( sk_sp<SkRuntimeEffect> runtimeEffect, const mat4& colorTransform, float maxDisplayLuminance, float currentDisplayLuminanceNits, float maxLuminance, AHardwareBuffer* buffer, aidl::android::hardware::graphics::composer3::RenderIntent renderIntent) { - ATRACE_CALL(); + SFTRACE_CALL(); SkRuntimeShaderBuilder effectBuilder(runtimeEffect); effectBuilder.child("child") = shader; diff --git a/libs/renderengine/skia/filters/MouriMap.cpp b/libs/renderengine/skia/filters/MouriMap.cpp index 7d8b8a5116..b099bcf3d7 100644 --- a/libs/renderengine/skia/filters/MouriMap.cpp +++ b/libs/renderengine/skia/filters/MouriMap.cpp @@ -35,7 +35,7 @@ const SkString kCrosstalkAndChunk16x16(R"( float maximum = 0.0; for (int y = 0; y < 16; y++) { for (int x = 0; x < 16; x++) { - float3 linear = toLinearSrgb(bitmap.eval(xy * 16 + vec2(x, y)).rgb) * hdrSdrRatio; + float3 linear = toLinearSrgb(bitmap.eval((xy - 0.5) * 16 + 0.5 + vec2(x, y)).rgb) * hdrSdrRatio; float maxRGB = max(linear.r, max(linear.g, linear.b)); maximum = max(maximum, log2(max(maxRGB, 1.0))); } @@ -49,7 +49,7 @@ const SkString kChunk8x8(R"( float maximum = 0.0; for (int y = 0; y < 8; y++) { for (int x = 0; x < 8; x++) { - maximum = max(maximum, bitmap.eval(xy * 8 + vec2(x, y)).r); + maximum = max(maximum, bitmap.eval((xy - 0.5) * 8 + 0.5 + vec2(x, y)).r); } } return float4(float3(maximum), 1.0); @@ -67,7 +67,7 @@ const SkString kBlur(R"( float result = 0.0; for (int y = -2; y <= 2; y++) { for (int x = -2; x <= 2; x++) { - result += C[y + 2] * C[x + 2] * bitmap.eval(xy + vec2(x, y)).r; + result += C[y + 2] * C[x + 2] * bitmap.eval(xy + vec2(x, y)).r; } } return float4(float3(exp2(result)), 1.0); @@ -78,19 +78,21 @@ const SkString kTonemap(R"( uniform shader lux; uniform float scaleFactor; uniform float hdrSdrRatio; + uniform float targetHdrSdrRatio; vec4 main(vec2 xy) { float localMax = lux.eval(xy * scaleFactor).r; float4 rgba = image.eval(xy); float3 linear = toLinearSrgb(rgba.rgb) * hdrSdrRatio; - if (localMax <= 1.0) { - return float4(fromLinearSrgb(linear), 1.0); + if (localMax <= targetHdrSdrRatio) { + return float4(fromLinearSrgb(linear), rgba.a); } float maxRGB = max(linear.r, max(linear.g, linear.b)); localMax = max(localMax, maxRGB); - float gain = (1 + maxRGB / (localMax * localMax)) / (1 + maxRGB); - return float4(fromLinearSrgb(linear * gain), 1.0); + float gain = (1 + maxRGB * (targetHdrSdrRatio / (localMax * localMax))) + / (1 + maxRGB / targetHdrSdrRatio); + return float4(fromLinearSrgb(linear * gain), rgba.a); } )"); @@ -114,10 +116,10 @@ MouriMap::MouriMap() mTonemap(makeEffect(kTonemap)) {} sk_sp<SkShader> MouriMap::mouriMap(SkiaGpuContext* context, sk_sp<SkShader> input, - float hdrSdrRatio) { + float hdrSdrRatio, float targetHdrSdrRatio) { auto downchunked = downchunk(context, input, hdrSdrRatio); auto localLux = blur(context, downchunked.get()); - return tonemap(input, localLux.get(), hdrSdrRatio); + return tonemap(input, localLux.get(), hdrSdrRatio, targetHdrSdrRatio); } sk_sp<SkImage> MouriMap::downchunk(SkiaGpuContext* context, sk_sp<SkShader> input, @@ -166,8 +168,8 @@ sk_sp<SkImage> MouriMap::blur(SkiaGpuContext* context, SkImage* input) const { LOG_ALWAYS_FATAL_IF(!blurSurface, "%s: Failed to create surface!", __func__); return makeImage(blurSurface.get(), blurBuilder); } -sk_sp<SkShader> MouriMap::tonemap(sk_sp<SkShader> input, SkImage* localLux, - float hdrSdrRatio) const { +sk_sp<SkShader> MouriMap::tonemap(sk_sp<SkShader> input, SkImage* localLux, float hdrSdrRatio, + float targetHdrSdrRatio) const { static constexpr float kScaleFactor = 1.0f / 128.0f; SkRuntimeShaderBuilder tonemapBuilder(mTonemap); tonemapBuilder.child("image") = input; @@ -176,8 +178,9 @@ sk_sp<SkShader> MouriMap::tonemap(sk_sp<SkShader> input, SkImage* localLux, SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone)); tonemapBuilder.uniform("scaleFactor") = kScaleFactor; tonemapBuilder.uniform("hdrSdrRatio") = hdrSdrRatio; + tonemapBuilder.uniform("targetHdrSdrRatio") = targetHdrSdrRatio; return tonemapBuilder.makeShader(); } } // namespace skia } // namespace renderengine -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/libs/renderengine/skia/filters/MouriMap.h b/libs/renderengine/skia/filters/MouriMap.h index 3c0df8abf0..9ba2b6ff9d 100644 --- a/libs/renderengine/skia/filters/MouriMap.h +++ b/libs/renderengine/skia/filters/MouriMap.h @@ -64,13 +64,16 @@ public: // Apply the MouriMap tonemmaping operator to the input. // The HDR/SDR ratio describes the luminace range of the input. 1.0 means SDR. Anything larger // then 1.0 means that there is headroom above the SDR region. - sk_sp<SkShader> mouriMap(SkiaGpuContext* context, sk_sp<SkShader> input, float hdrSdrRatio); + // Similarly, the target HDR/SDR ratio describes the luminance range of the output. + sk_sp<SkShader> mouriMap(SkiaGpuContext* context, sk_sp<SkShader> input, float inputHdrSdrRatio, + float targetHdrSdrRatio); private: sk_sp<SkImage> downchunk(SkiaGpuContext* context, sk_sp<SkShader> input, float hdrSdrRatio) const; sk_sp<SkImage> blur(SkiaGpuContext* context, SkImage* input) const; - sk_sp<SkShader> tonemap(sk_sp<SkShader> input, SkImage* localLux, float hdrSdrRatio) const; + sk_sp<SkShader> tonemap(sk_sp<SkShader> input, SkImage* localLux, float hdrSdrRatio, + float targetHdrSdrRatio) const; const sk_sp<SkRuntimeEffect> mCrosstalkAndChunk16x16; const sk_sp<SkRuntimeEffect> mChunk8x8; const sk_sp<SkRuntimeEffect> mBlur; @@ -78,4 +81,4 @@ private: }; } // namespace skia } // namespace renderengine -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/libs/renderengine/tests/Android.bp b/libs/renderengine/tests/Android.bp index 0783714eb9..7fbbf49586 100644 --- a/libs/renderengine/tests/Android.bp +++ b/libs/renderengine/tests/Android.bp @@ -66,5 +66,6 @@ cc_test { "libutils", "server_configurable_flags", "libaconfig_storage_read_api_cc", + "libtracing_perfetto", ], } diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp index a8a98236e2..b5cc65f27d 100644 --- a/libs/renderengine/tests/RenderEngineTest.cpp +++ b/libs/renderengine/tests/RenderEngineTest.cpp @@ -34,9 +34,12 @@ #include <ui/ColorSpace.h> #include <ui/PixelFormat.h> +#include <algorithm> #include <chrono> #include <condition_variable> +#include <filesystem> #include <fstream> +#include <system_error> #include "../skia/SkiaGLRenderEngine.h" #include "../skia/SkiaVkRenderEngine.h" @@ -259,22 +262,51 @@ public: ~RenderEngineTest() { if (WRITE_BUFFER_TO_FILE_ON_FAILURE && ::testing::Test::HasFailure()) { - writeBufferToFile("/data/texture_out_"); + writeBufferToFile("/data/local/tmp/RenderEngineTest/"); } const ::testing::TestInfo* const test_info = ::testing::UnitTest::GetInstance()->current_test_info(); ALOGI("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name()); } - void writeBufferToFile(const char* basename) { - std::string filename(basename); - filename.append(::testing::UnitTest::GetInstance()->current_test_info()->name()); - filename.append(".ppm"); - std::ofstream file(filename.c_str(), std::ios::binary); + // If called during e.g. + // `PerRenderEngineType/RenderEngineTest#drawLayers_fillBufferCheckersRotate90_colorSource/0` + // with a directory of `/data/local/tmp/RenderEngineTest`, then mBuffer will be dumped to + // `/data/local/tmp/RenderEngineTest/drawLayers_fillBufferCheckersRotate90_colorSource-0.ppm` + // + // Note: if `directory` does not exist, then its full path will be recursively created with 777 + // permissions. If `directory` already exists but does not grant the executing user write + // permissions, then saving the buffer will fail. + // + // Since this is test-only code, no security considerations are made. + void writeBufferToFile(const filesystem::path& directory) { + const auto currentTestInfo = ::testing::UnitTest::GetInstance()->current_test_info(); + LOG_ALWAYS_FATAL_IF(!currentTestInfo, + "writeBufferToFile must be called during execution of a test"); + + std::string fileName(currentTestInfo->name()); + // Test names may include the RenderEngine variant separated by '/', which would separate + // the file name into a subdirectory if not corrected. + std::replace(fileName.begin(), fileName.end(), '/', '-'); + fileName.append(".ppm"); + + std::error_code err; + filesystem::create_directories(directory, err); + if (err.value()) { + ALOGE("Unable to create directory %s for writing %s (%d: %s)", directory.c_str(), + fileName.c_str(), err.value(), err.message().c_str()); + return; + } + + // Append operator ("/") ensures exactly one "/" directly before the argument. + const filesystem::path filePath = directory / fileName; + std::ofstream file(filePath.c_str(), std::ios::binary); if (!file.is_open()) { - ALOGE("Unable to open file: %s", filename.c_str()); - ALOGE("You may need to do: \"adb shell setenforce 0\" to enable " - "surfaceflinger to write debug images"); + ALOGE("Unable to open file: %s", filePath.c_str()); + ALOGE("You may need to do: \"adb shell setenforce 0\" to enable surfaceflinger to " + "write debug images, or the %s directory might not give the executing user write " + "permission", + directory.c_str()); return; } @@ -304,6 +336,7 @@ public: } } file.write(reinterpret_cast<char*>(outBuffer.data()), outBuffer.size()); + ALOGI("Image of incorrect output written to %s", filePath.c_str()); mBuffer->getBuffer()->unlock(); } @@ -3147,6 +3180,214 @@ TEST_P(RenderEngineTest, r8_respects_color_transform_when_device_handles) { expectBufferColor(Rect(0, 0, 1, 1), 0, 70, 0, 255); } +TEST_P(RenderEngineTest, localTonemap_preservesFullscreenSdr) { + if (!GetParam()->apiSupported()) { + GTEST_SKIP(); + } + + initializeRenderEngine(); + + mBuffer = std::make_shared< + renderengine::impl:: + ExternalTexture>(sp<GraphicBuffer>::make(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, + GRALLOC_USAGE_SW_READ_OFTEN | + GRALLOC_USAGE_SW_WRITE_OFTEN | + GRALLOC_USAGE_HW_RENDER | + GRALLOC_USAGE_HW_TEXTURE, + "output"), + *mRE, + renderengine::impl::ExternalTexture::Usage::READABLE | + renderengine::impl::ExternalTexture::Usage::WRITEABLE); + ASSERT_EQ(0, mBuffer->getBuffer()->initCheck()); + + const auto whiteBuffer = allocateAndFillSourceBuffer(1, 1, ubyte4(51, 51, 51, 255)); + + const auto rect = Rect(0, 0, 1, 1); + const renderengine::DisplaySettings display{ + .physicalDisplay = rect, + .clip = rect, + .outputDataspace = ui::Dataspace::SRGB, + .targetLuminanceNits = 40, + .tonemapStrategy = renderengine::DisplaySettings::TonemapStrategy::Local, + }; + + const renderengine::LayerSettings whiteLayer{ + .geometry.boundaries = rect.toFloatRect(), + .source = + renderengine::PixelSource{ + .buffer = + renderengine::Buffer{ + .buffer = whiteBuffer, + }, + }, + .alpha = 1.0f, + .sourceDataspace = ui::Dataspace::V0_SCRGB_LINEAR, + .whitePointNits = 200, + }; + + std::vector<renderengine::LayerSettings> layers{whiteLayer}; + invokeDraw(display, layers); + + expectBufferColor(Rect(0, 0, 1, 1), 255, 255, 255, 255); +} + +TEST_P(RenderEngineTest, localTonemap_preservesFarawaySdrRegions) { + if (!GetParam()->apiSupported()) { + GTEST_SKIP(); + } + + initializeRenderEngine(); + + const auto blockWidth = 256; + const auto width = blockWidth * 4; + + const auto buffer = allocateSourceBuffer(width, 1); + + mBuffer = std::make_shared< + renderengine::impl:: + ExternalTexture>(sp<GraphicBuffer>::make(width, 1, HAL_PIXEL_FORMAT_RGBA_8888, + 1, + GRALLOC_USAGE_SW_READ_OFTEN | + GRALLOC_USAGE_SW_WRITE_OFTEN | + GRALLOC_USAGE_HW_RENDER | + GRALLOC_USAGE_HW_TEXTURE, + "output"), + *mRE, + renderengine::impl::ExternalTexture::Usage::READABLE | + renderengine::impl::ExternalTexture::Usage::WRITEABLE); + + { + uint8_t* pixels; + buffer->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, + reinterpret_cast<void**>(&pixels)); + uint8_t* dst = pixels; + for (uint32_t i = 0; i < width; i++) { + uint8_t value = 0; + if (i < blockWidth) { + value = 51; + } else if (i >= blockWidth * 3) { + value = 255; + } + dst[0] = value; + dst[1] = value; + dst[2] = value; + dst[3] = 255; + dst += 4; + } + buffer->getBuffer()->unlock(); + } + + const auto rect = Rect(0, 0, width, 1); + const renderengine::DisplaySettings display{ + .physicalDisplay = rect, + .clip = rect, + .outputDataspace = ui::Dataspace::V0_SRGB_LINEAR, + .targetLuminanceNits = 40, + .tonemapStrategy = renderengine::DisplaySettings::TonemapStrategy::Local, + }; + + const renderengine::LayerSettings whiteLayer{ + .geometry.boundaries = rect.toFloatRect(), + .source = + renderengine::PixelSource{ + .buffer = + renderengine::Buffer{ + .buffer = buffer, + }, + }, + .alpha = 1.0f, + .sourceDataspace = ui::Dataspace::V0_SCRGB_LINEAR, + .whitePointNits = 200, + }; + + std::vector<renderengine::LayerSettings> layers{whiteLayer}; + invokeDraw(display, layers); + + // SDR regions are boosted to preserve SDR detail. + expectBufferColor(Rect(0, 0, blockWidth, 1), 255, 255, 255, 255); + expectBufferColor(Rect(blockWidth, 0, blockWidth * 2, 1), 0, 0, 0, 255); + expectBufferColor(Rect(blockWidth * 2, 0, blockWidth * 3, 1), 0, 0, 0, 255); + expectBufferColor(Rect(blockWidth * 3, 0, blockWidth * 4, 1), 255, 255, 255, 255); +} + +TEST_P(RenderEngineTest, localTonemap_tonemapsNearbySdrRegions) { + if (!GetParam()->apiSupported()) { + GTEST_SKIP(); + } + + initializeRenderEngine(); + + const auto blockWidth = 2; + const auto width = blockWidth * 2; + + mBuffer = std::make_shared< + renderengine::impl:: + ExternalTexture>(sp<GraphicBuffer>::make(width, 1, HAL_PIXEL_FORMAT_RGBA_8888, + 1, + GRALLOC_USAGE_SW_READ_OFTEN | + GRALLOC_USAGE_SW_WRITE_OFTEN | + GRALLOC_USAGE_HW_RENDER | + GRALLOC_USAGE_HW_TEXTURE, + "output"), + *mRE, + renderengine::impl::ExternalTexture::Usage::READABLE | + renderengine::impl::ExternalTexture::Usage::WRITEABLE); + + const auto buffer = allocateSourceBuffer(width, 1); + + { + uint8_t* pixels; + buffer->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, + reinterpret_cast<void**>(&pixels)); + uint8_t* dst = pixels; + for (uint32_t i = 0; i < width; i++) { + uint8_t value = 0; + if (i < blockWidth) { + value = 51; + } else if (i >= blockWidth) { + value = 255; + } + dst[0] = value; + dst[1] = value; + dst[2] = value; + dst[3] = 255; + dst += 4; + } + buffer->getBuffer()->unlock(); + } + + const auto rect = Rect(0, 0, width, 1); + const renderengine::DisplaySettings display{ + .physicalDisplay = rect, + .clip = rect, + .outputDataspace = ui::Dataspace::V0_SRGB_LINEAR, + .targetLuminanceNits = 40, + .tonemapStrategy = renderengine::DisplaySettings::TonemapStrategy::Local, + }; + + const renderengine::LayerSettings whiteLayer{ + .geometry.boundaries = rect.toFloatRect(), + .source = + renderengine::PixelSource{ + .buffer = + renderengine::Buffer{ + .buffer = buffer, + }, + }, + .alpha = 1.0f, + .sourceDataspace = ui::Dataspace::V0_SCRGB_LINEAR, + .whitePointNits = 200, + }; + + std::vector<renderengine::LayerSettings> layers{whiteLayer}; + invokeDraw(display, layers); + + // SDR regions remain "dimmed", but preserve detail with a roll-off curve. + expectBufferColor(Rect(0, 0, blockWidth, 1), 132, 132, 132, 255, 2); + // HDR regions are not dimmed. + expectBufferColor(Rect(blockWidth, 0, blockWidth * 2, 1), 255, 255, 255, 255); +} + TEST_P(RenderEngineTest, primeShaderCache) { // TODO: b/331447071 - Fix in Graphite and re-enable. if (GetParam()->skiaBackend() == renderengine::RenderEngine::SkiaBackend::GRAPHITE) { diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp index d27c151e72..c187f93089 100644 --- a/libs/renderengine/threaded/RenderEngineThreaded.cpp +++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp @@ -23,9 +23,9 @@ #include <future> #include <android-base/stringprintf.h> +#include <common/trace.h> #include <private/gui/SyncFeatures.h> #include <processgroup/processgroup.h> -#include <utils/Trace.h> using namespace std::chrono_literals; @@ -39,7 +39,7 @@ std::unique_ptr<RenderEngineThreaded> RenderEngineThreaded::create(CreateInstanc RenderEngineThreaded::RenderEngineThreaded(CreateInstanceFactory factory) : RenderEngine(Threaded::YES) { - ATRACE_CALL(); + SFTRACE_CALL(); std::lock_guard lockThread(mThreadMutex); mThread = std::thread(&RenderEngineThreaded::threadMain, this, factory); @@ -76,7 +76,7 @@ status_t RenderEngineThreaded::setSchedFifo(bool enabled) { // NO_THREAD_SAFETY_ANALYSIS is because std::unique_lock presently lacks thread safety annotations. void RenderEngineThreaded::threadMain(CreateInstanceFactory factory) NO_THREAD_SAFETY_ANALYSIS { - ATRACE_CALL(); + SFTRACE_CALL(); if (!SetTaskProfiles(0, {"SFRenderEnginePolicy"})) { ALOGW("Failed to set render-engine task profile!"); @@ -133,13 +133,13 @@ void RenderEngineThreaded::waitUntilInitialized() const { std::future<void> RenderEngineThreaded::primeCache(PrimeCacheConfig config) { const auto resultPromise = std::make_shared<std::promise<void>>(); std::future<void> resultFuture = resultPromise->get_future(); - ATRACE_CALL(); + SFTRACE_CALL(); // This function is designed so it can run asynchronously, so we do not need to wait // for the futures. { std::lock_guard lock(mThreadMutex); mFunctionCalls.push([resultPromise, config](renderengine::RenderEngine& instance) { - ATRACE_NAME("REThreaded::primeCache"); + SFTRACE_NAME("REThreaded::primeCache"); if (setSchedFifo(false) != NO_ERROR) { ALOGW("Couldn't set SCHED_OTHER for primeCache"); } @@ -163,7 +163,7 @@ void RenderEngineThreaded::dump(std::string& result) { { std::lock_guard lock(mThreadMutex); mFunctionCalls.push([&resultPromise, &result](renderengine::RenderEngine& instance) { - ATRACE_NAME("REThreaded::dump"); + SFTRACE_NAME("REThreaded::dump"); std::string localResult = result; instance.dump(localResult); resultPromise.set_value(std::move(localResult)); @@ -176,13 +176,13 @@ void RenderEngineThreaded::dump(std::string& result) { void RenderEngineThreaded::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, bool isRenderable) { - ATRACE_CALL(); + SFTRACE_CALL(); // This function is designed so it can run asynchronously, so we do not need to wait // for the futures. { std::lock_guard lock(mThreadMutex); mFunctionCalls.push([=](renderengine::RenderEngine& instance) { - ATRACE_NAME("REThreaded::mapExternalTextureBuffer"); + SFTRACE_NAME("REThreaded::mapExternalTextureBuffer"); instance.mapExternalTextureBuffer(buffer, isRenderable); }); } @@ -190,14 +190,14 @@ void RenderEngineThreaded::mapExternalTextureBuffer(const sp<GraphicBuffer>& buf } void RenderEngineThreaded::unmapExternalTextureBuffer(sp<GraphicBuffer>&& buffer) { - ATRACE_CALL(); + SFTRACE_CALL(); // This function is designed so it can run asynchronously, so we do not need to wait // for the futures. { std::lock_guard lock(mThreadMutex); mFunctionCalls.push( [=, buffer = std::move(buffer)](renderengine::RenderEngine& instance) mutable { - ATRACE_NAME("REThreaded::unmapExternalTextureBuffer"); + SFTRACE_NAME("REThreaded::unmapExternalTextureBuffer"); instance.unmapExternalTextureBuffer(std::move(buffer)); }); } @@ -229,7 +229,7 @@ void RenderEngineThreaded::cleanupPostRender() { { std::lock_guard lock(mThreadMutex); mFunctionCalls.push([=](renderengine::RenderEngine& instance) { - ATRACE_NAME("REThreaded::cleanupPostRender"); + SFTRACE_NAME("REThreaded::cleanupPostRender"); instance.cleanupPostRender(); }); mNeedsPostRenderCleanup = false; @@ -249,10 +249,20 @@ void RenderEngineThreaded::drawLayersInternal( return; } +void RenderEngineThreaded::drawGainmapInternal( + const std::shared_ptr<std::promise<FenceResult>>&& resultPromise, + const std::shared_ptr<ExternalTexture>& sdr, base::borrowed_fd&& sdrFence, + const std::shared_ptr<ExternalTexture>& hdr, base::borrowed_fd&& hdrFence, + float hdrSdrRatio, ui::Dataspace dataspace, + const std::shared_ptr<ExternalTexture>& gainmap) { + resultPromise->set_value(Fence::NO_FENCE); + return; +} + ftl::Future<FenceResult> RenderEngineThreaded::drawLayers( const DisplaySettings& display, const std::vector<LayerSettings>& layers, const std::shared_ptr<ExternalTexture>& buffer, base::unique_fd&& bufferFence) { - ATRACE_CALL(); + SFTRACE_CALL(); const auto resultPromise = std::make_shared<std::promise<FenceResult>>(); std::future<FenceResult> resultFuture = resultPromise->get_future(); int fd = bufferFence.release(); @@ -261,8 +271,8 @@ ftl::Future<FenceResult> RenderEngineThreaded::drawLayers( mNeedsPostRenderCleanup = true; mFunctionCalls.push( [resultPromise, display, layers, buffer, fd](renderengine::RenderEngine& instance) { - ATRACE_NAME("REThreaded::drawLayers"); - instance.updateProtectedContext(layers, buffer); + SFTRACE_NAME("REThreaded::drawLayers"); + instance.updateProtectedContext(layers, {buffer.get()}); instance.drawLayersInternal(std::move(resultPromise), display, layers, buffer, base::unique_fd(fd)); }); @@ -271,13 +281,37 @@ ftl::Future<FenceResult> RenderEngineThreaded::drawLayers( return resultFuture; } +ftl::Future<FenceResult> RenderEngineThreaded::drawGainmap( + const std::shared_ptr<ExternalTexture>& sdr, base::borrowed_fd&& sdrFence, + const std::shared_ptr<ExternalTexture>& hdr, base::borrowed_fd&& hdrFence, + float hdrSdrRatio, ui::Dataspace dataspace, + const std::shared_ptr<ExternalTexture>& gainmap) { + SFTRACE_CALL(); + const auto resultPromise = std::make_shared<std::promise<FenceResult>>(); + std::future<FenceResult> resultFuture = resultPromise->get_future(); + { + std::lock_guard lock(mThreadMutex); + mNeedsPostRenderCleanup = true; + mFunctionCalls.push([resultPromise, sdr, sdrFence = std::move(sdrFence), hdr, + hdrFence = std::move(hdrFence), hdrSdrRatio, dataspace, + gainmap](renderengine::RenderEngine& instance) mutable { + SFTRACE_NAME("REThreaded::drawGainmap"); + instance.updateProtectedContext({}, {sdr.get(), hdr.get(), gainmap.get()}); + instance.drawGainmapInternal(std::move(resultPromise), sdr, std::move(sdrFence), hdr, + std::move(hdrFence), hdrSdrRatio, dataspace, gainmap); + }); + } + mCondition.notify_one(); + return resultFuture; +} + int RenderEngineThreaded::getContextPriority() { std::promise<int> resultPromise; std::future<int> resultFuture = resultPromise.get_future(); { std::lock_guard lock(mThreadMutex); mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) { - ATRACE_NAME("REThreaded::getContextPriority"); + SFTRACE_NAME("REThreaded::getContextPriority"); int priority = instance.getContextPriority(); resultPromise.set_value(priority); }); @@ -297,7 +331,7 @@ void RenderEngineThreaded::onActiveDisplaySizeChanged(ui::Size size) { { std::lock_guard lock(mThreadMutex); mFunctionCalls.push([size](renderengine::RenderEngine& instance) { - ATRACE_NAME("REThreaded::onActiveDisplaySizeChanged"); + SFTRACE_NAME("REThreaded::onActiveDisplaySizeChanged"); instance.onActiveDisplaySizeChanged(size); }); } @@ -324,7 +358,7 @@ void RenderEngineThreaded::setEnableTracing(bool tracingEnabled) { { std::lock_guard lock(mThreadMutex); mFunctionCalls.push([tracingEnabled](renderengine::RenderEngine& instance) { - ATRACE_NAME("REThreaded::setEnableTracing"); + SFTRACE_NAME("REThreaded::setEnableTracing"); instance.setEnableTracing(tracingEnabled); }); } diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h index d4997d6c93..cb6e924d81 100644 --- a/libs/renderengine/threaded/RenderEngineThreaded.h +++ b/libs/renderengine/threaded/RenderEngineThreaded.h @@ -55,6 +55,12 @@ public: const std::vector<LayerSettings>& layers, const std::shared_ptr<ExternalTexture>& buffer, base::unique_fd&& bufferFence) override; + ftl::Future<FenceResult> drawGainmap(const std::shared_ptr<ExternalTexture>& sdr, + base::borrowed_fd&& sdrFence, + const std::shared_ptr<ExternalTexture>& hdr, + base::borrowed_fd&& hdrFence, float hdrSdrRatio, + ui::Dataspace dataspace, + const std::shared_ptr<ExternalTexture>& gainmap) override; int getContextPriority() override; bool supportsBackgroundBlur() override; @@ -71,6 +77,13 @@ protected: const std::vector<LayerSettings>& layers, const std::shared_ptr<ExternalTexture>& buffer, base::unique_fd&& bufferFence) override; + void drawGainmapInternal(const std::shared_ptr<std::promise<FenceResult>>&& resultPromise, + const std::shared_ptr<ExternalTexture>& sdr, + base::borrowed_fd&& sdrFence, + const std::shared_ptr<ExternalTexture>& hdr, + base::borrowed_fd&& hdrFence, float hdrSdrRatio, + ui::Dataspace dataspace, + const std::shared_ptr<ExternalTexture>& gainmap) override; private: void threadMain(CreateInstanceFactory factory); diff --git a/libs/sensor/Android.bp b/libs/sensor/Android.bp index 7fa47b45f0..659666d6b6 100644 --- a/libs/sensor/Android.bp +++ b/libs/sensor/Android.bp @@ -63,6 +63,8 @@ cc_library { "libhardware", "libpermission", "android.companion.virtual.virtualdevice_aidl-cpp", + "libaconfig_storage_read_api_cc", + "server_configurable_flags", ], static_libs: [ diff --git a/libs/sensor/SensorEventQueue.cpp b/libs/sensor/SensorEventQueue.cpp index 4438d454c0..bec9255fd7 100644 --- a/libs/sensor/SensorEventQueue.cpp +++ b/libs/sensor/SensorEventQueue.cpp @@ -15,31 +15,41 @@ */ #define LOG_TAG "Sensors" +#define ATRACE_TAG ATRACE_TAG_SYSTEM_SERVER +#include <android/sensor.h> +#include <com_android_hardware_libsensor_flags.h> +#include <cutils/trace.h> +#include <hardware/sensors-base.h> +#include <sensor/BitTube.h> +#include <sensor/ISensorEventConnection.h> +#include <sensor/Sensor.h> #include <sensor/SensorEventQueue.h> - -#include <algorithm> +#include <sensor/SensorManager.h> #include <sys/socket.h> - -#include <utils/RefBase.h> #include <utils/Looper.h> +#include <utils/RefBase.h> -#include <sensor/Sensor.h> -#include <sensor/BitTube.h> -#include <sensor/ISensorEventConnection.h> - -#include <android/sensor.h> -#include <hardware/sensors-base.h> +#include <algorithm> +#include <cinttypes> +#include <string> using std::min; +namespace libsensor_flags = com::android::hardware::libsensor::flags; // ---------------------------------------------------------------------------- namespace android { // ---------------------------------------------------------------------------- -SensorEventQueue::SensorEventQueue(const sp<ISensorEventConnection>& connection) - : mSensorEventConnection(connection), mRecBuffer(nullptr), mAvailable(0), mConsumed(0), - mNumAcksToSend(0) { +SensorEventQueue::SensorEventQueue(const sp<ISensorEventConnection>& connection, + SensorManager& sensorManager, String8 packageName) + : mSensorEventConnection(connection), + mRecBuffer(nullptr), + mSensorManager(sensorManager), + mPackageName(packageName), + mAvailable(0), + mConsumed(0), + mNumAcksToSend(0) { mRecBuffer = new ASensorEvent[MAX_RECEIVE_BUFFER_EVENT_COUNT]; } @@ -65,8 +75,8 @@ ssize_t SensorEventQueue::write(const sp<BitTube>& tube, ssize_t SensorEventQueue::read(ASensorEvent* events, size_t numEvents) { if (mAvailable == 0) { - ssize_t err = BitTube::recvObjects(mSensorChannel, - mRecBuffer, MAX_RECEIVE_BUFFER_EVENT_COUNT); + ssize_t err = + BitTube::recvObjects(mSensorChannel, mRecBuffer, MAX_RECEIVE_BUFFER_EVENT_COUNT); if (err < 0) { return err; } @@ -75,6 +85,23 @@ ssize_t SensorEventQueue::read(ASensorEvent* events, size_t numEvents) { } size_t count = min(numEvents, mAvailable); memcpy(events, mRecBuffer + mConsumed, count * sizeof(ASensorEvent)); + + if (CC_UNLIKELY(ATRACE_ENABLED()) && + libsensor_flags::sensor_event_queue_report_sensor_usage_in_tracing()) { + for (size_t i = 0; i < count; i++) { + std::optional<std::string_view> sensorName = + mSensorManager.getSensorNameByHandle(events->sensor); + if (sensorName.has_value()) { + char buffer[UINT8_MAX]; + IPCThreadState* thread = IPCThreadState::self(); + pid_t pid = (thread != nullptr) ? thread->getCallingPid() : -1; + std::snprintf(buffer, sizeof(buffer), + "Sensor event from %s to %s PID: %d (%zu/%zu)", + sensorName.value().data(), mPackageName.c_str(), pid, i, count); + ATRACE_INSTANT_FOR_TRACK(LOG_TAG, buffer); + } + } + } mAvailable -= count; mConsumed += count; return static_cast<ssize_t>(count); diff --git a/libs/sensor/SensorManager.cpp b/libs/sensor/SensorManager.cpp index 9411e204e9..7b4a86c215 100644 --- a/libs/sensor/SensorManager.cpp +++ b/libs/sensor/SensorManager.cpp @@ -38,6 +38,7 @@ #include <sensor/SensorEventQueue.h> #include <com_android_hardware_libsensor_flags.h> +namespace libsensor_flags = com::android::hardware::libsensor::flags; // ---------------------------------------------------------------------------- namespace android { @@ -72,12 +73,25 @@ int getDeviceIdForUid(uid_t uid) { return deviceId; } } - } else { - ALOGW("Cannot get virtualdevice_native service"); } return DEVICE_ID_DEFAULT; } +bool findSensorNameInList(int32_t handle, const Vector<Sensor>& sensorList, + std::string* outString) { + for (auto& sensor : sensorList) { + if (sensor.getHandle() == handle) { + std::ostringstream oss; + oss << sensor.getStringType() << ":" << sensor.getName(); + if (outString) { + *outString = oss.str(); + } + return true; + } + } + return false; +} + } // namespace Mutex SensorManager::sLock; @@ -355,6 +369,25 @@ Sensor const* SensorManager::getDefaultSensor(int type) return nullptr; } +std::optional<std::string_view> SensorManager::getSensorNameByHandle(int32_t handle) { + std::lock_guard<std::mutex> lock(mSensorHandleToNameMutex); + auto iterator = mSensorHandleToName.find(handle); + if (iterator != mSensorHandleToName.end()) { + return iterator->second; + } + + std::string sensorName; + if (!findSensorNameInList(handle, mSensors, &sensorName) && + !findSensorNameInList(handle, mDynamicSensors, &sensorName)) { + ALOGW("Cannot find sensor with handle %d", handle); + return std::nullopt; + } + + mSensorHandleToName[handle] = std::move(sensorName); + + return mSensorHandleToName[handle]; +} + sp<SensorEventQueue> SensorManager::createEventQueue( String8 packageName, int mode, String16 attributionTag) { sp<SensorEventQueue> queue; @@ -368,7 +401,7 @@ sp<SensorEventQueue> SensorManager::createEventQueue( ALOGE("createEventQueue: connection is NULL."); return nullptr; } - queue = new SensorEventQueue(connection); + queue = new SensorEventQueue(connection, *this, packageName); break; } return queue; diff --git a/libs/sensor/include/sensor/SensorEventQueue.h b/libs/sensor/include/sensor/SensorEventQueue.h index 8c3fde0fa1..d31def7425 100644 --- a/libs/sensor/include/sensor/SensorEventQueue.h +++ b/libs/sensor/include/sensor/SensorEventQueue.h @@ -20,9 +20,10 @@ #include <sys/types.h> #include <utils/Errors.h> +#include <utils/Mutex.h> #include <utils/RefBase.h> +#include <utils/String8.h> #include <utils/Timers.h> -#include <utils/Mutex.h> #include <sensor/BitTube.h> @@ -42,6 +43,7 @@ namespace android { // ---------------------------------------------------------------------------- class ISensorEventConnection; +class SensorManager; class Sensor; class Looper; @@ -65,7 +67,8 @@ public: // Default sensor sample period static constexpr int32_t SENSOR_DELAY_NORMAL = 200000; - explicit SensorEventQueue(const sp<ISensorEventConnection>& connection); + explicit SensorEventQueue(const sp<ISensorEventConnection>& connection, + SensorManager& sensorManager, String8 packageName); virtual ~SensorEventQueue(); virtual void onFirstRef(); @@ -107,6 +110,8 @@ private: mutable Mutex mLock; mutable sp<Looper> mLooper; ASensorEvent* mRecBuffer; + SensorManager& mSensorManager; + String8 mPackageName; size_t mAvailable; size_t mConsumed; uint32_t mNumAcksToSend; diff --git a/libs/sensor/include/sensor/SensorManager.h b/libs/sensor/include/sensor/SensorManager.h index 49f050a0ac..8d7237d548 100644 --- a/libs/sensor/include/sensor/SensorManager.h +++ b/libs/sensor/include/sensor/SensorManager.h @@ -17,22 +17,20 @@ #ifndef ANDROID_GUI_SENSOR_MANAGER_H #define ANDROID_GUI_SENSOR_MANAGER_H -#include <map> -#include <unordered_map> - -#include <stdint.h> -#include <sys/types.h> - #include <binder/IBinder.h> #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> - +#include <sensor/SensorEventQueue.h> +#include <stdint.h> +#include <sys/types.h> #include <utils/Errors.h> +#include <utils/String8.h> #include <utils/StrongPointer.h> #include <utils/Vector.h> -#include <utils/String8.h> -#include <sensor/SensorEventQueue.h> +#include <map> +#include <string> +#include <unordered_map> // ---------------------------------------------------------------------------- // Concrete types for the NDK @@ -66,6 +64,7 @@ public: sp<SensorEventQueue> createEventQueue( String8 packageName = String8(""), int mode = 0, String16 attributionTag = String16("")); bool isDataInjectionEnabled(); + std::optional<std::string_view> getSensorNameByHandle(int32_t handle); bool isReplayDataInjectionEnabled(); bool isHalBypassReplayDataInjectionEnabled(); int createDirectChannel(size_t size, int channelType, const native_handle_t *channelData); @@ -97,6 +96,9 @@ private: const String16 mOpPackageName; const int mDeviceId; std::unordered_map<int, sp<ISensorEventConnection>> mDirectConnection; + + std::mutex mSensorHandleToNameMutex; + std::unordered_map<int32_t, std::string> mSensorHandleToName; int32_t mDirectConnectionHandle; }; diff --git a/libs/tracing_perfetto/Android.bp b/libs/tracing_perfetto/Android.bp index 3a4c869e46..b5c56c5c52 100644 --- a/libs/tracing_perfetto/Android.bp +++ b/libs/tracing_perfetto/Android.bp @@ -40,6 +40,7 @@ cc_library_shared { ], shared_libs: [ + "libbase", "libcutils", "libperfetto_c", "android.os.flags-aconfig-cc-host", diff --git a/libs/tracing_perfetto/include/tracing_perfetto.h b/libs/tracing_perfetto/include/tracing_perfetto.h index 2c1c2a49e7..59c43d6dcc 100644 --- a/libs/tracing_perfetto/include/tracing_perfetto.h +++ b/libs/tracing_perfetto/include/tracing_perfetto.h @@ -14,40 +14,40 @@ * limitations under the License. */ -#ifndef TRACING_PERFETTO_H -#define TRACING_PERFETTO_H +#pragma once #include <stdint.h> -#include "trace_result.h" - namespace tracing_perfetto { void registerWithPerfetto(bool test = false); -Result traceBegin(uint64_t category, const char* name); +void traceBegin(uint64_t category, const char* name); + +void traceEnd(uint64_t category); -Result traceEnd(uint64_t category); +void traceAsyncBegin(uint64_t category, const char* name, int32_t cookie); -Result traceAsyncBegin(uint64_t category, const char* name, int32_t cookie); +void traceFormatBegin(uint64_t category, const char* fmt, ...); -Result traceAsyncEnd(uint64_t category, const char* name, int32_t cookie); +void traceAsyncEnd(uint64_t category, const char* name, int32_t cookie); -Result traceAsyncBeginForTrack(uint64_t category, const char* name, +void traceAsyncBeginForTrack(uint64_t category, const char* name, const char* trackName, int32_t cookie); -Result traceAsyncEndForTrack(uint64_t category, const char* trackName, +void traceAsyncEndForTrack(uint64_t category, const char* trackName, int32_t cookie); -Result traceInstant(uint64_t category, const char* name); +void traceInstant(uint64_t category, const char* name); + +void traceFormatInstant(uint64_t category, const char* fmt, ...); -Result traceInstantForTrack(uint64_t category, const char* trackName, +void traceInstantForTrack(uint64_t category, const char* trackName, const char* name); -Result traceCounter(uint64_t category, const char* name, int64_t value); +void traceCounter(uint64_t category, const char* name, int64_t value); -bool isTagEnabled(uint64_t category); +void traceCounter32(uint64_t category, const char* name, int32_t value); +bool isTagEnabled(uint64_t category); } // namespace tracing_perfetto - -#endif // TRACING_PERFETTO_H diff --git a/libs/tracing_perfetto/tests/Android.bp b/libs/tracing_perfetto/tests/Android.bp index a35b0e0c83..d203467783 100644 --- a/libs/tracing_perfetto/tests/Android.bp +++ b/libs/tracing_perfetto/tests/Android.bp @@ -26,6 +26,7 @@ cc_test { static_libs: [ "libflagtest", "libgmock", + "perfetto_trace_protos", ], cflags: [ "-Wall", @@ -35,6 +36,8 @@ cc_test { "android.os.flags-aconfig-cc-host", "libbase", "libperfetto_c", + "liblog", + "libprotobuf-cpp-lite", "libtracing_perfetto", ], srcs: [ diff --git a/libs/tracing_perfetto/tests/tracing_perfetto_test.cpp b/libs/tracing_perfetto/tests/tracing_perfetto_test.cpp index 7716b9a316..e9fee2e6cf 100644 --- a/libs/tracing_perfetto/tests/tracing_perfetto_test.cpp +++ b/libs/tracing_perfetto/tests/tracing_perfetto_test.cpp @@ -16,10 +16,10 @@ #include "tracing_perfetto.h" -#include <thread> - #include <android_os.h> #include <flag_macros.h> +#include <thread> +#include <unistd.h> #include "gtest/gtest.h" #include "perfetto/public/abi/data_source_abi.h" @@ -45,67 +45,182 @@ #include "trace_categories.h" #include "utils.h" +#include "protos/perfetto/trace/trace.pb.h" +#include "protos/perfetto/trace/trace_packet.pb.h" +#include "protos/perfetto/trace/interned_data/interned_data.pb.h" + +#include <fstream> +#include <iterator> namespace tracing_perfetto { -using ::perfetto::shlib::test_utils::AllFieldsWithId; -using ::perfetto::shlib::test_utils::FieldView; -using ::perfetto::shlib::test_utils::IdFieldView; -using ::perfetto::shlib::test_utils::MsgField; -using ::perfetto::shlib::test_utils::PbField; -using ::perfetto::shlib::test_utils::StringField; +using ::perfetto::protos::Trace; +using ::perfetto::protos::TracePacket; +using ::perfetto::protos::EventCategory; +using ::perfetto::protos::EventName; +using ::perfetto::protos::FtraceEvent; +using ::perfetto::protos::FtraceEventBundle; +using ::perfetto::protos::InternedData; + using ::perfetto::shlib::test_utils::TracingSession; -using ::perfetto::shlib::test_utils::VarIntField; -using ::testing::_; -using ::testing::ElementsAre; -using ::testing::UnorderedElementsAre; const auto PERFETTO_SDK_TRACING = ACONFIG_FLAG(android::os, perfetto_sdk_tracing); +// TODO(b/303199244): Add tests for all the library functions. class TracingPerfettoTest : public testing::Test { protected: void SetUp() override { - tracing_perfetto::registerWithPerfetto(true /* test */); + tracing_perfetto::registerWithPerfetto(false /* test */); } }; -// TODO(b/303199244): Add tests for all the library functions. - -TEST_F_WITH_FLAGS(TracingPerfettoTest, traceInstant, - REQUIRES_FLAGS_ENABLED(PERFETTO_SDK_TRACING)) { - TracingSession tracing_session = - TracingSession::Builder().set_data_source_name("track_event").Build(); - tracing_perfetto::traceInstant(TRACE_CATEGORY_INPUT, ""); - +Trace stopSession(TracingSession& tracing_session) { + tracing_session.FlushBlocking(5000); tracing_session.StopBlocking(); std::vector<uint8_t> data = tracing_session.ReadBlocking(); + std::string data_string(data.begin(), data.end()); + + perfetto::protos::Trace trace; + trace.ParseFromString(data_string); + + return trace; +} + +void verifyTrackEvent(const Trace& trace, const std::string expected_category, + const std::string& expected_name) { bool found = false; - for (struct PerfettoPbDecoderField trace_field : FieldView(data)) { - ASSERT_THAT(trace_field, PbField(perfetto_protos_Trace_packet_field_number, - MsgField(_))); - IdFieldView track_event( - trace_field, perfetto_protos_TracePacket_track_event_field_number); - if (track_event.size() == 0) { - continue; + for (const TracePacket& packet: trace.packet()) { + if (packet.has_track_event() && packet.has_interned_data()) { + + const InternedData& interned_data = packet.interned_data(); + if (interned_data.event_categories_size() > 0) { + const EventCategory& event_category = packet.interned_data().event_categories(0); + if (event_category.name() == expected_category) { + found = true; + } + } + + if (interned_data.event_names_size() > 0) { + const EventName& event_name = packet.interned_data().event_names(0); + if (event_name.name() == expected_name) { + found &= true; + } + } + + if (found) { + break; + } + } + } + EXPECT_TRUE(found); +} + +void verifyAtraceEvent(const Trace& trace, const std::string& expected_name) { + std::string expected_print_buf = "I|" + std::to_string(gettid()) + "|" + expected_name + "\n"; + + bool found = false; + for (const TracePacket& packet: trace.packet()) { + if (packet.has_ftrace_events()) { + const FtraceEventBundle& ftrace_events_bundle = packet.ftrace_events(); + + if (ftrace_events_bundle.event_size() > 0) { + const FtraceEvent& ftrace_event = ftrace_events_bundle.event(0); + if (ftrace_event.has_print() && (ftrace_event.print().buf() == expected_print_buf)) { + found = true; + break; + } + } } - found = true; - IdFieldView cat_iid_fields( - track_event.front(), - perfetto_protos_TrackEvent_category_iids_field_number); - ASSERT_THAT(cat_iid_fields, ElementsAre(VarIntField(_))); - uint64_t cat_iid = cat_iid_fields.front().value.integer64; - EXPECT_THAT( - trace_field, - AllFieldsWithId( - perfetto_protos_TracePacket_interned_data_field_number, - ElementsAre(AllFieldsWithId( - perfetto_protos_InternedData_event_categories_field_number, - ElementsAre(MsgField(UnorderedElementsAre( - PbField(perfetto_protos_EventCategory_iid_field_number, - VarIntField(cat_iid)), - PbField(perfetto_protos_EventCategory_name_field_number, - StringField("input"))))))))); } EXPECT_TRUE(found); } -} // namespace tracing_perfetto
\ No newline at end of file +TEST_F_WITH_FLAGS(TracingPerfettoTest, traceInstantWithPerfetto, + REQUIRES_FLAGS_ENABLED(PERFETTO_SDK_TRACING)) { + std::string event_category = "input"; + std::string event_name = "traceInstantWithPerfetto"; + + TracingSession tracing_session = + TracingSession::Builder().add_enabled_category(event_category).Build(); + + tracing_perfetto::traceInstant(TRACE_CATEGORY_INPUT, event_name.c_str()); + + Trace trace = stopSession(tracing_session); + + verifyTrackEvent(trace, event_category, event_name); +} + +TEST_F_WITH_FLAGS(TracingPerfettoTest, traceInstantWithAtrace, + REQUIRES_FLAGS_ENABLED(PERFETTO_SDK_TRACING)) { + std::string event_category = "input"; + std::string event_name = "traceInstantWithAtrace"; + + TracingSession tracing_session = + TracingSession::Builder().add_atrace_category(event_category).Build(); + + tracing_perfetto::traceInstant(TRACE_CATEGORY_INPUT, event_name.c_str()); + + Trace trace = stopSession(tracing_session); + + verifyAtraceEvent(trace, event_name); +} + +TEST_F_WITH_FLAGS(TracingPerfettoTest, traceInstantWithPerfettoAndAtrace, + REQUIRES_FLAGS_ENABLED(PERFETTO_SDK_TRACING)) { + std::string event_category = "input"; + std::string event_name = "traceInstantWithPerfettoAndAtrace"; + + TracingSession tracing_session = + TracingSession::Builder() + .add_atrace_category(event_category) + .add_enabled_category(event_category).Build(); + + tracing_perfetto::traceInstant(TRACE_CATEGORY_INPUT, event_name.c_str()); + + Trace trace = stopSession(tracing_session); + + verifyAtraceEvent(trace, event_name); +} + +TEST_F_WITH_FLAGS(TracingPerfettoTest, traceInstantWithPerfettoAndAtraceAndPreferTrackEvent, + REQUIRES_FLAGS_ENABLED(PERFETTO_SDK_TRACING)) { + std::string event_category = "input"; + std::string event_name = "traceInstantWithPerfettoAndAtraceAndPreferTrackEvent"; + + TracingSession tracing_session = + TracingSession::Builder() + .add_atrace_category(event_category) + .add_atrace_category_prefer_sdk(event_category) + .add_enabled_category(event_category).Build(); + + tracing_perfetto::traceInstant(TRACE_CATEGORY_INPUT, event_name.c_str()); + + Trace trace = stopSession(tracing_session); + + verifyTrackEvent(trace, event_category, event_name); +} + +TEST_F_WITH_FLAGS(TracingPerfettoTest, traceInstantWithPerfettoAndAtraceConcurrently, + REQUIRES_FLAGS_ENABLED(PERFETTO_SDK_TRACING)) { + std::string event_category = "input"; + std::string event_name = "traceInstantWithPerfettoAndAtraceConcurrently"; + + TracingSession perfetto_tracing_session = + TracingSession::Builder() + .add_atrace_category(event_category) + .add_atrace_category_prefer_sdk(event_category) + .add_enabled_category(event_category).Build(); + + TracingSession atrace_tracing_session = + TracingSession::Builder() + .add_atrace_category(event_category) + .add_enabled_category(event_category).Build(); + + tracing_perfetto::traceInstant(TRACE_CATEGORY_INPUT, event_name.c_str()); + + Trace atrace_trace = stopSession(atrace_tracing_session); + Trace perfetto_trace = stopSession(perfetto_tracing_session); + + verifyAtraceEvent(atrace_trace, event_name); + verifyAtraceEvent(perfetto_trace, event_name); +} +} // namespace tracing_perfetto diff --git a/libs/tracing_perfetto/tests/utils.cpp b/libs/tracing_perfetto/tests/utils.cpp index 9c4202808a..8c4d4a8925 100644 --- a/libs/tracing_perfetto/tests/utils.cpp +++ b/libs/tracing_perfetto/tests/utils.cpp @@ -26,6 +26,11 @@ #include "perfetto/public/protos/config/track_event/track_event_config.pzc.h" #include "perfetto/public/tracing_session.h" +#include "protos/perfetto/config/ftrace/ftrace_config.pb.h" +#include "protos/perfetto/config/track_event/track_event_config.pb.h" +#include "protos/perfetto/config/data_source_config.pb.h" +#include "protos/perfetto/config/trace_config.pb.h" + namespace perfetto { namespace shlib { namespace test_utils { @@ -44,63 +49,54 @@ std::string ToHexChars(uint8_t val) { } // namespace TracingSession TracingSession::Builder::Build() { - struct PerfettoPbMsgWriter writer; - struct PerfettoHeapBuffer* hb = PerfettoHeapBufferCreate(&writer.writer); + perfetto::protos::TraceConfig trace_config; + trace_config.add_buffers()->set_size_kb(1024); - struct perfetto_protos_TraceConfig cfg; - PerfettoPbMsgInit(&cfg.msg, &writer); + auto* track_event_ds_config = trace_config.add_data_sources()->mutable_config(); + auto* ftrace_ds_config = trace_config.add_data_sources()->mutable_config(); - { - struct perfetto_protos_TraceConfig_BufferConfig buffers; - perfetto_protos_TraceConfig_begin_buffers(&cfg, &buffers); + track_event_ds_config->set_name("track_event"); + track_event_ds_config->set_target_buffer(0); + + ftrace_ds_config->set_name("linux.ftrace"); + ftrace_ds_config->set_target_buffer(0); - perfetto_protos_TraceConfig_BufferConfig_set_size_kb(&buffers, 1024); + { + auto* ftrace_config = ftrace_ds_config->mutable_ftrace_config(); + if (!atrace_categories_.empty()) { + ftrace_config->add_ftrace_events("ftrace/print"); + for (const std::string& cat : atrace_categories_) { + ftrace_config->add_atrace_categories(cat); + } - perfetto_protos_TraceConfig_end_buffers(&cfg, &buffers); + for (const std::string& cat : atrace_categories_prefer_sdk_) { + ftrace_config->add_atrace_categories_prefer_sdk(cat); + } + } } { - struct perfetto_protos_TraceConfig_DataSource data_sources; - perfetto_protos_TraceConfig_begin_data_sources(&cfg, &data_sources); - - { - struct perfetto_protos_DataSourceConfig ds_cfg; - perfetto_protos_TraceConfig_DataSource_begin_config(&data_sources, - &ds_cfg); - - perfetto_protos_DataSourceConfig_set_cstr_name(&ds_cfg, - data_source_name_.c_str()); - if (!enabled_categories_.empty() && !disabled_categories_.empty()) { - perfetto_protos_TrackEventConfig te_cfg; - perfetto_protos_DataSourceConfig_begin_track_event_config(&ds_cfg, - &te_cfg); - for (const std::string& cat : enabled_categories_) { - perfetto_protos_TrackEventConfig_set_enabled_categories( - &te_cfg, cat.data(), cat.size()); - } - for (const std::string& cat : disabled_categories_) { - perfetto_protos_TrackEventConfig_set_disabled_categories( - &te_cfg, cat.data(), cat.size()); - } - perfetto_protos_DataSourceConfig_end_track_event_config(&ds_cfg, - &te_cfg); + auto* track_event_config = track_event_ds_config->mutable_track_event_config(); + if (!enabled_categories_.empty() || !disabled_categories_.empty()) { + for (const std::string& cat : enabled_categories_) { + track_event_config->add_enabled_categories(cat); } - perfetto_protos_TraceConfig_DataSource_end_config(&data_sources, &ds_cfg); + for (const std::string& cat : disabled_categories_) { + track_event_config->add_disabled_categories(cat); + } } - - perfetto_protos_TraceConfig_end_data_sources(&cfg, &data_sources); } - size_t cfg_size = PerfettoStreamWriterGetWrittenSize(&writer.writer); - std::unique_ptr<uint8_t[]> ser(new uint8_t[cfg_size]); - PerfettoHeapBufferCopyInto(hb, &writer.writer, ser.get(), cfg_size); - PerfettoHeapBufferDestroy(hb, &writer.writer); struct PerfettoTracingSessionImpl* ts = - PerfettoTracingSessionCreate(PERFETTO_BACKEND_IN_PROCESS); + PerfettoTracingSessionCreate(PERFETTO_BACKEND_SYSTEM); + + std::string trace_config_string; + trace_config.SerializeToString(&trace_config_string); - PerfettoTracingSessionSetup(ts, ser.get(), cfg_size); + PerfettoTracingSessionSetup(ts, trace_config_string.data(), trace_config_string.length()); + // Fails to start here PerfettoTracingSessionStartBlocking(ts); return TracingSession::Adopt(ts); diff --git a/libs/tracing_perfetto/tests/utils.h b/libs/tracing_perfetto/tests/utils.h index 4353554963..8edb4143ee 100644 --- a/libs/tracing_perfetto/tests/utils.h +++ b/libs/tracing_perfetto/tests/utils.h @@ -74,10 +74,6 @@ class TracingSession { class Builder { public: Builder() = default; - Builder& set_data_source_name(std::string data_source_name) { - data_source_name_ = std::move(data_source_name); - return *this; - } Builder& add_enabled_category(std::string category) { enabled_categories_.push_back(std::move(category)); return *this; @@ -86,12 +82,21 @@ class TracingSession { disabled_categories_.push_back(std::move(category)); return *this; } + Builder& add_atrace_category(std::string category) { + atrace_categories_.push_back(std::move(category)); + return *this; + } + Builder& add_atrace_category_prefer_sdk(std::string category) { + atrace_categories_prefer_sdk_.push_back(std::move(category)); + return *this; + } TracingSession Build(); private: - std::string data_source_name_; std::vector<std::string> enabled_categories_; std::vector<std::string> disabled_categories_; + std::vector<std::string> atrace_categories_; + std::vector<std::string> atrace_categories_prefer_sdk_; }; static TracingSession Adopt(struct PerfettoTracingSessionImpl*); diff --git a/libs/tracing_perfetto/tracing_perfetto.cpp b/libs/tracing_perfetto/tracing_perfetto.cpp index 6f716eea9a..c35e078b02 100644 --- a/libs/tracing_perfetto/tracing_perfetto.cpp +++ b/libs/tracing_perfetto/tracing_perfetto.cpp @@ -17,6 +17,7 @@ #include "tracing_perfetto.h" #include <cutils/trace.h> +#include <cstdarg> #include "perfetto/public/te_category_macros.h" #include "trace_categories.h" @@ -28,116 +29,172 @@ void registerWithPerfetto(bool test) { internal::registerWithPerfetto(test); } -Result traceBegin(uint64_t category, const char* name) { +void traceBegin(uint64_t category, const char* name) { struct PerfettoTeCategory* perfettoTeCategory = internal::toPerfettoCategory(category); - if (perfettoTeCategory != nullptr) { - return internal::perfettoTraceBegin(*perfettoTeCategory, name); - } else { + + if (internal::shouldPreferAtrace(perfettoTeCategory, category)) { atrace_begin(category, name); - return Result::SUCCESS; + } else if (internal::isPerfettoCategoryEnabled(perfettoTeCategory)) { + internal::perfettoTraceBegin(*perfettoTeCategory, name); + } +} + +void traceFormatBegin(uint64_t category, const char* fmt, ...) { + struct PerfettoTeCategory* perfettoTeCategory = + internal::toPerfettoCategory(category); + const bool preferAtrace = internal::shouldPreferAtrace(perfettoTeCategory, category); + const bool preferPerfetto = internal::isPerfettoCategoryEnabled(perfettoTeCategory); + if (CC_LIKELY(!(preferAtrace || preferPerfetto))) { + return; + } + + const int BUFFER_SIZE = 256; + va_list ap; + char buf[BUFFER_SIZE]; + + va_start(ap, fmt); + vsnprintf(buf, BUFFER_SIZE, fmt, ap); + va_end(ap); + + + if (preferAtrace) { + atrace_begin(category, buf); + } else if (preferPerfetto) { + internal::perfettoTraceBegin(*perfettoTeCategory, buf); } } -Result traceEnd(uint64_t category) { +void traceEnd(uint64_t category) { struct PerfettoTeCategory* perfettoTeCategory = internal::toPerfettoCategory(category); - if (perfettoTeCategory != nullptr) { - return internal::perfettoTraceEnd(*perfettoTeCategory); - } else { + + if (internal::shouldPreferAtrace(perfettoTeCategory, category)) { atrace_end(category); - return Result::SUCCESS; + } else if (internal::isPerfettoCategoryEnabled(perfettoTeCategory)) { + internal::perfettoTraceEnd(*perfettoTeCategory); } } -Result traceAsyncBegin(uint64_t category, const char* name, int32_t cookie) { +void traceAsyncBegin(uint64_t category, const char* name, int32_t cookie) { struct PerfettoTeCategory* perfettoTeCategory = internal::toPerfettoCategory(category); - if (perfettoTeCategory != nullptr) { - return internal::perfettoTraceAsyncBegin(*perfettoTeCategory, name, cookie); - } else { + + if (internal::shouldPreferAtrace(perfettoTeCategory, category)) { atrace_async_begin(category, name, cookie); - return Result::SUCCESS; + } else if (internal::isPerfettoCategoryEnabled(perfettoTeCategory)) { + internal::perfettoTraceAsyncBegin(*perfettoTeCategory, name, cookie); } } -Result traceAsyncEnd(uint64_t category, const char* name, int32_t cookie) { +void traceAsyncEnd(uint64_t category, const char* name, int32_t cookie) { struct PerfettoTeCategory* perfettoTeCategory = internal::toPerfettoCategory(category); - if (perfettoTeCategory != nullptr) { - return internal::perfettoTraceAsyncEnd(*perfettoTeCategory, name, cookie); - } else { + + if (internal::shouldPreferAtrace(perfettoTeCategory, category)) { atrace_async_end(category, name, cookie); - return Result::SUCCESS; + } else if (internal::isPerfettoCategoryEnabled(perfettoTeCategory)) { + internal::perfettoTraceAsyncEnd(*perfettoTeCategory, name, cookie); } } -Result traceAsyncBeginForTrack(uint64_t category, const char* name, +void traceAsyncBeginForTrack(uint64_t category, const char* name, const char* trackName, int32_t cookie) { struct PerfettoTeCategory* perfettoTeCategory = internal::toPerfettoCategory(category); - if (perfettoTeCategory != nullptr) { - return internal::perfettoTraceAsyncBeginForTrack(*perfettoTeCategory, name, trackName, cookie); - } else { + + if (internal::shouldPreferAtrace(perfettoTeCategory, category)) { atrace_async_for_track_begin(category, trackName, name, cookie); - return Result::SUCCESS; + } else if (internal::isPerfettoCategoryEnabled(perfettoTeCategory)) { + internal::perfettoTraceAsyncBeginForTrack(*perfettoTeCategory, name, trackName, cookie); } } -Result traceAsyncEndForTrack(uint64_t category, const char* trackName, +void traceAsyncEndForTrack(uint64_t category, const char* trackName, int32_t cookie) { struct PerfettoTeCategory* perfettoTeCategory = internal::toPerfettoCategory(category); - if (perfettoTeCategory != nullptr) { - return internal::perfettoTraceAsyncEndForTrack(*perfettoTeCategory, trackName, cookie); - } else { + + if (internal::shouldPreferAtrace(perfettoTeCategory, category)) { atrace_async_for_track_end(category, trackName, cookie); - return Result::SUCCESS; + } else if (internal::isPerfettoCategoryEnabled(perfettoTeCategory)) { + internal::perfettoTraceAsyncEndForTrack(*perfettoTeCategory, trackName, cookie); } } -Result traceInstant(uint64_t category, const char* name) { +void traceInstant(uint64_t category, const char* name) { struct PerfettoTeCategory* perfettoTeCategory = internal::toPerfettoCategory(category); - if (perfettoTeCategory != nullptr) { - return internal::perfettoTraceInstant(*perfettoTeCategory, name); - } else { + + if (internal::shouldPreferAtrace(perfettoTeCategory, category)) { atrace_instant(category, name); - return Result::SUCCESS; + } else if (internal::isPerfettoCategoryEnabled(perfettoTeCategory)) { + internal::perfettoTraceInstant(*perfettoTeCategory, name); + } +} + +void traceFormatInstant(uint64_t category, const char* fmt, ...) { + struct PerfettoTeCategory* perfettoTeCategory = + internal::toPerfettoCategory(category); + const bool preferAtrace = internal::shouldPreferAtrace(perfettoTeCategory, category); + const bool preferPerfetto = internal::isPerfettoCategoryEnabled(perfettoTeCategory); + if (CC_LIKELY(!(preferAtrace || preferPerfetto))) { + return; + } + + const int BUFFER_SIZE = 256; + va_list ap; + char buf[BUFFER_SIZE]; + + va_start(ap, fmt); + vsnprintf(buf, BUFFER_SIZE, fmt, ap); + va_end(ap); + + if (preferAtrace) { + atrace_instant(category, buf); + } else if (preferPerfetto) { + internal::perfettoTraceInstant(*perfettoTeCategory, buf); } } -Result traceInstantForTrack(uint64_t category, const char* trackName, +void traceInstantForTrack(uint64_t category, const char* trackName, const char* name) { struct PerfettoTeCategory* perfettoTeCategory = internal::toPerfettoCategory(category); - if (perfettoTeCategory != nullptr) { - return internal::perfettoTraceInstantForTrack(*perfettoTeCategory, trackName, name); - } else { + + if (internal::shouldPreferAtrace(perfettoTeCategory, category)) { atrace_instant_for_track(category, trackName, name); - return Result::SUCCESS; + } else if (internal::isPerfettoCategoryEnabled(perfettoTeCategory)) { + internal::perfettoTraceInstantForTrack(*perfettoTeCategory, trackName, name); } } -Result traceCounter(uint64_t category, const char* name, int64_t value) { +void traceCounter(uint64_t category, const char* name, int64_t value) { struct PerfettoTeCategory* perfettoTeCategory = internal::toPerfettoCategory(category); - if (perfettoTeCategory != nullptr) { - return internal::perfettoTraceCounter(*perfettoTeCategory, name, value); - } else { + + if (internal::shouldPreferAtrace(perfettoTeCategory, category)) { atrace_int64(category, name, value); - return Result::SUCCESS; + } else if (internal::isPerfettoCategoryEnabled(perfettoTeCategory)) { + internal::perfettoTraceCounter(*perfettoTeCategory, name, value); + } +} + +void traceCounter32(uint64_t category, const char* name, int32_t value) { + struct PerfettoTeCategory* perfettoTeCategory = internal::toPerfettoCategory(category); + if (internal::shouldPreferAtrace(perfettoTeCategory, category)) { + atrace_int(category, name, value); + } else if (internal::isPerfettoCategoryEnabled(perfettoTeCategory)) { + internal::perfettoTraceCounter(*perfettoTeCategory, name, + static_cast<int64_t>(value)); } } bool isTagEnabled(uint64_t category) { struct PerfettoTeCategory* perfettoTeCategory = internal::toPerfettoCategory(category); - if (perfettoTeCategory != nullptr) { - return true; - } else { - return (atrace_get_enabled_tags() & category) != 0; - } + return internal::isPerfettoCategoryEnabled(perfettoTeCategory) + || atrace_is_tag_enabled(category); } } // namespace tracing_perfetto diff --git a/libs/tracing_perfetto/tracing_perfetto_internal.cpp b/libs/tracing_perfetto/tracing_perfetto_internal.cpp index 758ace63ab..9a0042aee5 100644 --- a/libs/tracing_perfetto/tracing_perfetto_internal.cpp +++ b/libs/tracing_perfetto/tracing_perfetto_internal.cpp @@ -44,13 +44,13 @@ C(rro, "rro", "RRO category") \ C(thermal, "thermal", "Thermal category") -#include "tracing_perfetto_internal.h" - -#include <inttypes.h> - +#include <atomic> #include <mutex> #include <android_os.h> +#include <android-base/properties.h> +#include <cutils/trace.h> +#include <inttypes.h> #include "perfetto/public/compiler.h" #include "perfetto/public/producer.h" @@ -58,19 +58,42 @@ #include "perfetto/public/te_macros.h" #include "perfetto/public/track_event.h" #include "trace_categories.h" -#include "trace_result.h" +#include "tracing_perfetto_internal.h" + +#ifdef __BIONIC__ +#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_ +#include <sys/_system_properties.h> +#endif namespace tracing_perfetto { namespace internal { namespace { - PERFETTO_TE_CATEGORIES_DECLARE(FRAMEWORK_CATEGORIES); PERFETTO_TE_CATEGORIES_DEFINE(FRAMEWORK_CATEGORIES); -std::atomic_bool is_perfetto_registered = false; +static constexpr char kPreferFlagProperty[] = "debug.atrace.prefer_sdk"; +static std::atomic<const prop_info*> prefer_property_info = nullptr; +static std::atomic_uint32_t last_prefer_seq_num = 0; +static std::atomic_uint64_t prefer_flags = 0; + +static const prop_info* system_property_find(const char* name [[maybe_unused]]) { + #ifdef __BIONIC__ + return __system_property_find(name); + #endif + + return nullptr; +} + +static uint32_t system_property_serial(const prop_info* pi [[maybe_unused]]) { + #ifdef __BIONIC__ + return __system_property_serial(pi); + #endif + + return last_prefer_seq_num; +} struct PerfettoTeCategory* toCategory(uint64_t inCategory) { switch (inCategory) { @@ -137,8 +160,60 @@ struct PerfettoTeCategory* toCategory(uint64_t inCategory) { } // namespace -bool isPerfettoRegistered() { - return is_perfetto_registered; +bool isPerfettoCategoryEnabled(PerfettoTeCategory* category) { + return category != nullptr; +} + +/** + * Updates the cached |prefer_flags|. + * + * We cache the prefer_flags because reading it on every trace event is expensive. + * The cache is invalidated when a sys_prop sequence number changes. + */ +void updatePreferFlags() { + if (!prefer_property_info.load(std::memory_order_acquire)) { + auto* new_prefer_property_info = system_property_find(kPreferFlagProperty); + prefer_flags.store(android::base::GetIntProperty(kPreferFlagProperty, 0), + std::memory_order_relaxed); + + if (!new_prefer_property_info) { + // This should never happen. If it does, we fail gracefully and end up reading the property + // traced event. + return; + } + + last_prefer_seq_num = system_property_serial(new_prefer_property_info); + prefer_property_info.store(new_prefer_property_info, std::memory_order_release); + } + + uint32_t prefer_seq_num = system_property_serial(prefer_property_info); + if (prefer_seq_num != last_prefer_seq_num.load(std::memory_order_acquire)) { + prefer_flags.store(android::base::GetIntProperty(kPreferFlagProperty, 0), + std::memory_order_relaxed); + last_prefer_seq_num.store(prefer_seq_num, std::memory_order_release); + } +} + +bool shouldPreferAtrace(PerfettoTeCategory *perfettoCategory, uint64_t atraceCategory) { + // There are 3 cases: + // 1. Atrace is not enabled. + if (!atrace_is_tag_enabled(atraceCategory)) { + return false; + } + + // 2. Atrace is enabled but perfetto is not enabled. + if (!isPerfettoCategoryEnabled(perfettoCategory)) { + return true; + } + + // Update prefer_flags before checking it below + updatePreferFlags(); + + // 3. Atrace and perfetto are enabled. + // Even though this category is enabled for track events, the config mandates that we downgrade + // it to atrace if the same atrace category is currently enabled. This prevents missing the + // event from a concurrent session that needs the same category in atrace. + return (atraceCategory & prefer_flags.load(std::memory_order_relaxed)) == 0; } struct PerfettoTeCategory* toPerfettoCategory(uint64_t category) { @@ -148,7 +223,7 @@ struct PerfettoTeCategory* toPerfettoCategory(uint64_t category) { } bool enabled = PERFETTO_UNLIKELY(PERFETTO_ATOMIC_LOAD_EXPLICIT( - (*perfettoCategory).enabled, PERFETTO_MEMORY_ORDER_RELAXED)); + (*perfettoCategory).enabled, PERFETTO_MEMORY_ORDER_RELAXED)); return enabled ? perfettoCategory : nullptr; } @@ -164,70 +239,57 @@ void registerWithPerfetto(bool test) { PerfettoProducerInit(args); PerfettoTeInit(); PERFETTO_TE_REGISTER_CATEGORIES(FRAMEWORK_CATEGORIES); - is_perfetto_registered = true; }); } -Result perfettoTraceBegin(const struct PerfettoTeCategory& category, const char* name) { +void perfettoTraceBegin(const struct PerfettoTeCategory& category, const char* name) { PERFETTO_TE(category, PERFETTO_TE_SLICE_BEGIN(name)); - return Result::SUCCESS; } -Result perfettoTraceEnd(const struct PerfettoTeCategory& category) { +void perfettoTraceEnd(const struct PerfettoTeCategory& category) { PERFETTO_TE(category, PERFETTO_TE_SLICE_END()); - return Result::SUCCESS; } -Result perfettoTraceAsyncBeginForTrack(const struct PerfettoTeCategory& category, const char* name, +void perfettoTraceAsyncBeginForTrack(const struct PerfettoTeCategory& category, const char* name, const char* trackName, uint64_t cookie) { PERFETTO_TE( category, PERFETTO_TE_SLICE_BEGIN(name), PERFETTO_TE_NAMED_TRACK(trackName, cookie, PerfettoTeProcessTrackUuid())); - return Result::SUCCESS; } -Result perfettoTraceAsyncEndForTrack(const struct PerfettoTeCategory& category, +void perfettoTraceAsyncEndForTrack(const struct PerfettoTeCategory& category, const char* trackName, uint64_t cookie) { PERFETTO_TE( category, PERFETTO_TE_SLICE_END(), PERFETTO_TE_NAMED_TRACK(trackName, cookie, PerfettoTeProcessTrackUuid())); - return Result::SUCCESS; } -Result perfettoTraceAsyncBegin(const struct PerfettoTeCategory& category, const char* name, +void perfettoTraceAsyncBegin(const struct PerfettoTeCategory& category, const char* name, uint64_t cookie) { - return perfettoTraceAsyncBeginForTrack(category, name, name, cookie); + perfettoTraceAsyncBeginForTrack(category, name, name, cookie); } -Result perfettoTraceAsyncEnd(const struct PerfettoTeCategory& category, const char* name, +void perfettoTraceAsyncEnd(const struct PerfettoTeCategory& category, const char* name, uint64_t cookie) { - return perfettoTraceAsyncEndForTrack(category, name, cookie); + perfettoTraceAsyncEndForTrack(category, name, cookie); } -Result perfettoTraceInstant(const struct PerfettoTeCategory& category, const char* name) { +void perfettoTraceInstant(const struct PerfettoTeCategory& category, const char* name) { PERFETTO_TE(category, PERFETTO_TE_INSTANT(name)); - return Result::SUCCESS; } -Result perfettoTraceInstantForTrack(const struct PerfettoTeCategory& category, +void perfettoTraceInstantForTrack(const struct PerfettoTeCategory& category, const char* trackName, const char* name) { PERFETTO_TE( category, PERFETTO_TE_INSTANT(name), PERFETTO_TE_NAMED_TRACK(trackName, 1, PerfettoTeProcessTrackUuid())); - return Result::SUCCESS; } -Result perfettoTraceCounter(const struct PerfettoTeCategory& category, +void perfettoTraceCounter(const struct PerfettoTeCategory& category, [[maybe_unused]] const char* name, int64_t value) { PERFETTO_TE(category, PERFETTO_TE_COUNTER(), PERFETTO_TE_INT_COUNTER(value)); - return Result::SUCCESS; -} - -uint64_t getDefaultCategories() { - return TRACE_CATEGORIES; } - } // namespace internal } // namespace tracing_perfetto diff --git a/libs/tracing_perfetto/tracing_perfetto_internal.h b/libs/tracing_perfetto/tracing_perfetto_internal.h index 79e4b8f1b4..3e1ac2a112 100644 --- a/libs/tracing_perfetto/tracing_perfetto_internal.h +++ b/libs/tracing_perfetto/tracing_perfetto_internal.h @@ -19,7 +19,6 @@ #include <stdint.h> -#include "include/trace_result.h" #include "perfetto/public/te_category_macros.h" namespace tracing_perfetto { @@ -32,31 +31,33 @@ struct PerfettoTeCategory* toPerfettoCategory(uint64_t category); void registerWithPerfetto(bool test = false); -Result perfettoTraceBegin(const struct PerfettoTeCategory& category, const char* name); +void perfettoTraceBegin(const struct PerfettoTeCategory& category, const char* name); -Result perfettoTraceEnd(const struct PerfettoTeCategory& category); +void perfettoTraceEnd(const struct PerfettoTeCategory& category); -Result perfettoTraceAsyncBegin(const struct PerfettoTeCategory& category, const char* name, +void perfettoTraceAsyncBegin(const struct PerfettoTeCategory& category, const char* name, uint64_t cookie); -Result perfettoTraceAsyncEnd(const struct PerfettoTeCategory& category, const char* name, +void perfettoTraceAsyncEnd(const struct PerfettoTeCategory& category, const char* name, uint64_t cookie); -Result perfettoTraceAsyncBeginForTrack(const struct PerfettoTeCategory& category, const char* name, +void perfettoTraceAsyncBeginForTrack(const struct PerfettoTeCategory& category, const char* name, const char* trackName, uint64_t cookie); -Result perfettoTraceAsyncEndForTrack(const struct PerfettoTeCategory& category, +void perfettoTraceAsyncEndForTrack(const struct PerfettoTeCategory& category, const char* trackName, uint64_t cookie); -Result perfettoTraceInstant(const struct PerfettoTeCategory& category, const char* name); +void perfettoTraceInstant(const struct PerfettoTeCategory& category, const char* name); -Result perfettoTraceInstantForTrack(const struct PerfettoTeCategory& category, +void perfettoTraceInstantForTrack(const struct PerfettoTeCategory& category, const char* trackName, const char* name); -Result perfettoTraceCounter(const struct PerfettoTeCategory& category, const char* name, +void perfettoTraceCounter(const struct PerfettoTeCategory& category, const char* name, int64_t value); -uint64_t getDefaultCategories(); +bool isPerfettoCategoryEnabled(PerfettoTeCategory *perfettoTeCategory); + +bool shouldPreferAtrace(PerfettoTeCategory *perfettoTeCategory, uint64_t category); } // namespace internal diff --git a/libs/ui/DisplayIdentification.cpp b/libs/ui/DisplayIdentification.cpp index e5af7406ed..8b13d78840 100644 --- a/libs/ui/DisplayIdentification.cpp +++ b/libs/ui/DisplayIdentification.cpp @@ -26,6 +26,7 @@ #include <ftl/hash.h> #include <log/log.h> #include <ui/DisplayIdentification.h> +#include <ui/Size.h> namespace android { namespace { @@ -46,6 +47,10 @@ std::optional<uint8_t> getEdidDescriptorType(const byte_view& view) { return view[3]; } +bool isDetailedTimingDescriptor(const byte_view& view) { + return view[0] != 0 && view[1] != 0; +} + std::string_view parseEdidText(const byte_view& view) { std::string_view text(reinterpret_cast<const char*>(view.data()), view.size()); text = text.substr(0, text.find('\n')); @@ -219,6 +224,8 @@ std::optional<Edid> parseEdid(const DisplayIdentificationData& edid) { std::string_view displayName; std::string_view serialNumber; std::string_view asciiText; + ui::Size preferredDTDPixelSize; + ui::Size preferredDTDPhysicalSize; constexpr size_t kDescriptorCount = 4; constexpr size_t kDescriptorLength = 18; @@ -243,6 +250,35 @@ std::optional<Edid> parseEdid(const DisplayIdentificationData& edid) { serialNumber = parseEdidText(descriptor); break; } + } else if (isDetailedTimingDescriptor(view)) { + static constexpr size_t kHorizontalPhysicalLsbOffset = 12; + static constexpr size_t kHorizontalPhysicalMsbOffset = 14; + static constexpr size_t kVerticalPhysicalLsbOffset = 13; + static constexpr size_t kVerticalPhysicalMsbOffset = 14; + const uint32_t hSize = + static_cast<uint32_t>(view[kHorizontalPhysicalLsbOffset] | + ((view[kHorizontalPhysicalMsbOffset] >> 4) << 8)); + const uint32_t vSize = + static_cast<uint32_t>(view[kVerticalPhysicalLsbOffset] | + ((view[kVerticalPhysicalMsbOffset] & 0b1111) << 8)); + + static constexpr size_t kHorizontalPixelLsbOffset = 2; + static constexpr size_t kHorizontalPixelMsbOffset = 4; + static constexpr size_t kVerticalPixelLsbOffset = 5; + static constexpr size_t kVerticalPixelMsbOffset = 7; + + const uint8_t hLsb = view[kHorizontalPixelLsbOffset]; + const uint8_t hMsb = view[kHorizontalPixelMsbOffset]; + const int32_t hPixel = hLsb + ((hMsb & 0xF0) << 4); + + const uint8_t vLsb = view[kVerticalPixelLsbOffset]; + const uint8_t vMsb = view[kVerticalPixelMsbOffset]; + const int32_t vPixel = vLsb + ((vMsb & 0xF0) << 4); + + preferredDTDPixelSize.setWidth(hPixel); + preferredDTDPixelSize.setHeight(vPixel); + preferredDTDPhysicalSize.setWidth(hSize); + preferredDTDPhysicalSize.setHeight(vSize); } view = view.subspan(kDescriptorLength); @@ -297,14 +333,22 @@ std::optional<Edid> parseEdid(const DisplayIdentificationData& edid) { } } - return Edid{.manufacturerId = manufacturerId, - .productId = productId, - .pnpId = *pnpId, - .modelHash = modelHash, - .displayName = displayName, - .manufactureOrModelYear = manufactureOrModelYear, - .manufactureWeek = manufactureWeek, - .cea861Block = cea861Block}; + DetailedTimingDescriptor preferredDetailedTimingDescriptor{ + .pixelSizeCount = preferredDTDPixelSize, + .physicalSizeInMm = preferredDTDPhysicalSize, + }; + + return Edid{ + .manufacturerId = manufacturerId, + .productId = productId, + .pnpId = *pnpId, + .modelHash = modelHash, + .displayName = displayName, + .manufactureOrModelYear = manufactureOrModelYear, + .manufactureWeek = manufactureWeek, + .cea861Block = cea861Block, + .preferredDetailedTimingDescriptor = preferredDetailedTimingDescriptor, + }; } std::optional<PnpId> getPnpId(uint16_t manufacturerId) { @@ -336,9 +380,12 @@ std::optional<DisplayIdentificationInfo> parseDisplayIdentificationData( } const auto displayId = PhysicalDisplayId::fromEdid(port, edid->manufacturerId, edid->modelHash); - return DisplayIdentificationInfo{.id = displayId, - .name = std::string(edid->displayName), - .deviceProductInfo = buildDeviceProductInfo(*edid)}; + return DisplayIdentificationInfo{ + .id = displayId, + .name = std::string(edid->displayName), + .deviceProductInfo = buildDeviceProductInfo(*edid), + .preferredDetailedTimingDescriptor = edid->preferredDetailedTimingDescriptor, + }; } PhysicalDisplayId getVirtualDisplayId(uint32_t id) { diff --git a/libs/ui/Transform.cpp b/libs/ui/Transform.cpp index 42dd85e959..23249fa4a9 100644 --- a/libs/ui/Transform.cpp +++ b/libs/ui/Transform.cpp @@ -110,10 +110,12 @@ const vec3& Transform::operator [] (size_t i) const { return mMatrix[i]; } +// x translate float Transform::tx() const { return mMatrix[2][0]; } +// y translate float Transform::ty() const { return mMatrix[2][1]; } @@ -167,11 +169,15 @@ void Transform::set(float tx, float ty) { } } -void Transform::set(float a, float b, float c, float d) { +// x and y are the coordinates in the destination (i.e. the screen) +// s and t are the coordinates in the source (i.e. the texture) +// d means derivative +// dsdx means ds/dx derivative of s with respect to x, etc. +void Transform::set(float dsdx, float dtdy, float dtdx, float dsdy) { mat33& M(mMatrix); - M[0][0] = a; M[1][0] = b; - M[0][1] = c; M[1][1] = d; - M[0][2] = 0; M[1][2] = 0; + M[0][0] = dsdx; M[1][0] = dtdy; + M[0][1] = dtdx; M[1][1] = dsdy; + M[0][2] = 0; M[1][2] = 0; mType = UNKNOWN_TYPE; } diff --git a/libs/ui/include/ui/DisplayIdentification.h b/libs/ui/include/ui/DisplayIdentification.h index 8bc2017b55..648e024d86 100644 --- a/libs/ui/include/ui/DisplayIdentification.h +++ b/libs/ui/include/ui/DisplayIdentification.h @@ -25,6 +25,7 @@ #include <ui/DeviceProductInfo.h> #include <ui/DisplayId.h> +#include <ui/Size.h> #define LEGACY_DISPLAY_TYPE_PRIMARY 0 #define LEGACY_DISPLAY_TYPE_EXTERNAL 1 @@ -33,10 +34,16 @@ namespace android { using DisplayIdentificationData = std::vector<uint8_t>; +struct DetailedTimingDescriptor { + ui::Size pixelSizeCount; + ui::Size physicalSizeInMm; +}; + struct DisplayIdentificationInfo { PhysicalDisplayId id; std::string name; std::optional<DeviceProductInfo> deviceProductInfo; + std::optional<DetailedTimingDescriptor> preferredDetailedTimingDescriptor; }; struct ExtensionBlock { @@ -68,6 +75,7 @@ struct Edid { uint8_t manufactureOrModelYear; uint8_t manufactureWeek; std::optional<Cea861ExtensionBlock> cea861Block; + std::optional<DetailedTimingDescriptor> preferredDetailedTimingDescriptor; }; bool isEdid(const DisplayIdentificationData&); diff --git a/libs/ui/include/ui/EdgeExtensionEffect.h b/libs/ui/include/ui/EdgeExtensionEffect.h new file mode 100644 index 0000000000..02126b12be --- /dev/null +++ b/libs/ui/include/ui/EdgeExtensionEffect.h @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +/** + * Stores the edge that will be extended + */ +namespace android { + +enum CanonicalDirections { NONE = 0, LEFT = 1, RIGHT = 2, TOP = 4, BOTTOM = 8 }; + +inline std::string to_string(CanonicalDirections direction) { + switch (direction) { + case LEFT: + return "LEFT"; + case RIGHT: + return "RIGHT"; + case TOP: + return "TOP"; + case BOTTOM: + return "BOTTOM"; + case NONE: + return "NONE"; + } +} + +struct EdgeExtensionEffect { + EdgeExtensionEffect(bool left, bool right, bool top, bool bottom) { + extensionEdges = NONE; + if (left) { + extensionEdges |= LEFT; + } + if (right) { + extensionEdges |= RIGHT; + } + if (top) { + extensionEdges |= TOP; + } + if (bottom) { + extensionEdges |= BOTTOM; + } + } + + EdgeExtensionEffect() { EdgeExtensionEffect(false, false, false, false); } + + bool extendsEdge(CanonicalDirections edge) const { return extensionEdges & edge; } + + bool hasEffect() const { return extensionEdges != NONE; }; + + void reset() { extensionEdges = NONE; } + + bool operator==(const EdgeExtensionEffect& other) const { + return extensionEdges == other.extensionEdges; + } + + bool operator!=(const EdgeExtensionEffect& other) const { return !(*this == other); } + +private: + int extensionEdges; +}; + +inline std::string to_string(const EdgeExtensionEffect& effect) { + std::string s = "EdgeExtensionEffect={edges=["; + if (effect.hasEffect()) { + for (CanonicalDirections edge : {LEFT, RIGHT, TOP, BOTTOM}) { + if (effect.extendsEdge(edge)) { + s += to_string(edge) + ", "; + } + } + } else { + s += to_string(NONE); + } + s += "]}"; + return s; +} + +inline std::ostream& operator<<(std::ostream& os, const EdgeExtensionEffect effect) { + os << to_string(effect); + return os; +} +} // namespace android diff --git a/libs/ui/include/ui/Fence.h b/libs/ui/include/ui/Fence.h index 9aae145c04..a75ba37d2c 100644 --- a/libs/ui/include/ui/Fence.h +++ b/libs/ui/include/ui/Fence.h @@ -156,6 +156,6 @@ private: base::unique_fd mFenceFd; }; -}; // namespace android +} // namespace android #endif // ANDROID_FENCE_H diff --git a/libs/ui/include/ui/GraphicBuffer.h b/libs/ui/include/ui/GraphicBuffer.h index 652d8ba709..936bf8f862 100644 --- a/libs/ui/include/ui/GraphicBuffer.h +++ b/libs/ui/include/ui/GraphicBuffer.h @@ -297,6 +297,6 @@ private: mDeathCallbacks; }; -}; // namespace android +} // namespace android #endif // ANDROID_GRAPHIC_BUFFER_H diff --git a/libs/ui/include/ui/GraphicBufferAllocator.h b/libs/ui/include/ui/GraphicBufferAllocator.h index bbb2d77058..97ed05af6d 100644 --- a/libs/ui/include/ui/GraphicBufferAllocator.h +++ b/libs/ui/include/ui/GraphicBufferAllocator.h @@ -137,6 +137,6 @@ protected: }; // --------------------------------------------------------------------------- -}; // namespace android +} // namespace android #endif // ANDROID_BUFFER_ALLOCATOR_H diff --git a/libs/ui/include/ui/GraphicBufferMapper.h b/libs/ui/include/ui/GraphicBufferMapper.h index 9da1447aed..91aabe9f12 100644 --- a/libs/ui/include/ui/GraphicBufferMapper.h +++ b/libs/ui/include/ui/GraphicBufferMapper.h @@ -188,7 +188,7 @@ private: // --------------------------------------------------------------------------- -}; // namespace android +} // namespace android #endif // ANDROID_UI_BUFFER_MAPPER_H diff --git a/libs/ui/include/ui/PixelFormat.h b/libs/ui/include/ui/PixelFormat.h index cf5c2e8c12..1f20787bb0 100644 --- a/libs/ui/include/ui/PixelFormat.h +++ b/libs/ui/include/ui/PixelFormat.h @@ -72,6 +72,6 @@ typedef int32_t PixelFormat; uint32_t bytesPerPixel(PixelFormat format); -}; // namespace android +} // namespace android #endif // UI_PIXELFORMAT_H diff --git a/libs/ui/include/ui/Point.h b/libs/ui/include/ui/Point.h index d050ede02d..97a54beca7 100644 --- a/libs/ui/include/ui/Point.h +++ b/libs/ui/include/ui/Point.h @@ -83,6 +83,6 @@ public: ANDROID_BASIC_TYPES_TRAITS(Point) -}; // namespace android +} // namespace android #endif // ANDROID_UI_POINT diff --git a/libs/ui/include/ui/Rect.h b/libs/ui/include/ui/Rect.h index 9e24a077ff..2eb9330cc9 100644 --- a/libs/ui/include/ui/Rect.h +++ b/libs/ui/include/ui/Rect.h @@ -233,7 +233,7 @@ void PrintTo(const Rect& rect, ::std::ostream* os); ANDROID_BASIC_TYPES_TRAITS(Rect) -}; // namespace android +} // namespace android namespace std { template <> diff --git a/libs/ui/include/ui/Region.h b/libs/ui/include/ui/Region.h index 927c334c85..d1b38f3bd3 100644 --- a/libs/ui/include/ui/Region.h +++ b/libs/ui/include/ui/Region.h @@ -233,7 +233,7 @@ static inline void PrintTo(const Region& region, ::std::ostream* os) { } // --------------------------------------------------------------------------- -}; // namespace android +} // namespace android namespace std { template <> diff --git a/libs/ui/tests/DisplayIdentification_test.cpp b/libs/ui/tests/DisplayIdentification_test.cpp index 721b46688e..76e3f66e1b 100644 --- a/libs/ui/tests/DisplayIdentification_test.cpp +++ b/libs/ui/tests/DisplayIdentification_test.cpp @@ -194,6 +194,10 @@ TEST(DisplayIdentificationTest, parseEdid) { EXPECT_EQ(21, edid->manufactureOrModelYear); EXPECT_EQ(0, edid->manufactureWeek); EXPECT_FALSE(edid->cea861Block); + EXPECT_EQ(1280, edid->preferredDetailedTimingDescriptor->pixelSizeCount.width); + EXPECT_EQ(800, edid->preferredDetailedTimingDescriptor->pixelSizeCount.height); + EXPECT_EQ(261, edid->preferredDetailedTimingDescriptor->physicalSizeInMm.width); + EXPECT_EQ(163, edid->preferredDetailedTimingDescriptor->physicalSizeInMm.height); edid = parseEdid(getExternalEdid()); ASSERT_TRUE(edid); @@ -206,6 +210,10 @@ TEST(DisplayIdentificationTest, parseEdid) { EXPECT_EQ(22, edid->manufactureOrModelYear); EXPECT_EQ(2, edid->manufactureWeek); EXPECT_FALSE(edid->cea861Block); + EXPECT_EQ(1280, edid->preferredDetailedTimingDescriptor->pixelSizeCount.width); + EXPECT_EQ(800, edid->preferredDetailedTimingDescriptor->pixelSizeCount.height); + EXPECT_EQ(641, edid->preferredDetailedTimingDescriptor->physicalSizeInMm.width); + EXPECT_EQ(400, edid->preferredDetailedTimingDescriptor->physicalSizeInMm.height); edid = parseEdid(getExternalEedid()); ASSERT_TRUE(edid); @@ -224,6 +232,10 @@ TEST(DisplayIdentificationTest, parseEdid) { EXPECT_EQ(0, physicalAddress.b); EXPECT_EQ(0, physicalAddress.c); EXPECT_EQ(0, physicalAddress.d); + EXPECT_EQ(1366, edid->preferredDetailedTimingDescriptor->pixelSizeCount.width); + EXPECT_EQ(768, edid->preferredDetailedTimingDescriptor->pixelSizeCount.height); + EXPECT_EQ(160, edid->preferredDetailedTimingDescriptor->physicalSizeInMm.width); + EXPECT_EQ(90, edid->preferredDetailedTimingDescriptor->physicalSizeInMm.height); edid = parseEdid(getPanasonicTvEdid()); ASSERT_TRUE(edid); @@ -242,6 +254,10 @@ TEST(DisplayIdentificationTest, parseEdid) { EXPECT_EQ(0, physicalAddress.b); EXPECT_EQ(0, physicalAddress.c); EXPECT_EQ(0, physicalAddress.d); + EXPECT_EQ(1920, edid->preferredDetailedTimingDescriptor->pixelSizeCount.width); + EXPECT_EQ(1080, edid->preferredDetailedTimingDescriptor->pixelSizeCount.height); + EXPECT_EQ(698, edid->preferredDetailedTimingDescriptor->physicalSizeInMm.width); + EXPECT_EQ(392, edid->preferredDetailedTimingDescriptor->physicalSizeInMm.height); edid = parseEdid(getHisenseTvEdid()); ASSERT_TRUE(edid); @@ -260,6 +276,10 @@ TEST(DisplayIdentificationTest, parseEdid) { EXPECT_EQ(2, physicalAddress.b); EXPECT_EQ(3, physicalAddress.c); EXPECT_EQ(4, physicalAddress.d); + EXPECT_EQ(1920, edid->preferredDetailedTimingDescriptor->pixelSizeCount.width); + EXPECT_EQ(1080, edid->preferredDetailedTimingDescriptor->pixelSizeCount.height); + EXPECT_EQ(575, edid->preferredDetailedTimingDescriptor->physicalSizeInMm.width); + EXPECT_EQ(323, edid->preferredDetailedTimingDescriptor->physicalSizeInMm.height); edid = parseEdid(getCtlDisplayEdid()); ASSERT_TRUE(edid); @@ -273,6 +293,10 @@ TEST(DisplayIdentificationTest, parseEdid) { EXPECT_EQ(0xff, edid->manufactureWeek); ASSERT_TRUE(edid->cea861Block); EXPECT_FALSE(edid->cea861Block->hdmiVendorDataBlock); + EXPECT_EQ(1360, edid->preferredDetailedTimingDescriptor->pixelSizeCount.width); + EXPECT_EQ(768, edid->preferredDetailedTimingDescriptor->pixelSizeCount.height); + EXPECT_EQ(521, edid->preferredDetailedTimingDescriptor->physicalSizeInMm.width); + EXPECT_EQ(293, edid->preferredDetailedTimingDescriptor->physicalSizeInMm.height); } TEST(DisplayIdentificationTest, parseInvalidEdid) { diff --git a/libs/vibrator/ExternalVibration.cpp b/libs/vibrator/ExternalVibration.cpp index c97e496083..cae2de273e 100644 --- a/libs/vibrator/ExternalVibration.cpp +++ b/libs/vibrator/ExternalVibration.cpp @@ -93,8 +93,8 @@ os::HapticScale ExternalVibration::externalVibrationScaleToHapticScale( externalVibrationScale.scaleLevel); } - return {/*level=*/scaleLevel, /*adaptiveScaleFactor=*/ - externalVibrationScale.adaptiveHapticsScale}; + return os::HapticScale(scaleLevel, externalVibrationScale.scaleFactor, + externalVibrationScale.adaptiveHapticsScale); } } // namespace os diff --git a/libs/vibrator/ExternalVibrationUtils.cpp b/libs/vibrator/ExternalVibrationUtils.cpp index 706f3d7a56..ca13afcbaf 100644 --- a/libs/vibrator/ExternalVibrationUtils.cpp +++ b/libs/vibrator/ExternalVibrationUtils.cpp @@ -13,6 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#define LOG_TAG "ExternalVibrationUtils" + #include <cstring> #include <android_os_vibrator.h> @@ -20,6 +22,7 @@ #include <algorithm> #include <math.h> +#include <log/log.h> #include <vibrator/ExternalVibrationUtils.h> namespace android::os { @@ -29,6 +32,7 @@ static constexpr float HAPTIC_SCALE_VERY_LOW_RATIO = 2.0f / 3.0f; static constexpr float HAPTIC_SCALE_LOW_RATIO = 3.0f / 4.0f; static constexpr float HAPTIC_MAX_AMPLITUDE_FLOAT = 1.0f; static constexpr float SCALE_GAMMA = 0.65f; // Same as VibrationEffect.SCALE_GAMMA +static constexpr float SCALE_LEVEL_GAIN = 1.4f; // Same as VibrationConfig.DEFAULT_SCALE_LEVEL_GAIN float getOldHapticScaleGamma(HapticLevel level) { switch (level) { @@ -60,9 +64,34 @@ float getOldHapticMaxAmplitudeRatio(HapticLevel level) { } } -/* Same as VibrationScaler.SCALE_LEVEL_* */ -float getHapticScaleFactor(HapticLevel level) { - switch (level) { +/* Same as VibrationScaler.getScaleFactor */ +float getHapticScaleFactor(HapticScale scale) { + if (android_os_vibrator_haptics_scale_v2_enabled()) { + if (scale.getScaleFactor() >= 0) { + // ExternalVibratorService provided the scale factor, use it. + return scale.getScaleFactor(); + } + + HapticLevel level = scale.getLevel(); + switch (level) { + case HapticLevel::MUTE: + return 0.0f; + case HapticLevel::NONE: + return 1.0f; + default: + float scaleFactor = powf(SCALE_LEVEL_GAIN, static_cast<int32_t>(level)); + if (scaleFactor <= 0) { + ALOGE("Invalid scale factor %.2f for level %d, using fallback to 1.0", + scaleFactor, static_cast<int32_t>(level)); + scaleFactor = 1.0f; + } + return scaleFactor; + } + } + // Same as VibrationScaler.SCALE_FACTOR_* + switch (scale.getLevel()) { + case HapticLevel::MUTE: + return 0.0f; case HapticLevel::VERY_LOW: return 0.6f; case HapticLevel::LOW: @@ -83,6 +112,14 @@ float applyOldHapticScale(float value, float gamma, float maxAmplitudeRatio) { } float applyNewHapticScale(float value, float scaleFactor) { + if (android_os_vibrator_haptics_scale_v2_enabled()) { + if (scaleFactor <= 1 || value == 0) { + return value * scaleFactor; + } else { + // Using S * x / (1 + (S - 1) * x^2) as the scale up function to converge to 1.0. + return (value * scaleFactor) / (1 + (scaleFactor - 1) * value * value); + } + } float scale = powf(scaleFactor, 1.0f / SCALE_GAMMA); if (scaleFactor <= 1) { // Scale down is simply a gamma corrected application of scaleFactor to the intensity. @@ -115,21 +152,22 @@ void applyHapticScale(float* buffer, size_t length, HapticScale scale) { return; } HapticLevel hapticLevel = scale.getLevel(); - float scaleFactor = getHapticScaleFactor(hapticLevel); + float scaleFactor = getHapticScaleFactor(scale); float adaptiveScaleFactor = scale.getAdaptiveScaleFactor(); float oldGamma = getOldHapticScaleGamma(hapticLevel); float oldMaxAmplitudeRatio = getOldHapticMaxAmplitudeRatio(hapticLevel); for (size_t i = 0; i < length; i++) { if (hapticLevel != HapticLevel::NONE) { - if (android_os_vibrator_fix_audio_coupled_haptics_scaling()) { + if (android_os_vibrator_fix_audio_coupled_haptics_scaling() || + android_os_vibrator_haptics_scale_v2_enabled()) { buffer[i] = applyNewHapticScale(buffer[i], scaleFactor); } else { buffer[i] = applyOldHapticScale(buffer[i], oldGamma, oldMaxAmplitudeRatio); } } - if (adaptiveScaleFactor != 1.0f) { + if (adaptiveScaleFactor >= 0 && adaptiveScaleFactor != 1.0f) { buffer[i] *= adaptiveScaleFactor; } } diff --git a/libs/vibrator/include/vibrator/ExternalVibrationUtils.h b/libs/vibrator/include/vibrator/ExternalVibrationUtils.h index d9a2b81ddf..f0760fda1b 100644 --- a/libs/vibrator/include/vibrator/ExternalVibrationUtils.h +++ b/libs/vibrator/include/vibrator/ExternalVibrationUtils.h @@ -17,9 +17,13 @@ #ifndef ANDROID_EXTERNAL_VIBRATION_UTILS_H #define ANDROID_EXTERNAL_VIBRATION_UTILS_H +#include <cstring> +#include <sstream> +#include <string> + namespace android::os { -enum class HapticLevel { +enum class HapticLevel : int32_t { MUTE = -100, VERY_LOW = -2, LOW = -1, @@ -31,32 +35,44 @@ enum class HapticLevel { class HapticScale { private: HapticLevel mLevel = HapticLevel::NONE; +float mScaleFactor = -1.0f; // undefined, use haptic level to define scale factor float mAdaptiveScaleFactor = 1.0f; public: -constexpr HapticScale(HapticLevel level, float adaptiveScaleFactor) - : mLevel(level), mAdaptiveScaleFactor(adaptiveScaleFactor) {} -constexpr HapticScale(HapticLevel level) : mLevel(level) {} -constexpr HapticScale() {} + explicit HapticScale(HapticLevel level, float scaleFactor, float adaptiveScaleFactor) + : mLevel(level), mScaleFactor(scaleFactor), mAdaptiveScaleFactor(adaptiveScaleFactor) {} + explicit HapticScale(HapticLevel level) : mLevel(level) {} + constexpr HapticScale() {} -HapticLevel getLevel() const { return mLevel; } -float getAdaptiveScaleFactor() const { return mAdaptiveScaleFactor; } + HapticLevel getLevel() const { return mLevel; } + float getScaleFactor() const { return mScaleFactor; } + float getAdaptiveScaleFactor() const { return mAdaptiveScaleFactor; } -bool operator==(const HapticScale& other) const { - return mLevel == other.mLevel && mAdaptiveScaleFactor == other.mAdaptiveScaleFactor; -} + bool operator==(const HapticScale& other) const { + return mLevel == other.mLevel && mScaleFactor == other.mScaleFactor && + mAdaptiveScaleFactor == other.mAdaptiveScaleFactor; + } bool isScaleNone() const { - return mLevel == HapticLevel::NONE && mAdaptiveScaleFactor == 1.0f; + return (mLevel == HapticLevel::NONE || mScaleFactor == 1.0f) && mAdaptiveScaleFactor == 1.0f; } bool isScaleMute() const { - return mLevel == HapticLevel::MUTE; + return mLevel == HapticLevel::MUTE || mScaleFactor == 0 || mAdaptiveScaleFactor == 0; } -static HapticScale mute() { - return {/*level=*/os::HapticLevel::MUTE}; +std::string toString() const { + std::ostringstream os; + os << "HapticScale { level: " << static_cast<int>(mLevel); + os << ", scaleFactor: " << mScaleFactor; + os << ", adaptiveScaleFactor: " << mAdaptiveScaleFactor; + os << "}"; + return os.str(); } + +static HapticScale mute() { return os::HapticScale(os::HapticLevel::MUTE); } + +static HapticScale none() { return os::HapticScale(os::HapticLevel::NONE); } }; bool isValidHapticScale(HapticScale scale); diff --git a/libs/vibrator/tests/ExternalVibrationTest.cpp b/libs/vibrator/tests/ExternalVibrationTest.cpp index 3141380219..4133836785 100644 --- a/libs/vibrator/tests/ExternalVibrationTest.cpp +++ b/libs/vibrator/tests/ExternalVibrationTest.cpp @@ -57,11 +57,14 @@ TEST_F(ExternalVibrationTest, TestReadAndWriteToParcel) { originalAttrs.usage = AUDIO_USAGE_ASSISTANCE_SONIFICATION; originalAttrs.source = AUDIO_SOURCE_VOICE_COMMUNICATION; originalAttrs.flags = AUDIO_FLAG_BYPASS_MUTE; + sp<TestVibrationController> vibrationController = new TestVibrationController(); ASSERT_NE(vibrationController, nullptr); + sp<os::ExternalVibration> original = new os::ExternalVibration(uid, pkg, originalAttrs, vibrationController); ASSERT_NE(original, nullptr); + EXPECT_EQ(original->getUid(), uid); EXPECT_EQ(original->getPackage(), pkg); EXPECT_EQ(original->getAudioAttributes().content_type, originalAttrs.content_type); @@ -69,18 +72,22 @@ TEST_F(ExternalVibrationTest, TestReadAndWriteToParcel) { EXPECT_EQ(original->getAudioAttributes().source, originalAttrs.source); EXPECT_EQ(original->getAudioAttributes().flags, originalAttrs.flags); EXPECT_EQ(original->getController(), vibrationController); + audio_attributes_t defaultAttrs; defaultAttrs.content_type = AUDIO_CONTENT_TYPE_UNKNOWN; defaultAttrs.usage = AUDIO_USAGE_UNKNOWN; defaultAttrs.source = AUDIO_SOURCE_DEFAULT; defaultAttrs.flags = AUDIO_FLAG_NONE; + sp<os::ExternalVibration> parceled = new os::ExternalVibration(0, std::string(""), defaultAttrs, nullptr); ASSERT_NE(parceled, nullptr); + Parcel parcel; original->writeToParcel(&parcel); parcel.setDataPosition(0); parceled->readFromParcel(&parcel); + EXPECT_EQ(parceled->getUid(), uid); EXPECT_EQ(parceled->getPackage(), pkg); EXPECT_EQ(parceled->getAudioAttributes().content_type, originalAttrs.content_type); @@ -93,12 +100,17 @@ TEST_F(ExternalVibrationTest, TestReadAndWriteToParcel) { TEST_F(ExternalVibrationTest, TestExternalVibrationScaleToHapticScale) { os::ExternalVibrationScale externalVibrationScale; externalVibrationScale.scaleLevel = ScaleLevel::SCALE_HIGH; + externalVibrationScale.scaleFactor = 0.5f; externalVibrationScale.adaptiveHapticsScale = 0.8f; + os::HapticScale hapticScale = os::ExternalVibration::externalVibrationScaleToHapticScale(externalVibrationScale); - // Check scale factor is forwarded. + + // Check scale factors are forwarded. EXPECT_EQ(hapticScale.getLevel(), HapticLevel::HIGH); + EXPECT_EQ(hapticScale.getScaleFactor(), 0.5f); EXPECT_EQ(hapticScale.getAdaptiveScaleFactor(), 0.8f); + // Check conversion for all levels. EXPECT_EQ(toHapticLevel(ScaleLevel::SCALE_MUTE), HapticLevel::MUTE); EXPECT_EQ(toHapticLevel(ScaleLevel::SCALE_VERY_LOW), HapticLevel::VERY_LOW); diff --git a/libs/vibrator/tests/ExternalVibrationUtilsTest.cpp b/libs/vibrator/tests/ExternalVibrationUtilsTest.cpp index 3d8dd9cb5b..9369f80da3 100644 --- a/libs/vibrator/tests/ExternalVibrationUtilsTest.cpp +++ b/libs/vibrator/tests/ExternalVibrationUtilsTest.cpp @@ -41,7 +41,7 @@ public: protected: void scaleBuffer(HapticLevel hapticLevel) { - scaleBuffer(HapticScale(hapticLevel), 0 /* limit */); + scaleBuffer(HapticScale(hapticLevel)); } void scaleBuffer(HapticLevel hapticLevel, float adaptiveScaleFactor) { @@ -49,7 +49,11 @@ protected: } void scaleBuffer(HapticLevel hapticLevel, float adaptiveScaleFactor, float limit) { - scaleBuffer(HapticScale(hapticLevel, adaptiveScaleFactor), limit); + scaleBuffer(HapticScale(hapticLevel, -1 /* scaleFactor */, adaptiveScaleFactor), limit); + } + + void scaleBuffer(HapticScale hapticScale) { + scaleBuffer(hapticScale, 0 /* limit */); } void scaleBuffer(HapticScale hapticScale, float limit) { @@ -60,11 +64,9 @@ protected: float mBuffer[TEST_BUFFER_LENGTH]; }; -TEST_F_WITH_FLAGS( - ExternalVibrationUtilsTest, - TestLegacyScaleMute, - REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling)) -) { +TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestLegacyScaleMute, + REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling), + ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) { float expected[TEST_BUFFER_LENGTH]; std::fill(std::begin(expected), std::end(expected), 0); @@ -72,11 +74,9 @@ TEST_F_WITH_FLAGS( EXPECT_FLOATS_NEARLY_EQ(expected, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); } -TEST_F_WITH_FLAGS( - ExternalVibrationUtilsTest, - TestFixedScaleMute, - REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling)) -) { +TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestFixedScaleMute, + REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling)), + REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) { float expected[TEST_BUFFER_LENGTH]; std::fill(std::begin(expected), std::end(expected), 0); @@ -85,10 +85,19 @@ TEST_F_WITH_FLAGS( } TEST_F_WITH_FLAGS( - ExternalVibrationUtilsTest, - TestLegacyScaleNone, - REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling)) -) { + ExternalVibrationUtilsTest, TestScaleV2Mute, + // Value of fix_audio_coupled_haptics_scaling is not important, should work with either + REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) { + float expected[TEST_BUFFER_LENGTH]; + std::fill(std::begin(expected), std::end(expected), 0); + + scaleBuffer(HapticLevel::MUTE); + EXPECT_FLOATS_NEARLY_EQ(expected, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); +} + +TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestLegacyScaleNone, + REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling), + ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) { float expected[TEST_BUFFER_LENGTH]; std::copy(std::begin(TEST_BUFFER), std::end(TEST_BUFFER), std::begin(expected)); @@ -96,11 +105,9 @@ TEST_F_WITH_FLAGS( EXPECT_FLOATS_NEARLY_EQ(expected, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); } -TEST_F_WITH_FLAGS( - ExternalVibrationUtilsTest, - TestFixedScaleNone, - REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling)) -) { +TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestFixedScaleNone, + REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling)), + REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) { float expected[TEST_BUFFER_LENGTH]; std::copy(std::begin(TEST_BUFFER), std::end(TEST_BUFFER), std::begin(expected)); @@ -109,10 +116,19 @@ TEST_F_WITH_FLAGS( } TEST_F_WITH_FLAGS( - ExternalVibrationUtilsTest, - TestLegacyScaleToHapticLevel, - REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling)) -) { + ExternalVibrationUtilsTest, TestScaleV2None, + // Value of fix_audio_coupled_haptics_scaling is not important, should work with either + REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) { + float expected[TEST_BUFFER_LENGTH]; + std::copy(std::begin(TEST_BUFFER), std::end(TEST_BUFFER), std::begin(expected)); + + scaleBuffer(HapticLevel::NONE); + EXPECT_FLOATS_NEARLY_EQ(expected, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); +} + +TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestLegacyScaleToHapticLevel, + REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling), + ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) { float expectedVeryHigh[TEST_BUFFER_LENGTH] = { 1, -1, 0.84f, -0.66f }; scaleBuffer(HapticLevel::VERY_HIGH); EXPECT_FLOATS_NEARLY_EQ(expectedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); @@ -130,11 +146,9 @@ TEST_F_WITH_FLAGS( EXPECT_FLOATS_NEARLY_EQ(expectedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); } -TEST_F_WITH_FLAGS( - ExternalVibrationUtilsTest, - TestFixedScaleToHapticLevel, - REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling)) -) { +TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestFixedScaleToHapticLevel, + REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling)), + REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) { float expectedVeryHigh[TEST_BUFFER_LENGTH] = { 1, -1, 0.79f, -0.39f }; scaleBuffer(HapticLevel::VERY_HIGH); EXPECT_FLOATS_NEARLY_EQ(expectedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); @@ -153,10 +167,76 @@ TEST_F_WITH_FLAGS( } TEST_F_WITH_FLAGS( - ExternalVibrationUtilsTest, - TestAdaptiveScaleFactorAppliedAfterLegacyScale, - REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling)) -) { + ExternalVibrationUtilsTest, TestScaleV2ToHapticLevel, + // Value of fix_audio_coupled_haptics_scaling is not important, should work with either + REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) { + float expectedVeryHigh[TEST_BUFFER_LENGTH] = { 1, -1, 0.8f, -0.38f }; + scaleBuffer(HapticLevel::VERY_HIGH); + EXPECT_FLOATS_NEARLY_EQ(expectedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); + + float expectedHigh[TEST_BUFFER_LENGTH] = { 1, -1, 0.63f, -0.27f }; + scaleBuffer(HapticLevel::HIGH); + EXPECT_FLOATS_NEARLY_EQ(expectedHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); + + float expectedLow[TEST_BUFFER_LENGTH] = { 0.71f, -0.71f, 0.35f, -0.14f }; + scaleBuffer(HapticLevel::LOW); + EXPECT_FLOATS_NEARLY_EQ(expectedLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); + + float expectedVeryLow[TEST_BUFFER_LENGTH] = { 0.51f, -0.51f, 0.25f, -0.1f }; + scaleBuffer(HapticLevel::VERY_LOW); + EXPECT_FLOATS_NEARLY_EQ(expectedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); +} + +TEST_F_WITH_FLAGS( + ExternalVibrationUtilsTest, TestScaleV2ToScaleFactorUndefinedUsesHapticLevel, + // Value of fix_audio_coupled_haptics_scaling is not important, should work with either + REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) { + constexpr float adaptiveScaleNone = 1.0f; + float expectedVeryHigh[TEST_BUFFER_LENGTH] = {1, -1, 0.8f, -0.38f}; + scaleBuffer(HapticScale(HapticLevel::VERY_HIGH, -1.0f /* scaleFactor */, adaptiveScaleNone)); + EXPECT_FLOATS_NEARLY_EQ(expectedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); +} + +TEST_F_WITH_FLAGS( + ExternalVibrationUtilsTest, TestScaleV2ToScaleFactorIgnoresLevel, + // Value of fix_audio_coupled_haptics_scaling is not important, should work with either + REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) { + constexpr float adaptiveScaleNone = 1.0f; + + float expectedVeryHigh[TEST_BUFFER_LENGTH] = { 1, -1, 1, -0.55f }; + scaleBuffer(HapticScale(HapticLevel::LOW, 3.0f /* scaleFactor */, adaptiveScaleNone)); + EXPECT_FLOATS_NEARLY_EQ(expectedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); + + float expectedHigh[TEST_BUFFER_LENGTH] = { 1, -1, 0.66f, -0.29f }; + scaleBuffer(HapticScale(HapticLevel::LOW, 1.5f /* scaleFactor */, adaptiveScaleNone)); + EXPECT_FLOATS_NEARLY_EQ(expectedHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); + + float expectedLow[TEST_BUFFER_LENGTH] = { 0.8f, -0.8f, 0.4f, -0.16f }; + scaleBuffer(HapticScale(HapticLevel::HIGH, 0.8f /* scaleFactor */, adaptiveScaleNone)); + EXPECT_FLOATS_NEARLY_EQ(expectedLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); + + float expectedVeryLow[TEST_BUFFER_LENGTH] = { 0.4f, -0.4f, 0.2f, -0.08f }; + scaleBuffer(HapticScale(HapticLevel::HIGH, 0.4f /* scaleFactor */, adaptiveScaleNone)); + EXPECT_FLOATS_NEARLY_EQ(expectedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); +} + +TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestAdaptiveScaleFactorUndefinedIsIgnoredLegacyScale, + REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling), + ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) { + float expectedVeryHigh[TEST_BUFFER_LENGTH] = {1, -1, 0.79f, -0.39f}; + scaleBuffer(HapticLevel::VERY_HIGH, -1.0f /* adaptiveScaleFactor */); + EXPECT_FLOATS_NEARLY_EQ(expectedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); +} + +TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestAdaptiveScaleFactorAppliedAfterLegacyScale, + REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling), + ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) { + // Adaptive scale mutes vibration + float expectedMuted[TEST_BUFFER_LENGTH]; + std::fill(std::begin(expectedMuted), std::end(expectedMuted), 0); + scaleBuffer(HapticLevel::VERY_HIGH, 0.0f /* adaptiveScaleFactor */); + EXPECT_FLOATS_NEARLY_EQ(expectedMuted, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); + // Haptic level scale up then adaptive scale down float expectedVeryHigh[TEST_BUFFER_LENGTH] = { 0.2, -0.2, 0.16f, -0.13f }; scaleBuffer(HapticLevel::VERY_HIGH, 0.2f /* adaptiveScaleFactor */); @@ -178,11 +258,23 @@ TEST_F_WITH_FLAGS( EXPECT_FLOATS_NEARLY_EQ(expectedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); } -TEST_F_WITH_FLAGS( - ExternalVibrationUtilsTest, - TestAdaptiveScaleFactorAppliedAfterFixedScale, - REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling)) -) { +TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestAdaptiveScaleFactorUndefinedIgnoredFixedScale, + REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling)), + REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) { + float expectedVeryHigh[TEST_BUFFER_LENGTH] = {1, -1, 0.79f, -0.39f}; + scaleBuffer(HapticLevel::VERY_HIGH, -1.0f /* adaptiveScaleFactor */); + EXPECT_FLOATS_NEARLY_EQ(expectedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); +} + +TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestAdaptiveScaleFactorAppliedAfterFixedScale, + REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling)), + REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) { + // Adaptive scale mutes vibration + float expectedMuted[TEST_BUFFER_LENGTH]; + std::fill(std::begin(expectedMuted), std::end(expectedMuted), 0); + scaleBuffer(HapticLevel::VERY_HIGH, 0.0f /* adaptiveScaleFactor */); + EXPECT_FLOATS_NEARLY_EQ(expectedMuted, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); + // Haptic level scale up then adaptive scale down float expectedVeryHigh[TEST_BUFFER_LENGTH] = { 0.2, -0.2, 0.16f, -0.07f }; scaleBuffer(HapticLevel::VERY_HIGH, 0.2f /* adaptiveScaleFactor */); @@ -205,10 +297,48 @@ TEST_F_WITH_FLAGS( } TEST_F_WITH_FLAGS( - ExternalVibrationUtilsTest, - TestLimitAppliedAfterLegacyScale, - REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling)) -) { + ExternalVibrationUtilsTest, TestAdaptiveScaleFactorUndefinedIgnoredScaleV2, + // Value of fix_audio_coupled_haptics_scaling is not important, should work with either + REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) { + float expectedVeryHigh[TEST_BUFFER_LENGTH] = {1, -1, 0.8f, -0.38f}; + scaleBuffer(HapticLevel::VERY_HIGH, -1.0f /* adaptiveScaleFactor */); + EXPECT_FLOATS_NEARLY_EQ(expectedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); +} + +TEST_F_WITH_FLAGS( + ExternalVibrationUtilsTest, TestAdaptiveScaleFactorAppliedAfterScaleV2, + // Value of fix_audio_coupled_haptics_scaling is not important, should work with either + REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) { + // Adaptive scale mutes vibration + float expectedMuted[TEST_BUFFER_LENGTH]; + std::fill(std::begin(expectedMuted), std::end(expectedMuted), 0); + scaleBuffer(HapticLevel::VERY_HIGH, 0.0f /* adaptiveScaleFactor */); + EXPECT_FLOATS_NEARLY_EQ(expectedMuted, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); + + // Haptic level scale up then adaptive scale down + float expectedVeryHigh[TEST_BUFFER_LENGTH] = { 0.2, -0.2, 0.15f, -0.07f }; + scaleBuffer(HapticLevel::VERY_HIGH, 0.2f /* adaptiveScaleFactor */); + EXPECT_FLOATS_NEARLY_EQ(expectedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); + + // Haptic level scale up then adaptive scale up + float expectedHigh[TEST_BUFFER_LENGTH] = { 1.5f, -1.5f, 0.95f, -0.41f }; + scaleBuffer(HapticLevel::HIGH, 1.5f /* adaptiveScaleFactor */); + EXPECT_FLOATS_NEARLY_EQ(expectedHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); + + // Haptic level scale down then adaptive scale down + float expectedLow[TEST_BUFFER_LENGTH] = { 0.42f, -0.42f, 0.21f, -0.08f }; + scaleBuffer(HapticLevel::LOW, 0.6f /* adaptiveScaleFactor */); + EXPECT_FLOATS_NEARLY_EQ(expectedLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); + + // Haptic level scale down then adaptive scale up + float expectedVeryLow[TEST_BUFFER_LENGTH] = { 1.02f, -1.02f, 0.51f, -0.2f }; + scaleBuffer(HapticLevel::VERY_LOW, 2 /* adaptiveScaleFactor */); + EXPECT_FLOATS_NEARLY_EQ(expectedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); +} + +TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestLimitAppliedAfterLegacyScale, + REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling), + ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) { // Scaled = { 0.2, -0.2, 0.16f, -0.13f }; float expectedClippedVeryHigh[TEST_BUFFER_LENGTH] = { 0.15f, -0.15f, 0.15f, -0.13f }; scaleBuffer(HapticLevel::VERY_HIGH, 0.2f /* adaptiveScaleFactor */, 0.15f /* limit */); @@ -220,11 +350,9 @@ TEST_F_WITH_FLAGS( EXPECT_FLOATS_NEARLY_EQ(expectedClippedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); } -TEST_F_WITH_FLAGS( - ExternalVibrationUtilsTest, - TestLimitAppliedAfterFixedScale, - REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling)) -) { +TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestLimitAppliedAfterFixedScale, + REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling)), + REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) { // Scaled = { 0.2, -0.2, 0.16f, -0.13f }; float expectedClippedVeryHigh[TEST_BUFFER_LENGTH] = { 0.15f, -0.15f, 0.15f, -0.07f }; scaleBuffer(HapticLevel::VERY_HIGH, 0.2f /* adaptiveScaleFactor */, 0.15f /* limit */); @@ -235,3 +363,18 @@ TEST_F_WITH_FLAGS( scaleBuffer(HapticLevel::VERY_LOW, 2 /* adaptiveScaleFactor */, 0.7f /* limit */); EXPECT_FLOATS_NEARLY_EQ(expectedClippedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); } + +TEST_F_WITH_FLAGS( + ExternalVibrationUtilsTest, TestLimitAppliedAfterScaleV2, + // Value of fix_audio_coupled_haptics_scaling is not important, should work with either + REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) { + // Scaled = { 0.2, -0.2, 0.15f, -0.07f }; + float expectedClippedVeryHigh[TEST_BUFFER_LENGTH] = { 0.15f, -0.15f, 0.15f, -0.07f }; + scaleBuffer(HapticLevel::VERY_HIGH, 0.2f /* adaptiveScaleFactor */, 0.15f /* limit */); + EXPECT_FLOATS_NEARLY_EQ(expectedClippedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); + + // Scaled = { 1.02f, -1.02f, 0.51f, -0.2f } + float expectedClippedVeryLow[TEST_BUFFER_LENGTH] = { 0.7f, -0.7f, 0.51f, -0.2f }; + scaleBuffer(HapticLevel::VERY_LOW, 2 /* adaptiveScaleFactor */, 0.7f /* limit */); + EXPECT_FLOATS_NEARLY_EQ(expectedClippedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); +} diff --git a/opengl/include/EGL/egl.h b/opengl/include/EGL/egl.h index c9e8b7c52c..782b6d93ac 100644 --- a/opengl/include/EGL/egl.h +++ b/opengl/include/EGL/egl.h @@ -6,39 +6,24 @@ extern "C" { #endif /* -** Copyright (c) 2013-2017 The Khronos Group Inc. +** Copyright 2013-2020 The Khronos Group Inc. +** SPDX-License-Identifier: Apache-2.0 ** -** Permission is hereby granted, free of charge, to any person obtaining a -** copy of this software and/or associated documentation files (the -** "Materials"), to deal in the Materials without restriction, including -** without limitation the rights to use, copy, modify, merge, publish, -** distribute, sublicense, and/or sell copies of the Materials, and to -** permit persons to whom the Materials are furnished to do so, subject to -** the following conditions: -** -** The above copyright notice and this permission notice shall be included -** in all copies or substantial portions of the Materials. -** -** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. -*/ -/* -** This header is generated from the Khronos OpenGL / OpenGL ES XML -** API Registry. The current version of the Registry, generator scripts +** This header is generated from the Khronos EGL XML API Registry. +** The current version of the Registry, generator scripts ** used to make the header, and the header can be found at ** http://www.khronos.org/registry/egl ** -** Khronos $Git commit SHA1: bae3518c48 $ on $Git commit date: 2018-05-17 10:56:57 -0700 $ +** Khronos $Git commit SHA1: 800219cd6e $ on $Git commit date: 2024-05-13 00:13:13 -0700 $ */ #include <EGL/eglplatform.h> -/* Generated on date 20180517 */ +#ifndef EGL_EGL_PROTOTYPES +#define EGL_EGL_PROTOTYPES 1 +#endif + +/* Generated on date 20240715 */ /* Generated C header for: * API: egl @@ -118,6 +103,31 @@ typedef void (*__eglMustCastToProperFunctionPointerType)(void); #define EGL_VERSION 0x3054 #define EGL_WIDTH 0x3057 #define EGL_WINDOW_BIT 0x0004 +typedef EGLBoolean (EGLAPIENTRYP PFNEGLCHOOSECONFIGPROC) (EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size, EGLint *num_config); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLCOPYBUFFERSPROC) (EGLDisplay dpy, EGLSurface surface, EGLNativePixmapType target); +typedef EGLContext (EGLAPIENTRYP PFNEGLCREATECONTEXTPROC) (EGLDisplay dpy, EGLConfig config, EGLContext share_context, const EGLint *attrib_list); +typedef EGLSurface (EGLAPIENTRYP PFNEGLCREATEPBUFFERSURFACEPROC) (EGLDisplay dpy, EGLConfig config, const EGLint *attrib_list); +typedef EGLSurface (EGLAPIENTRYP PFNEGLCREATEPIXMAPSURFACEPROC) (EGLDisplay dpy, EGLConfig config, EGLNativePixmapType pixmap, const EGLint *attrib_list); +typedef EGLSurface (EGLAPIENTRYP PFNEGLCREATEWINDOWSURFACEPROC) (EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, const EGLint *attrib_list); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLDESTROYCONTEXTPROC) (EGLDisplay dpy, EGLContext ctx); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLDESTROYSURFACEPROC) (EGLDisplay dpy, EGLSurface surface); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLGETCONFIGATTRIBPROC) (EGLDisplay dpy, EGLConfig config, EGLint attribute, EGLint *value); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLGETCONFIGSPROC) (EGLDisplay dpy, EGLConfig *configs, EGLint config_size, EGLint *num_config); +typedef EGLDisplay (EGLAPIENTRYP PFNEGLGETCURRENTDISPLAYPROC) (void); +typedef EGLSurface (EGLAPIENTRYP PFNEGLGETCURRENTSURFACEPROC) (EGLint readdraw); +typedef EGLDisplay (EGLAPIENTRYP PFNEGLGETDISPLAYPROC) (EGLNativeDisplayType display_id); +typedef EGLint (EGLAPIENTRYP PFNEGLGETERRORPROC) (void); +typedef __eglMustCastToProperFunctionPointerType (EGLAPIENTRYP PFNEGLGETPROCADDRESSPROC) (const char *procname); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLINITIALIZEPROC) (EGLDisplay dpy, EGLint *major, EGLint *minor); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLMAKECURRENTPROC) (EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYCONTEXTPROC) (EGLDisplay dpy, EGLContext ctx, EGLint attribute, EGLint *value); +typedef const char *(EGLAPIENTRYP PFNEGLQUERYSTRINGPROC) (EGLDisplay dpy, EGLint name); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYSURFACEPROC) (EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint *value); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLSWAPBUFFERSPROC) (EGLDisplay dpy, EGLSurface surface); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLTERMINATEPROC) (EGLDisplay dpy); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLWAITGLPROC) (void); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLWAITNATIVEPROC) (EGLint engine); +#if EGL_EGL_PROTOTYPES EGLAPI EGLBoolean EGLAPIENTRY eglChooseConfig (EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size, EGLint *num_config); EGLAPI EGLBoolean EGLAPIENTRY eglCopyBuffers (EGLDisplay dpy, EGLSurface surface, EGLNativePixmapType target); EGLAPI EGLContext EGLAPIENTRY eglCreateContext (EGLDisplay dpy, EGLConfig config, EGLContext share_context, const EGLint *attrib_list); @@ -142,6 +152,7 @@ EGLAPI EGLBoolean EGLAPIENTRY eglSwapBuffers (EGLDisplay dpy, EGLSurface surface EGLAPI EGLBoolean EGLAPIENTRY eglTerminate (EGLDisplay dpy); EGLAPI EGLBoolean EGLAPIENTRY eglWaitGL (void); EGLAPI EGLBoolean EGLAPIENTRY eglWaitNative (EGLint engine); +#endif #endif /* EGL_VERSION_1_0 */ #ifndef EGL_VERSION_1_1 @@ -160,10 +171,16 @@ EGLAPI EGLBoolean EGLAPIENTRY eglWaitNative (EGLint engine); #define EGL_TEXTURE_RGB 0x305D #define EGL_TEXTURE_RGBA 0x305E #define EGL_TEXTURE_TARGET 0x3081 +typedef EGLBoolean (EGLAPIENTRYP PFNEGLBINDTEXIMAGEPROC) (EGLDisplay dpy, EGLSurface surface, EGLint buffer); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLRELEASETEXIMAGEPROC) (EGLDisplay dpy, EGLSurface surface, EGLint buffer); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLSURFACEATTRIBPROC) (EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLSWAPINTERVALPROC) (EGLDisplay dpy, EGLint interval); +#if EGL_EGL_PROTOTYPES EGLAPI EGLBoolean EGLAPIENTRY eglBindTexImage (EGLDisplay dpy, EGLSurface surface, EGLint buffer); EGLAPI EGLBoolean EGLAPIENTRY eglReleaseTexImage (EGLDisplay dpy, EGLSurface surface, EGLint buffer); EGLAPI EGLBoolean EGLAPIENTRY eglSurfaceAttrib (EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value); EGLAPI EGLBoolean EGLAPIENTRY eglSwapInterval (EGLDisplay dpy, EGLint interval); +#endif #endif /* EGL_VERSION_1_1 */ #ifndef EGL_VERSION_1_2 @@ -199,11 +216,18 @@ typedef void *EGLClientBuffer; #define EGL_SWAP_BEHAVIOR 0x3093 #define EGL_UNKNOWN EGL_CAST(EGLint,-1) #define EGL_VERTICAL_RESOLUTION 0x3091 +typedef EGLBoolean (EGLAPIENTRYP PFNEGLBINDAPIPROC) (EGLenum api); +typedef EGLenum (EGLAPIENTRYP PFNEGLQUERYAPIPROC) (void); +typedef EGLSurface (EGLAPIENTRYP PFNEGLCREATEPBUFFERFROMCLIENTBUFFERPROC) (EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer, EGLConfig config, const EGLint *attrib_list); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLRELEASETHREADPROC) (void); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLWAITCLIENTPROC) (void); +#if EGL_EGL_PROTOTYPES EGLAPI EGLBoolean EGLAPIENTRY eglBindAPI (EGLenum api); EGLAPI EGLenum EGLAPIENTRY eglQueryAPI (void); EGLAPI EGLSurface EGLAPIENTRY eglCreatePbufferFromClientBuffer (EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer, EGLConfig config, const EGLint *attrib_list); EGLAPI EGLBoolean EGLAPIENTRY eglReleaseThread (void); EGLAPI EGLBoolean EGLAPIENTRY eglWaitClient (void); +#endif #endif /* EGL_VERSION_1_2 */ #ifndef EGL_VERSION_1_3 @@ -232,7 +256,10 @@ EGLAPI EGLBoolean EGLAPIENTRY eglWaitClient (void); #define EGL_OPENGL_API 0x30A2 #define EGL_OPENGL_BIT 0x0008 #define EGL_SWAP_BEHAVIOR_PRESERVED_BIT 0x0400 +typedef EGLContext (EGLAPIENTRYP PFNEGLGETCURRENTCONTEXTPROC) (void); +#if EGL_EGL_PROTOTYPES EGLAPI EGLContext EGLAPIENTRY eglGetCurrentContext (void); +#endif #endif /* EGL_VERSION_1_4 */ #ifndef EGL_VERSION_1_5 @@ -284,6 +311,17 @@ typedef void *EGLImage; #define EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x30B8 #define EGL_IMAGE_PRESERVED 0x30D2 #define EGL_NO_IMAGE EGL_CAST(EGLImage,0) +typedef EGLSync (EGLAPIENTRYP PFNEGLCREATESYNCPROC) (EGLDisplay dpy, EGLenum type, const EGLAttrib *attrib_list); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLDESTROYSYNCPROC) (EGLDisplay dpy, EGLSync sync); +typedef EGLint (EGLAPIENTRYP PFNEGLCLIENTWAITSYNCPROC) (EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTime timeout); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLGETSYNCATTRIBPROC) (EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLAttrib *value); +typedef EGLImage (EGLAPIENTRYP PFNEGLCREATEIMAGEPROC) (EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLAttrib *attrib_list); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLDESTROYIMAGEPROC) (EGLDisplay dpy, EGLImage image); +typedef EGLDisplay (EGLAPIENTRYP PFNEGLGETPLATFORMDISPLAYPROC) (EGLenum platform, void *native_display, const EGLAttrib *attrib_list); +typedef EGLSurface (EGLAPIENTRYP PFNEGLCREATEPLATFORMWINDOWSURFACEPROC) (EGLDisplay dpy, EGLConfig config, void *native_window, const EGLAttrib *attrib_list); +typedef EGLSurface (EGLAPIENTRYP PFNEGLCREATEPLATFORMPIXMAPSURFACEPROC) (EGLDisplay dpy, EGLConfig config, void *native_pixmap, const EGLAttrib *attrib_list); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLWAITSYNCPROC) (EGLDisplay dpy, EGLSync sync, EGLint flags); +#if EGL_EGL_PROTOTYPES EGLAPI EGLSync EGLAPIENTRY eglCreateSync (EGLDisplay dpy, EGLenum type, const EGLAttrib *attrib_list); EGLAPI EGLBoolean EGLAPIENTRY eglDestroySync (EGLDisplay dpy, EGLSync sync); EGLAPI EGLint EGLAPIENTRY eglClientWaitSync (EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTime timeout); @@ -294,6 +332,7 @@ EGLAPI EGLDisplay EGLAPIENTRY eglGetPlatformDisplay (EGLenum platform, void *nat EGLAPI EGLSurface EGLAPIENTRY eglCreatePlatformWindowSurface (EGLDisplay dpy, EGLConfig config, void *native_window, const EGLAttrib *attrib_list); EGLAPI EGLSurface EGLAPIENTRY eglCreatePlatformPixmapSurface (EGLDisplay dpy, EGLConfig config, void *native_pixmap, const EGLAttrib *attrib_list); EGLAPI EGLBoolean EGLAPIENTRY eglWaitSync (EGLDisplay dpy, EGLSync sync, EGLint flags); +#endif #endif /* EGL_VERSION_1_5 */ #ifdef __cplusplus diff --git a/opengl/include/EGL/eglext.h b/opengl/include/EGL/eglext.h index c787fc9717..4d14c69d77 100644 --- a/opengl/include/EGL/eglext.h +++ b/opengl/include/EGL/eglext.h @@ -6,39 +6,20 @@ extern "C" { #endif /* -** Copyright (c) 2013-2017 The Khronos Group Inc. +** Copyright 2013-2020 The Khronos Group Inc. +** SPDX-License-Identifier: Apache-2.0 ** -** Permission is hereby granted, free of charge, to any person obtaining a -** copy of this software and/or associated documentation files (the -** "Materials"), to deal in the Materials without restriction, including -** without limitation the rights to use, copy, modify, merge, publish, -** distribute, sublicense, and/or sell copies of the Materials, and to -** permit persons to whom the Materials are furnished to do so, subject to -** the following conditions: -** -** The above copyright notice and this permission notice shall be included -** in all copies or substantial portions of the Materials. -** -** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. -*/ -/* ** This header is generated from the Khronos EGL XML API Registry. ** The current version of the Registry, generator scripts ** used to make the header, and the header can be found at ** http://www.khronos.org/registry/egl ** -** Khronos $Git commit SHA1: 726475c203 $ on $Git commit date: 2018-10-03 23:51:49 -0700 $ +** Khronos $Git commit SHA1: 800219cd6e $ on $Git commit date: 2024-05-13 00:13:13 -0700 $ */ #include <EGL/eglplatform.h> -#define EGL_EGLEXT_VERSION 20181204 +#define EGL_EGLEXT_VERSION 20240715 /* Generated C header for: * API: egl @@ -443,9 +424,9 @@ EGLAPI EGLSurface EGLAPIENTRY eglCreateStreamProducerSurfaceKHR (EGLDisplay dpy, #ifndef EGL_KHR_swap_buffers_with_damage #define EGL_KHR_swap_buffers_with_damage 1 -typedef EGLBoolean (EGLAPIENTRYP PFNEGLSWAPBUFFERSWITHDAMAGEKHRPROC) (EGLDisplay dpy, EGLSurface surface, EGLint *rects, EGLint n_rects); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLSWAPBUFFERSWITHDAMAGEKHRPROC) (EGLDisplay dpy, EGLSurface surface, const EGLint *rects, EGLint n_rects); #ifdef EGL_EGLEXT_PROTOTYPES -EGLAPI EGLBoolean EGLAPIENTRY eglSwapBuffersWithDamageKHR (EGLDisplay dpy, EGLSurface surface, EGLint *rects, EGLint n_rects); +EGLAPI EGLBoolean EGLAPIENTRY eglSwapBuffersWithDamageKHR (EGLDisplay dpy, EGLSurface surface, const EGLint *rects, EGLint n_rects); #endif #endif /* EGL_KHR_swap_buffers_with_damage */ @@ -462,6 +443,10 @@ EGLAPI EGLint EGLAPIENTRY eglWaitSyncKHR (EGLDisplay dpy, EGLSyncKHR sync, EGLin #endif #endif /* EGL_KHR_wait_sync */ +#ifndef EGL_ANDROID_GLES_layers +#define EGL_ANDROID_GLES_layers 1 +#endif /* EGL_ANDROID_GLES_layers */ + #ifndef EGL_ANDROID_blob_cache #define EGL_ANDROID_blob_cache 1 typedef khronos_ssize_t EGLsizeiANDROID; @@ -566,6 +551,11 @@ EGLAPI EGLBoolean EGLAPIENTRY eglPresentationTimeANDROID (EGLDisplay dpy, EGLSur #define EGL_RECORDABLE_ANDROID 0x3142 #endif /* EGL_ANDROID_recordable */ +#ifndef EGL_ANDROID_telemetry_hint +#define EGL_ANDROID_telemetry_hint 1 +#define EGL_TELEMETRY_HINT_ANDROID 0x3570 +#endif /* EGL_ANDROID_telemetry_hint */ + #ifndef EGL_ANGLE_d3d_share_handle_client_buffer #define EGL_ANGLE_d3d_share_handle_client_buffer 1 #define EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE 0x3200 @@ -589,11 +579,25 @@ EGLAPI EGLBoolean EGLAPIENTRY eglQuerySurfacePointerANGLE (EGLDisplay dpy, EGLSu #define EGL_ANGLE_surface_d3d_texture_2d_share_handle 1 #endif /* EGL_ANGLE_surface_d3d_texture_2d_share_handle */ +#ifndef EGL_ANGLE_sync_control_rate +#define EGL_ANGLE_sync_control_rate 1 +typedef EGLBoolean (EGLAPIENTRYP PFNEGLGETMSCRATEANGLEPROC) (EGLDisplay dpy, EGLSurface surface, EGLint *numerator, EGLint *denominator); +#ifdef EGL_EGLEXT_PROTOTYPES +EGLAPI EGLBoolean EGLAPIENTRY eglGetMscRateANGLE (EGLDisplay dpy, EGLSurface surface, EGLint *numerator, EGLint *denominator); +#endif +#endif /* EGL_ANGLE_sync_control_rate */ + #ifndef EGL_ANGLE_window_fixed_size #define EGL_ANGLE_window_fixed_size 1 #define EGL_FIXED_SIZE_ANGLE 0x3201 #endif /* EGL_ANGLE_window_fixed_size */ +#ifndef EGL_ARM_image_format +#define EGL_ARM_image_format 1 +#define EGL_COLOR_COMPONENT_TYPE_UNSIGNED_INTEGER_ARM 0x3287 +#define EGL_COLOR_COMPONENT_TYPE_INTEGER_ARM 0x3288 +#endif /* EGL_ARM_image_format */ + #ifndef EGL_ARM_implicit_external_sync #define EGL_ARM_implicit_external_sync 1 #define EGL_SYNC_PRIOR_COMMANDS_IMPLICIT_EXTERNAL_ARM 0x328A @@ -652,6 +656,11 @@ EGLAPI EGLBoolean EGLAPIENTRY eglCompositorSwapPolicyEXT (EGLint external_win_id #endif #endif /* EGL_EXT_compositor */ +#ifndef EGL_EXT_config_select_group +#define EGL_EXT_config_select_group 1 +#define EGL_CONFIG_SELECT_GROUP_EXT 0x34C0 +#endif /* EGL_EXT_config_select_group */ + #ifndef EGL_EXT_create_context_robustness #define EGL_EXT_create_context_robustness 1 #define EGL_CONTEXT_OPENGL_ROBUST_ACCESS_EXT 0x30BF @@ -684,6 +693,11 @@ EGLAPI EGLBoolean EGLAPIENTRY eglQueryDisplayAttribEXT (EGLDisplay dpy, EGLint a #define EGL_DRM_MASTER_FD_EXT 0x333C #endif /* EGL_EXT_device_drm */ +#ifndef EGL_EXT_device_drm_render_node +#define EGL_EXT_device_drm_render_node 1 +#define EGL_DRM_RENDER_NODE_FILE_EXT 0x3377 +#endif /* EGL_EXT_device_drm_render_node */ + #ifndef EGL_EXT_device_enumeration #define EGL_EXT_device_enumeration 1 #endif /* EGL_EXT_device_enumeration */ @@ -691,12 +705,33 @@ EGLAPI EGLBoolean EGLAPIENTRY eglQueryDisplayAttribEXT (EGLDisplay dpy, EGLint a #ifndef EGL_EXT_device_openwf #define EGL_EXT_device_openwf 1 #define EGL_OPENWF_DEVICE_ID_EXT 0x3237 +#define EGL_OPENWF_DEVICE_EXT 0x333D #endif /* EGL_EXT_device_openwf */ +#ifndef EGL_EXT_device_persistent_id +#define EGL_EXT_device_persistent_id 1 +#define EGL_DEVICE_UUID_EXT 0x335C +#define EGL_DRIVER_UUID_EXT 0x335D +#define EGL_DRIVER_NAME_EXT 0x335E +typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYDEVICEBINARYEXTPROC) (EGLDeviceEXT device, EGLint name, EGLint max_size, void *value, EGLint *size); +#ifdef EGL_EGLEXT_PROTOTYPES +EGLAPI EGLBoolean EGLAPIENTRY eglQueryDeviceBinaryEXT (EGLDeviceEXT device, EGLint name, EGLint max_size, void *value, EGLint *size); +#endif +#endif /* EGL_EXT_device_persistent_id */ + #ifndef EGL_EXT_device_query #define EGL_EXT_device_query 1 #endif /* EGL_EXT_device_query */ +#ifndef EGL_EXT_device_query_name +#define EGL_EXT_device_query_name 1 +#define EGL_RENDERER_EXT 0x335F +#endif /* EGL_EXT_device_query_name */ + +#ifndef EGL_EXT_explicit_device +#define EGL_EXT_explicit_device 1 +#endif /* EGL_EXT_explicit_device */ + #ifndef EGL_EXT_gl_colorspace_bt2020_hlg #define EGL_EXT_gl_colorspace_bt2020_hlg 1 #define EGL_GL_COLORSPACE_BT2020_HLG_EXT 0x3540 @@ -878,6 +913,17 @@ EGLAPI EGLSurface EGLAPIENTRY eglCreatePlatformPixmapSurfaceEXT (EGLDisplay dpy, #define EGL_PLATFORM_X11_SCREEN_EXT 0x31D6 #endif /* EGL_EXT_platform_x11 */ +#ifndef EGL_EXT_platform_xcb +#define EGL_EXT_platform_xcb 1 +#define EGL_PLATFORM_XCB_EXT 0x31DC +#define EGL_PLATFORM_XCB_SCREEN_EXT 0x31DE +#endif /* EGL_EXT_platform_xcb */ + +#ifndef EGL_EXT_present_opaque +#define EGL_EXT_present_opaque 1 +#define EGL_PRESENT_OPAQUE_EXT 0x31DF +#endif /* EGL_EXT_present_opaque */ + #ifndef EGL_EXT_protected_content #define EGL_EXT_protected_content 1 #define EGL_PROTECTED_CONTENT_EXT 0x32C0 @@ -887,6 +933,10 @@ EGLAPI EGLSurface EGLAPIENTRY eglCreatePlatformPixmapSurfaceEXT (EGLDisplay dpy, #define EGL_EXT_protected_surface 1 #endif /* EGL_EXT_protected_surface */ +#ifndef EGL_EXT_query_reset_notification_strategy +#define EGL_EXT_query_reset_notification_strategy 1 +#endif /* EGL_EXT_query_reset_notification_strategy */ + #ifndef EGL_EXT_stream_consumer_egloutput #define EGL_EXT_stream_consumer_egloutput 1 typedef EGLBoolean (EGLAPIENTRYP PFNEGLSTREAMCONSUMEROUTPUTEXTPROC) (EGLDisplay dpy, EGLStreamKHR stream, EGLOutputLayerEXT layer); @@ -916,11 +966,36 @@ EGLAPI EGLBoolean EGLAPIENTRY eglStreamConsumerOutputEXT (EGLDisplay dpy, EGLStr #define EGL_METADATA_SCALING_EXT 50000 #endif /* EGL_EXT_surface_SMPTE2086_metadata */ +#ifndef EGL_EXT_surface_compression +#define EGL_EXT_surface_compression 1 +#define EGL_SURFACE_COMPRESSION_EXT 0x34B0 +#define EGL_SURFACE_COMPRESSION_PLANE1_EXT 0x328E +#define EGL_SURFACE_COMPRESSION_PLANE2_EXT 0x328F +#define EGL_SURFACE_COMPRESSION_FIXED_RATE_NONE_EXT 0x34B1 +#define EGL_SURFACE_COMPRESSION_FIXED_RATE_DEFAULT_EXT 0x34B2 +#define EGL_SURFACE_COMPRESSION_FIXED_RATE_1BPC_EXT 0x34B4 +#define EGL_SURFACE_COMPRESSION_FIXED_RATE_2BPC_EXT 0x34B5 +#define EGL_SURFACE_COMPRESSION_FIXED_RATE_3BPC_EXT 0x34B6 +#define EGL_SURFACE_COMPRESSION_FIXED_RATE_4BPC_EXT 0x34B7 +#define EGL_SURFACE_COMPRESSION_FIXED_RATE_5BPC_EXT 0x34B8 +#define EGL_SURFACE_COMPRESSION_FIXED_RATE_6BPC_EXT 0x34B9 +#define EGL_SURFACE_COMPRESSION_FIXED_RATE_7BPC_EXT 0x34BA +#define EGL_SURFACE_COMPRESSION_FIXED_RATE_8BPC_EXT 0x34BB +#define EGL_SURFACE_COMPRESSION_FIXED_RATE_9BPC_EXT 0x34BC +#define EGL_SURFACE_COMPRESSION_FIXED_RATE_10BPC_EXT 0x34BD +#define EGL_SURFACE_COMPRESSION_FIXED_RATE_11BPC_EXT 0x34BE +#define EGL_SURFACE_COMPRESSION_FIXED_RATE_12BPC_EXT 0x34BF +typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYSUPPORTEDCOMPRESSIONRATESEXTPROC) (EGLDisplay dpy, EGLConfig config, const EGLAttrib *attrib_list, EGLint *rates, EGLint rate_size, EGLint *num_rates); +#ifdef EGL_EGLEXT_PROTOTYPES +EGLAPI EGLBoolean EGLAPIENTRY eglQuerySupportedCompressionRatesEXT (EGLDisplay dpy, EGLConfig config, const EGLAttrib *attrib_list, EGLint *rates, EGLint rate_size, EGLint *num_rates); +#endif +#endif /* EGL_EXT_surface_compression */ + #ifndef EGL_EXT_swap_buffers_with_damage #define EGL_EXT_swap_buffers_with_damage 1 -typedef EGLBoolean (EGLAPIENTRYP PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC) (EGLDisplay dpy, EGLSurface surface, EGLint *rects, EGLint n_rects); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC) (EGLDisplay dpy, EGLSurface surface, const EGLint *rects, EGLint n_rects); #ifdef EGL_EGLEXT_PROTOTYPES -EGLAPI EGLBoolean EGLAPIENTRY eglSwapBuffersWithDamageEXT (EGLDisplay dpy, EGLSurface surface, EGLint *rects, EGLint n_rects); +EGLAPI EGLBoolean EGLAPIENTRY eglSwapBuffersWithDamageEXT (EGLDisplay dpy, EGLSurface surface, const EGLint *rects, EGLint n_rects); #endif #endif /* EGL_EXT_swap_buffers_with_damage */ @@ -1036,6 +1111,16 @@ EGLAPI EGLBoolean EGLAPIENTRY eglExportDMABUFImageMESA (EGLDisplay dpy, EGLImage #define EGL_PLATFORM_SURFACELESS_MESA 0x31DD #endif /* EGL_MESA_platform_surfaceless */ +#ifndef EGL_MESA_query_driver +#define EGL_MESA_query_driver 1 +typedef char *(EGLAPIENTRYP PFNEGLGETDISPLAYDRIVERCONFIGPROC) (EGLDisplay dpy); +typedef const char *(EGLAPIENTRYP PFNEGLGETDISPLAYDRIVERNAMEPROC) (EGLDisplay dpy); +#ifdef EGL_EGLEXT_PROTOTYPES +EGLAPI char *EGLAPIENTRY eglGetDisplayDriverConfig (EGLDisplay dpy); +EGLAPI const char *EGLAPIENTRY eglGetDisplayDriverName (EGLDisplay dpy); +#endif +#endif /* EGL_MESA_query_driver */ + #ifndef EGL_NOK_swap_region #define EGL_NOK_swap_region 1 typedef EGLBoolean (EGLAPIENTRYP PFNEGLSWAPBUFFERSREGIONNOKPROC) (EGLDisplay dpy, EGLSurface surface, EGLint numRects, const EGLint *rects); @@ -1124,11 +1209,39 @@ EGLAPI EGLBoolean EGLAPIENTRY eglPostSubBufferNV (EGLDisplay dpy, EGLSurface sur #endif #endif /* EGL_NV_post_sub_buffer */ +#ifndef EGL_NV_quadruple_buffer +#define EGL_NV_quadruple_buffer 1 +#define EGL_QUADRUPLE_BUFFER_NV 0x3231 +#endif /* EGL_NV_quadruple_buffer */ + #ifndef EGL_NV_robustness_video_memory_purge #define EGL_NV_robustness_video_memory_purge 1 #define EGL_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV 0x334C #endif /* EGL_NV_robustness_video_memory_purge */ +#ifndef EGL_NV_stream_consumer_eglimage +#define EGL_NV_stream_consumer_eglimage 1 +#define EGL_STREAM_CONSUMER_IMAGE_NV 0x3373 +#define EGL_STREAM_IMAGE_ADD_NV 0x3374 +#define EGL_STREAM_IMAGE_REMOVE_NV 0x3375 +#define EGL_STREAM_IMAGE_AVAILABLE_NV 0x3376 +typedef EGLBoolean (EGLAPIENTRYP PFNEGLSTREAMIMAGECONSUMERCONNECTNVPROC) (EGLDisplay dpy, EGLStreamKHR stream, EGLint num_modifiers, const EGLuint64KHR *modifiers, const EGLAttrib *attrib_list); +typedef EGLint (EGLAPIENTRYP PFNEGLQUERYSTREAMCONSUMEREVENTNVPROC) (EGLDisplay dpy, EGLStreamKHR stream, EGLTime timeout, EGLenum *event, EGLAttrib *aux); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLSTREAMACQUIREIMAGENVPROC) (EGLDisplay dpy, EGLStreamKHR stream, EGLImage *pImage, EGLSync sync); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLSTREAMRELEASEIMAGENVPROC) (EGLDisplay dpy, EGLStreamKHR stream, EGLImage image, EGLSync sync); +#ifdef EGL_EGLEXT_PROTOTYPES +EGLAPI EGLBoolean EGLAPIENTRY eglStreamImageConsumerConnectNV (EGLDisplay dpy, EGLStreamKHR stream, EGLint num_modifiers, const EGLuint64KHR *modifiers, const EGLAttrib *attrib_list); +EGLAPI EGLint EGLAPIENTRY eglQueryStreamConsumerEventNV (EGLDisplay dpy, EGLStreamKHR stream, EGLTime timeout, EGLenum *event, EGLAttrib *aux); +EGLAPI EGLBoolean EGLAPIENTRY eglStreamAcquireImageNV (EGLDisplay dpy, EGLStreamKHR stream, EGLImage *pImage, EGLSync sync); +EGLAPI EGLBoolean EGLAPIENTRY eglStreamReleaseImageNV (EGLDisplay dpy, EGLStreamKHR stream, EGLImage image, EGLSync sync); +#endif +#endif /* EGL_NV_stream_consumer_eglimage */ + +#ifndef EGL_NV_stream_consumer_eglimage_use_scanout_attrib +#define EGL_NV_stream_consumer_eglimage_use_scanout_attrib 1 +#define EGL_STREAM_CONSUMER_IMAGE_USE_SCANOUT_NV 0x3378 +#endif /* EGL_NV_stream_consumer_eglimage_use_scanout_attrib */ + #ifndef EGL_NV_stream_consumer_gltexture_yuv #define EGL_NV_stream_consumer_gltexture_yuv 1 #define EGL_YUV_PLANE0_TEXTURE_UNIT_NV 0x332C @@ -1165,6 +1278,12 @@ EGLAPI EGLBoolean EGLAPIENTRY eglStreamConsumerGLTextureExternalAttribsNV (EGLDi #define EGL_STREAM_CROSS_SYSTEM_NV 0x334F #endif /* EGL_NV_stream_cross_system */ +#ifndef EGL_NV_stream_dma +#define EGL_NV_stream_dma 1 +#define EGL_STREAM_DMA_NV 0x3371 +#define EGL_STREAM_DMA_SERVER_NV 0x3372 +#endif /* EGL_NV_stream_dma */ + #ifndef EGL_NV_stream_fifo_next #define EGL_NV_stream_fifo_next 1 #define EGL_PENDING_FRAME_NV 0x3329 @@ -1216,6 +1335,21 @@ EGLAPI EGLBoolean EGLAPIENTRY eglQueryStreamMetadataNV (EGLDisplay dpy, EGLStrea #endif #endif /* EGL_NV_stream_metadata */ +#ifndef EGL_NV_stream_origin +#define EGL_NV_stream_origin 1 +#define EGL_STREAM_FRAME_ORIGIN_X_NV 0x3366 +#define EGL_STREAM_FRAME_ORIGIN_Y_NV 0x3367 +#define EGL_STREAM_FRAME_MAJOR_AXIS_NV 0x3368 +#define EGL_CONSUMER_AUTO_ORIENTATION_NV 0x3369 +#define EGL_PRODUCER_AUTO_ORIENTATION_NV 0x336A +#define EGL_LEFT_NV 0x336B +#define EGL_RIGHT_NV 0x336C +#define EGL_TOP_NV 0x336D +#define EGL_BOTTOM_NV 0x336E +#define EGL_X_AXIS_NV 0x336F +#define EGL_Y_AXIS_NV 0x3370 +#endif /* EGL_NV_stream_origin */ + #ifndef EGL_NV_stream_remote #define EGL_NV_stream_remote 1 #define EGL_STREAM_STATE_INITIALIZING_NV 0x3240 @@ -1312,6 +1446,21 @@ EGLAPI EGLuint64NV EGLAPIENTRY eglGetSystemTimeNV (void); #endif /* KHRONOS_SUPPORT_INT64 */ #endif /* EGL_NV_system_time */ +#ifndef EGL_NV_triple_buffer +#define EGL_NV_triple_buffer 1 +#define EGL_TRIPLE_BUFFER_NV 0x3230 +#endif /* EGL_NV_triple_buffer */ + +#ifndef EGL_QNX_image_native_buffer +#define EGL_QNX_image_native_buffer 1 +#define EGL_NATIVE_BUFFER_QNX 0x3551 +#endif /* EGL_QNX_image_native_buffer */ + +#ifndef EGL_QNX_platform_screen +#define EGL_QNX_platform_screen 1 +#define EGL_PLATFORM_SCREEN_QNX 0x3550 +#endif /* EGL_QNX_platform_screen */ + #ifndef EGL_TIZEN_image_native_buffer #define EGL_TIZEN_image_native_buffer 1 #define EGL_NATIVE_BUFFER_TIZEN 0x32A0 @@ -1322,6 +1471,40 @@ EGLAPI EGLuint64NV EGLAPIENTRY eglGetSystemTimeNV (void); #define EGL_NATIVE_SURFACE_TIZEN 0x32A1 #endif /* EGL_TIZEN_image_native_surface */ +#ifndef EGL_WL_bind_wayland_display +#define EGL_WL_bind_wayland_display 1 +#define PFNEGLBINDWAYLANDDISPLAYWL PFNEGLBINDWAYLANDDISPLAYWLPROC +#define PFNEGLUNBINDWAYLANDDISPLAYWL PFNEGLUNBINDWAYLANDDISPLAYWLPROC +#define PFNEGLQUERYWAYLANDBUFFERWL PFNEGLQUERYWAYLANDBUFFERWLPROC +struct wl_display; +struct wl_resource; +#define EGL_WAYLAND_BUFFER_WL 0x31D5 +#define EGL_WAYLAND_PLANE_WL 0x31D6 +#define EGL_TEXTURE_Y_U_V_WL 0x31D7 +#define EGL_TEXTURE_Y_UV_WL 0x31D8 +#define EGL_TEXTURE_Y_XUXV_WL 0x31D9 +#define EGL_TEXTURE_EXTERNAL_WL 0x31DA +#define EGL_WAYLAND_Y_INVERTED_WL 0x31DB +typedef EGLBoolean (EGLAPIENTRYP PFNEGLBINDWAYLANDDISPLAYWLPROC) (EGLDisplay dpy, struct wl_display *display); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLUNBINDWAYLANDDISPLAYWLPROC) (EGLDisplay dpy, struct wl_display *display); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYWAYLANDBUFFERWLPROC) (EGLDisplay dpy, struct wl_resource *buffer, EGLint attribute, EGLint *value); +#ifdef EGL_EGLEXT_PROTOTYPES +EGLAPI EGLBoolean EGLAPIENTRY eglBindWaylandDisplayWL (EGLDisplay dpy, struct wl_display *display); +EGLAPI EGLBoolean EGLAPIENTRY eglUnbindWaylandDisplayWL (EGLDisplay dpy, struct wl_display *display); +EGLAPI EGLBoolean EGLAPIENTRY eglQueryWaylandBufferWL (EGLDisplay dpy, struct wl_resource *buffer, EGLint attribute, EGLint *value); +#endif +#endif /* EGL_WL_bind_wayland_display */ + +#ifndef EGL_WL_create_wayland_buffer_from_image +#define EGL_WL_create_wayland_buffer_from_image 1 +#define PFNEGLCREATEWAYLANDBUFFERFROMIMAGEWL PFNEGLCREATEWAYLANDBUFFERFROMIMAGEWLPROC +struct wl_buffer; +typedef struct wl_buffer *(EGLAPIENTRYP PFNEGLCREATEWAYLANDBUFFERFROMIMAGEWLPROC) (EGLDisplay dpy, EGLImageKHR image); +#ifdef EGL_EGLEXT_PROTOTYPES +EGLAPI struct wl_buffer *EGLAPIENTRY eglCreateWaylandBufferFromImageWL (EGLDisplay dpy, EGLImageKHR image); +#endif +#endif /* EGL_WL_create_wayland_buffer_from_image */ + #ifdef __cplusplus } #endif diff --git a/opengl/include/EGL/eglplatform.h b/opengl/include/EGL/eglplatform.h index 0bc2cb9a74..6786afd90b 100644 --- a/opengl/include/EGL/eglplatform.h +++ b/opengl/include/EGL/eglplatform.h @@ -2,36 +2,17 @@ #define __eglplatform_h_ /* -** Copyright (c) 2007-2016 The Khronos Group Inc. -** -** Permission is hereby granted, free of charge, to any person obtaining a -** copy of this software and/or associated documentation files (the -** "Materials"), to deal in the Materials without restriction, including -** without limitation the rights to use, copy, modify, merge, publish, -** distribute, sublicense, and/or sell copies of the Materials, and to -** permit persons to whom the Materials are furnished to do so, subject to -** the following conditions: -** -** The above copyright notice and this permission notice shall be included -** in all copies or substantial portions of the Materials. -** -** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +** Copyright 2007-2020 The Khronos Group Inc. +** SPDX-License-Identifier: Apache-2.0 */ /* Platform-specific types and definitions for egl.h - * $Revision: 30994 $ on $Date: 2015-04-30 13:36:48 -0700 (Thu, 30 Apr 2015) $ * * Adopters may modify khrplatform.h and this file to suit their platform. * You are encouraged to submit all modifications to the Khronos group so that * they can be included in future versions of this file. Please submit changes - * by sending them to the public Khronos Bugzilla (http://khronos.org/bugzilla) - * by filing a bug against product "EGL" component "Registry". + * by filing an issue or pull request on the public Khronos EGL Registry, at + * https://www.github.com/KhronosGroup/EGL-Registry/ */ #include <KHR/khrplatform.h> @@ -67,7 +48,13 @@ * implementations. */ -#if defined(_WIN32) || defined(__VC32__) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) /* Win32 and WinCE */ +#if defined(EGL_NO_PLATFORM_SPECIFIC_TYPES) + +typedef void *EGLNativeDisplayType; +typedef void *EGLNativePixmapType; +typedef void *EGLNativeWindowType; + +#elif defined(_WIN32) || defined(__VC32__) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) /* Win32 and WinCE */ #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN 1 #endif @@ -77,11 +64,23 @@ typedef HDC EGLNativeDisplayType; typedef HBITMAP EGLNativePixmapType; typedef HWND EGLNativeWindowType; +#elif defined(__QNX__) + +typedef khronos_uintptr_t EGLNativeDisplayType; +typedef struct _screen_pixmap* EGLNativePixmapType; /* screen_pixmap_t */ +typedef struct _screen_window* EGLNativeWindowType; /* screen_window_t */ + +#elif defined(__EMSCRIPTEN__) + +typedef int EGLNativeDisplayType; +typedef int EGLNativePixmapType; +typedef int EGLNativeWindowType; + #elif defined(__WINSCW__) || defined(__SYMBIAN32__) /* Symbian */ typedef int EGLNativeDisplayType; -typedef void *EGLNativeWindowType; typedef void *EGLNativePixmapType; +typedef void *EGLNativeWindowType; #elif defined(WL_EGL_PLATFORM) @@ -100,17 +99,17 @@ typedef void *EGLNativeWindowType; struct ANativeWindow; struct egl_native_pixmap_t; -typedef struct ANativeWindow* EGLNativeWindowType; -typedef struct egl_native_pixmap_t* EGLNativePixmapType; typedef void* EGLNativeDisplayType; +typedef struct egl_native_pixmap_t* EGLNativePixmapType; +typedef struct ANativeWindow* EGLNativeWindowType; #elif defined(USE_OZONE) typedef intptr_t EGLNativeDisplayType; -typedef intptr_t EGLNativeWindowType; typedef intptr_t EGLNativePixmapType; +typedef intptr_t EGLNativeWindowType; -#elif defined(__unix__) || defined(USE_X11) +#elif defined(USE_X11) /* X11 (tentative) */ #include <X11/Xlib.h> @@ -120,11 +119,17 @@ typedef Display *EGLNativeDisplayType; typedef Pixmap EGLNativePixmapType; typedef Window EGLNativeWindowType; +#elif defined(__unix__) + +typedef void *EGLNativeDisplayType; +typedef khronos_uintptr_t EGLNativePixmapType; +typedef khronos_uintptr_t EGLNativeWindowType; + #elif defined(__APPLE__) typedef int EGLNativeDisplayType; -typedef void *EGLNativeWindowType; typedef void *EGLNativePixmapType; +typedef void *EGLNativeWindowType; #elif defined(__HAIKU__) @@ -134,6 +139,12 @@ typedef void *EGLNativeDisplayType; typedef khronos_uintptr_t EGLNativePixmapType; typedef khronos_uintptr_t EGLNativeWindowType; +#elif defined(__Fuchsia__) + +typedef void *EGLNativeDisplayType; +typedef khronos_uintptr_t EGLNativePixmapType; +typedef khronos_uintptr_t EGLNativeWindowType; + #else #error "Platform not recognized" #endif diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp index 502c14f0f1..8cb637b948 100644 --- a/opengl/libs/EGL/eglApi.cpp +++ b/opengl/libs/EGL/eglApi.cpp @@ -248,7 +248,7 @@ __eglMustCastToProperFunctionPointerType eglGetProcAddress(const char* procname) return cnx->platform.eglGetProcAddress(procname); } -EGLBoolean eglSwapBuffersWithDamageKHR(EGLDisplay dpy, EGLSurface draw, EGLint* rects, +EGLBoolean eglSwapBuffersWithDamageKHR(EGLDisplay dpy, EGLSurface draw, const EGLint* rects, EGLint n_rects) { ATRACE_CALL(); clearError(); diff --git a/opengl/libs/EGL/egl_entries.in b/opengl/libs/EGL/egl_entries.in index 1c91f1d61b..b6f2c3410b 100644 --- a/opengl/libs/EGL/egl_entries.in +++ b/opengl/libs/EGL/egl_entries.in @@ -106,5 +106,5 @@ EGL_ENTRY(EGLuint64NV, eglGetSystemTimeNV, void) /* Partial update extensions */ -EGL_ENTRY(EGLBoolean, eglSwapBuffersWithDamageKHR, EGLDisplay, EGLSurface, EGLint *, EGLint) +EGL_ENTRY(EGLBoolean, eglSwapBuffersWithDamageKHR, EGLDisplay, EGLSurface, const EGLint *, EGLint) EGL_ENTRY(EGLBoolean, eglSetDamageRegionKHR, EGLDisplay, EGLSurface, EGLint *, EGLint) diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp index 0bfefd6706..6713a5c69d 100644 --- a/opengl/libs/EGL/egl_platform_entries.cpp +++ b/opengl/libs/EGL/egl_platform_entries.cpp @@ -916,42 +916,72 @@ EGLContext eglCreateContextImpl(EGLDisplay dpy, EGLConfig config, EGLContext sha egl_context_t* const c = get_context(share_list); share_list = c->context; } + + bool skip_telemetry = false; + + auto findAttribute = [](const EGLint* attrib_ptr, GLint attribute, GLint* value) { + while (attrib_ptr && *attrib_ptr != EGL_NONE) { + GLint attr = *attrib_ptr++; + GLint val = *attrib_ptr++; + if (attr == attribute) { + if (value) { + *value = val; + } + return true; + } + } + return false; + }; + + std::vector<EGLint> replacement_attrib_list; + GLint telemetry_value; + if (findAttribute(attrib_list, EGL_TELEMETRY_HINT_ANDROID, &telemetry_value)) { + skip_telemetry = (telemetry_value == android::GpuStatsInfo::SKIP_TELEMETRY); + + // We need to remove EGL_TELEMETRY_HINT_ANDROID or the underlying drivers will + // complain about an unexpected attribute + const EGLint* attrib_ptr = attrib_list; + while (attrib_ptr && *attrib_ptr != EGL_NONE) { + GLint attr = *attrib_ptr++; + GLint val = *attrib_ptr++; + if (attr != EGL_TELEMETRY_HINT_ANDROID) { + replacement_attrib_list.push_back(attr); + replacement_attrib_list.push_back(val); + } + } + replacement_attrib_list.push_back(EGL_NONE); + attrib_list = replacement_attrib_list.data(); + } // b/111083885 - If we are presenting EGL 1.4 interface to apps // error out on robust access attributes that are invalid // in EGL 1.4 as the driver may be fine with them but dEQP expects // tests to fail according to spec. if (attrib_list && (cnx->driverVersion < EGL_MAKE_VERSION(1, 5, 0))) { - const EGLint* attrib_ptr = attrib_list; - while (*attrib_ptr != EGL_NONE) { - GLint attr = *attrib_ptr++; - GLint value = *attrib_ptr++; - if (attr == EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR) { - // We are GL ES context with EGL 1.4, this is an invalid - // attribute - return setError(EGL_BAD_ATTRIBUTE, EGL_NO_CONTEXT); - } - }; + if (findAttribute(attrib_list, EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR, + nullptr)) { + // We are GL ES context with EGL 1.4, this is an invalid attribute + return setError(EGL_BAD_ATTRIBUTE, EGL_NO_CONTEXT); + } } EGLContext context = cnx->egl.eglCreateContext(dp->disp.dpy, config, share_list, attrib_list); if (context != EGL_NO_CONTEXT) { // figure out if it's a GLESv1 or GLESv2 int version = egl_connection_t::GLESv1_INDEX; - if (attrib_list) { - while (*attrib_list != EGL_NONE) { - GLint attr = *attrib_list++; - GLint value = *attrib_list++; - if (attr == EGL_CONTEXT_CLIENT_VERSION && (value == 2 || value == 3)) { - version = egl_connection_t::GLESv2_INDEX; - } - }; + GLint version_value; + if (findAttribute(attrib_list, EGL_CONTEXT_CLIENT_VERSION, &version_value)) { + if (version_value == 2 || version_value == 3) { + version = egl_connection_t::GLESv2_INDEX; + } } if (version == egl_connection_t::GLESv1_INDEX) { android::GraphicsEnv::getInstance().setTargetStats( android::GpuStatsInfo::Stats::GLES_1_IN_USE); } - android::GraphicsEnv::getInstance().setTargetStats( - android::GpuStatsInfo::Stats::CREATED_GLES_CONTEXT); + if (!skip_telemetry) { + android::GraphicsEnv::getInstance().setTargetStats( + android::GpuStatsInfo::Stats::CREATED_GLES_CONTEXT); + } egl_context_t* c = new egl_context_t(dpy, context, config, cnx, version); return c; } @@ -1324,7 +1354,7 @@ private: std::mutex mMutex; }; -EGLBoolean eglSwapBuffersWithDamageKHRImpl(EGLDisplay dpy, EGLSurface draw, EGLint* rects, +EGLBoolean eglSwapBuffersWithDamageKHRImpl(EGLDisplay dpy, EGLSurface draw, const EGLint* rects, EGLint n_rects) { const egl_display_t* dp = validate_display(dpy); if (!dp) return EGL_FALSE; diff --git a/opengl/libs/GLES2/gl2ext_api.in b/opengl/libs/GLES2/gl2ext_api.in index 4a0d4b9e08..dc99e09ef3 100644 --- a/opengl/libs/GLES2/gl2ext_api.in +++ b/opengl/libs/GLES2/gl2ext_api.in @@ -427,9 +427,6 @@ void API_ENTRY(glDrawRangeElementsBaseVertexEXT)(GLenum mode, GLuint start, GLui void API_ENTRY(glDrawElementsInstancedBaseVertexEXT)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex) { CALL_GL_API(glDrawElementsInstancedBaseVertexEXT, mode, count, type, indices, instancecount, basevertex); } -void API_ENTRY(glMultiDrawElementsBaseVertexOES)(GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount, const GLint *basevertex) { - CALL_GL_API(glMultiDrawElementsBaseVertexOES, mode, count, type, indices, primcount, basevertex); -} void API_ENTRY(glDrawArraysInstancedEXT)(GLenum mode, GLint start, GLsizei count, GLsizei primcount) { CALL_GL_API(glDrawArraysInstancedEXT, mode, start, count, primcount); } diff --git a/opengl/libs/entries.in b/opengl/libs/entries.in index a30651098b..4c1eefc0c7 100644 --- a/opengl/libs/entries.in +++ b/opengl/libs/entries.in @@ -605,7 +605,6 @@ GL_ENTRY(void, glMultMatrixxOES, const GLfixed *m) GL_ENTRY(void, glMultiDrawArraysEXT, GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount) GL_ENTRY(void, glMultiDrawArraysIndirectEXT, GLenum mode, const void *indirect, GLsizei drawcount, GLsizei stride) GL_ENTRY(void, glMultiDrawElementsBaseVertexEXT, GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount, const GLint *basevertex) -GL_ENTRY(void, glMultiDrawElementsBaseVertexOES, GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount, const GLint *basevertex) GL_ENTRY(void, glMultiDrawElementsEXT, GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount) GL_ENTRY(void, glMultiDrawElementsIndirectEXT, GLenum mode, GLenum type, const void *indirect, GLsizei drawcount, GLsizei stride) GL_ENTRY(void, glMultiTexCoord4f, GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q) diff --git a/opengl/libs/platform_entries.in b/opengl/libs/platform_entries.in index 46734112d3..004aa5acb9 100644 --- a/opengl/libs/platform_entries.in +++ b/opengl/libs/platform_entries.in @@ -24,7 +24,7 @@ EGL_ENTRY(EGLBoolean, eglWaitGL, void) EGL_ENTRY(EGLBoolean, eglWaitNative, EGLint) EGL_ENTRY(EGLint, eglGetError, void) EGL_ENTRY(__eglMustCastToProperFunctionPointerType, eglGetProcAddress, const char*) -EGL_ENTRY(EGLBoolean, eglSwapBuffersWithDamageKHR, EGLDisplay, EGLSurface, EGLint*, EGLint) +EGL_ENTRY(EGLBoolean, eglSwapBuffersWithDamageKHR, EGLDisplay, EGLSurface, const EGLint*, EGLint) EGL_ENTRY(EGLBoolean, eglSwapBuffers, EGLDisplay, EGLSurface) EGL_ENTRY(EGLBoolean, eglCopyBuffers, EGLDisplay, EGLSurface, NativePixmapType) EGL_ENTRY(const char*, eglQueryString, EGLDisplay, EGLint) diff --git a/services/audiomanager/IAudioManager.cpp b/services/audiomanager/IAudioManager.cpp index 3ef5049230..da1aae2acb 100644 --- a/services/audiomanager/IAudioManager.cpp +++ b/services/audiomanager/IAudioManager.cpp @@ -152,6 +152,12 @@ public: data.writeNullableParcelable(extras); return remote()->transact(PORT_EVENT, data, &reply, IBinder::FLAG_ONEWAY); } + + virtual status_t permissionUpdateBarrier() { + Parcel data, reply; + data.writeInterfaceToken(IAudioManager::getInterfaceDescriptor()); + return remote()->transact(PERMISSION_UPDATE_BARRIER, data, &reply, 0); + } }; IMPLEMENT_META_INTERFACE(AudioManager, "android.media.IAudioService"); diff --git a/services/gpuservice/gpuwork/GpuWork.cpp b/services/gpuservice/gpuwork/GpuWork.cpp index 1a744abc6d..7628745537 100644 --- a/services/gpuservice/gpuwork/GpuWork.cpp +++ b/services/gpuservice/gpuwork/GpuWork.cpp @@ -44,7 +44,7 @@ #include "gpuwork/gpuWork.h" -#define ONE_MS_IN_NS (10000000) +#define MSEC_PER_NSEC (1000LU * 1000LU) namespace android { namespace gpuwork { @@ -118,6 +118,9 @@ GpuWork::~GpuWork() { } void GpuWork::initialize() { + // Workaround b/347947040 by allowing time for statsd / bpf setup. + std::this_thread::sleep_for(std::chrono::seconds(30)); + // Make sure BPF programs are loaded. bpf::waitForProgsLoaded(); @@ -382,10 +385,11 @@ AStatsManager_PullAtomCallbackReturn GpuWork::pullWorkAtoms(AStatsEventList* dat ALOGI("pullWorkAtoms: after random selection: uids.size() == %zu", uids.size()); auto now = std::chrono::steady_clock::now(); - long long duration = - std::chrono::duration_cast<std::chrono::seconds>(now - mPreviousMapClearTimePoint) - .count(); - if (duration > std::numeric_limits<int32_t>::max() || duration < 0) { + int32_t duration = + static_cast<int32_t>( + std::chrono::duration_cast<std::chrono::seconds>(now - mPreviousMapClearTimePoint) + .count()); + if (duration < 0) { // This is essentially impossible. If it does somehow happen, give up, // but still clear the map. clearMap(); @@ -401,13 +405,14 @@ AStatsManager_PullAtomCallbackReturn GpuWork::pullWorkAtoms(AStatsEventList* dat } const UidTrackingInfo& info = it->second; - uint64_t total_active_duration_ms = info.total_active_duration_ns / ONE_MS_IN_NS; - uint64_t total_inactive_duration_ms = info.total_inactive_duration_ns / ONE_MS_IN_NS; + int32_t total_active_duration_ms = + static_cast<int32_t>(info.total_active_duration_ns / MSEC_PER_NSEC); + int32_t total_inactive_duration_ms = + static_cast<int32_t>(info.total_inactive_duration_ns / MSEC_PER_NSEC); // Skip this atom if any numbers are out of range. |duration| is // already checked above. - if (total_active_duration_ms > std::numeric_limits<int32_t>::max() || - total_inactive_duration_ms > std::numeric_limits<int32_t>::max()) { + if (total_active_duration_ms < 0 || total_inactive_duration_ms < 0) { continue; } @@ -418,11 +423,11 @@ AStatsManager_PullAtomCallbackReturn GpuWork::pullWorkAtoms(AStatsEventList* dat // gpu_id bitcast_int32(gpuId), // time_duration_seconds - static_cast<int32_t>(duration), + duration, // total_active_duration_millis - static_cast<int32_t>(total_active_duration_ms), + total_active_duration_ms, // total_inactive_duration_millis - static_cast<int32_t>(total_inactive_duration_ms)); + total_inactive_duration_ms); } } clearMap(); diff --git a/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h b/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h index e70da540b9..60cd2c703e 100644 --- a/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h +++ b/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h @@ -125,7 +125,7 @@ private: static constexpr size_t kNumGpusHardLimit = 32; // The minimum GPU time needed to actually log stats for a UID. - static constexpr uint64_t kMinGpuTimeNanoseconds = 30U * 1000000000U; // 30 seconds. + static constexpr uint64_t kMinGpuTimeNanoseconds = 10LLU * 1000000000LLU; // 10 seconds. // The previous time point at which |mGpuWorkMap| was cleared. std::chrono::steady_clock::time_point mPreviousMapClearTimePoint GUARDED_BY(mMutex); diff --git a/services/gpuservice/vts/TEST_MAPPING b/services/gpuservice/vts/TEST_MAPPING index b33e9624e2..a809be18e1 100644 --- a/services/gpuservice/vts/TEST_MAPPING +++ b/services/gpuservice/vts/TEST_MAPPING @@ -1,7 +1,13 @@ { "presubmit": [ { - "name": "GpuServiceVendorTests" + "name": "GpuServiceVendorTests", + "options": [ + { + // Exclude test methods that require a physical device to run. + "exclude-annotation": "android.platform.test.annotations.RequiresDevice" + } + ] } ] } diff --git a/services/gpuservice/vts/src/com/android/tests/gpuservice/GpuWorkTracepointTest.java b/services/gpuservice/vts/src/com/android/tests/gpuservice/GpuWorkTracepointTest.java index 6c16335359..5c12323633 100644 --- a/services/gpuservice/vts/src/com/android/tests/gpuservice/GpuWorkTracepointTest.java +++ b/services/gpuservice/vts/src/com/android/tests/gpuservice/GpuWorkTracepointTest.java @@ -19,7 +19,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import static org.junit.Assume.assumeTrue; -import android.platform.test.annotations.RestrictedBuildTest; +import android.platform.test.annotations.RequiresDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; @@ -63,7 +63,7 @@ public class GpuWorkTracepointTest extends BaseHostJUnit4Test { } @VsrTest(requirements={"VSR-3.3-004"}) - @RestrictedBuildTest + @RequiresDevice @Test public void testGpuWorkPeriodTracepointFormat() throws Exception { CommandResult commandResult = getDevice().executeShellV2Command( diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp index 70801dccb0..ca92ab5aca 100644 --- a/services/inputflinger/Android.bp +++ b/services/inputflinger/Android.bp @@ -172,9 +172,8 @@ cc_library_shared { export_static_lib_headers: [ "libinputdispatcher", ], - export_include_dirs: [ - ".", - "include", + export_shared_lib_headers: [ + "libinputflinger_base", ], } @@ -185,7 +184,16 @@ cc_library_shared { cc_library_headers { name: "libinputflinger_headers", host_supported: true, - export_include_dirs: ["include"], + export_include_dirs: [ + "include", + ".", + ], + header_libs: [ + "libchrome-gestures_headers", + ], + export_header_lib_headers: [ + "libchrome-gestures_headers", + ], } filegroup { @@ -209,6 +217,7 @@ cc_defaults { "libcutils", "libinput", "liblog", + "libprocessgroup", "libstatslog", "libutils", ], diff --git a/services/inputflinger/InputDeviceMetricsCollector.cpp b/services/inputflinger/InputDeviceMetricsCollector.cpp index b5cb3cbb57..46211441a9 100644 --- a/services/inputflinger/InputDeviceMetricsCollector.cpp +++ b/services/inputflinger/InputDeviceMetricsCollector.cpp @@ -133,15 +133,6 @@ void InputDeviceMetricsCollector::notifyInputDevicesChanged( mNextListener.notify(args); } -void InputDeviceMetricsCollector::notifyConfigurationChanged( - const NotifyConfigurationChangedArgs& args) { - { - std::scoped_lock lock(mLock); - reportCompletedSessions(); - } - mNextListener.notify(args); -} - void InputDeviceMetricsCollector::notifyKey(const NotifyKeyArgs& args) { { std::scoped_lock lock(mLock); diff --git a/services/inputflinger/InputDeviceMetricsCollector.h b/services/inputflinger/InputDeviceMetricsCollector.h index 1bcd527626..0a520e6869 100644 --- a/services/inputflinger/InputDeviceMetricsCollector.h +++ b/services/inputflinger/InputDeviceMetricsCollector.h @@ -107,7 +107,6 @@ public: std::chrono::nanoseconds usageSessionTimeout); void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override; - void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override; void notifyKey(const NotifyKeyArgs& args) override; void notifyMotion(const NotifyMotionArgs& args) override; void notifySwitch(const NotifySwitchArgs& args) override; diff --git a/services/inputflinger/InputFilter.cpp b/services/inputflinger/InputFilter.cpp index 8e73ce5d94..2ef94fbb07 100644 --- a/services/inputflinger/InputFilter.cpp +++ b/services/inputflinger/InputFilter.cpp @@ -60,6 +60,7 @@ void InputFilter::notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& AidlDeviceInfo& aidlInfo = mDeviceInfos.emplace_back(); aidlInfo.deviceId = info.getId(); aidlInfo.external = info.isExternal(); + aidlInfo.keyboardType = info.getKeyboardType(); } if (isFilterEnabled()) { LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyInputDevicesChanged(mDeviceInfos).isOk()); @@ -67,10 +68,6 @@ void InputFilter::notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& mNextListener.notify(args); } -void InputFilter::notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) { - mNextListener.notify(args); -} - void InputFilter::notifyKey(const NotifyKeyArgs& args) { if (isFilterEnabled()) { LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyKey(notifyKeyArgsToKeyEvent(args)).isOk()); @@ -149,6 +146,12 @@ void InputFilter::notifyConfigurationChangedLocked() { void InputFilter::dump(std::string& dump) { dump += "InputFilter:\n"; + if (isFilterEnabled()) { + std::string result; + LOG_ALWAYS_FATAL_IF(!mInputFilterRust->dumpFilter(&result).isOk()); + dump += result; + dump += "\n"; + } } } // namespace android diff --git a/services/inputflinger/InputFilter.h b/services/inputflinger/InputFilter.h index 4ddc9f4f6b..f626703524 100644 --- a/services/inputflinger/InputFilter.h +++ b/services/inputflinger/InputFilter.h @@ -53,7 +53,6 @@ public: InputFilterPolicyInterface& policy); ~InputFilter() override = default; void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override; - void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override; void notifyKey(const NotifyKeyArgs& args) override; void notifyMotion(const NotifyMotionArgs& args) override; void notifySwitch(const NotifySwitchArgs& args) override; diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp index 016ae045a9..8b6accf0f8 100644 --- a/services/inputflinger/InputListener.cpp +++ b/services/inputflinger/InputListener.cpp @@ -47,7 +47,6 @@ Visitor(V...) -> Visitor<V...>; void InputListenerInterface::notify(const NotifyArgs& generalArgs) { Visitor v{ [&](const NotifyInputDevicesChangedArgs& args) { notifyInputDevicesChanged(args); }, - [&](const NotifyConfigurationChangedArgs& args) { notifyConfigurationChanged(args); }, [&](const NotifyKeyArgs& args) { notifyKey(args); }, [&](const NotifyMotionArgs& args) { notifyMotion(args); }, [&](const NotifySwitchArgs& args) { notifySwitch(args); }, @@ -68,10 +67,6 @@ void QueuedInputListener::notifyInputDevicesChanged(const NotifyInputDevicesChan mArgsQueue.emplace_back(args); } -void QueuedInputListener::notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) { - mArgsQueue.emplace_back(args); -} - void QueuedInputListener::notifyKey(const NotifyKeyArgs& args) { mArgsQueue.emplace_back(args); } @@ -119,13 +114,6 @@ void TracedInputListener::notifyInputDevicesChanged(const NotifyInputDevicesChan mInnerListener.notify(args); } -void TracedInputListener::notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) { - constexpr static auto& fnName = __func__; - ATRACE_NAME_IF(ATRACE_ENABLED(), - StringPrintf("%s::%s(id=0x%" PRIx32 ")", mName, fnName, args.id)); - mInnerListener.notify(args); -} - void TracedInputListener::notifyKey(const NotifyKeyArgs& args) { constexpr static auto& fnName = __func__; ATRACE_NAME_IF(ATRACE_ENABLED(), diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp index 41e5247b50..b155122749 100644 --- a/services/inputflinger/InputManager.cpp +++ b/services/inputflinger/InputManager.cpp @@ -250,6 +250,10 @@ void InputManager::dump(std::string& dump) { mCollector->dump(dump); dump += '\n'; } + if (ENABLE_INPUT_FILTER_RUST) { + mInputFilter->dump(dump); + dump += '\n'; + } mDispatcher->dump(dump); dump += '\n'; } diff --git a/services/inputflinger/InputProcessor.cpp b/services/inputflinger/InputProcessor.cpp index 6dd267ce8f..8b8b1ad4d8 100644 --- a/services/inputflinger/InputProcessor.cpp +++ b/services/inputflinger/InputProcessor.cpp @@ -419,12 +419,6 @@ void InputProcessor::notifyInputDevicesChanged(const NotifyInputDevicesChangedAr mQueuedListener.flush(); } -void InputProcessor::notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) { - // pass through - mQueuedListener.notifyConfigurationChanged(args); - mQueuedListener.flush(); -} - void InputProcessor::notifyKey(const NotifyKeyArgs& args) { // pass through mQueuedListener.notifyKey(args); diff --git a/services/inputflinger/InputProcessor.h b/services/inputflinger/InputProcessor.h index 7a00a2dae8..2945dd2277 100644 --- a/services/inputflinger/InputProcessor.h +++ b/services/inputflinger/InputProcessor.h @@ -246,7 +246,6 @@ public: explicit InputProcessor(InputListenerInterface& listener); void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override; - void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override; void notifyKey(const NotifyKeyArgs& args) override; void notifyMotion(const NotifyMotionArgs& args) override; void notifySwitch(const NotifySwitchArgs& args) override; diff --git a/services/inputflinger/InputThread.cpp b/services/inputflinger/InputThread.cpp index e74f258168..449eb45b4b 100644 --- a/services/inputflinger/InputThread.cpp +++ b/services/inputflinger/InputThread.cpp @@ -16,8 +16,14 @@ #include "InputThread.h" +#include <android-base/logging.h> +#include <com_android_input_flags.h> +#include <processgroup/processgroup.h> + namespace android { +namespace input_flags = com::android::input::flags; + namespace { // Implementation of Thread from libutils. @@ -43,6 +49,11 @@ InputThread::InputThread(std::string name, std::function<void()> loop, std::func : mName(name), mThreadWake(wake) { mThread = sp<InputThreadImpl>::make(loop); mThread->run(mName.c_str(), ANDROID_PRIORITY_URGENT_DISPLAY); + if (input_flags::enable_input_policy_profile()) { + if (!applyInputEventProfile()) { + LOG(ERROR) << "Couldn't apply input policy profile for " << name; + } + } } InputThread::~InputThread() { @@ -63,4 +74,14 @@ bool InputThread::isCallingThread() { #endif } +bool InputThread::applyInputEventProfile() { +#if defined(__ANDROID__) + return SetTaskProfiles(mThread->getTid(), {"InputPolicy"}); +#else + // Since thread information is not available and there's no benefit of + // applying the task profile on host, return directly. + return true; +#endif +} + } // namespace android
\ No newline at end of file diff --git a/services/inputflinger/NotifyArgs.cpp b/services/inputflinger/NotifyArgs.cpp index 19a4f26a1e..b2680a2139 100644 --- a/services/inputflinger/NotifyArgs.cpp +++ b/services/inputflinger/NotifyArgs.cpp @@ -35,11 +35,6 @@ NotifyInputDevicesChangedArgs::NotifyInputDevicesChangedArgs(int32_t id, std::vector<InputDeviceInfo> infos) : id(id), inputDeviceInfos(std::move(infos)) {} -// --- NotifyConfigurationChangedArgs --- - -NotifyConfigurationChangedArgs::NotifyConfigurationChangedArgs(int32_t id, nsecs_t eventTime) - : id(id), eventTime(eventTime) {} - // --- NotifyKeyArgs --- NotifyKeyArgs::NotifyKeyArgs(int32_t id, nsecs_t eventTime, nsecs_t readTime, int32_t deviceId, @@ -198,7 +193,6 @@ Visitor(V...) -> Visitor<V...>; const char* toString(const NotifyArgs& args) { Visitor toStringVisitor{ [&](const NotifyInputDevicesChangedArgs&) { return "NotifyInputDevicesChangedArgs"; }, - [&](const NotifyConfigurationChangedArgs&) { return "NotifyConfigurationChangedArgs"; }, [&](const NotifyKeyArgs&) { return "NotifyKeyArgs"; }, [&](const NotifyMotionArgs&) { return "NotifyMotionArgs"; }, [&](const NotifySensorArgs&) { return "NotifySensorArgs"; }, diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp index ed7714698e..006d507a40 100644 --- a/services/inputflinger/PointerChoreographer.cpp +++ b/services/inputflinger/PointerChoreographer.cpp @@ -165,10 +165,6 @@ void PointerChoreographer::notifyInputDevicesChanged(const NotifyInputDevicesCha mNextListener.notify(args); } -void PointerChoreographer::notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) { - mNextListener.notify(args); -} - void PointerChoreographer::notifyKey(const NotifyKeyArgs& args) { fadeMouseCursorOnKeyPress(args); mNextListener.notify(args); @@ -202,6 +198,7 @@ void PointerChoreographer::fadeMouseCursorOnKeyPress(const android::NotifyKeyArg } auto it = mMousePointersByDisplay.find(targetDisplay); if (it != mMousePointersByDisplay.end()) { + mPolicy.notifyMouseCursorFadedOnTyping(); it->second->fade(PointerControllerInterface::Transition::GRADUAL); } } @@ -410,7 +407,8 @@ void PointerChoreographer::processStylusHoverEventLocked(const NotifyMotionArgs& // TODO(b/315815559): Do not fade and reset the icon if the hover exit will be followed // immediately by a DOWN event. pc.fade(PointerControllerInterface::Transition::IMMEDIATE); - pc.updatePointerIcon(PointerIconStyle::TYPE_NOT_SPECIFIED); + pc.updatePointerIcon(mShowTouchesEnabled ? PointerIconStyle::TYPE_SPOT_HOVER + : PointerIconStyle::TYPE_NOT_SPECIFIED); } else if (canUnfadeOnDisplay(args.displayId)) { pc.unfade(PointerControllerInterface::Transition::IMMEDIATE); } @@ -514,8 +512,9 @@ void PointerChoreographer::dump(std::string& dump) { std::scoped_lock _l(mLock); dump += "PointerChoreographer:\n"; - dump += StringPrintf("show touches: %s\n", mShowTouchesEnabled ? "true" : "false"); - dump += StringPrintf("stylus pointer icon enabled: %s\n", + dump += StringPrintf(INDENT "Show Touches Enabled: %s\n", + mShowTouchesEnabled ? "true" : "false"); + dump += StringPrintf(INDENT "Stylus PointerIcon Enabled: %s\n", mStylusPointerIconEnabled ? "true" : "false"); dump += INDENT "MousePointerControllers:\n"; @@ -794,6 +793,13 @@ bool PointerChoreographer::setPointerIcon( if (isFromSource(sources, AINPUT_SOURCE_STYLUS)) { auto it = mStylusPointersByDevice.find(deviceId); if (it != mStylusPointersByDevice.end()) { + if (mShowTouchesEnabled) { + // If an app doesn't override the icon for the hovering stylus, show the hover icon. + auto* style = std::get_if<PointerIconStyle>(&icon); + if (style != nullptr && *style == PointerIconStyle::TYPE_NOT_SPECIFIED) { + *style = PointerIconStyle::TYPE_SPOT_HOVER; + } + } setIconForController(icon, *it->second); return true; } diff --git a/services/inputflinger/PointerChoreographer.h b/services/inputflinger/PointerChoreographer.h index aaf1e3e962..635487be6b 100644 --- a/services/inputflinger/PointerChoreographer.h +++ b/services/inputflinger/PointerChoreographer.h @@ -105,7 +105,6 @@ public: void setFocusedDisplay(ui::LogicalDisplayId displayId) override; void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override; - void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override; void notifyKey(const NotifyKeyArgs& args) override; void notifyMotion(const NotifyMotionArgs& args) override; void notifySwitch(const NotifySwitchArgs& args) override; diff --git a/services/inputflinger/TEST_MAPPING b/services/inputflinger/TEST_MAPPING index a4dd909f0a..10fec7480d 100644 --- a/services/inputflinger/TEST_MAPPING +++ b/services/inputflinger/TEST_MAPPING @@ -149,6 +149,9 @@ }, { "name": "monkey_test" + }, + { + "name": "CtsSurfaceControlTests" } ], "postsubmit": [ @@ -290,11 +293,19 @@ }, { "name": "monkey_test" + }, + { + "name": "CtsInputRootTestCases" } ], - "staged-platinum-postsubmit": [ + "platinum-postsubmit": [ { "name": "inputflinger_tests" } + ], + "staged-platinum-postsubmit": [ + { + "name": "libinput_tests" + } ] } diff --git a/services/inputflinger/UnwantedInteractionBlocker.cpp b/services/inputflinger/UnwantedInteractionBlocker.cpp index 1e2b9b3ad3..0e9ec914b7 100644 --- a/services/inputflinger/UnwantedInteractionBlocker.cpp +++ b/services/inputflinger/UnwantedInteractionBlocker.cpp @@ -342,12 +342,6 @@ UnwantedInteractionBlocker::UnwantedInteractionBlocker(InputListenerInterface& l bool enablePalmRejection) : mQueuedListener(listener), mEnablePalmRejection(enablePalmRejection) {} -void UnwantedInteractionBlocker::notifyConfigurationChanged( - const NotifyConfigurationChangedArgs& args) { - mQueuedListener.notifyConfigurationChanged(args); - mQueuedListener.flush(); -} - void UnwantedInteractionBlocker::notifyKey(const NotifyKeyArgs& args) { mQueuedListener.notifyKey(args); mQueuedListener.flush(); diff --git a/services/inputflinger/UnwantedInteractionBlocker.h b/services/inputflinger/UnwantedInteractionBlocker.h index 419da8366e..8a66e25f58 100644 --- a/services/inputflinger/UnwantedInteractionBlocker.h +++ b/services/inputflinger/UnwantedInteractionBlocker.h @@ -91,7 +91,6 @@ public: explicit UnwantedInteractionBlocker(InputListenerInterface& listener, bool enablePalmRejection); void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override; - void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override; void notifyKey(const NotifyKeyArgs& args) override; void notifyMotion(const NotifyMotionArgs& args) override; void notifySwitch(const NotifySwitchArgs& args) override; diff --git a/services/inputflinger/aidl/com/android/server/inputflinger/DeviceInfo.aidl b/services/inputflinger/aidl/com/android/server/inputflinger/DeviceInfo.aidl index b9e6a03560..5b0b9ac5ef 100644 --- a/services/inputflinger/aidl/com/android/server/inputflinger/DeviceInfo.aidl +++ b/services/inputflinger/aidl/com/android/server/inputflinger/DeviceInfo.aidl @@ -23,4 +23,5 @@ package com.android.server.inputflinger; parcelable DeviceInfo { int deviceId; boolean external; + int keyboardType; }
\ No newline at end of file diff --git a/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl b/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl index 994d1c4b1a..31b72319ac 100644 --- a/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl +++ b/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl @@ -54,5 +54,7 @@ interface IInputFilter { /** Notifies when configuration changes */ void notifyConfigurationChanged(in InputFilterConfiguration config); + + String dumpFilter(); } diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp index ad9cec1791..ff407af8e1 100644 --- a/services/inputflinger/dispatcher/Entry.cpp +++ b/services/inputflinger/dispatcher/Entry.cpp @@ -68,15 +68,6 @@ EventEntry::EventEntry(int32_t id, Type type, nsecs_t eventTime, uint32_t policy injectionState(nullptr), dispatchInProgress(false) {} -// --- ConfigurationChangedEntry --- - -ConfigurationChangedEntry::ConfigurationChangedEntry(int32_t id, nsecs_t eventTime) - : EventEntry(id, Type::CONFIGURATION_CHANGED, eventTime, 0) {} - -std::string ConfigurationChangedEntry::getDescription() const { - return StringPrintf("ConfigurationChangedEvent(), policyFlags=0x%08x", policyFlags); -} - // --- DeviceResetEntry --- DeviceResetEntry::DeviceResetEntry(int32_t id, nsecs_t eventTime, int32_t deviceId) diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h index f2f31d88ef..becfb05f67 100644 --- a/services/inputflinger/dispatcher/Entry.h +++ b/services/inputflinger/dispatcher/Entry.h @@ -32,7 +32,6 @@ namespace android::inputdispatcher { struct EventEntry { enum class Type { - CONFIGURATION_CHANGED, DEVICE_RESET, FOCUS, KEY, @@ -78,11 +77,6 @@ struct EventEntry { virtual ~EventEntry() = default; }; -struct ConfigurationChangedEntry : EventEntry { - explicit ConfigurationChangedEntry(int32_t id, nsecs_t eventTime); - std::string getDescription() const override; -}; - struct DeviceResetEntry : EventEntry { int32_t deviceId; diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 7eb7e36386..5db21fdfa5 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -52,6 +52,7 @@ #include "Connection.h" #include "DebugConfig.h" #include "InputDispatcher.h" +#include "InputEventTimeline.h" #include "trace/InputTracer.h" #include "trace/InputTracingPerfettoBackend.h" #include "trace/ThreadedBackend.h" @@ -558,7 +559,6 @@ bool isConnectionResponsive(const Connection& connection) { // Returns true if the event type passed as argument represents a user activity. bool isUserActivityEvent(const EventEntry& eventEntry) { switch (eventEntry.type) { - case EventEntry::Type::CONFIGURATION_CHANGED: case EventEntry::Type::DEVICE_RESET: case EventEntry::Type::DRAG: case EventEntry::Type::FOCUS: @@ -694,7 +694,8 @@ std::optional<nsecs_t> getDownTime(const EventEntry& eventEntry) { */ std::vector<TouchedWindow> getHoveringWindowsLocked(const TouchState* oldState, const TouchState& newTouchState, - const MotionEntry& entry) { + const MotionEntry& entry, + std::function<void()> dump) { const int32_t maskedAction = MotionEvent::getActionMasked(entry.action); if (maskedAction == AMOTION_EVENT_ACTION_SCROLL) { @@ -742,6 +743,7 @@ std::vector<TouchedWindow> getHoveringWindowsLocked(const TouchState* oldState, // crashing the device with FATAL. severity = android::base::LogSeverity::ERROR; } + dump(); LOG(severity) << "Expected ACTION_HOVER_MOVE instead of " << entry.getDescription(); } touchedWindow.dispatchMode = InputTarget::DispatchMode::AS_IS; @@ -904,6 +906,24 @@ private: const nsecs_t mProcessingTimestamp; }; +/** + * This is needed to help use "InputEventInjectionResult" with base::Result. + */ +template <typename T> +struct EnumErrorWrapper { + T mVal; + EnumErrorWrapper(T&& e) : mVal(std::forward<T>(e)) {} + operator const T&() const { return mVal; } + T value() const { return mVal; } + std::string print() const { return ftl::enum_string(mVal); } +}; + +Error<EnumErrorWrapper<InputEventInjectionResult>> injectionError(InputEventInjectionResult&& e) { + LOG_ALWAYS_FATAL_IF(e == InputEventInjectionResult::SUCCEEDED); + return Error<EnumErrorWrapper<InputEventInjectionResult>>( + std::forward<InputEventInjectionResult>(e)); +} + } // namespace // --- InputDispatcher --- @@ -1156,14 +1176,6 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t& nextWakeupTime) { } switch (mPendingEvent->type) { - case EventEntry::Type::CONFIGURATION_CHANGED: { - const ConfigurationChangedEntry& typedEntry = - static_cast<const ConfigurationChangedEntry&>(*mPendingEvent); - done = dispatchConfigurationChangedLocked(currentTime, typedEntry); - dropReason = DropReason::NOT_DROPPED; // configuration changes are never dropped - break; - } - case EventEntry::Type::DEVICE_RESET: { const DeviceResetEntry& typedEntry = static_cast<const DeviceResetEntry&>(*mPendingEvent); @@ -1311,7 +1323,7 @@ bool InputDispatcher::shouldPruneInboundQueueLocked(const MotionEntry& motionEnt // Alternatively, maybe there's a spy window that could handle this event. const std::vector<sp<WindowInfoHandle>> touchedSpies = - findTouchedSpyWindowsAtLocked(displayId, x, y, isStylus); + findTouchedSpyWindowsAtLocked(displayId, x, y, isStylus, motionEntry.deviceId); for (const auto& windowHandle : touchedSpies) { const std::shared_ptr<Connection> connection = getConnectionLocked(windowHandle->getToken()); @@ -1395,7 +1407,6 @@ bool InputDispatcher::enqueueInboundEventLocked(std::unique_ptr<EventEntry> newE break; } case EventEntry::Type::TOUCH_MODE_CHANGED: - case EventEntry::Type::CONFIGURATION_CHANGED: case EventEntry::Type::DEVICE_RESET: case EventEntry::Type::SENSOR: case EventEntry::Type::POINTER_CAPTURE_CHANGED: @@ -1466,15 +1477,27 @@ std::vector<InputTarget> InputDispatcher::findOutsideTargetsLocked( } std::vector<sp<WindowInfoHandle>> InputDispatcher::findTouchedSpyWindowsAtLocked( - ui::LogicalDisplayId displayId, float x, float y, bool isStylus) const { + ui::LogicalDisplayId displayId, float x, float y, bool isStylus, DeviceId deviceId) const { // Traverse windows from front to back and gather the touched spy windows. std::vector<sp<WindowInfoHandle>> spyWindows; const auto& windowHandles = getWindowHandlesLocked(displayId); for (const sp<WindowInfoHandle>& windowHandle : windowHandles) { const WindowInfo& info = *windowHandle->getInfo(); - if (!windowAcceptsTouchAt(info, displayId, x, y, isStylus, getTransformLocked(displayId))) { - continue; + // Generally, we would skip any pointer that's outside of the window. However, if the + // spy prevents splitting, and already has some of the pointers from this device, then + // it should get more pointers from the same device, even if they are outside of that + // window + if (info.supportsSplitTouch()) { + continue; + } + + // We know that split touch is not supported. Skip this window only if it doesn't have + // any touching pointers for this device already. + if (!windowHasTouchingPointersLocked(windowHandle, deviceId)) { + continue; + } + // If it already has pointers down for this device, then give it this pointer, too. } if (!info.isSpy()) { // The first touched non-spy window was found, so return the spy windows touched so far. @@ -1557,7 +1580,6 @@ void InputDispatcher::dropInboundEventLocked(const EventEntry& entry, DropReason } case EventEntry::Type::FOCUS: case EventEntry::Type::TOUCH_MODE_CHANGED: - case EventEntry::Type::CONFIGURATION_CHANGED: case EventEntry::Type::DEVICE_RESET: { LOG_ALWAYS_FATAL("Should not drop %s events", ftl::enum_string(entry.type).c_str()); break; @@ -1646,24 +1668,6 @@ std::shared_ptr<KeyEntry> InputDispatcher::synthesizeKeyRepeatLocked(nsecs_t cur return newEntry; } -bool InputDispatcher::dispatchConfigurationChangedLocked(nsecs_t currentTime, - const ConfigurationChangedEntry& entry) { - if (DEBUG_OUTBOUND_EVENT_DETAILS) { - ALOGD("dispatchConfigurationChanged - eventTime=%" PRId64, entry.eventTime); - } - - // Reset key repeating in case a keyboard device was added or removed or something. - resetKeyRepeatLocked(); - - // Enqueue a command to run outside the lock to tell the policy that the configuration changed. - auto command = [this, eventTime = entry.eventTime]() REQUIRES(mLock) { - scoped_unlock unlock(mLock); - mPolicy.notifyConfigurationChanged(eventTime); - }; - postCommandLocked(std::move(command)); - return true; -} - bool InputDispatcher::dispatchDeviceResetLocked(nsecs_t currentTime, const DeviceResetEntry& entry) { if (DEBUG_OUTBOUND_EVENT_DETAILS) { @@ -1926,20 +1930,21 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<con } // Identify targets. - InputEventInjectionResult injectionResult; - sp<WindowInfoHandle> focusedWindow = - findFocusedWindowTargetLocked(currentTime, *entry, nextWakeupTime, - /*byref*/ injectionResult); - if (injectionResult == InputEventInjectionResult::PENDING) { - return false; - } + Result<sp<WindowInfoHandle>, InputEventInjectionResult> result = + findFocusedWindowTargetLocked(currentTime, *entry, nextWakeupTime); - setInjectionResult(*entry, injectionResult); - if (injectionResult != InputEventInjectionResult::SUCCEEDED) { + if (!result.ok()) { + if (result.error().code() == InputEventInjectionResult::PENDING) { + return false; + } + setInjectionResult(*entry, result.error().code()); return true; } + sp<WindowInfoHandle>& focusedWindow = *result; LOG_ALWAYS_FATAL_IF(focusedWindow == nullptr); + setInjectionResult(*entry, InputEventInjectionResult::SUCCEEDED); + std::vector<InputTarget> inputTargets; addWindowTargetLocked(focusedWindow, InputTarget::DispatchMode::AS_IS, InputTarget::Flags::FOREGROUND, getDownTime(*entry), inputTargets); @@ -2044,19 +2049,28 @@ bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, pilferPointersLocked(mDragState->dragWindow->getToken()); } - inputTargets = - findTouchedWindowTargetsLocked(currentTime, *entry, /*byref*/ injectionResult); - LOG_ALWAYS_FATAL_IF(injectionResult != InputEventInjectionResult::SUCCEEDED && - !inputTargets.empty()); + Result<std::vector<InputTarget>, InputEventInjectionResult> result = + findTouchedWindowTargetsLocked(currentTime, *entry); + + if (result.ok()) { + inputTargets = std::move(*result); + injectionResult = InputEventInjectionResult::SUCCEEDED; + } else { + injectionResult = result.error().code(); + } } else { // Non touch event. (eg. trackball) - sp<WindowInfoHandle> focusedWindow = - findFocusedWindowTargetLocked(currentTime, *entry, nextWakeupTime, injectionResult); - if (injectionResult == InputEventInjectionResult::SUCCEEDED) { + Result<sp<WindowInfoHandle>, InputEventInjectionResult> result = + findFocusedWindowTargetLocked(currentTime, *entry, nextWakeupTime); + if (result.ok()) { + sp<WindowInfoHandle>& focusedWindow = *result; LOG_ALWAYS_FATAL_IF(focusedWindow == nullptr); addWindowTargetLocked(focusedWindow, InputTarget::DispatchMode::AS_IS, InputTarget::Flags::FOREGROUND, getDownTime(*entry), inputTargets); + injectionResult = InputEventInjectionResult::SUCCEEDED; + } else { + injectionResult = result.error().code(); } } if (injectionResult == InputEventInjectionResult::PENDING) { @@ -2117,19 +2131,16 @@ void InputDispatcher::logOutboundMotionDetails(const char* prefix, const MotionE if (DEBUG_OUTBOUND_EVENT_DETAILS) { ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=%s, displayId=%s, policyFlags=0x%x, " "action=%s, actionButton=0x%x, flags=0x%x, " - "metaState=0x%x, buttonState=0x%x," - "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%" PRId64, + "metaState=0x%x, buttonState=0x%x, downTime=%" PRId64, prefix, entry.eventTime, entry.deviceId, inputEventSourceToString(entry.source).c_str(), entry.displayId.toString().c_str(), entry.policyFlags, MotionEvent::actionToString(entry.action).c_str(), - entry.actionButton, entry.flags, entry.metaState, entry.buttonState, entry.edgeFlags, - entry.xPrecision, entry.yPrecision, entry.downTime); + entry.actionButton, entry.flags, entry.metaState, entry.buttonState, entry.downTime); for (uint32_t i = 0; i < entry.getPointerCount(); i++) { ALOGD(" Pointer %d: id=%d, toolType=%s, " "x=%f, y=%f, pressure=%f, size=%f, " - "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, " - "orientation=%f", + "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, orientation=%f", i, entry.pointerProperties[i].id, ftl::enum_string(entry.pointerProperties[i].toolType).c_str(), entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X), @@ -2224,7 +2235,6 @@ ui::LogicalDisplayId InputDispatcher::getTargetDisplayId(const EventEntry& entry case EventEntry::Type::TOUCH_MODE_CHANGED: case EventEntry::Type::POINTER_CAPTURE_CHANGED: case EventEntry::Type::FOCUS: - case EventEntry::Type::CONFIGURATION_CHANGED: case EventEntry::Type::DEVICE_RESET: case EventEntry::Type::SENSOR: case EventEntry::Type::DRAG: { @@ -2266,11 +2276,9 @@ bool InputDispatcher::shouldWaitToSendKeyLocked(nsecs_t currentTime, return false; } -sp<WindowInfoHandle> InputDispatcher::findFocusedWindowTargetLocked( - nsecs_t currentTime, const EventEntry& entry, nsecs_t& nextWakeupTime, - InputEventInjectionResult& outInjectionResult) { - outInjectionResult = InputEventInjectionResult::FAILED; // Default result - +Result<sp<WindowInfoHandle>, InputEventInjectionResult> +InputDispatcher::findFocusedWindowTargetLocked(nsecs_t currentTime, const EventEntry& entry, + nsecs_t& nextWakeupTime) { ui::LogicalDisplayId displayId = getTargetDisplayId(entry); sp<WindowInfoHandle> focusedWindowHandle = getFocusedWindowHandleLocked(displayId); std::shared_ptr<InputApplicationHandle> focusedApplicationHandle = @@ -2282,12 +2290,12 @@ sp<WindowInfoHandle> InputDispatcher::findFocusedWindowTargetLocked( ALOGI("Dropping %s event because there is no focused window or focused application in " "display %s.", ftl::enum_string(entry.type).c_str(), displayId.toString().c_str()); - return nullptr; + return injectionError(InputEventInjectionResult::FAILED); } // Drop key events if requested by input feature if (focusedWindowHandle != nullptr && shouldDropInput(entry, focusedWindowHandle)) { - return nullptr; + return injectionError(InputEventInjectionResult::FAILED); } // Compatibility behavior: raise ANR if there is a focused application, but no focused window. @@ -2307,17 +2315,15 @@ sp<WindowInfoHandle> InputDispatcher::findFocusedWindowTargetLocked( "window when it finishes starting up. Will wait for %" PRId64 "ms", mAwaitedFocusedApplication->getName().c_str(), millis(timeout)); nextWakeupTime = std::min(nextWakeupTime, *mNoFocusedWindowTimeoutTime); - outInjectionResult = InputEventInjectionResult::PENDING; - return nullptr; + return injectionError(InputEventInjectionResult::PENDING); } else if (currentTime > *mNoFocusedWindowTimeoutTime) { // Already raised ANR. Drop the event ALOGE("Dropping %s event because there is no focused window", ftl::enum_string(entry.type).c_str()); - return nullptr; + return injectionError(InputEventInjectionResult::FAILED); } else { // Still waiting for the focused window - outInjectionResult = InputEventInjectionResult::PENDING; - return nullptr; + return injectionError(InputEventInjectionResult::PENDING); } } @@ -2327,15 +2333,13 @@ sp<WindowInfoHandle> InputDispatcher::findFocusedWindowTargetLocked( // Verify targeted injection. if (const auto err = verifyTargetedInjection(focusedWindowHandle, entry); err) { ALOGW("Dropping injected event: %s", (*err).c_str()); - outInjectionResult = InputEventInjectionResult::TARGET_MISMATCH; - return nullptr; + return injectionError(InputEventInjectionResult::TARGET_MISMATCH); } if (focusedWindowHandle->getInfo()->inputConfig.test( WindowInfo::InputConfig::PAUSE_DISPATCHING)) { ALOGI("Waiting because %s is paused", focusedWindowHandle->getName().c_str()); - outInjectionResult = InputEventInjectionResult::PENDING; - return nullptr; + return injectionError(InputEventInjectionResult::PENDING); } // If the event is a key event, then we must wait for all previous events to @@ -2352,12 +2356,10 @@ sp<WindowInfoHandle> InputDispatcher::findFocusedWindowTargetLocked( if (entry.type == EventEntry::Type::KEY) { if (shouldWaitToSendKeyLocked(currentTime, focusedWindowHandle->getName().c_str())) { nextWakeupTime = std::min(nextWakeupTime, *mKeyIsWaitingForEventsTimeout); - outInjectionResult = InputEventInjectionResult::PENDING; - return nullptr; + return injectionError(InputEventInjectionResult::PENDING); } } - - outInjectionResult = InputEventInjectionResult::SUCCEEDED; + // Success! return focusedWindowHandle; } @@ -2381,9 +2383,8 @@ std::vector<Monitor> InputDispatcher::selectResponsiveMonitorsLocked( return responsiveMonitors; } -std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( - nsecs_t currentTime, const MotionEntry& entry, - InputEventInjectionResult& outInjectionResult) { +base::Result<std::vector<InputTarget>, android::os::InputEventInjectionResult> +InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry& entry) { ATRACE_CALL(); std::vector<InputTarget> targets; @@ -2393,9 +2394,6 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( const int32_t action = entry.action; const int32_t maskedAction = MotionEvent::getActionMasked(action); - // Update the touch state as needed based on the properties of the touch event. - outInjectionResult = InputEventInjectionResult::PENDING; - // Copy current touch state into tempTouchState. // This state will be used to update mTouchStatesByDisplay at the end of this function. // If no state for the specified display exists, then our initial state will be empty. @@ -2435,8 +2433,7 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( // Started hovering, but the device is already down: reject the hover event LOG(ERROR) << "Got hover event " << entry.getDescription() << " but the device is already down " << oldState->dump(); - outInjectionResult = InputEventInjectionResult::FAILED; - return {}; + return injectionError(InputEventInjectionResult::FAILED); } // For hover actions, we will treat 'tempTouchState' as a new state, so let's erase // all of the existing hovering pointers and recompute. @@ -2457,20 +2454,25 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( if (isDown) { targets += findOutsideTargetsLocked(displayId, newTouchedWindowHandle, pointer.id); } + LOG_IF(INFO, newTouchedWindowHandle == nullptr) + << "No new touched window at (" << std::format("{:.1f}, {:.1f}", x, y) + << ") in display " << displayId; // Handle the case where we did not find a window. - if (newTouchedWindowHandle == nullptr) { - ALOGD("No new touched window at (%.1f, %.1f) in display %s", x, y, - displayId.toString().c_str()); - // Try to assign the pointer to the first foreground window we find, if there is one. - newTouchedWindowHandle = tempTouchState.getFirstForegroundWindowHandle(entry.deviceId); + if (!input_flags::split_all_touches()) { + // If we are force splitting all touches, then touches outside of the window should + // be dropped, even if this device already has pointers down in another window. + if (newTouchedWindowHandle == nullptr) { + // Try to assign the pointer to the first foreground window we find, if there is + // one. + newTouchedWindowHandle = + tempTouchState.getFirstForegroundWindowHandle(entry.deviceId); + } } // Verify targeted injection. if (const auto err = verifyTargetedInjection(newTouchedWindowHandle, entry); err) { ALOGW("Dropping injected touch event: %s", (*err).c_str()); - outInjectionResult = os::InputEventInjectionResult::TARGET_MISMATCH; - newTouchedWindowHandle = nullptr; - return {}; + return injectionError(InputEventInjectionResult::TARGET_MISMATCH); } // Figure out whether splitting will be allowed for this window. @@ -2493,18 +2495,16 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( } std::vector<sp<WindowInfoHandle>> newTouchedWindows = - findTouchedSpyWindowsAtLocked(displayId, x, y, isStylus); + findTouchedSpyWindowsAtLocked(displayId, x, y, isStylus, entry.deviceId); if (newTouchedWindowHandle != nullptr) { // Process the foreground window first so that it is the first to receive the event. newTouchedWindows.insert(newTouchedWindows.begin(), newTouchedWindowHandle); } if (newTouchedWindows.empty()) { - ALOGI("Dropping event because there is no touchable window at (%.1f, %.1f) on display " - "%s.", - x, y, displayId.toString().c_str()); - outInjectionResult = InputEventInjectionResult::FAILED; - return {}; + LOG(INFO) << "Dropping event because there is no touchable window at (" << x << ", " + << y << ") on display " << displayId << ": " << entry; + return injectionError(InputEventInjectionResult::FAILED); } for (const sp<WindowInfoHandle>& windowHandle : newTouchedWindows) { @@ -2605,8 +2605,7 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( << " is not down or we previously dropped the pointer down event in " << "display " << displayId << ": " << entry.getDescription(); } - outInjectionResult = InputEventInjectionResult::FAILED; - return {}; + return injectionError(InputEventInjectionResult::FAILED); } // If the pointer is not currently hovering, then ignore the event. @@ -2617,8 +2616,7 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( LOG(INFO) << "Dropping event because the hovering pointer is not in any windows in " "display " << displayId << ": " << entry.getDescription(); - outInjectionResult = InputEventInjectionResult::FAILED; - return {}; + return injectionError(InputEventInjectionResult::FAILED); } tempTouchState.removeHoveringPointer(entry.deviceId, pointerId); } @@ -2639,8 +2637,7 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( // Verify targeted injection. if (const auto err = verifyTargetedInjection(newTouchedWindowHandle, entry); err) { ALOGW("Dropping injected event: %s", (*err).c_str()); - outInjectionResult = os::InputEventInjectionResult::TARGET_MISMATCH; - return {}; + return injectionError(InputEventInjectionResult::TARGET_MISMATCH); } // Do not slide events to the window which can not receive motion event @@ -2693,7 +2690,7 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( // Check if the wallpaper window should deliver the corresponding event. slipWallpaperTouch(targetFlags, oldTouchedWindowHandle, newTouchedWindowHandle, - tempTouchState, entry.deviceId, pointer, targets); + tempTouchState, entry, targets); tempTouchState.removeTouchingPointerFromWindow(entry.deviceId, pointer.id, oldTouchedWindowHandle); } @@ -2720,7 +2717,9 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( // Update dispatching for hover enter and exit. { std::vector<TouchedWindow> hoveringWindows = - getHoveringWindowsLocked(oldState, tempTouchState, entry); + getHoveringWindowsLocked(oldState, tempTouchState, entry, + std::bind_front(&InputDispatcher::logDispatchStateLocked, + this)); // Hardcode to single hovering pointer for now. std::bitset<MAX_POINTER_ID + 1> pointerIds; pointerIds.set(entry.pointerProperties[0].id); @@ -2743,8 +2742,7 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( ALOGW("Dropping targeted injection: At least one touched window is not owned by uid " "%s:%s", entry.injectionState->targetUid->toString().c_str(), errs.c_str()); - outInjectionResult = InputEventInjectionResult::TARGET_MISMATCH; - return {}; + return injectionError(InputEventInjectionResult::TARGET_MISMATCH); } } @@ -2801,8 +2799,7 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( if (targets.empty()) { LOG(INFO) << "Dropping event because no targets were found: " << entry.getDescription(); - outInjectionResult = InputEventInjectionResult::FAILED; - return {}; + return injectionError(InputEventInjectionResult::FAILED); } // If we only have windows getting ACTION_OUTSIDE, then drop the event, because there is no @@ -2812,12 +2809,9 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( })) { LOG(INFO) << "Dropping event because all windows would just receive ACTION_OUTSIDE: " << entry.getDescription(); - outInjectionResult = InputEventInjectionResult::FAILED; - return {}; + return injectionError(InputEventInjectionResult::FAILED); } - outInjectionResult = InputEventInjectionResult::SUCCEEDED; - // Now that we have generated all of the input targets for this event, reset the dispatch // mode for all touched window to AS_IS. for (TouchedWindow& touchedWindow : tempTouchState.windows) { @@ -3626,7 +3620,6 @@ void InputDispatcher::enqueueDispatchEntryLocked(const std::shared_ptr<Connectio LOG_ALWAYS_FATAL("SENSOR events should not go to apps via input channel"); break; } - case EventEntry::Type::CONFIGURATION_CHANGED: case EventEntry::Type::DEVICE_RESET: { LOG_ALWAYS_FATAL("%s events should not go to apps", ftl::enum_string(eventEntry->type).c_str()); @@ -3839,6 +3832,10 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, } const MotionEntry& motionEntry = static_cast<const MotionEntry&>(eventEntry); status = publishMotionEvent(*connection, *dispatchEntry); + if (status == BAD_VALUE) { + logDispatchStateLocked(); + LOG(FATAL) << "Publisher failed for " << motionEntry; + } if (mTracer) { ensureEventTraced(motionEntry); mTracer->traceEventDispatch(*dispatchEntry, *motionEntry.traceTracker); @@ -3883,7 +3880,6 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, break; } - case EventEntry::Type::CONFIGURATION_CHANGED: case EventEntry::Type::DEVICE_RESET: case EventEntry::Type::SENSOR: { LOG_ALWAYS_FATAL("Should never start dispatch cycles for %s events", @@ -4279,7 +4275,6 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( ftl::enum_string(cancelationEventEntry->type).c_str()); break; } - case EventEntry::Type::CONFIGURATION_CHANGED: case EventEntry::Type::DEVICE_RESET: case EventEntry::Type::SENSOR: { LOG_ALWAYS_FATAL("%s event should not be found inside Connections's queue", @@ -4362,7 +4357,6 @@ void InputDispatcher::synthesizePointerDownEventsForConnectionLocked( case EventEntry::Type::KEY: case EventEntry::Type::FOCUS: case EventEntry::Type::TOUCH_MODE_CHANGED: - case EventEntry::Type::CONFIGURATION_CHANGED: case EventEntry::Type::DEVICE_RESET: case EventEntry::Type::POINTER_CAPTURE_CHANGED: case EventEntry::Type::SENSOR: @@ -4448,28 +4442,11 @@ std::unique_ptr<MotionEntry> InputDispatcher::splitMotionEvent( void InputDispatcher::notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) { std::scoped_lock _l(mLock); + // Reset key repeating in case a keyboard device was added or removed or something. + resetKeyRepeatLocked(); mLatencyTracker.setInputDevices(args.inputDeviceInfos); } -void InputDispatcher::notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) { - if (debugInboundEventDetails()) { - ALOGD("notifyConfigurationChanged - eventTime=%" PRId64, args.eventTime); - } - - bool needWake = false; - { // acquire lock - std::scoped_lock _l(mLock); - - std::unique_ptr<ConfigurationChangedEntry> newEntry = - std::make_unique<ConfigurationChangedEntry>(args.id, args.eventTime); - needWake = enqueueInboundEventLocked(std::move(newEntry)); - } // release lock - - if (needWake) { - mLooper->wake(); - } -} - void InputDispatcher::notifyKey(const NotifyKeyArgs& args) { ALOGD_IF(debugInboundEventDetails(), "notifyKey - id=%" PRIx32 ", eventTime=%" PRId64 @@ -4519,6 +4496,10 @@ void InputDispatcher::notifyKey(const NotifyKeyArgs& args) { { // acquire lock mLock.lock(); + if (input_flags::keyboard_repeat_keys() && !mConfig.keyRepeatEnabled) { + policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT; + } + if (shouldSendKeyToInputFilterLocked(args)) { mLock.unlock(); @@ -4557,13 +4538,12 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs& args) { ALOGD("notifyMotion - id=%" PRIx32 " eventTime=%" PRId64 ", deviceId=%d, source=%s, " "displayId=%s, policyFlags=0x%x, " "action=%s, actionButton=0x%x, flags=0x%x, metaState=0x%x, buttonState=0x%x, " - "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, xCursorPosition=%f, " - "yCursorPosition=%f, downTime=%" PRId64, + "xCursorPosition=%f, yCursorPosition=%f, downTime=%" PRId64, args.id, args.eventTime, args.deviceId, inputEventSourceToString(args.source).c_str(), args.displayId.toString().c_str(), args.policyFlags, MotionEvent::actionToString(args.action).c_str(), args.actionButton, args.flags, - args.metaState, args.buttonState, args.edgeFlags, args.xPrecision, args.yPrecision, - args.xCursorPosition, args.yCursorPosition, args.downTime); + args.metaState, args.buttonState, args.xCursorPosition, args.yCursorPosition, + args.downTime); for (uint32_t i = 0; i < args.getPointerCount(); i++) { ALOGD(" Pointer %d: id=%d, toolType=%s, x=%f, y=%f, pressure=%f, size=%f, " "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, orientation=%f", @@ -4672,10 +4652,9 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs& args) { if (args.id != android::os::IInputConstants::INVALID_INPUT_EVENT_ID && IdGenerator::getSource(args.id) == IdGenerator::Source::INPUT_READER && !mInputFilterEnabled) { - const bool isDown = args.action == AMOTION_EVENT_ACTION_DOWN; std::set<InputDeviceUsageSource> sources = getUsageSourcesForMotionArgs(args); - mLatencyTracker.trackListener(args.id, isDown, args.eventTime, args.readTime, - args.deviceId, sources); + mLatencyTracker.trackListener(args.id, args.eventTime, args.readTime, args.deviceId, + sources, args.action, InputEventType::MOTION); } needWake = enqueueInboundEventLocked(std::move(newEntry)); @@ -4913,6 +4892,10 @@ InputEventInjectionResult InputDispatcher::injectInputEvent(const InputEvent* ev logDispatchStateLocked(); LOG(ERROR) << "Inconsistent event: " << motionEvent << ", reason: " << result.error(); + if (policyFlags & POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY) { + mLock.unlock(); + return InputEventInjectionResult::FAILED; + } } } @@ -5459,7 +5442,8 @@ void InputDispatcher::setInputWindowsLocked( for (DeviceId deviceId : erasedDevices) { CancelationOptions options(CancelationOptions::Mode::CANCEL_HOVER_EVENTS, - "WindowInfo changed", traceContext.getTracker()); + "WindowInfo changed", + traceContext.getTracker()); options.deviceId = deviceId; synthesizeCancelationEventsForWindowLocked(touchedWindow.windowHandle, options); } @@ -5710,7 +5694,7 @@ void InputDispatcher::setMaximumObscuringOpacityForTouch(float opacity) { mMaximumObscuringOpacityForTouch = opacity; } -std::tuple<TouchState*, TouchedWindow*, ui::LogicalDisplayId /*displayId*/> +std::tuple<TouchState*, TouchedWindow*, ui::LogicalDisplayId> InputDispatcher::findTouchStateWindowAndDisplayLocked(const sp<IBinder>& token) { for (auto& [displayId, state] : mTouchStatesByDisplay) { for (TouchedWindow& w : state.windows) { @@ -5722,6 +5706,22 @@ InputDispatcher::findTouchStateWindowAndDisplayLocked(const sp<IBinder>& token) return std::make_tuple(nullptr, nullptr, ui::LogicalDisplayId::DEFAULT); } +std::tuple<const TouchState*, const TouchedWindow*, ui::LogicalDisplayId> +InputDispatcher::findTouchStateWindowAndDisplayLocked(const sp<IBinder>& token) const { + return const_cast<InputDispatcher*>(this)->findTouchStateWindowAndDisplayLocked(token); +} + +bool InputDispatcher::windowHasTouchingPointersLocked(const sp<WindowInfoHandle>& windowHandle, + DeviceId deviceId) const { + const auto& [touchState, touchedWindow, _] = + findTouchStateWindowAndDisplayLocked(windowHandle->getToken()); + if (touchState == nullptr) { + // No touching pointers at all + return false; + } + return touchState->hasTouchingPointers(deviceId); +} + bool InputDispatcher::transferTouchGesture(const sp<IBinder>& fromToken, const sp<IBinder>& toToken, bool isDragDrop) { if (fromToken == toToken) { @@ -6058,17 +6058,12 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) const { dump += StringPrintf(INDENT3 "OutboundQueue: length=%zu\n", connection->outboundQueue.size()); dump += dumpQueue(connection->outboundQueue, currentTime); - - } else { - dump += INDENT3 "OutboundQueue: <empty>\n"; } if (!connection->waitQueue.empty()) { dump += StringPrintf(INDENT3 "WaitQueue: length=%zu\n", connection->waitQueue.size()); dump += dumpQueue(connection->waitQueue, currentTime); - } else { - dump += INDENT3 "WaitQueue: <empty>\n"; } std::string inputStateDump = streamableToString(connection->inputState); if (!inputStateDump.empty()) { @@ -7062,6 +7057,13 @@ void InputDispatcher::onWindowInfosChanged(const gui::WindowInfosUpdate& update) for (const auto& info : update.windowInfos) { handlesPerDisplay.emplace(info.displayId, std::vector<sp<WindowInfoHandle>>()); handlesPerDisplay[info.displayId].push_back(sp<WindowInfoHandle>::make(info)); + if (input_flags::split_all_touches()) { + handlesPerDisplay[info.displayId] + .back() + ->editInfo() + ->setInputConfig(android::gui::WindowInfo::InputConfig::PREVENT_SPLITTING, + false); + } } { // acquire lock @@ -7137,9 +7139,11 @@ void InputDispatcher::setMonitorDispatchingTimeoutForTest(std::chrono::nanosecon void InputDispatcher::slipWallpaperTouch(ftl::Flags<InputTarget::Flags> targetFlags, const sp<WindowInfoHandle>& oldWindowHandle, const sp<WindowInfoHandle>& newWindowHandle, - TouchState& state, DeviceId deviceId, - const PointerProperties& pointerProperties, + TouchState& state, const MotionEntry& entry, std::vector<InputTarget>& targets) const { + LOG_IF(FATAL, entry.getPointerCount() != 1) << "Entry not eligible for slip: " << entry; + const DeviceId deviceId = entry.deviceId; + const PointerProperties& pointerProperties = entry.pointerProperties[0]; std::vector<PointerProperties> pointers{pointerProperties}; const bool oldHasWallpaper = oldWindowHandle->getInfo()->inputConfig.test( gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER); @@ -7166,7 +7170,7 @@ void InputDispatcher::slipWallpaperTouch(ftl::Flags<InputTarget::Flags> targetFl state.addOrUpdateWindow(newWallpaper, InputTarget::DispatchMode::SLIPPERY_ENTER, InputTarget::Flags::WINDOW_IS_OBSCURED | InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED, - deviceId, pointers); + deviceId, pointers, entry.eventTime); } } @@ -7240,11 +7244,13 @@ sp<WindowInfoHandle> InputDispatcher::findWallpaperWindowBelow( } void InputDispatcher::setKeyRepeatConfiguration(std::chrono::nanoseconds timeout, - std::chrono::nanoseconds delay) { + std::chrono::nanoseconds delay, + bool keyRepeatEnabled) { std::scoped_lock _l(mLock); mConfig.keyRepeatTimeout = timeout.count(); mConfig.keyRepeatDelay = delay.count(); + mConfig.keyRepeatEnabled = keyRepeatEnabled; } bool InputDispatcher::isPointerInWindow(const sp<android::IBinder>& token, diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index e2fc7a0d4f..1904058080 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -97,7 +97,6 @@ public: status_t stop() override; void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override; - void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override; void notifyKey(const NotifyKeyArgs& args) override; void notifyMotion(const NotifyMotionArgs& args) override; void notifySwitch(const NotifySwitchArgs& args) override; @@ -154,8 +153,8 @@ public: // Public to allow tests to verify that a Monitor can get ANR. void setMonitorDispatchingTimeoutForTest(std::chrono::nanoseconds timeout); - void setKeyRepeatConfiguration(std::chrono::nanoseconds timeout, - std::chrono::nanoseconds delay) override; + void setKeyRepeatConfiguration(std::chrono::nanoseconds timeout, std::chrono::nanoseconds delay, + bool keyRepeatEnabled) override; bool isPointerInWindow(const sp<IBinder>& token, ui::LogicalDisplayId displayId, DeviceId deviceId, int32_t pointerId) override; @@ -258,7 +257,8 @@ private: int32_t pointerId) const REQUIRES(mLock); std::vector<sp<android::gui::WindowInfoHandle>> findTouchedSpyWindowsAtLocked( - ui::LogicalDisplayId displayId, float x, float y, bool isStylus) const REQUIRES(mLock); + ui::LogicalDisplayId displayId, float x, float y, bool isStylus, + DeviceId deviceId) const REQUIRES(mLock); sp<android::gui::WindowInfoHandle> findTouchedForegroundWindowLocked( ui::LogicalDisplayId displayId) const REQUIRES(mLock); @@ -446,8 +446,6 @@ private: REQUIRES(mLock); // Dispatch inbound events. - bool dispatchConfigurationChangedLocked(nsecs_t currentTime, - const ConfigurationChangedEntry& entry) REQUIRES(mLock); bool dispatchDeviceResetLocked(nsecs_t currentTime, const DeviceResetEntry& entry) REQUIRES(mLock); bool dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<const KeyEntry> entry, @@ -536,12 +534,11 @@ private: void resetNoFocusedWindowTimeoutLocked() REQUIRES(mLock); ui::LogicalDisplayId getTargetDisplayId(const EventEntry& entry); - sp<android::gui::WindowInfoHandle> findFocusedWindowTargetLocked( - nsecs_t currentTime, const EventEntry& entry, nsecs_t& nextWakeupTime, - android::os::InputEventInjectionResult& outInjectionResult) REQUIRES(mLock); - std::vector<InputTarget> findTouchedWindowTargetsLocked( - nsecs_t currentTime, const MotionEntry& entry, - android::os::InputEventInjectionResult& outInjectionResult) REQUIRES(mLock); + base::Result<sp<android::gui::WindowInfoHandle>, android::os::InputEventInjectionResult> + findFocusedWindowTargetLocked(nsecs_t currentTime, const EventEntry& entry, + nsecs_t& nextWakeupTime) REQUIRES(mLock); + base::Result<std::vector<InputTarget>, android::os::InputEventInjectionResult> + findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry& entry) REQUIRES(mLock); std::vector<Monitor> selectResponsiveMonitorsLocked( const std::vector<Monitor>& gestureMonitors) const REQUIRES(mLock); @@ -684,16 +681,21 @@ private: const std::string& reason) REQUIRES(mLock); void updateLastAnrStateLocked(const std::string& windowLabel, const std::string& reason) REQUIRES(mLock); - std::map<ui::LogicalDisplayId /*displayId*/, InputVerifier> mVerifiersByDisplay; + std::map<ui::LogicalDisplayId, InputVerifier> mVerifiersByDisplay; // Returns a fallback KeyEntry that should be sent to the connection, if required. std::unique_ptr<const KeyEntry> afterKeyEventLockedInterruptable( const std::shared_ptr<Connection>& connection, DispatchEntry* dispatchEntry, bool handled) REQUIRES(mLock); // Find touched state and touched window by token. - std::tuple<TouchState*, TouchedWindow*, ui::LogicalDisplayId /*displayId*/> + std::tuple<TouchState*, TouchedWindow*, ui::LogicalDisplayId> findTouchStateWindowAndDisplayLocked(const sp<IBinder>& token) REQUIRES(mLock); + std::tuple<const TouchState*, const TouchedWindow*, ui::LogicalDisplayId> + findTouchStateWindowAndDisplayLocked(const sp<IBinder>& token) const REQUIRES(mLock); + bool windowHasTouchingPointersLocked(const sp<android::gui::WindowInfoHandle>& windowHandle, + DeviceId deviceId) const REQUIRES(mLock); + // Statistics gathering. LatencyAggregator mLatencyAggregator GUARDED_BY(mLock); LatencyTracker mLatencyTracker GUARDED_BY(mLock); @@ -707,11 +709,23 @@ private: sp<InputReporterInterface> mReporter; + /** + * Slip the wallpaper touch if necessary. + * + * @param targetFlags the target flags + * @param oldWindowHandle the old window that the touch slipped out of + * @param newWindowHandle the new window that the touch is slipping into + * @param state the current touch state. This will be updated if necessary to reflect the new + * windows that are receiving touch. + * @param deviceId the device id of the current motion being processed + * @param pointerProperties the pointer properties of the current motion being processed + * @param targets the current targets to add the walpaper ones to + * @param eventTime the new downTime for the wallpaper target + */ void slipWallpaperTouch(ftl::Flags<InputTarget::Flags> targetFlags, const sp<android::gui::WindowInfoHandle>& oldWindowHandle, const sp<android::gui::WindowInfoHandle>& newWindowHandle, - TouchState& state, DeviceId deviceId, - const PointerProperties& pointerProperties, + TouchState& state, const MotionEntry& entry, std::vector<InputTarget>& targets) const REQUIRES(mLock); void transferWallpaperTouch(ftl::Flags<InputTarget::Flags> oldTargetFlags, ftl::Flags<InputTarget::Flags> newTargetFlags, diff --git a/services/inputflinger/dispatcher/InputEventTimeline.cpp b/services/inputflinger/dispatcher/InputEventTimeline.cpp index a7c6d162d8..688196461a 100644 --- a/services/inputflinger/dispatcher/InputEventTimeline.cpp +++ b/services/inputflinger/dispatcher/InputEventTimeline.cpp @@ -66,15 +66,16 @@ bool ConnectionTimeline::operator!=(const ConnectionTimeline& rhs) const { return !operator==(rhs); } -InputEventTimeline::InputEventTimeline(bool isDown, nsecs_t eventTime, nsecs_t readTime, - uint16_t vendorId, uint16_t productId, - std::set<InputDeviceUsageSource> sources) - : isDown(isDown), - eventTime(eventTime), +InputEventTimeline::InputEventTimeline(nsecs_t eventTime, nsecs_t readTime, uint16_t vendorId, + uint16_t productId, + const std::set<InputDeviceUsageSource>& sources, + InputEventActionType inputEventActionType) + : eventTime(eventTime), readTime(readTime), vendorId(vendorId), productId(productId), - sources(sources) {} + sources(sources), + inputEventActionType(inputEventActionType) {} bool InputEventTimeline::operator==(const InputEventTimeline& rhs) const { if (connectionTimelines.size() != rhs.connectionTimelines.size()) { @@ -89,8 +90,9 @@ bool InputEventTimeline::operator==(const InputEventTimeline& rhs) const { return false; } } - return isDown == rhs.isDown && eventTime == rhs.eventTime && readTime == rhs.readTime && - vendorId == rhs.vendorId && productId == rhs.productId && sources == rhs.sources; + return eventTime == rhs.eventTime && readTime == rhs.readTime && vendorId == rhs.vendorId && + productId == rhs.productId && sources == rhs.sources && + inputEventActionType == rhs.inputEventActionType; } } // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/InputEventTimeline.h b/services/inputflinger/dispatcher/InputEventTimeline.h index e9deb2d3cf..951fcc8d0c 100644 --- a/services/inputflinger/dispatcher/InputEventTimeline.h +++ b/services/inputflinger/dispatcher/InputEventTimeline.h @@ -74,15 +74,38 @@ private: bool mHasGraphicsTimeline = false; }; +enum class InputEventActionType : int32_t { + UNKNOWN_INPUT_EVENT = 0, + MOTION_ACTION_DOWN = 1, + // Motion events for ACTION_MOVE (characterizes scrolling motion) + MOTION_ACTION_MOVE = 2, + // Motion events for ACTION_UP (when the pointer first goes up) + MOTION_ACTION_UP = 3, + // Motion events for ACTION_HOVER_MOVE (pointer position on screen changes but pointer is not + // down) + MOTION_ACTION_HOVER_MOVE = 4, + // Motion events for ACTION_SCROLL (moving the mouse wheel) + MOTION_ACTION_SCROLL = 5, + // Key events for both ACTION_DOWN and ACTION_UP (key press and key release) + KEY = 6, + + ftl_first = UNKNOWN_INPUT_EVENT, + ftl_last = KEY, + // Used by latency fuzzer + kMaxValue = ftl_last + +}; + struct InputEventTimeline { - InputEventTimeline(bool isDown, nsecs_t eventTime, nsecs_t readTime, uint16_t vendorId, - uint16_t productId, std::set<InputDeviceUsageSource> sources); - const bool isDown; // True if this is an ACTION_DOWN event + InputEventTimeline(nsecs_t eventTime, nsecs_t readTime, uint16_t vendorId, uint16_t productId, + const std::set<InputDeviceUsageSource>& sources, + InputEventActionType inputEventActionType); const nsecs_t eventTime; const nsecs_t readTime; const uint16_t vendorId; const uint16_t productId; const std::set<InputDeviceUsageSource> sources; + const InputEventActionType inputEventActionType; struct IBinderHash { std::size_t operator()(const sp<IBinder>& b) const { diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp index e283fc3b4d..9b5a79b24d 100644 --- a/services/inputflinger/dispatcher/InputState.cpp +++ b/services/inputflinger/dispatcher/InputState.cpp @@ -649,7 +649,9 @@ std::ostream& operator<<(std::ostream& out, const InputState& state) { if (!state.mMotionMementos.empty()) { out << "mMotionMementos: "; for (const InputState::MotionMemento& memento : state.mMotionMementos) { - out << "{deviceId= " << memento.deviceId << ", hovering=" << memento.hovering << "}, "; + out << "{deviceId=" << memento.deviceId + << ", hovering=" << std::to_string(memento.hovering) + << ", downTime=" << memento.downTime << "}, "; } } return out; diff --git a/services/inputflinger/dispatcher/LatencyAggregator.cpp b/services/inputflinger/dispatcher/LatencyAggregator.cpp index e09d97a13b..4ddd2e9e95 100644 --- a/services/inputflinger/dispatcher/LatencyAggregator.cpp +++ b/services/inputflinger/dispatcher/LatencyAggregator.cpp @@ -134,7 +134,9 @@ void LatencyAggregator::processStatistics(const InputEventTimeline& timeline) { mNumSketchEventsProcessed++; std::array<std::unique_ptr<KllQuantile>, SketchIndex::SIZE>& sketches = - timeline.isDown ? mDownSketches : mMoveSketches; + timeline.inputEventActionType == InputEventActionType::MOTION_ACTION_DOWN + ? mDownSketches + : mMoveSketches; // Process common ones first const nsecs_t eventToRead = timeline.readTime - timeline.eventTime; @@ -242,7 +244,9 @@ void LatencyAggregator::processSlowEvent(const InputEventTimeline& timeline) { const nsecs_t consumeToGpuComplete = gpuCompletedTime - connectionTimeline.consumeTime; const nsecs_t gpuCompleteToPresent = presentTime - gpuCompletedTime; - android::util::stats_write(android::util::SLOW_INPUT_EVENT_REPORTED, timeline.isDown, + android::util::stats_write(android::util::SLOW_INPUT_EVENT_REPORTED, + timeline.inputEventActionType == + InputEventActionType::MOTION_ACTION_DOWN, static_cast<int32_t>(ns2us(eventToRead)), static_cast<int32_t>(ns2us(readToDeliver)), static_cast<int32_t>(ns2us(deliverToConsume)), diff --git a/services/inputflinger/dispatcher/LatencyTracker.cpp b/services/inputflinger/dispatcher/LatencyTracker.cpp index 698bd9ff08..69024b326a 100644 --- a/services/inputflinger/dispatcher/LatencyTracker.cpp +++ b/services/inputflinger/dispatcher/LatencyTracker.cpp @@ -67,9 +67,10 @@ LatencyTracker::LatencyTracker(InputEventTimelineProcessor* processor) LOG_ALWAYS_FATAL_IF(processor == nullptr); } -void LatencyTracker::trackListener(int32_t inputEventId, bool isDown, nsecs_t eventTime, - nsecs_t readTime, DeviceId deviceId, - const std::set<InputDeviceUsageSource>& sources) { +void LatencyTracker::trackListener(int32_t inputEventId, nsecs_t eventTime, nsecs_t readTime, + DeviceId deviceId, + const std::set<InputDeviceUsageSource>& sources, + int32_t inputEventAction, InputEventType inputEventType) { reportAndPruneMatureRecords(eventTime); const auto it = mTimelines.find(inputEventId); if (it != mTimelines.end()) { @@ -101,9 +102,41 @@ void LatencyTracker::trackListener(int32_t inputEventId, bool isDown, nsecs_t ev return; } + const InputEventActionType inputEventActionType = [&]() { + switch (inputEventType) { + case InputEventType::MOTION: { + switch (MotionEvent::getActionMasked(inputEventAction)) { + case AMOTION_EVENT_ACTION_DOWN: + return InputEventActionType::MOTION_ACTION_DOWN; + case AMOTION_EVENT_ACTION_MOVE: + return InputEventActionType::MOTION_ACTION_MOVE; + case AMOTION_EVENT_ACTION_UP: + return InputEventActionType::MOTION_ACTION_UP; + case AMOTION_EVENT_ACTION_HOVER_MOVE: + return InputEventActionType::MOTION_ACTION_HOVER_MOVE; + case AMOTION_EVENT_ACTION_SCROLL: + return InputEventActionType::MOTION_ACTION_SCROLL; + default: + return InputEventActionType::UNKNOWN_INPUT_EVENT; + } + } + case InputEventType::KEY: { + switch (inputEventAction) { + case AKEY_EVENT_ACTION_DOWN: + case AKEY_EVENT_ACTION_UP: + return InputEventActionType::KEY; + default: + return InputEventActionType::UNKNOWN_INPUT_EVENT; + } + } + default: + return InputEventActionType::UNKNOWN_INPUT_EVENT; + } + }(); + mTimelines.emplace(inputEventId, - InputEventTimeline(isDown, eventTime, readTime, identifier->vendor, - identifier->product, sources)); + InputEventTimeline(eventTime, readTime, identifier->vendor, + identifier->product, sources, inputEventActionType)); mEventTimes.emplace(eventTime, inputEventId); } diff --git a/services/inputflinger/dispatcher/LatencyTracker.h b/services/inputflinger/dispatcher/LatencyTracker.h index 890d61d431..b4053ba1b3 100644 --- a/services/inputflinger/dispatcher/LatencyTracker.h +++ b/services/inputflinger/dispatcher/LatencyTracker.h @@ -52,8 +52,9 @@ public: * duplicate events that happen to have the same eventTime and inputEventId. Therefore, we * must drop all duplicate data. */ - void trackListener(int32_t inputEventId, bool isDown, nsecs_t eventTime, nsecs_t readTime, - DeviceId deviceId, const std::set<InputDeviceUsageSource>& sources); + void trackListener(int32_t inputEventId, nsecs_t eventTime, nsecs_t readTime, DeviceId deviceId, + const std::set<InputDeviceUsageSource>& sources, int32_t inputEventAction, + InputEventType inputEventType); void trackFinishedEvent(int32_t inputEventId, const sp<IBinder>& connectionToken, nsecs_t deliveryTime, nsecs_t consumeTime, nsecs_t finishTime); void trackGraphicsLatency(int32_t inputEventId, const sp<IBinder>& connectionToken, diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h index 3fbe584a64..451d91704c 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::nullopt); + std::optional<nsecs_t> firstDownTimeInTarget); void addHoveringPointerToWindow(const sp<android::gui::WindowInfoHandle>& windowHandle, DeviceId deviceId, const PointerProperties& pointer, float x, float y); diff --git a/services/inputflinger/dispatcher/include/InputDispatcherConfiguration.h b/services/inputflinger/dispatcher/include/InputDispatcherConfiguration.h index 5eb3a32ef9..ba197d45fd 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherConfiguration.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherConfiguration.h @@ -34,8 +34,13 @@ struct InputDispatcherConfiguration { // The key repeat inter-key delay. nsecs_t keyRepeatDelay; + // Whether key repeat is enabled. + bool keyRepeatEnabled; + InputDispatcherConfiguration() - : keyRepeatTimeout(500 * 1000000LL), keyRepeatDelay(50 * 1000000LL) {} + : keyRepeatTimeout(500 * 1000000LL), + keyRepeatDelay(50 * 1000000LL), + keyRepeatEnabled(true) {} }; } // namespace android diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h index 653f595670..463a95238b 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h @@ -227,10 +227,11 @@ public: virtual void cancelCurrentTouch() = 0; /* - * Updates key repeat configuration timeout and delay. + * Updates whether key repeat is enabled and key repeat configuration timeout and delay. */ virtual void setKeyRepeatConfiguration(std::chrono::nanoseconds timeout, - std::chrono::nanoseconds delay) = 0; + std::chrono::nanoseconds delay, + bool keyRepeatEnabled) = 0; /* * Determine if a pointer from a device is being dispatched to the given window. diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h index 65fb76d274..b885ba17f3 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h @@ -43,9 +43,6 @@ public: InputDispatcherPolicyInterface() = default; virtual ~InputDispatcherPolicyInterface() = default; - /* Notifies the system that a configuration change has occurred. */ - virtual void notifyConfigurationChanged(nsecs_t when) = 0; - /* Notifies the system that an application does not have a focused window. */ virtual void notifyNoFocusedWindowAnr( diff --git a/services/inputflinger/include/InputListener.h b/services/inputflinger/include/InputListener.h index 0b7f7c2831..d8a9afaedd 100644 --- a/services/inputflinger/include/InputListener.h +++ b/services/inputflinger/include/InputListener.h @@ -38,7 +38,6 @@ public: virtual ~InputListenerInterface() { } virtual void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) = 0; - virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) = 0; virtual void notifyKey(const NotifyKeyArgs& args) = 0; virtual void notifyMotion(const NotifyMotionArgs& args) = 0; virtual void notifySwitch(const NotifySwitchArgs& args) = 0; @@ -60,7 +59,6 @@ public: explicit QueuedInputListener(InputListenerInterface& innerListener); virtual void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override; - virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override; virtual void notifyKey(const NotifyKeyArgs& args) override; virtual void notifyMotion(const NotifyMotionArgs& args) override; virtual void notifySwitch(const NotifySwitchArgs& args) override; @@ -84,7 +82,6 @@ public: explicit TracedInputListener(const char* name, InputListenerInterface& innerListener); virtual void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override; - virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override; virtual void notifyKey(const NotifyKeyArgs& args) override; virtual void notifyMotion(const NotifyMotionArgs& args) override; virtual void notifySwitch(const NotifySwitchArgs& args) override; diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h index 889ee09e6f..2f6c6d74e7 100644 --- a/services/inputflinger/include/InputReaderBase.h +++ b/services/inputflinger/include/InputReaderBase.h @@ -34,7 +34,9 @@ #include <vector> #include "PointerControllerInterface.h" +#include "TouchpadHardwareState.h" #include "VibrationElement.h" +#include "include/gestures.h" // Maximum supported size of a vibration pattern. // Must be at least 2. @@ -91,6 +93,9 @@ struct InputReaderConfiguration { // The touchpad settings changed. TOUCHPAD_SETTINGS = 1u << 13, + // The key remapping has changed. + KEY_REMAPPING = 1u << 14, + // All devices must be reopened. MUST_REOPEN = 1u << 31, }; @@ -227,6 +232,9 @@ struct InputReaderConfiguration { // True to enable tap dragging on touchpads. bool touchpadTapDraggingEnabled; + // True if hardware state update notifications should be sent to the policy. + bool shouldNotifyTouchpadHardwareState; + // True to enable a zone on the right-hand side of touchpads where clicks will be turned into // context (a.k.a. "right") clicks. bool touchpadRightClickZoneEnabled; @@ -241,6 +249,9 @@ struct InputReaderConfiguration { // True if a pointer icon should be shown for direct stylus pointers. bool stylusPointerIconEnabled; + // Keycodes to be remapped. + std::map<int32_t /* fromKeyCode */, int32_t /* toKeyCode */> keyRemapping; + InputReaderConfiguration() : virtualKeyQuietTime(0), defaultPointerDisplayId(ui::LogicalDisplayId::DEFAULT), @@ -268,6 +279,7 @@ struct InputReaderConfiguration { touchpadNaturalScrollingEnabled(true), touchpadTapToClickEnabled(true), touchpadTapDraggingEnabled(false), + shouldNotifyTouchpadHardwareState(false), touchpadRightClickZoneEnabled(false), stylusButtonMotionEventsEnabled(true), stylusPointerIconEnabled(false) {} @@ -327,9 +339,6 @@ public: virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask, int32_t keyCode) = 0; virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw) = 0; - virtual void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, - int32_t toKeyCode) const = 0; - virtual int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const = 0; /* Toggle Caps Lock */ @@ -363,6 +372,8 @@ public: virtual std::vector<InputDeviceSensorInfo> getSensors(int32_t deviceId) = 0; + virtual std::optional<HardwareProperties> getTouchpadHardwareProperties(int32_t deviceId) = 0; + /* Return true if the device can send input events to the specified display. */ virtual bool canDispatchToDisplay(int32_t deviceId, ui::LogicalDisplayId displayId) = 0; @@ -397,6 +408,9 @@ public: * Returns ReservedInputDeviceId::INVALID_INPUT_DEVICE_ID if no device has been used since boot. */ virtual DeviceId getLastUsedInputDeviceId() = 0; + + /* Notifies that mouse cursor faded due to typing. */ + virtual void notifyMouseCursorFadedOnTyping() = 0; }; // --- TouchAffineTransformation --- @@ -451,6 +465,13 @@ public: */ virtual void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) = 0; + /* Sends the hardware state of a connected touchpad */ + virtual void notifyTouchpadHardwareState(const SelfContainedHardwareState& schs, + int32_t deviceId) = 0; + + /* Sends the Info of gestures that happen on the touchpad. */ + virtual void notifyTouchpadGestureInfo(GestureType type, int32_t deviceId) = 0; + /* Gets the keyboard layout for a particular input device. */ virtual std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay( const InputDeviceIdentifier& identifier, diff --git a/services/inputflinger/include/InputThread.h b/services/inputflinger/include/InputThread.h index 5e75027056..fcd913db7c 100644 --- a/services/inputflinger/include/InputThread.h +++ b/services/inputflinger/include/InputThread.h @@ -38,6 +38,7 @@ private: std::string mName; std::function<void()> mThreadWake; sp<Thread> mThread; + bool applyInputEventProfile(); }; } // namespace android diff --git a/services/inputflinger/include/NotifyArgs.h b/services/inputflinger/include/NotifyArgs.h index db417cf830..14487fe04e 100644 --- a/services/inputflinger/include/NotifyArgs.h +++ b/services/inputflinger/include/NotifyArgs.h @@ -39,21 +39,6 @@ struct NotifyInputDevicesChangedArgs { NotifyInputDevicesChangedArgs& operator=(const NotifyInputDevicesChangedArgs&) = default; }; -/* Describes a configuration change event. */ -struct NotifyConfigurationChangedArgs { - int32_t id; - nsecs_t eventTime; - - inline NotifyConfigurationChangedArgs() {} - - NotifyConfigurationChangedArgs(int32_t id, nsecs_t eventTime); - - bool operator==(const NotifyConfigurationChangedArgs& rhs) const = default; - - NotifyConfigurationChangedArgs(const NotifyConfigurationChangedArgs& other) = default; - NotifyConfigurationChangedArgs& operator=(const NotifyConfigurationChangedArgs&) = default; -}; - /* Describes a key event. */ struct NotifyKeyArgs { int32_t id; @@ -234,8 +219,8 @@ struct NotifyVibratorStateArgs { }; using NotifyArgs = - std::variant<NotifyInputDevicesChangedArgs, NotifyConfigurationChangedArgs, NotifyKeyArgs, - NotifyMotionArgs, NotifySensorArgs, NotifySwitchArgs, NotifyDeviceResetArgs, + std::variant<NotifyInputDevicesChangedArgs, NotifyKeyArgs, NotifyMotionArgs, + NotifySensorArgs, NotifySwitchArgs, NotifyDeviceResetArgs, NotifyPointerCaptureChangedArgs, NotifyVibratorStateArgs>; const char* toString(const NotifyArgs& args); diff --git a/services/inputflinger/include/PointerChoreographerPolicyInterface.h b/services/inputflinger/include/PointerChoreographerPolicyInterface.h index 7a85c12559..e1f8fdaaa0 100644 --- a/services/inputflinger/include/PointerChoreographerPolicyInterface.h +++ b/services/inputflinger/include/PointerChoreographerPolicyInterface.h @@ -58,6 +58,9 @@ public: /* Returns true if any InputConnection is currently active. */ virtual bool isInputMethodConnectionActive() = 0; + + /* Notifies that mouse cursor faded due to typing. */ + virtual void notifyMouseCursorFadedOnTyping() = 0; }; } // namespace android diff --git a/services/inputflinger/include/PointerControllerInterface.h b/services/inputflinger/include/PointerControllerInterface.h index e34ed0fbd8..8f3d9ca778 100644 --- a/services/inputflinger/include/PointerControllerInterface.h +++ b/services/inputflinger/include/PointerControllerInterface.h @@ -72,10 +72,6 @@ public: /* Dumps the state of the pointer controller. */ virtual std::string dump() = 0; - /* Gets the bounds of the region that the pointer can traverse. - * Returns true if the bounds are available. */ - virtual std::optional<FloatRect> getBounds() const = 0; - /* Move the pointer. */ virtual void move(float deltaX, float deltaY) = 0; diff --git a/services/inputflinger/include/TouchpadHardwareState.h b/services/inputflinger/include/TouchpadHardwareState.h new file mode 100644 index 0000000000..a8dd44c82e --- /dev/null +++ b/services/inputflinger/include/TouchpadHardwareState.h @@ -0,0 +1,32 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <include/gestures.h> +#include <vector> + +namespace android { + +// A Gestures library HardwareState struct (from libchrome-gestures), but bundled +// with a vector to contain its FingerStates, so you don't have to worry about where +// that memory is allocated. +struct SelfContainedHardwareState { + HardwareState state; + std::vector<FingerState> fingers; +}; + +} // namespace android diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp index e76b648ce5..b76e8c515f 100644 --- a/services/inputflinger/reader/Android.bp +++ b/services/inputflinger/reader/Android.bp @@ -78,6 +78,7 @@ cc_defaults { name: "libinputreader_defaults", srcs: [":libinputreader_sources"], shared_libs: [ + "android.companion.virtualdevice.flags-aconfig-cc", "libbase", "libcap", "libcrypto", diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp index fe70a51b81..0865eed4a2 100644 --- a/services/inputflinger/reader/EventHub.cpp +++ b/services/inputflinger/reader/EventHub.cpp @@ -33,6 +33,8 @@ #include <sys/sysmacros.h> #include <unistd.h> +#include <android_companion_virtualdevice_flags.h> + #define LOG_TAG "EventHub" // #define LOG_NDEBUG 0 @@ -68,6 +70,8 @@ using android::base::StringPrintf; namespace android { +namespace vd_flags = android::companion::virtualdevice::flags; + using namespace ftl::flag_operators; static const char* DEVICE_INPUT_PATH = "/dev/input"; @@ -513,10 +517,10 @@ ftl::Flags<InputDeviceClass> getAbsAxisUsage(int32_t axis, // --- RawAbsoluteAxisInfo --- -std::ostream& operator<<(std::ostream& out, const RawAbsoluteAxisInfo& info) { - if (info.valid) { - out << "min=" << info.minValue << ", max=" << info.maxValue << ", flat=" << info.flat - << ", fuzz=" << info.fuzz << ", resolution=" << info.resolution; +std::ostream& operator<<(std::ostream& out, const std::optional<RawAbsoluteAxisInfo>& info) { + if (info) { + out << "min=" << info->minValue << ", max=" << info->maxValue << ", flat=" << info->flat + << ", fuzz=" << info->fuzz << ", resolution=" << info->resolution; } else { out << "unknown range"; } @@ -645,7 +649,6 @@ void EventHub::Device::populateAbsoluteAxisStates() { continue; } auto& [axisInfo, value] = absState[axis]; - axisInfo.valid = true; axisInfo.minValue = info.minimum; axisInfo.maxValue = info.maximum; axisInfo.flat = info.flat; @@ -885,7 +888,6 @@ EventHub::EventHub(void) : mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD), mNextDeviceId(1), mControllerNumbers(), - mNeedToSendFinishedDeviceScan(false), mNeedToReopenDevices(false), mNeedToScanDevices(true), mPendingEventCount(0), @@ -998,26 +1000,23 @@ std::optional<PropertyMap> EventHub::getConfiguration(int32_t deviceId) const { return *device->configuration; } -status_t EventHub::getAbsoluteAxisInfo(int32_t deviceId, int axis, - RawAbsoluteAxisInfo* outAxisInfo) const { - outAxisInfo->clear(); +std::optional<RawAbsoluteAxisInfo> EventHub::getAbsoluteAxisInfo(int32_t deviceId, int axis) const { if (axis < 0 || axis > ABS_MAX) { - return NAME_NOT_FOUND; + return std::nullopt; } std::scoped_lock _l(mLock); const Device* device = getDeviceLocked(deviceId); if (device == nullptr) { - return NAME_NOT_FOUND; + return std::nullopt; } // We can read the RawAbsoluteAxisInfo even if the device is disabled and doesn't have a valid // fd, because the info is populated once when the device is first opened, and it doesn't change // throughout the device lifecycle. auto it = device->absState.find(axis); if (it == device->absState.end()) { - return NAME_NOT_FOUND; + return std::nullopt; } - *outAxisInfo = it->second.info; - return OK; + return it->second.info; } bool EventHub::hasRelativeAxis(int32_t deviceId, int axis) const { @@ -1130,22 +1129,20 @@ int32_t EventHub::getSwitchState(int32_t deviceId, int32_t sw) const { return device->swState.test(sw) ? AKEY_STATE_DOWN : AKEY_STATE_UP; } -status_t EventHub::getAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t* outValue) const { - *outValue = 0; +std::optional<int32_t> EventHub::getAbsoluteAxisValue(int32_t deviceId, int32_t axis) const { if (axis < 0 || axis > ABS_MAX) { - return NAME_NOT_FOUND; + return std::nullopt; } std::scoped_lock _l(mLock); const Device* device = getDeviceLocked(deviceId); if (device == nullptr || !device->hasValidFd()) { - return NAME_NOT_FOUND; + return std::nullopt; } const auto it = device->absState.find(axis); if (it == device->absState.end()) { - return NAME_NOT_FOUND; + return std::nullopt; } - *outValue = it->second.value; - return OK; + return it->second.value; } base::Result<std::vector<int32_t>> EventHub::getMtSlotValues(int32_t deviceId, int32_t axis, @@ -1180,7 +1177,8 @@ bool EventHub::markSupportedKeyCodes(int32_t deviceId, const std::vector<int32_t return false; } -void EventHub::addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const { +void EventHub::setKeyRemapping(int32_t deviceId, + const std::map<int32_t, int32_t>& keyRemapping) const { std::scoped_lock _l(mLock); Device* device = getDeviceLocked(deviceId); if (device == nullptr) { @@ -1188,7 +1186,7 @@ void EventHub::addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t to } const std::shared_ptr<KeyCharacterMap> kcm = device->getKeyCharacterMap(); if (kcm) { - kcm->addKeyRemapping(fromKeyCode, toKeyCode); + kcm->setKeyRemapping(keyRemapping); } } @@ -1879,7 +1877,6 @@ std::vector<RawEvent> EventHub::getEvents(int timeoutMillis) { .type = DEVICE_REMOVED, }); it = mClosingDevices.erase(it); - mNeedToSendFinishedDeviceScan = true; if (events.size() == EVENT_BUFFER_SIZE) { break; } @@ -1888,7 +1885,6 @@ std::vector<RawEvent> EventHub::getEvents(int timeoutMillis) { if (mNeedToScanDevices) { mNeedToScanDevices = false; scanDevicesLocked(); - mNeedToSendFinishedDeviceScan = true; } while (!mOpeningDevices.empty()) { @@ -1917,18 +1913,6 @@ std::vector<RawEvent> EventHub::getEvents(int timeoutMillis) { if (!inserted) { ALOGW("Device id %d exists, replaced.", device->id); } - mNeedToSendFinishedDeviceScan = true; - if (events.size() == EVENT_BUFFER_SIZE) { - break; - } - } - - if (mNeedToSendFinishedDeviceScan) { - mNeedToSendFinishedDeviceScan = false; - events.push_back({ - .when = now, - .type = FINISHED_DEVICE_SCAN, - }); if (events.size() == EVENT_BUFFER_SIZE) { break; } @@ -2503,6 +2487,12 @@ void EventHub::openDeviceLocked(const std::string& devicePath) { } } + // See if the device is a rotary encoder with a single scroll axis and nothing else. + if (vd_flags::virtual_rotary() && device->classes == ftl::Flags<InputDeviceClass>(0) && + device->relBitmask.test(REL_WHEEL) && !device->relBitmask.test(REL_HWHEEL)) { + device->classes |= InputDeviceClass::ROTARY_ENCODER; + } + // If the device isn't recognized as something we handle, don't monitor it. if (device->classes == ftl::Flags<InputDeviceClass>(0)) { ALOGV("Dropping device: id=%d, path='%s', name='%s'", deviceId, devicePath.c_str(), diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp index 2daf195757..6185f1ab9e 100644 --- a/services/inputflinger/reader/InputDevice.cpp +++ b/services/inputflinger/reader/InputDevice.cpp @@ -365,6 +365,18 @@ std::list<NotifyArgs> InputDevice::configureInternal(nsecs_t when, // so update the enabled state when there is a change in display info. out += updateEnableState(when, readerConfig, forceEnable); } + + if (!changes.any() || changes.test(InputReaderConfiguration::Change::KEY_REMAPPING)) { + const bool isFullKeyboard = + (mSources & AINPUT_SOURCE_KEYBOARD) == AINPUT_SOURCE_KEYBOARD && + mKeyboardType == KeyboardType::ALPHABETIC; + if (isFullKeyboard) { + for_each_subdevice([&readerConfig](auto& context) { + context.setKeyRemapping(readerConfig.keyRemapping); + }); + bumpGeneration(); + } + } } return out; } @@ -689,12 +701,6 @@ void InputDevice::updateMetaState(int32_t keyCode) { }); } -void InputDevice::addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode) { - for_each_subdevice([fromKeyCode, toKeyCode](auto& context) { - context.addKeyRemapping(fromKeyCode, toKeyCode); - }); -} - void InputDevice::bumpGeneration() { mGeneration = mContext->bumpGeneration(); } @@ -725,6 +731,15 @@ size_t InputDevice::getMapperCount() { return count; } +std::optional<HardwareProperties> InputDevice::getTouchpadHardwareProperties() { + std::optional<HardwareProperties> result = first_in_mappers<HardwareProperties>( + [](InputMapper& mapper) -> std::optional<HardwareProperties> { + return mapper.getTouchpadHardwareProperties(); + }); + + return result; +} + void InputDevice::updateLedState(bool reset) { for_each_mapper([reset](InputMapper& mapper) { mapper.updateLedState(reset); }); } diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp index ab13ad489b..e579390005 100644 --- a/services/inputflinger/reader/InputReader.cpp +++ b/services/inputflinger/reader/InputReader.cpp @@ -33,6 +33,7 @@ #include <utils/Thread.h> #include "InputDevice.h" +#include "include/gestures.h" using android::base::StringPrintf; @@ -180,6 +181,9 @@ void InputReader::loopOnce() { } if (oldGeneration != mGeneration) { + // Reset global meta state because it depends on connected input devices. + updateGlobalMetaStateLocked(); + inputDevicesChanged = true; inputDevices = getInputDevicesLocked(); mPendingArgs.emplace_back( @@ -247,9 +251,6 @@ std::list<NotifyArgs> InputReader::processEventsLocked(const RawEvent* rawEvents case EventHubInterface::DEVICE_REMOVED: removeDeviceLocked(rawEvent->when, rawEvent->deviceId); break; - case EventHubInterface::FINISHED_DEVICE_SCAN: - handleConfigurationChangedLocked(rawEvent->when); - break; default: ALOG_ASSERT(false); // can't happen break; @@ -414,14 +415,6 @@ int32_t InputReader::nextInputDeviceIdLocked() { return ++mNextInputDeviceId; } -void InputReader::handleConfigurationChangedLocked(nsecs_t when) { - // Reset global meta state because it depends on the list of all configured devices. - updateGlobalMetaStateLocked(); - - // Enqueue configuration changed. - mPendingArgs.emplace_back(NotifyConfigurationChangedArgs{mContext.getNextId(), when}); -} - void InputReader::refreshConfigurationLocked(ConfigurationChanges changes) { mPolicy->getReaderConfiguration(&mConfig); mEventHub->setExcludedDevices(mConfig.excludedDeviceNames); @@ -632,15 +625,6 @@ bool InputReader::markSupportedKeyCodesLocked(int32_t deviceId, uint32_t sourceM return result; } -void InputReader::addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const { - std::scoped_lock _l(mLock); - - InputDevice* device = findInputDeviceLocked(deviceId); - if (device != nullptr) { - device->addKeyRemapping(fromKeyCode, toKeyCode); - } -} - int32_t InputReader::getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const { std::scoped_lock _l(mLock); @@ -825,6 +809,18 @@ std::vector<InputDeviceSensorInfo> InputReader::getSensors(int32_t deviceId) { return device->getDeviceInfo().getSensors(); } +std::optional<HardwareProperties> InputReader::getTouchpadHardwareProperties(int32_t deviceId) { + std::scoped_lock _l(mLock); + + InputDevice* device = findInputDeviceLocked(deviceId); + + if (device == nullptr) { + return {}; + } + + return device->getTouchpadHardwareProperties(); +} + bool InputReader::setLightColor(int32_t deviceId, int32_t lightId, int32_t color) { std::scoped_lock _l(mLock); @@ -907,6 +903,12 @@ DeviceId InputReader::getLastUsedInputDeviceId() { return mLastUsedDeviceId; } +void InputReader::notifyMouseCursorFadedOnTyping() { + std::scoped_lock _l(mLock); + // disable touchpad taps when cursor has faded due to typing + mPreventingTouchpadTaps = true; +} + void InputReader::dump(std::string& dump) { std::scoped_lock _l(mLock); diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h index 7cf584df78..edc30379b2 100644 --- a/services/inputflinger/reader/include/EventHub.h +++ b/services/inputflinger/reader/include/EventHub.h @@ -21,6 +21,7 @@ #include <filesystem> #include <functional> #include <map> +#include <optional> #include <ostream> #include <string> #include <unordered_map> @@ -70,18 +71,14 @@ struct RawEvent { /* Describes an absolute axis. */ struct RawAbsoluteAxisInfo { - bool valid{false}; // true if the information is valid, false otherwise - int32_t minValue{}; // minimum value int32_t maxValue{}; // maximum value int32_t flat{}; // center flat position, eg. flat == 8 means center is between -8 and 8 int32_t fuzz{}; // error tolerance, eg. fuzz == 4 means value is +/- 4 due to noise int32_t resolution{}; // resolution in units per mm or radians per mm - - inline void clear() { *this = RawAbsoluteAxisInfo(); } }; -std::ostream& operator<<(std::ostream& out, const RawAbsoluteAxisInfo& info); +std::ostream& operator<<(std::ostream& out, const std::optional<RawAbsoluteAxisInfo>& info); /* * Input device classes. @@ -257,9 +254,6 @@ public: DEVICE_ADDED = 0x10000000, // Sent when a device is removed. DEVICE_REMOVED = 0x20000000, - // Sent when all added/removed devices from the most recent scan have been reported. - // This event is always sent at least once. - FINISHED_DEVICE_SCAN = 0x30000000, FIRST_SYNTHETIC_EVENT = DEVICE_ADDED, }; @@ -278,8 +272,8 @@ public: */ virtual std::optional<PropertyMap> getConfiguration(int32_t deviceId) const = 0; - virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis, - RawAbsoluteAxisInfo* outAxisInfo) const = 0; + virtual std::optional<RawAbsoluteAxisInfo> getAbsoluteAxisInfo(int32_t deviceId, + int axis) const = 0; virtual bool hasRelativeAxis(int32_t deviceId, int axis) const = 0; @@ -287,8 +281,8 @@ public: virtual bool hasMscEvent(int32_t deviceId, int mscEvent) const = 0; - virtual void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, - int32_t toKeyCode) const = 0; + virtual void setKeyRemapping(int32_t deviceId, + const std::map<int32_t, int32_t>& keyRemapping) const = 0; virtual status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState, int32_t* outKeycode, int32_t* outMetaState, @@ -339,8 +333,7 @@ public: virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const = 0; virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const = 0; virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const = 0; - virtual status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis, - int32_t* outValue) const = 0; + virtual std::optional<int32_t> getAbsoluteAxisValue(int32_t deviceId, int32_t axis) const = 0; /* Query Multi-Touch slot values for an axis. Returns error or an 1 indexed array of size * (slotCount + 1). The value at the 0 index is set to queried axis. */ virtual base::Result<std::vector<int32_t>> getMtSlotValues(int32_t deviceId, int32_t axis, @@ -511,8 +504,8 @@ public: std::optional<PropertyMap> getConfiguration(int32_t deviceId) const override final; - status_t getAbsoluteAxisInfo(int32_t deviceId, int axis, - RawAbsoluteAxisInfo* outAxisInfo) const override final; + std::optional<RawAbsoluteAxisInfo> getAbsoluteAxisInfo(int32_t deviceId, + int axis) const override final; bool hasRelativeAxis(int32_t deviceId, int axis) const override final; @@ -520,8 +513,8 @@ public: bool hasMscEvent(int32_t deviceId, int mscEvent) const override final; - void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, - int32_t toKeyCode) const override final; + void setKeyRemapping(int32_t deviceId, + const std::map<int32_t, int32_t>& keyRemapping) const override final; status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState, int32_t* outKeycode, int32_t* outMetaState, @@ -559,8 +552,8 @@ public: int32_t getSwitchState(int32_t deviceId, int32_t sw) const override final; int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const override final; - status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis, - int32_t* outValue) const override final; + std::optional<int32_t> getAbsoluteAxisValue(int32_t deviceId, + int32_t axis) const override final; base::Result<std::vector<int32_t>> getMtSlotValues(int32_t deviceId, int32_t axis, size_t slotCount) const override final; @@ -793,7 +786,6 @@ private: std::vector<std::unique_ptr<Device>> mOpeningDevices; std::vector<std::unique_ptr<Device>> mClosingDevices; - bool mNeedToSendFinishedDeviceScan; bool mNeedToReopenDevices; bool mNeedToScanDevices; std::vector<std::string> mExcludedDevices; diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h index 4374ff5438..62cc4da5ec 100644 --- a/services/inputflinger/reader/include/InputDevice.h +++ b/services/inputflinger/reader/include/InputDevice.h @@ -72,7 +72,7 @@ public: inline std::optional<std::string> getDeviceTypeAssociation() const { return mAssociatedDeviceType; } - inline std::optional<DisplayViewport> getAssociatedViewport() const { + inline virtual std::optional<DisplayViewport> getAssociatedViewport() const { return mAssociatedViewport; } inline bool hasMic() const { return mHasMic; } @@ -124,8 +124,6 @@ public: int32_t getMetaState(); void updateMetaState(int32_t keyCode); - void addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode); - void setKeyboardType(KeyboardType keyboardType); void bumpGeneration(); @@ -141,6 +139,8 @@ public: size_t getMapperCount(); + std::optional<HardwareProperties> getTouchpadHardwareProperties(); + // construct and add a mapper to the input device template <class T, typename... Args> T& addMapper(int32_t eventHubId, Args... args) { @@ -306,19 +306,17 @@ public: inline int32_t getDeviceControllerNumber() const { return mEventHub->getDeviceControllerNumber(mId); } - inline status_t getAbsoluteAxisInfo(int32_t code, RawAbsoluteAxisInfo* axisInfo) const { - if (const auto status = mEventHub->getAbsoluteAxisInfo(mId, code, axisInfo); status != OK) { - return status; - } + inline std::optional<RawAbsoluteAxisInfo> getAbsoluteAxisInfo(int32_t code) const { + std::optional<RawAbsoluteAxisInfo> info = mEventHub->getAbsoluteAxisInfo(mId, code); // Validate axis info for InputDevice. - if (axisInfo->valid && axisInfo->minValue == axisInfo->maxValue) { + if (info && info->minValue == info->maxValue) { // Historically, we deem axes with the same min and max values as invalid to avoid // dividing by zero when scaling by max - min. // TODO(b/291772515): Perform axis info validation on a per-axis basis when it is used. - axisInfo->valid = false; + return std::nullopt; } - return OK; + return info; } inline bool hasRelativeAxis(int32_t code) const { return mEventHub->hasRelativeAxis(mId, code); @@ -329,8 +327,8 @@ public: inline bool hasMscEvent(int mscEvent) const { return mEventHub->hasMscEvent(mId, mscEvent); } - inline void addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode) const { - mEventHub->addKeyRemapping(mId, fromKeyCode, toKeyCode); + inline void setKeyRemapping(const std::map<int32_t, int32_t>& keyRemapping) const { + mEventHub->setKeyRemapping(mId, keyRemapping); } inline status_t mapKey(int32_t scanCode, int32_t usageCode, int32_t metaState, @@ -380,8 +378,8 @@ public: return mEventHub->getKeyCodeForKeyLocation(mId, locationKeyCode); } inline int32_t getSwitchState(int32_t sw) const { return mEventHub->getSwitchState(mId, sw); } - inline status_t getAbsoluteAxisValue(int32_t code, int32_t* outValue) const { - return mEventHub->getAbsoluteAxisValue(mId, code, outValue); + inline std::optional<int32_t> getAbsoluteAxisValue(int32_t code) const { + return mEventHub->getAbsoluteAxisValue(mId, code); } inline base::Result<std::vector<int32_t>> getMtSlotValues(int32_t axis, size_t slotCount) const { @@ -433,9 +431,7 @@ public: } inline bool hasAbsoluteAxis(int32_t code) const { - RawAbsoluteAxisInfo info; - mEventHub->getAbsoluteAxisInfo(mId, code, &info); - return info.valid; + return mEventHub->getAbsoluteAxisInfo(mId, code).has_value(); } inline bool isKeyPressed(int32_t scanCode) const { return mEventHub->getScanCodeState(mId, scanCode) == AKEY_STATE_DOWN; @@ -443,11 +439,6 @@ public: inline bool isKeyCodePressed(int32_t keyCode) const { return mEventHub->getKeyCodeState(mId, keyCode) == AKEY_STATE_DOWN; } - inline int32_t getAbsoluteAxisValue(int32_t code) const { - int32_t value; - mEventHub->getAbsoluteAxisValue(mId, code, &value); - return value; - } inline bool isDeviceEnabled() { return mEventHub->isDeviceEnabled(mId); } inline status_t enableDevice() { return mEventHub->enableDevice(mId); } inline status_t disableDevice() { return mEventHub->disableDevice(mId); } diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h index 6f8c289093..100387195a 100644 --- a/services/inputflinger/reader/include/InputReader.h +++ b/services/inputflinger/reader/include/InputReader.h @@ -65,8 +65,6 @@ public: int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask, int32_t keyCode) override; int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw) override; - void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const override; - int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const override; void toggleCapsLockState(int32_t deviceId) override; @@ -104,6 +102,8 @@ public: std::vector<InputDeviceSensorInfo> getSensors(int32_t deviceId) override; + std::optional<HardwareProperties> getTouchpadHardwareProperties(int32_t deviceId) override; + bool setLightColor(int32_t deviceId, int32_t lightId, int32_t color) override; bool setLightPlayerId(int32_t deviceId, int32_t lightId, int32_t playerId) override; @@ -118,6 +118,8 @@ public: DeviceId getLastUsedInputDeviceId() override; + void notifyMouseCursorFadedOnTyping() override; + protected: // These members are protected so they can be instrumented by test cases. virtual std::shared_ptr<InputDevice> createDeviceLocked(nsecs_t when, int32_t deviceId, @@ -199,7 +201,7 @@ private: std::unordered_map<std::shared_ptr<InputDevice>, std::vector<int32_t> /*eventHubId*/> mDeviceToEventHubIdsMap GUARDED_BY(mLock); - // true if tap-to-click on touchpad currently disabled + // true if tap-to-click on touchpad is currently disabled bool mPreventingTouchpadTaps GUARDED_BY(mLock){false}; // records timestamp of the last key press on the physical keyboard @@ -219,8 +221,6 @@ private: size_t count) REQUIRES(mLock); [[nodiscard]] std::list<NotifyArgs> timeoutExpiredLocked(nsecs_t when) REQUIRES(mLock); - void handleConfigurationChangedLocked(nsecs_t when) REQUIRES(mLock); - int32_t mGlobalMetaState GUARDED_BY(mLock); void updateGlobalMetaStateLocked() REQUIRES(mLock); int32_t getGlobalMetaStateLocked() REQUIRES(mLock); diff --git a/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp b/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp index 90685dec2b..dd46bbc543 100644 --- a/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp +++ b/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp @@ -16,17 +16,23 @@ #include "CapturedTouchpadEventConverter.h" +#include <optional> #include <sstream> #include <android-base/stringprintf.h> +#include <com_android_input_flags.h> #include <input/PrintTools.h> #include <linux/input-event-codes.h> #include <log/log_main.h> +namespace input_flags = com::android::input::flags; + namespace android { namespace { +static constexpr uint32_t SOURCE = AINPUT_SOURCE_TOUCHPAD; + int32_t actionWithIndex(int32_t action, int32_t index) { return action | (index << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); } @@ -42,6 +48,12 @@ size_t firstUnmarkedBit(T set) { return i; } +void addRawMotionRange(InputDeviceInfo& deviceInfo, int32_t androidAxis, + RawAbsoluteAxisInfo& evdevAxis) { + deviceInfo.addMotionRange(androidAxis, SOURCE, evdevAxis.minValue, evdevAxis.maxValue, + evdevAxis.flat, evdevAxis.fuzz, evdevAxis.resolution); +} + } // namespace CapturedTouchpadEventConverter::CapturedTouchpadEventConverter( @@ -53,32 +65,33 @@ CapturedTouchpadEventConverter::CapturedTouchpadEventConverter( mMotionAccumulator(motionAccumulator), mHasTouchMinor(deviceContext.hasAbsoluteAxis(ABS_MT_TOUCH_MINOR)), mHasToolMinor(deviceContext.hasAbsoluteAxis(ABS_MT_WIDTH_MINOR)) { - RawAbsoluteAxisInfo orientationInfo; - deviceContext.getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &orientationInfo); - if (orientationInfo.valid) { - if (orientationInfo.maxValue > 0) { - mOrientationScale = M_PI_2 / orientationInfo.maxValue; - } else if (orientationInfo.minValue < 0) { - mOrientationScale = -M_PI_2 / orientationInfo.minValue; + if (std::optional<RawAbsoluteAxisInfo> orientation = + deviceContext.getAbsoluteAxisInfo(ABS_MT_ORIENTATION); + orientation) { + if (orientation->maxValue > 0) { + mOrientationScale = M_PI_2 / orientation->maxValue; + } else if (orientation->minValue < 0) { + mOrientationScale = -M_PI_2 / orientation->minValue; } } // TODO(b/275369880): support touch.pressure.calibration and .scale properties when captured. - RawAbsoluteAxisInfo pressureInfo; - deviceContext.getAbsoluteAxisInfo(ABS_MT_PRESSURE, &pressureInfo); - if (pressureInfo.valid && pressureInfo.maxValue > 0) { - mPressureScale = 1.0 / pressureInfo.maxValue; + if (std::optional<RawAbsoluteAxisInfo> pressure = + deviceContext.getAbsoluteAxisInfo(ABS_MT_PRESSURE); + pressure && pressure->maxValue > 0) { + mPressureScale = 1.0 / pressure->maxValue; } - RawAbsoluteAxisInfo touchMajorInfo, toolMajorInfo; - deviceContext.getAbsoluteAxisInfo(ABS_MT_TOUCH_MAJOR, &touchMajorInfo); - deviceContext.getAbsoluteAxisInfo(ABS_MT_WIDTH_MAJOR, &toolMajorInfo); - mHasTouchMajor = touchMajorInfo.valid; - mHasToolMajor = toolMajorInfo.valid; - if (mHasTouchMajor && touchMajorInfo.maxValue != 0) { - mSizeScale = 1.0f / touchMajorInfo.maxValue; - } else if (mHasToolMajor && toolMajorInfo.maxValue != 0) { - mSizeScale = 1.0f / toolMajorInfo.maxValue; + std::optional<RawAbsoluteAxisInfo> touchMajor = + deviceContext.getAbsoluteAxisInfo(ABS_MT_TOUCH_MAJOR); + std::optional<RawAbsoluteAxisInfo> toolMajor = + deviceContext.getAbsoluteAxisInfo(ABS_MT_WIDTH_MAJOR); + mHasTouchMajor = touchMajor.has_value(); + mHasToolMajor = toolMajor.has_value(); + if (mHasTouchMajor && touchMajor->maxValue != 0) { + mSizeScale = 1.0f / touchMajor->maxValue; + } else if (mHasToolMajor && toolMajor->maxValue != 0) { + mSizeScale = 1.0f / toolMajor->maxValue; } } @@ -106,22 +119,27 @@ std::string CapturedTouchpadEventConverter::dump() const { } void CapturedTouchpadEventConverter::populateMotionRanges(InputDeviceInfo& info) const { - tryAddRawMotionRange(/*byref*/ info, AMOTION_EVENT_AXIS_X, ABS_MT_POSITION_X); - tryAddRawMotionRange(/*byref*/ info, AMOTION_EVENT_AXIS_Y, ABS_MT_POSITION_Y); + if (input_flags::include_relative_axis_values_for_captured_touchpads()) { + tryAddRawMotionRangeWithRelative(/*byref*/ info, AMOTION_EVENT_AXIS_X, + AMOTION_EVENT_AXIS_RELATIVE_X, ABS_MT_POSITION_X); + tryAddRawMotionRangeWithRelative(/*byref*/ info, AMOTION_EVENT_AXIS_Y, + AMOTION_EVENT_AXIS_RELATIVE_Y, ABS_MT_POSITION_Y); + } else { + tryAddRawMotionRange(/*byref*/ info, AMOTION_EVENT_AXIS_X, ABS_MT_POSITION_X); + tryAddRawMotionRange(/*byref*/ info, AMOTION_EVENT_AXIS_Y, ABS_MT_POSITION_Y); + } tryAddRawMotionRange(/*byref*/ info, AMOTION_EVENT_AXIS_TOUCH_MAJOR, ABS_MT_TOUCH_MAJOR); tryAddRawMotionRange(/*byref*/ info, AMOTION_EVENT_AXIS_TOUCH_MINOR, ABS_MT_TOUCH_MINOR); tryAddRawMotionRange(/*byref*/ info, AMOTION_EVENT_AXIS_TOOL_MAJOR, ABS_MT_WIDTH_MAJOR); tryAddRawMotionRange(/*byref*/ info, AMOTION_EVENT_AXIS_TOOL_MINOR, ABS_MT_WIDTH_MINOR); - RawAbsoluteAxisInfo pressureInfo; - mDeviceContext.getAbsoluteAxisInfo(ABS_MT_PRESSURE, &pressureInfo); - if (pressureInfo.valid) { + if (mDeviceContext.hasAbsoluteAxis(ABS_MT_PRESSURE)) { info.addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, SOURCE, 0, 1, 0, 0, 0); } - RawAbsoluteAxisInfo orientationInfo; - mDeviceContext.getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &orientationInfo); - if (orientationInfo.valid && (orientationInfo.maxValue > 0 || orientationInfo.minValue < 0)) { + if (std::optional<RawAbsoluteAxisInfo> orientation = + mDeviceContext.getAbsoluteAxisInfo(ABS_MT_ORIENTATION); + orientation && (orientation->maxValue > 0 || orientation->minValue < 0)) { info.addMotionRange(AMOTION_EVENT_AXIS_ORIENTATION, SOURCE, -M_PI_2, M_PI_2, 0, 0, 0); } @@ -133,11 +151,25 @@ void CapturedTouchpadEventConverter::populateMotionRanges(InputDeviceInfo& info) void CapturedTouchpadEventConverter::tryAddRawMotionRange(InputDeviceInfo& deviceInfo, int32_t androidAxis, int32_t evdevAxis) const { - RawAbsoluteAxisInfo info; - mDeviceContext.getAbsoluteAxisInfo(evdevAxis, &info); - if (info.valid) { - deviceInfo.addMotionRange(androidAxis, SOURCE, info.minValue, info.maxValue, info.flat, - info.fuzz, info.resolution); + std::optional<RawAbsoluteAxisInfo> info = mDeviceContext.getAbsoluteAxisInfo(evdevAxis); + if (info) { + addRawMotionRange(/*byref*/ deviceInfo, androidAxis, *info); + } +} + +void CapturedTouchpadEventConverter::tryAddRawMotionRangeWithRelative(InputDeviceInfo& deviceInfo, + int32_t androidAxis, + int32_t androidRelativeAxis, + int32_t evdevAxis) const { + std::optional<RawAbsoluteAxisInfo> axisInfo = mDeviceContext.getAbsoluteAxisInfo(evdevAxis); + if (axisInfo) { + addRawMotionRange(/*byref*/ deviceInfo, androidAxis, *axisInfo); + + // The largest movement we could possibly report on a relative axis is from the minimum to + // the maximum (or vice versa) of the absolute axis. + float range = axisInfo->maxValue - axisInfo->minValue; + deviceInfo.addMotionRange(androidRelativeAxis, SOURCE, -range, range, axisInfo->flat, + axisInfo->fuzz, axisInfo->resolution); } } @@ -164,7 +196,7 @@ std::list<NotifyArgs> CapturedTouchpadEventConverter::sync(nsecs_t when, nsecs_t std::list<NotifyArgs> out; std::vector<PointerCoords> coords; std::vector<PointerProperties> properties; - std::map<size_t, size_t> coordsIndexForSlotNumber; + std::map<size_t /*slotNumber*/, size_t /*coordsIndex*/> coordsIndexForSlotNumber; // For all the touches that were already down, send a MOVE event with their updated coordinates. // A convention of the MotionEvent API is that pointer coordinates in UP events match the @@ -176,11 +208,19 @@ std::list<NotifyArgs> CapturedTouchpadEventConverter::sync(nsecs_t when, nsecs_t // to stay perfectly still between frames, and if it does the worst that can happen is // an extra MOVE event, so it's not worth the overhead of checking for changes. coordsIndexForSlotNumber[slotNumber] = coords.size(); - coords.push_back(makePointerCoordsForSlot(mMotionAccumulator.getSlot(slotNumber))); + coords.push_back(makePointerCoordsForSlot(slotNumber)); properties.push_back({.id = pointerId, .toolType = ToolType::FINGER}); } out.push_back( makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_MOVE, coords, properties)); + if (input_flags::include_relative_axis_values_for_captured_touchpads()) { + // For any further events we send from this sync, the pointers won't have moved relative + // to the positions we just reported in this MOVE event, so zero out the relative axes. + for (PointerCoords& pointer : coords) { + pointer.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0); + pointer.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0); + } + } } std::vector<size_t> upSlots, downSlots; @@ -235,6 +275,9 @@ std::list<NotifyArgs> CapturedTouchpadEventConverter::sync(nsecs_t when, nsecs_t /*flags=*/cancel ? AMOTION_EVENT_FLAG_CANCELED : 0)); freePointerIdForSlot(slotNumber); + if (input_flags::include_relative_axis_values_for_captured_touchpads()) { + mPreviousCoordsForSlotNumber.erase(slotNumber); + } coords.erase(coords.begin() + indexToRemove); properties.erase(properties.begin() + indexToRemove); // Now that we've removed some coords and properties, we might have to update the slot @@ -255,7 +298,7 @@ std::list<NotifyArgs> CapturedTouchpadEventConverter::sync(nsecs_t when, nsecs_t : actionWithIndex(AMOTION_EVENT_ACTION_POINTER_DOWN, coordsIndex); coordsIndexForSlotNumber[slotNumber] = coordsIndex; - coords.push_back(makePointerCoordsForSlot(mMotionAccumulator.getSlot(slotNumber))); + coords.push_back(makePointerCoordsForSlot(slotNumber)); properties.push_back( {.id = allocatePointerIdToSlot(slotNumber), .toolType = ToolType::FINGER}); @@ -287,12 +330,22 @@ NotifyMotionArgs CapturedTouchpadEventConverter::makeMotionArgs( AMOTION_EVENT_INVALID_CURSOR_POSITION, mDownTime, /*videoFrames=*/{}); } -PointerCoords CapturedTouchpadEventConverter::makePointerCoordsForSlot( - const MultiTouchMotionAccumulator::Slot& slot) const { +PointerCoords CapturedTouchpadEventConverter::makePointerCoordsForSlot(size_t slotNumber) { + const MultiTouchMotionAccumulator::Slot& slot = mMotionAccumulator.getSlot(slotNumber); PointerCoords coords; coords.clear(); coords.setAxisValue(AMOTION_EVENT_AXIS_X, slot.getX()); coords.setAxisValue(AMOTION_EVENT_AXIS_Y, slot.getY()); + if (input_flags::include_relative_axis_values_for_captured_touchpads()) { + if (auto it = mPreviousCoordsForSlotNumber.find(slotNumber); + it != mPreviousCoordsForSlotNumber.end()) { + auto [oldX, oldY] = it->second; + coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, slot.getX() - oldX); + coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, slot.getY() - oldY); + } + mPreviousCoordsForSlotNumber[slotNumber] = std::make_pair(slot.getX(), slot.getY()); + } + coords.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, slot.getTouchMajor()); coords.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, slot.getTouchMinor()); coords.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, slot.getToolMajor()); diff --git a/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.h b/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.h index 9b6df7a222..d6c0708f4a 100644 --- a/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.h +++ b/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.h @@ -21,6 +21,7 @@ #include <map> #include <set> #include <string> +#include <utility> #include <vector> #include <android/input.h> @@ -49,12 +50,14 @@ public: private: void tryAddRawMotionRange(InputDeviceInfo& deviceInfo, int32_t androidAxis, int32_t evdevAxis) const; + void tryAddRawMotionRangeWithRelative(InputDeviceInfo& deviceInfo, int32_t androidAxis, + int32_t androidRelativeAxis, int32_t evdevAxis) const; [[nodiscard]] std::list<NotifyArgs> sync(nsecs_t when, nsecs_t readTime); [[nodiscard]] NotifyMotionArgs makeMotionArgs(nsecs_t when, nsecs_t readTime, int32_t action, const std::vector<PointerCoords>& coords, const std::vector<PointerProperties>& properties, int32_t actionButton = 0, int32_t flags = 0); - PointerCoords makePointerCoordsForSlot(const MultiTouchMotionAccumulator::Slot& slot) const; + PointerCoords makePointerCoordsForSlot(size_t slotNumber); int32_t allocatePointerIdToSlot(size_t slotNumber); void freePointerIdForSlot(size_t slotNumber); @@ -76,8 +79,7 @@ private: std::bitset<MAX_POINTER_ID + 1> mPointerIdsInUse; std::map<size_t, int32_t> mPointerIdForSlotNumber; - - static constexpr uint32_t SOURCE = AINPUT_SOURCE_TOUCHPAD; + std::map<size_t, std::pair<float, float>> mPreviousCoordsForSlotNumber; }; } // namespace android diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h index 2108488936..3fc370cacd 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.h +++ b/services/inputflinger/reader/mapper/CursorInputMapper.h @@ -25,9 +25,6 @@ namespace android { -class CursorButtonAccumulator; -class CursorScrollAccumulator; - /* Keeps track of cursor movements. */ class CursorMotionAccumulator { public: diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp index 3af1d04073..4cd37d7e4a 100644 --- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp +++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp @@ -33,7 +33,7 @@ uint32_t ExternalStylusInputMapper::getSources() const { void ExternalStylusInputMapper::populateDeviceInfo(InputDeviceInfo& info) { InputMapper::populateDeviceInfo(info); - if (mRawPressureAxis.valid) { + if (mRawPressureAxis || mTouchButtonAccumulator.hasButtonTouch()) { info.addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, AINPUT_SOURCE_STYLUS, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f); } @@ -50,7 +50,7 @@ void ExternalStylusInputMapper::dump(std::string& dump) { std::list<NotifyArgs> ExternalStylusInputMapper::reconfigure(nsecs_t when, const InputReaderConfiguration& config, ConfigurationChanges changes) { - getAbsoluteAxisInfo(ABS_PRESSURE, &mRawPressureAxis); + mRawPressureAxis = getAbsoluteAxisInfo(ABS_PRESSURE); mTouchButtonAccumulator.configure(); return {}; } @@ -82,10 +82,10 @@ std::list<NotifyArgs> ExternalStylusInputMapper::sync(nsecs_t when) { mStylusState.toolType = ToolType::STYLUS; } - if (mRawPressureAxis.valid) { + if (mRawPressureAxis) { auto rawPressure = static_cast<float>(mSingleTouchMotionAccumulator.getAbsolutePressure()); - mStylusState.pressure = (rawPressure - mRawPressureAxis.minValue) / - static_cast<float>(mRawPressureAxis.maxValue - mRawPressureAxis.minValue); + mStylusState.pressure = (rawPressure - mRawPressureAxis->minValue) / + static_cast<float>(mRawPressureAxis->maxValue - mRawPressureAxis->minValue); } else if (mTouchButtonAccumulator.hasButtonTouch()) { mStylusState.pressure = mTouchButtonAccumulator.isHovering() ? 0.0f : 1.0f; } diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h index c040a7b996..d48fd9b469 100644 --- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h +++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h @@ -16,6 +16,8 @@ #pragma once +#include <optional> + #include "InputMapper.h" #include "SingleTouchMotionAccumulator.h" @@ -43,7 +45,7 @@ public: private: SingleTouchMotionAccumulator mSingleTouchMotionAccumulator; - RawAbsoluteAxisInfo mRawPressureAxis; + std::optional<RawAbsoluteAxisInfo> mRawPressureAxis; TouchButtonAccumulator mTouchButtonAccumulator; StylusState mStylusState; diff --git a/services/inputflinger/reader/mapper/InputMapper.cpp b/services/inputflinger/reader/mapper/InputMapper.cpp index b6c5c9806c..627df7fb5b 100644 --- a/services/inputflinger/reader/mapper/InputMapper.cpp +++ b/services/inputflinger/reader/mapper/InputMapper.cpp @@ -18,6 +18,7 @@ #include "InputMapper.h" +#include <optional> #include <sstream> #include <ftl/enum.h> @@ -116,15 +117,16 @@ std::list<NotifyArgs> InputMapper::updateExternalStylusState(const StylusState& return {}; } -status_t InputMapper::getAbsoluteAxisInfo(int32_t axis, RawAbsoluteAxisInfo* axisInfo) { - return getDeviceContext().getAbsoluteAxisInfo(axis, axisInfo); +std::optional<RawAbsoluteAxisInfo> InputMapper::getAbsoluteAxisInfo(int32_t axis) { + return getDeviceContext().getAbsoluteAxisInfo(axis); } void InputMapper::bumpGeneration() { getDeviceContext().bumpGeneration(); } -void InputMapper::dumpRawAbsoluteAxisInfo(std::string& dump, const RawAbsoluteAxisInfo& axis, +void InputMapper::dumpRawAbsoluteAxisInfo(std::string& dump, + const std::optional<RawAbsoluteAxisInfo>& axis, const char* name) { std::stringstream out; out << INDENT4 << name << ": " << axis << "\n"; @@ -138,4 +140,7 @@ void InputMapper::dumpStylusState(std::string& dump, const StylusState& state) { dump += StringPrintf(INDENT4 "Tool Type: %s\n", ftl::enum_string(state.toolType).c_str()); } +std::optional<HardwareProperties> InputMapper::getTouchpadHardwareProperties() { + return std::nullopt; +} } // namespace android diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h index 2c51448b4e..75cc4bb410 100644 --- a/services/inputflinger/reader/mapper/InputMapper.h +++ b/services/inputflinger/reader/mapper/InputMapper.h @@ -16,6 +16,8 @@ #pragma once +#include <optional> + #include "EventHub.h" #include "InputDevice.h" #include "InputListener.h" @@ -120,16 +122,19 @@ public: virtual std::optional<ui::LogicalDisplayId> getAssociatedDisplayId() { return std::nullopt; } virtual void updateLedState(bool reset) {} + virtual std::optional<HardwareProperties> getTouchpadHardwareProperties(); + protected: InputDeviceContext& mDeviceContext; explicit InputMapper(InputDeviceContext& deviceContext, const InputReaderConfiguration& readerConfig); - status_t getAbsoluteAxisInfo(int32_t axis, RawAbsoluteAxisInfo* axisInfo); + std::optional<RawAbsoluteAxisInfo> getAbsoluteAxisInfo(int32_t axis); void bumpGeneration(); - static void dumpRawAbsoluteAxisInfo(std::string& dump, const RawAbsoluteAxisInfo& axis, + static void dumpRawAbsoluteAxisInfo(std::string& dump, + const std::optional<RawAbsoluteAxisInfo>& axis, const char* name); static void dumpStylusState(std::string& dump, const StylusState& state); }; diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp index 41e018d392..3091714e00 100644 --- a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp +++ b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp @@ -117,9 +117,8 @@ std::list<NotifyArgs> JoystickInputMapper::reconfigure(nsecs_t when, continue; // axis must be claimed by a different device } - RawAbsoluteAxisInfo rawAxisInfo; - getAbsoluteAxisInfo(abs, &rawAxisInfo); - if (rawAxisInfo.valid) { + if (std::optional<RawAbsoluteAxisInfo> rawAxisInfo = getAbsoluteAxisInfo(abs); + rawAxisInfo) { // Map axis. AxisInfo axisInfo; const bool explicitlyMapped = !getDeviceContext().mapAxis(abs, &axisInfo); @@ -129,7 +128,7 @@ std::list<NotifyArgs> JoystickInputMapper::reconfigure(nsecs_t when, axisInfo.mode = AxisInfo::MODE_NORMAL; axisInfo.axis = -1; } - mAxes.insert({abs, createAxis(axisInfo, rawAxisInfo, explicitlyMapped)}); + mAxes.insert({abs, createAxis(axisInfo, rawAxisInfo.value(), explicitlyMapped)}); } } diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp index 8eb67306c4..38dcd65a81 100644 --- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp +++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp @@ -493,12 +493,6 @@ std::list<NotifyArgs> KeyboardInputMapper::cancelAllDownKeys(nsecs_t when) { void KeyboardInputMapper::onKeyDownProcessed(nsecs_t downTime) { InputReaderContext& context = *getContext(); context.setLastKeyDownTimestamp(downTime); - // Ignore meta keys or multiple simultaneous down keys as they are likely to be keyboard - // shortcuts - bool shouldHideCursor = mKeyDowns.size() == 1 && !isMetaKey(mKeyDowns[0].keyCode); - if (shouldHideCursor && context.getPolicy()->isInputMethodConnectionActive()) { - context.setPreventingTouchpadTaps(true); - } } uint32_t KeyboardInputMapper::getEventSource() const { diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp index 1986fe286a..fd8224a608 100644 --- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp @@ -133,7 +133,7 @@ void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) { bool isHovering = mTouchButtonAccumulator.getToolType() != ToolType::MOUSE && (mTouchButtonAccumulator.isHovering() || - (mRawPointerAxes.pressure.valid && inSlot.getPressure() <= 0)); + (mRawPointerAxes.pressure && inSlot.getPressure() <= 0)); outPointer.isHovering = isHovering; // Assign pointer id using tracking id if available. @@ -189,21 +189,27 @@ std::list<NotifyArgs> MultiTouchInputMapper::reconfigure(nsecs_t when, void MultiTouchInputMapper::configureRawPointerAxes() { TouchInputMapper::configureRawPointerAxes(); - getAbsoluteAxisInfo(ABS_MT_POSITION_X, &mRawPointerAxes.x); - getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &mRawPointerAxes.y); - getAbsoluteAxisInfo(ABS_MT_TOUCH_MAJOR, &mRawPointerAxes.touchMajor); - getAbsoluteAxisInfo(ABS_MT_TOUCH_MINOR, &mRawPointerAxes.touchMinor); - getAbsoluteAxisInfo(ABS_MT_WIDTH_MAJOR, &mRawPointerAxes.toolMajor); - getAbsoluteAxisInfo(ABS_MT_WIDTH_MINOR, &mRawPointerAxes.toolMinor); - getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &mRawPointerAxes.orientation); - getAbsoluteAxisInfo(ABS_MT_PRESSURE, &mRawPointerAxes.pressure); - getAbsoluteAxisInfo(ABS_MT_DISTANCE, &mRawPointerAxes.distance); - getAbsoluteAxisInfo(ABS_MT_TRACKING_ID, &mRawPointerAxes.trackingId); - getAbsoluteAxisInfo(ABS_MT_SLOT, &mRawPointerAxes.slot); - - if (mRawPointerAxes.trackingId.valid && mRawPointerAxes.slot.valid && - mRawPointerAxes.slot.minValue == 0 && mRawPointerAxes.slot.maxValue > 0) { - size_t slotCount = mRawPointerAxes.slot.maxValue + 1; + // TODO(b/351870641): Investigate why we are sometime not getting valid axis infos for the x/y + // axes, even though those axes are required to be supported. + if (const auto xInfo = getAbsoluteAxisInfo(ABS_MT_POSITION_X); xInfo.has_value()) { + mRawPointerAxes.x = *xInfo; + } + if (const auto yInfo = getAbsoluteAxisInfo(ABS_MT_POSITION_Y); yInfo.has_value()) { + mRawPointerAxes.y = *yInfo; + } + mRawPointerAxes.touchMajor = getAbsoluteAxisInfo(ABS_MT_TOUCH_MAJOR); + mRawPointerAxes.touchMinor = getAbsoluteAxisInfo(ABS_MT_TOUCH_MINOR); + mRawPointerAxes.toolMajor = getAbsoluteAxisInfo(ABS_MT_WIDTH_MAJOR); + mRawPointerAxes.toolMinor = getAbsoluteAxisInfo(ABS_MT_WIDTH_MINOR); + mRawPointerAxes.orientation = getAbsoluteAxisInfo(ABS_MT_ORIENTATION); + mRawPointerAxes.pressure = getAbsoluteAxisInfo(ABS_MT_PRESSURE); + mRawPointerAxes.distance = getAbsoluteAxisInfo(ABS_MT_DISTANCE); + mRawPointerAxes.trackingId = getAbsoluteAxisInfo(ABS_MT_TRACKING_ID); + mRawPointerAxes.slot = getAbsoluteAxisInfo(ABS_MT_SLOT); + + if (mRawPointerAxes.trackingId && mRawPointerAxes.slot && mRawPointerAxes.slot->minValue == 0 && + mRawPointerAxes.slot->maxValue > 0) { + size_t slotCount = mRawPointerAxes.slot->maxValue + 1; if (slotCount > MAX_SLOTS) { ALOGW("MultiTouch Device %s reported %zu slots but the framework " "only supports a maximum of %zu slots at this time.", diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp index 27ff52fa65..b72cc6e060 100644 --- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp +++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp @@ -27,11 +27,14 @@ namespace android { +constexpr float kDefaultScaleFactor = 1.0f; + RotaryEncoderInputMapper::RotaryEncoderInputMapper(InputDeviceContext& deviceContext, const InputReaderConfiguration& readerConfig) - : InputMapper(deviceContext, readerConfig), mOrientation(ui::ROTATION_0) { - mSource = AINPUT_SOURCE_ROTARY_ENCODER; -} + : InputMapper(deviceContext, readerConfig), + mSource(AINPUT_SOURCE_ROTARY_ENCODER), + mScalingFactor(kDefaultScaleFactor), + mOrientation(ui::ROTATION_0) {} RotaryEncoderInputMapper::~RotaryEncoderInputMapper() {} @@ -51,9 +54,10 @@ void RotaryEncoderInputMapper::populateDeviceInfo(InputDeviceInfo& info) { std::optional<float> scalingFactor = config.getFloat("device.scalingFactor"); if (!scalingFactor.has_value()) { ALOGW("Rotary Encoder device configuration file didn't specify scaling factor," - "default to 1.0!\n"); + "default to %f!\n", + kDefaultScaleFactor); } - mScalingFactor = scalingFactor.value_or(1.0f); + mScalingFactor = scalingFactor.value_or(kDefaultScaleFactor); info.addMotionRange(AMOTION_EVENT_AXIS_SCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, res.value_or(0.0f) * mScalingFactor); } @@ -84,12 +88,18 @@ std::list<NotifyArgs> RotaryEncoderInputMapper::reconfigure(nsecs_t when, } } if (!changes.any() || changes.test(InputReaderConfiguration::Change::DISPLAY_INFO)) { - std::optional<DisplayViewport> internalViewport = - config.getDisplayViewportByType(ViewportType::INTERNAL); - if (internalViewport) { - mOrientation = internalViewport->orientation; + if (getDeviceContext().getAssociatedViewport()) { + mDisplayId = getDeviceContext().getAssociatedViewport()->displayId; + mOrientation = getDeviceContext().getAssociatedViewport()->orientation; } else { - mOrientation = ui::ROTATION_0; + mDisplayId = ui::LogicalDisplayId::INVALID; + std::optional<DisplayViewport> internalViewport = + config.getDisplayViewportByType(ViewportType::INTERNAL); + if (internalViewport) { + mOrientation = internalViewport->orientation; + } else { + mOrientation = ui::ROTATION_0; + } } } return out; @@ -124,8 +134,6 @@ std::list<NotifyArgs> RotaryEncoderInputMapper::sync(nsecs_t when, nsecs_t readT // Send motion event. if (scrolled) { int32_t metaState = getContext()->getGlobalMetaState(); - // This is not a pointer, so it's not associated with a display. - ui::LogicalDisplayId displayId = ui::LogicalDisplayId::INVALID; if (mOrientation == ui::ROTATION_180) { scroll = -scroll; @@ -147,7 +155,7 @@ std::list<NotifyArgs> RotaryEncoderInputMapper::sync(nsecs_t when, nsecs_t readT out.push_back( NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(), mSource, - displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, + mDisplayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, /*buttonState=*/0, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h index 14c540bf6e..7e804150b1 100644 --- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h +++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h @@ -47,6 +47,7 @@ private: int32_t mSource; float mScalingFactor; ui::Rotation mOrientation; + ui::LogicalDisplayId mDisplayId = ui::LogicalDisplayId::INVALID; std::unique_ptr<SlopController> mSlopController; explicit RotaryEncoderInputMapper(InputDeviceContext& deviceContext, diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.cpp b/services/inputflinger/reader/mapper/SensorInputMapper.cpp index d7f2993daa..4233f789d6 100644 --- a/services/inputflinger/reader/mapper/SensorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/SensorInputMapper.cpp @@ -133,9 +133,8 @@ std::list<NotifyArgs> SensorInputMapper::reconfigure(nsecs_t when, .test(InputDeviceClass::SENSOR))) { continue; } - RawAbsoluteAxisInfo rawAxisInfo; - getAbsoluteAxisInfo(abs, &rawAxisInfo); - if (rawAxisInfo.valid) { + if (std::optional<RawAbsoluteAxisInfo> rawAxisInfo = getAbsoluteAxisInfo(abs); + rawAxisInfo) { AxisInfo axisInfo; // Axis doesn't need to be mapped, as sensor mapper doesn't generate any motion // input events @@ -146,7 +145,7 @@ std::list<NotifyArgs> SensorInputMapper::reconfigure(nsecs_t when, if (ret.ok()) { InputDeviceSensorType sensorType = (*ret).first; int32_t sensorDataIndex = (*ret).second; - const Axis& axis = createAxis(axisInfo, rawAxisInfo); + const Axis& axis = createAxis(axisInfo, rawAxisInfo.value()); parseSensorConfiguration(sensorType, abs, sensorDataIndex, axis); mAxes.insert({abs, axis}); diff --git a/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp b/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp index 140bb0c4ed..cef18375d4 100644 --- a/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp @@ -44,7 +44,7 @@ void SingleTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) { bool isHovering = mTouchButtonAccumulator.getToolType() != ToolType::MOUSE && (mTouchButtonAccumulator.isHovering() || - (mRawPointerAxes.pressure.valid && + (mRawPointerAxes.pressure && mSingleTouchMotionAccumulator.getAbsolutePressure() <= 0)); outState->rawPointerData.markIdBit(0, isHovering); @@ -72,13 +72,19 @@ void SingleTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) { void SingleTouchInputMapper::configureRawPointerAxes() { TouchInputMapper::configureRawPointerAxes(); - getAbsoluteAxisInfo(ABS_X, &mRawPointerAxes.x); - getAbsoluteAxisInfo(ABS_Y, &mRawPointerAxes.y); - getAbsoluteAxisInfo(ABS_PRESSURE, &mRawPointerAxes.pressure); - getAbsoluteAxisInfo(ABS_TOOL_WIDTH, &mRawPointerAxes.toolMajor); - getAbsoluteAxisInfo(ABS_DISTANCE, &mRawPointerAxes.distance); - getAbsoluteAxisInfo(ABS_TILT_X, &mRawPointerAxes.tiltX); - getAbsoluteAxisInfo(ABS_TILT_Y, &mRawPointerAxes.tiltY); + // TODO(b/351870641): Investigate why we are sometime not getting valid axis infos for the x/y + // axes, even though those axes are required to be supported. + if (const auto xInfo = getAbsoluteAxisInfo(ABS_X); xInfo.has_value()) { + mRawPointerAxes.x = *xInfo; + } + if (const auto yInfo = getAbsoluteAxisInfo(ABS_Y); yInfo.has_value()) { + mRawPointerAxes.y = *yInfo; + } + mRawPointerAxes.pressure = getAbsoluteAxisInfo(ABS_PRESSURE); + mRawPointerAxes.toolMajor = getAbsoluteAxisInfo(ABS_TOOL_WIDTH); + mRawPointerAxes.distance = getAbsoluteAxisInfo(ABS_DISTANCE); + mRawPointerAxes.tiltX = getAbsoluteAxisInfo(ABS_TILT_X); + mRawPointerAxes.tiltY = getAbsoluteAxisInfo(ABS_TILT_Y); } bool SingleTouchInputMapper::hasStylus() const { diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index 64df226715..5c90cbb6ce 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -149,7 +149,10 @@ uint32_t TouchInputMapper::getSources() const { // The SOURCE_BLUETOOTH_STYLUS is added to events dynamically if the current stream is modified // by the external stylus state. That's why we don't add it directly to mSource during // configuration. - return mSource | (hasExternalStylus() ? AINPUT_SOURCE_BLUETOOTH_STYLUS : 0); + return mSource | + (mExternalStylusPresence == ExternalStylusPresence::TOUCH_FUSION + ? AINPUT_SOURCE_BLUETOOTH_STYLUS + : 0); } void TouchInputMapper::populateDeviceInfo(InputDeviceInfo& info) { @@ -270,8 +273,8 @@ void TouchInputMapper::dump(std::string& dump) { } dump += INDENT3 "Stylus Fusion:\n"; - dump += StringPrintf(INDENT4 "ExternalStylusConnected: %s\n", - toString(mExternalStylusConnected)); + dump += StringPrintf(INDENT4 "ExternalStylusPresence: %s\n", + ftl::enum_string(mExternalStylusPresence).c_str()); dump += StringPrintf(INDENT4 "Fused External Stylus Pointer ID: %s\n", toString(mFusedStylusPointerId).c_str()); dump += StringPrintf(INDENT4 "External Stylus Data Timeout: %" PRId64 "\n", @@ -356,11 +359,19 @@ std::list<NotifyArgs> TouchInputMapper::reconfigure(nsecs_t when, void TouchInputMapper::resolveExternalStylusPresence() { std::vector<InputDeviceInfo> devices; getContext()->getExternalStylusDevices(devices); - mExternalStylusConnected = !devices.empty(); - - if (!mExternalStylusConnected) { + if (devices.empty()) { + mExternalStylusPresence = ExternalStylusPresence::NONE; resetExternalStylus(); + return; } + mExternalStylusPresence = + std::any_of(devices.begin(), devices.end(), + [](const auto& info) { + return info.getMotionRange(AMOTION_EVENT_AXIS_PRESSURE, + AINPUT_SOURCE_STYLUS) != nullptr; + }) + ? ExternalStylusPresence::TOUCH_FUSION + : ExternalStylusPresence::BUTTON_FUSION; } TouchInputMapper::Parameters TouchInputMapper::computeParameters( @@ -520,7 +531,7 @@ void TouchInputMapper::dumpRawPointerAxes(std::string& dump) { } bool TouchInputMapper::hasExternalStylus() const { - return mExternalStylusConnected; + return mExternalStylusPresence != ExternalStylusPresence::NONE; } /** @@ -600,10 +611,10 @@ void TouchInputMapper::initializeSizeRanges() { const float diagonalSize = hypotf(mDisplayBounds.width, mDisplayBounds.height); // Size factors. - if (mRawPointerAxes.touchMajor.valid && mRawPointerAxes.touchMajor.maxValue != 0) { - mSizeScale = 1.0f / mRawPointerAxes.touchMajor.maxValue; - } else if (mRawPointerAxes.toolMajor.valid && mRawPointerAxes.toolMajor.maxValue != 0) { - mSizeScale = 1.0f / mRawPointerAxes.toolMajor.maxValue; + if (mRawPointerAxes.touchMajor && mRawPointerAxes.touchMajor->maxValue != 0) { + mSizeScale = 1.0f / mRawPointerAxes.touchMajor->maxValue; + } else if (mRawPointerAxes.toolMajor && mRawPointerAxes.toolMajor->maxValue != 0) { + mSizeScale = 1.0f / mRawPointerAxes.toolMajor->maxValue; } else { mSizeScale = 0.0f; } @@ -618,18 +629,18 @@ void TouchInputMapper::initializeSizeRanges() { .resolution = 0, }; - if (mRawPointerAxes.touchMajor.valid) { - mRawPointerAxes.touchMajor.resolution = - clampResolution("touchMajor", mRawPointerAxes.touchMajor.resolution); - mOrientedRanges.touchMajor->resolution = mRawPointerAxes.touchMajor.resolution; + if (mRawPointerAxes.touchMajor) { + mRawPointerAxes.touchMajor->resolution = + clampResolution("touchMajor", mRawPointerAxes.touchMajor->resolution); + mOrientedRanges.touchMajor->resolution = mRawPointerAxes.touchMajor->resolution; } mOrientedRanges.touchMinor = mOrientedRanges.touchMajor; mOrientedRanges.touchMinor->axis = AMOTION_EVENT_AXIS_TOUCH_MINOR; - if (mRawPointerAxes.touchMinor.valid) { - mRawPointerAxes.touchMinor.resolution = - clampResolution("touchMinor", mRawPointerAxes.touchMinor.resolution); - mOrientedRanges.touchMinor->resolution = mRawPointerAxes.touchMinor.resolution; + if (mRawPointerAxes.touchMinor) { + mRawPointerAxes.touchMinor->resolution = + clampResolution("touchMinor", mRawPointerAxes.touchMinor->resolution); + mOrientedRanges.touchMinor->resolution = mRawPointerAxes.touchMinor->resolution; } mOrientedRanges.toolMajor = InputDeviceInfo::MotionRange{ @@ -641,18 +652,18 @@ void TouchInputMapper::initializeSizeRanges() { .fuzz = 0, .resolution = 0, }; - if (mRawPointerAxes.toolMajor.valid) { - mRawPointerAxes.toolMajor.resolution = - clampResolution("toolMajor", mRawPointerAxes.toolMajor.resolution); - mOrientedRanges.toolMajor->resolution = mRawPointerAxes.toolMajor.resolution; + if (mRawPointerAxes.toolMajor) { + mRawPointerAxes.toolMajor->resolution = + clampResolution("toolMajor", mRawPointerAxes.toolMajor->resolution); + mOrientedRanges.toolMajor->resolution = mRawPointerAxes.toolMajor->resolution; } mOrientedRanges.toolMinor = mOrientedRanges.toolMajor; mOrientedRanges.toolMinor->axis = AMOTION_EVENT_AXIS_TOOL_MINOR; - if (mRawPointerAxes.toolMinor.valid) { - mRawPointerAxes.toolMinor.resolution = - clampResolution("toolMinor", mRawPointerAxes.toolMinor.resolution); - mOrientedRanges.toolMinor->resolution = mRawPointerAxes.toolMinor.resolution; + if (mRawPointerAxes.toolMinor) { + mRawPointerAxes.toolMinor->resolution = + clampResolution("toolMinor", mRawPointerAxes.toolMinor->resolution); + mOrientedRanges.toolMinor->resolution = mRawPointerAxes.toolMinor->resolution; } if (mCalibration.sizeCalibration == Calibration::SizeCalibration::GEOMETRIC) { @@ -704,9 +715,10 @@ void TouchInputMapper::initializeOrientedRanges() { mCalibration.pressureCalibration == Calibration::PressureCalibration::AMPLITUDE) { if (mCalibration.pressureScale) { mPressureScale = *mCalibration.pressureScale; - pressureMax = mPressureScale * mRawPointerAxes.pressure.maxValue; - } else if (mRawPointerAxes.pressure.valid && mRawPointerAxes.pressure.maxValue != 0) { - mPressureScale = 1.0f / mRawPointerAxes.pressure.maxValue; + pressureMax = mPressureScale * + (mRawPointerAxes.pressure ? mRawPointerAxes.pressure->maxValue : 0); + } else if (mRawPointerAxes.pressure && mRawPointerAxes.pressure->maxValue != 0) { + mPressureScale = 1.0f / mRawPointerAxes.pressure->maxValue; } } @@ -725,18 +737,18 @@ void TouchInputMapper::initializeOrientedRanges() { mTiltXScale = 0; mTiltYCenter = 0; mTiltYScale = 0; - mHaveTilt = mRawPointerAxes.tiltX.valid && mRawPointerAxes.tiltY.valid; + mHaveTilt = mRawPointerAxes.tiltX && mRawPointerAxes.tiltY; if (mHaveTilt) { - mTiltXCenter = avg(mRawPointerAxes.tiltX.minValue, mRawPointerAxes.tiltX.maxValue); - mTiltYCenter = avg(mRawPointerAxes.tiltY.minValue, mRawPointerAxes.tiltY.maxValue); + mTiltXCenter = avg(mRawPointerAxes.tiltX->minValue, mRawPointerAxes.tiltX->maxValue); + mTiltYCenter = avg(mRawPointerAxes.tiltY->minValue, mRawPointerAxes.tiltY->maxValue); mTiltXScale = M_PI / 180; mTiltYScale = M_PI / 180; - if (mRawPointerAxes.tiltX.resolution) { - mTiltXScale = 1.0 / mRawPointerAxes.tiltX.resolution; + if (mRawPointerAxes.tiltX->resolution) { + mTiltXScale = 1.0 / mRawPointerAxes.tiltX->resolution; } - if (mRawPointerAxes.tiltY.resolution) { - mTiltYScale = 1.0 / mRawPointerAxes.tiltY.resolution; + if (mRawPointerAxes.tiltY->resolution) { + mTiltYScale = 1.0 / mRawPointerAxes.tiltY->resolution; } mOrientedRanges.tilt = InputDeviceInfo::MotionRange{ @@ -766,11 +778,11 @@ void TouchInputMapper::initializeOrientedRanges() { } else if (mCalibration.orientationCalibration != Calibration::OrientationCalibration::NONE) { if (mCalibration.orientationCalibration == Calibration::OrientationCalibration::INTERPOLATED) { - if (mRawPointerAxes.orientation.valid) { - if (mRawPointerAxes.orientation.maxValue > 0) { - mOrientationScale = M_PI_2 / mRawPointerAxes.orientation.maxValue; - } else if (mRawPointerAxes.orientation.minValue < 0) { - mOrientationScale = -M_PI_2 / mRawPointerAxes.orientation.minValue; + if (mRawPointerAxes.orientation) { + if (mRawPointerAxes.orientation->maxValue > 0) { + mOrientationScale = M_PI_2 / mRawPointerAxes.orientation->maxValue; + } else if (mRawPointerAxes.orientation->minValue < 0) { + mOrientationScale = -M_PI_2 / mRawPointerAxes.orientation->minValue; } else { mOrientationScale = 0; } @@ -795,14 +807,14 @@ void TouchInputMapper::initializeOrientedRanges() { mDistanceScale = mCalibration.distanceScale.value_or(1.0f); } + const bool hasDistance = mRawPointerAxes.distance.has_value(); mOrientedRanges.distance = InputDeviceInfo::MotionRange{ - .axis = AMOTION_EVENT_AXIS_DISTANCE, .source = mSource, - .min = mRawPointerAxes.distance.minValue * mDistanceScale, - .max = mRawPointerAxes.distance.maxValue * mDistanceScale, + .min = hasDistance ? mRawPointerAxes.distance->minValue * mDistanceScale : 0, + .max = hasDistance ? mRawPointerAxes.distance->maxValue * mDistanceScale : 0, .flat = 0, - .fuzz = mRawPointerAxes.distance.fuzz * mDistanceScale, + .fuzz = hasDistance ? mRawPointerAxes.distance->fuzz * mDistanceScale : 0, .resolution = 0, }; } @@ -931,7 +943,7 @@ void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded) mSource |= AINPUT_SOURCE_STYLUS; } } else if (mParameters.deviceType == Parameters::DeviceType::TOUCH_NAVIGATION) { - mSource = AINPUT_SOURCE_TOUCH_NAVIGATION; + mSource = AINPUT_SOURCE_TOUCH_NAVIGATION | AINPUT_SOURCE_TOUCHPAD; mDeviceMode = DeviceMode::NAVIGATION; } else { ALOGW("Touch device '%s' has invalid parameters or configuration. The device will be " @@ -943,12 +955,7 @@ void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded) const std::optional<DisplayViewport> newViewportOpt = findViewport(); // Ensure the device is valid and can be used. - if (!mRawPointerAxes.x.valid || !mRawPointerAxes.y.valid) { - ALOGW("Touch device '%s' did not report support for X or Y axis! " - "The device will be inoperable.", - getDeviceName().c_str()); - mDeviceMode = DeviceMode::DISABLED; - } else if (!newViewportOpt) { + if (!newViewportOpt) { ALOGI("Touch device '%s' could not query the properties of its associated " "display. The device will be inoperable until the display size " "becomes available.", @@ -1237,7 +1244,7 @@ void TouchInputMapper::parseCalibration() { void TouchInputMapper::resolveCalibration() { // Size - if (mRawPointerAxes.touchMajor.valid || mRawPointerAxes.toolMajor.valid) { + if (mRawPointerAxes.touchMajor || mRawPointerAxes.toolMajor) { if (mCalibration.sizeCalibration == Calibration::SizeCalibration::DEFAULT) { mCalibration.sizeCalibration = Calibration::SizeCalibration::GEOMETRIC; } @@ -1246,7 +1253,7 @@ void TouchInputMapper::resolveCalibration() { } // Pressure - if (mRawPointerAxes.pressure.valid) { + if (mRawPointerAxes.pressure) { if (mCalibration.pressureCalibration == Calibration::PressureCalibration::DEFAULT) { mCalibration.pressureCalibration = Calibration::PressureCalibration::PHYSICAL; } @@ -1255,7 +1262,7 @@ void TouchInputMapper::resolveCalibration() { } // Orientation - if (mRawPointerAxes.orientation.valid) { + if (mRawPointerAxes.orientation) { if (mCalibration.orientationCalibration == Calibration::OrientationCalibration::DEFAULT) { mCalibration.orientationCalibration = Calibration::OrientationCalibration::INTERPOLATED; } @@ -1264,7 +1271,7 @@ void TouchInputMapper::resolveCalibration() { } // Distance - if (mRawPointerAxes.distance.valid) { + if (mRawPointerAxes.distance) { if (mCalibration.distanceCalibration == Calibration::DistanceCalibration::DEFAULT) { mCalibration.distanceCalibration = Calibration::DistanceCalibration::SCALED; } @@ -2251,25 +2258,25 @@ void TouchInputMapper::cookPointerData() { case Calibration::SizeCalibration::DIAMETER: case Calibration::SizeCalibration::BOX: case Calibration::SizeCalibration::AREA: - if (mRawPointerAxes.touchMajor.valid && mRawPointerAxes.toolMajor.valid) { + if (mRawPointerAxes.touchMajor && mRawPointerAxes.toolMajor) { touchMajor = in.touchMajor; - touchMinor = mRawPointerAxes.touchMinor.valid ? in.touchMinor : in.touchMajor; + touchMinor = mRawPointerAxes.touchMinor ? in.touchMinor : in.touchMajor; toolMajor = in.toolMajor; - toolMinor = mRawPointerAxes.toolMinor.valid ? in.toolMinor : in.toolMajor; - size = mRawPointerAxes.touchMinor.valid ? avg(in.touchMajor, in.touchMinor) - : in.touchMajor; - } else if (mRawPointerAxes.touchMajor.valid) { + toolMinor = mRawPointerAxes.toolMinor ? in.toolMinor : in.toolMajor; + size = mRawPointerAxes.touchMinor ? avg(in.touchMajor, in.touchMinor) + : in.touchMajor; + } else if (mRawPointerAxes.touchMajor) { toolMajor = touchMajor = in.touchMajor; toolMinor = touchMinor = - mRawPointerAxes.touchMinor.valid ? in.touchMinor : in.touchMajor; - size = mRawPointerAxes.touchMinor.valid ? avg(in.touchMajor, in.touchMinor) - : in.touchMajor; - } else if (mRawPointerAxes.toolMajor.valid) { + mRawPointerAxes.touchMinor ? in.touchMinor : in.touchMajor; + size = mRawPointerAxes.touchMinor ? avg(in.touchMajor, in.touchMinor) + : in.touchMajor; + } else if (mRawPointerAxes.toolMajor) { touchMajor = toolMajor = in.toolMajor; touchMinor = toolMinor = - mRawPointerAxes.toolMinor.valid ? in.toolMinor : in.toolMajor; - size = mRawPointerAxes.toolMinor.valid ? avg(in.toolMajor, in.toolMinor) - : in.toolMajor; + mRawPointerAxes.toolMinor ? in.toolMinor : in.toolMajor; + size = mRawPointerAxes.toolMinor ? avg(in.toolMajor, in.toolMinor) + : in.toolMajor; } else { ALOG_ASSERT(false, "No touch or tool axes. " diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h index 30c58a59c5..ef0e02f40c 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.h +++ b/services/inputflinger/reader/mapper/TouchInputMapper.h @@ -61,17 +61,17 @@ static constexpr nsecs_t TOUCH_DATA_TIMEOUT = ms2ns(20); struct RawPointerAxes { RawAbsoluteAxisInfo x{}; RawAbsoluteAxisInfo y{}; - RawAbsoluteAxisInfo pressure{}; - RawAbsoluteAxisInfo touchMajor{}; - RawAbsoluteAxisInfo touchMinor{}; - RawAbsoluteAxisInfo toolMajor{}; - RawAbsoluteAxisInfo toolMinor{}; - RawAbsoluteAxisInfo orientation{}; - RawAbsoluteAxisInfo distance{}; - RawAbsoluteAxisInfo tiltX{}; - RawAbsoluteAxisInfo tiltY{}; - RawAbsoluteAxisInfo trackingId{}; - RawAbsoluteAxisInfo slot{}; + std::optional<RawAbsoluteAxisInfo> pressure{}; + std::optional<RawAbsoluteAxisInfo> touchMajor{}; + std::optional<RawAbsoluteAxisInfo> touchMinor{}; + std::optional<RawAbsoluteAxisInfo> toolMajor{}; + std::optional<RawAbsoluteAxisInfo> toolMinor{}; + std::optional<RawAbsoluteAxisInfo> orientation{}; + std::optional<RawAbsoluteAxisInfo> distance{}; + std::optional<RawAbsoluteAxisInfo> tiltX{}; + std::optional<RawAbsoluteAxisInfo> tiltY{}; + std::optional<RawAbsoluteAxisInfo> trackingId{}; + std::optional<RawAbsoluteAxisInfo> slot{}; inline int32_t getRawWidth() const { return x.maxValue - x.minValue + 1; } inline int32_t getRawHeight() const { return y.maxValue - y.minValue + 1; } @@ -339,8 +339,8 @@ protected: int32_t buttonState{}; // Scroll state. - int32_t rawVScroll{}; - int32_t rawHScroll{}; + float rawVScroll{}; + float rawHScroll{}; inline void clear() { *this = RawState(); } }; @@ -365,6 +365,16 @@ protected: RawState mLastRawState; CookedState mLastCookedState; + enum class ExternalStylusPresence { + // No external stylus connected. + NONE, + // An external stylus that can report touch/pressure that can be fused with the touchscreen. + TOUCH_FUSION, + // An external stylus that can only report buttons. + BUTTON_FUSION, + ftl_last = BUTTON_FUSION, + }; + ExternalStylusPresence mExternalStylusPresence{ExternalStylusPresence::NONE}; // State provided by an external stylus StylusState mExternalStylusState; // If an external stylus is capable of reporting pointer-specific data like pressure, we will @@ -460,8 +470,6 @@ private: float mTiltYCenter; float mTiltYScale; - bool mExternalStylusConnected; - // Oriented motion ranges for input device info. struct OrientedRanges { InputDeviceInfo::MotionRange x; diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp index 24efae893e..9a36bfbeaf 100644 --- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp @@ -36,6 +36,7 @@ #include <log/log_main.h> #include <stats_pull_atom_callback.h> #include <statslog.h> +#include "InputReaderBase.h" #include "TouchCursorInputMapperCommon.h" #include "TouchpadInputMapper.h" #include "gestures/HardwareProperties.h" @@ -240,28 +241,28 @@ TouchpadInputMapper::TouchpadInputMapper(InputDeviceContext& deviceContext, mGestureConverter(*getContext(), deviceContext, getDeviceId()), mCapturedEventConverter(*getContext(), deviceContext, mMotionAccumulator, getDeviceId()), mMetricsId(metricsIdFromInputDeviceIdentifier(deviceContext.getDeviceIdentifier())) { - RawAbsoluteAxisInfo slotAxisInfo; - deviceContext.getAbsoluteAxisInfo(ABS_MT_SLOT, &slotAxisInfo); - if (!slotAxisInfo.valid || slotAxisInfo.maxValue < 0) { + if (std::optional<RawAbsoluteAxisInfo> slotAxis = + deviceContext.getAbsoluteAxisInfo(ABS_MT_SLOT); + slotAxis && slotAxis->maxValue >= 0) { + mMotionAccumulator.configure(deviceContext, slotAxis->maxValue + 1, true); + } else { LOG(WARNING) << "Touchpad " << deviceContext.getName() << " doesn't have a valid ABS_MT_SLOT axis, and probably won't work properly."; - slotAxisInfo.maxValue = 0; + mMotionAccumulator.configure(deviceContext, 1, true); } - mMotionAccumulator.configure(deviceContext, slotAxisInfo.maxValue + 1, true); mGestureInterpreter->Initialize(GESTURES_DEVCLASS_TOUCHPAD); - mGestureInterpreter->SetHardwareProperties(createHardwareProperties(deviceContext)); + mHardwareProperties = createHardwareProperties(deviceContext); + mGestureInterpreter->SetHardwareProperties(mHardwareProperties); // Even though we don't explicitly delete copy/move semantics, it's safe to // give away pointers to TouchpadInputMapper and its members here because // 1) mGestureInterpreter's lifecycle is determined by TouchpadInputMapper, and // 2) TouchpadInputMapper is stored as a unique_ptr and not moved. mGestureInterpreter->SetPropProvider(const_cast<GesturesPropProvider*>(&gesturePropProvider), &mPropertyProvider); - if (input_flags::enable_gestures_library_timer_provider()) { - mGestureInterpreter->SetTimerProvider(const_cast<GesturesTimerProvider*>( - &kGestureTimerProvider), - &mTimerProvider); - } + mGestureInterpreter->SetTimerProvider(const_cast<GesturesTimerProvider*>( + &kGestureTimerProvider), + &mTimerProvider); mGestureInterpreter->SetCallback(gestureInterpreterCallback, this); } @@ -299,12 +300,8 @@ void TouchpadInputMapper::dump(std::string& dump) { dump += addLinePrefix(mGestureConverter.dump(), INDENT4); dump += INDENT3 "Gesture properties:\n"; dump += addLinePrefix(mPropertyProvider.dump(), INDENT4); - if (input_flags::enable_gestures_library_timer_provider()) { - dump += INDENT3 "Timer provider:\n"; - dump += addLinePrefix(mTimerProvider.dump(), INDENT4); - } else { - dump += INDENT3 "Timer provider: disabled by flag\n"; - } + dump += INDENT3 "Timer provider:\n"; + dump += addLinePrefix(mTimerProvider.dump(), INDENT4); dump += INDENT3 "Captured event converter:\n"; dump += addLinePrefix(mCapturedEventConverter.dump(), INDENT4); dump += StringPrintf(INDENT3 "DisplayId: %s\n", @@ -377,6 +374,7 @@ std::list<NotifyArgs> TouchpadInputMapper::reconfigure(nsecs_t when, .setBoolValues({config.touchpadTapDraggingEnabled}); mPropertyProvider.getProperty("Button Right Click Zone Enable") .setBoolValues({config.touchpadRightClickZoneEnabled}); + mTouchpadHardwareStateNotificationsEnabled = config.shouldNotifyTouchpadHardwareState; } std::list<NotifyArgs> out; if ((!changes.any() && config.pointerCaptureRequest.isEnable()) || @@ -426,6 +424,9 @@ std::list<NotifyArgs> TouchpadInputMapper::process(const RawEvent& rawEvent) { } std::optional<SelfContainedHardwareState> state = mStateConverter.processRawEvent(rawEvent); if (state) { + if (mTouchpadHardwareStateNotificationsEnabled) { + getPolicy()->notifyTouchpadHardwareState(*state, getDeviceId()); + } updatePalmDetectionMetrics(); return sendHardwareState(rawEvent.when, rawEvent.readTime, *state); } else { @@ -467,9 +468,6 @@ std::list<NotifyArgs> TouchpadInputMapper::sendHardwareState(nsecs_t when, nsecs } std::list<NotifyArgs> TouchpadInputMapper::timeoutExpired(nsecs_t when) { - if (!input_flags::enable_gestures_library_timer_provider()) { - return {}; - } mTimerProvider.triggerCallbacks(when); return processGestures(when, when); } @@ -482,6 +480,9 @@ void TouchpadInputMapper::consumeGesture(const Gesture* gesture) { return; } mGesturesToProcess.push_back(*gesture); + if (mTouchpadHardwareStateNotificationsEnabled) { + getPolicy()->notifyTouchpadGestureInfo(gesture->type, getDeviceId()); + } } std::list<NotifyArgs> TouchpadInputMapper::processGestures(nsecs_t when, nsecs_t readTime) { @@ -501,4 +502,8 @@ std::optional<ui::LogicalDisplayId> TouchpadInputMapper::getAssociatedDisplayId( return mDisplayId; } +std::optional<HardwareProperties> TouchpadInputMapper::getTouchpadHardwareProperties() { + return mHardwareProperties; +} + } // namespace android diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.h b/services/inputflinger/reader/mapper/TouchpadInputMapper.h index 8baa63e8e0..a2c4be9e50 100644 --- a/services/inputflinger/reader/mapper/TouchpadInputMapper.h +++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.h @@ -68,6 +68,8 @@ public: std::optional<ui::LogicalDisplayId> getAssociatedDisplayId() override; + std::optional<HardwareProperties> getTouchpadHardwareProperties() override; + private: void resetGestureInterpreter(nsecs_t when); explicit TouchpadInputMapper(InputDeviceContext& deviceContext, @@ -92,6 +94,7 @@ private: HardwareStateConverter mStateConverter; GestureConverter mGestureConverter; CapturedTouchpadEventConverter mCapturedEventConverter; + HardwareProperties mHardwareProperties; bool mPointerCaptured = false; bool mResettingInterpreter = false; @@ -112,6 +115,10 @@ private: std::optional<ui::LogicalDisplayId> mDisplayId; nsecs_t mGestureStartTime{0}; + + // True if hardware state update notifications is available for usage based on its feature flag + // and settings value. + bool mTouchpadHardwareStateNotificationsEnabled = false; }; } // namespace android diff --git a/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.cpp index f85cab205b..537344048b 100644 --- a/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.cpp +++ b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.cpp @@ -16,18 +16,29 @@ #include "CursorScrollAccumulator.h" +#include <android_companion_virtualdevice_flags.h> #include "EventHub.h" #include "InputDevice.h" namespace android { -CursorScrollAccumulator::CursorScrollAccumulator() : mHaveRelWheel(false), mHaveRelHWheel(false) { +namespace vd_flags = android::companion::virtualdevice::flags; + +CursorScrollAccumulator::CursorScrollAccumulator() + : mHaveRelWheel(false), + mHaveRelHWheel(false), + mHaveRelWheelHighRes(false), + mHaveRelHWheelHighRes(false) { clearRelativeAxes(); } void CursorScrollAccumulator::configure(InputDeviceContext& deviceContext) { mHaveRelWheel = deviceContext.hasRelativeAxis(REL_WHEEL); mHaveRelHWheel = deviceContext.hasRelativeAxis(REL_HWHEEL); + if (vd_flags::high_resolution_scroll()) { + mHaveRelWheelHighRes = deviceContext.hasRelativeAxis(REL_WHEEL_HI_RES); + mHaveRelHWheelHighRes = deviceContext.hasRelativeAxis(REL_HWHEEL_HI_RES); + } } void CursorScrollAccumulator::reset(InputDeviceContext& deviceContext) { @@ -42,11 +53,31 @@ void CursorScrollAccumulator::clearRelativeAxes() { void CursorScrollAccumulator::process(const RawEvent& rawEvent) { if (rawEvent.type == EV_REL) { switch (rawEvent.code) { + case REL_WHEEL_HI_RES: + if (mHaveRelWheelHighRes) { + mRelWheel = + rawEvent.value / static_cast<float>(kEvdevHighResScrollUnitsPerDetent); + } + break; + case REL_HWHEEL_HI_RES: + if (mHaveRelHWheelHighRes) { + mRelHWheel = + rawEvent.value / static_cast<float>(kEvdevHighResScrollUnitsPerDetent); + } + break; case REL_WHEEL: - mRelWheel = rawEvent.value; + // We should ignore regular scroll events, if we have already have high-res scroll + // enabled. + if (!mHaveRelWheelHighRes) { + mRelWheel = rawEvent.value; + } break; case REL_HWHEEL: - mRelHWheel = rawEvent.value; + // We should ignore regular scroll events, if we have already have high-res scroll + // enabled. + if (!mHaveRelHWheelHighRes) { + mRelHWheel = rawEvent.value; + } break; } } diff --git a/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h index e563620252..d3373ccd33 100644 --- a/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h +++ b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h @@ -16,15 +16,12 @@ #pragma once -#include <stdint.h> - namespace android { class InputDeviceContext; struct RawEvent; /* Keeps track of cursor scrolling motions. */ - class CursorScrollAccumulator { public: CursorScrollAccumulator(); @@ -37,19 +34,17 @@ public: inline bool haveRelativeVWheel() const { return mHaveRelWheel; } inline bool haveRelativeHWheel() const { return mHaveRelHWheel; } - inline int32_t getRelativeX() const { return mRelX; } - inline int32_t getRelativeY() const { return mRelY; } - inline int32_t getRelativeVWheel() const { return mRelWheel; } - inline int32_t getRelativeHWheel() const { return mRelHWheel; } + inline float getRelativeVWheel() const { return mRelWheel; } + inline float getRelativeHWheel() const { return mRelHWheel; } private: bool mHaveRelWheel; bool mHaveRelHWheel; + bool mHaveRelWheelHighRes; + bool mHaveRelHWheelHighRes; - int32_t mRelX; - int32_t mRelY; - int32_t mRelWheel; - int32_t mRelHWheel; + float mRelWheel; + float mRelHWheel; void clearRelativeAxes(); }; diff --git a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp index 4919068201..8dc6e4d397 100644 --- a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp +++ b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp @@ -139,13 +139,11 @@ void MultiTouchMotionAccumulator::populateCurrentSlot( if (!mUsingSlotsProtocol) { return; } - int32_t initialSlot; - if (const auto status = deviceContext.getAbsoluteAxisValue(ABS_MT_SLOT, &initialSlot); - status == OK) { - mCurrentSlot = initialSlot; + if (const std::optional<int32_t> initialSlot = deviceContext.getAbsoluteAxisValue(ABS_MT_SLOT); + initialSlot.has_value()) { + mCurrentSlot = initialSlot.value(); } else { - ALOGE("Could not retrieve current multi-touch slot index. status=%s", - statusToString(status).c_str()); + ALOGE("Could not retrieve current multi-touch slot index"); } } diff --git a/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.cpp index 2b82ddf33d..4cf9243653 100644 --- a/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.cpp +++ b/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.cpp @@ -26,13 +26,13 @@ SingleTouchMotionAccumulator::SingleTouchMotionAccumulator() { } void SingleTouchMotionAccumulator::reset(InputDeviceContext& deviceContext) { - mAbsX = deviceContext.getAbsoluteAxisValue(ABS_X); - mAbsY = deviceContext.getAbsoluteAxisValue(ABS_Y); - mAbsPressure = deviceContext.getAbsoluteAxisValue(ABS_PRESSURE); - mAbsToolWidth = deviceContext.getAbsoluteAxisValue(ABS_TOOL_WIDTH); - mAbsDistance = deviceContext.getAbsoluteAxisValue(ABS_DISTANCE); - mAbsTiltX = deviceContext.getAbsoluteAxisValue(ABS_TILT_X); - mAbsTiltY = deviceContext.getAbsoluteAxisValue(ABS_TILT_Y); + mAbsX = deviceContext.getAbsoluteAxisValue(ABS_X).value_or(0); + mAbsY = deviceContext.getAbsoluteAxisValue(ABS_Y).value_or(0); + mAbsPressure = deviceContext.getAbsoluteAxisValue(ABS_PRESSURE).value_or(0); + mAbsToolWidth = deviceContext.getAbsoluteAxisValue(ABS_TOOL_WIDTH).value_or(0); + mAbsDistance = deviceContext.getAbsoluteAxisValue(ABS_DISTANCE).value_or(0); + mAbsTiltX = deviceContext.getAbsoluteAxisValue(ABS_TILT_X).value_or(0); + mAbsTiltY = deviceContext.getAbsoluteAxisValue(ABS_TILT_Y).value_or(0); } void SingleTouchMotionAccumulator::clearAbsoluteAxes() { diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp index e8e7376e92..da2c683d95 100644 --- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp +++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp @@ -60,16 +60,33 @@ uint32_t gesturesButtonToMotionEventButton(uint32_t gesturesButton) { } } +bool isGestureNoFocusChange(MotionClassification classification) { + switch (classification) { + case MotionClassification::TWO_FINGER_SWIPE: + case MotionClassification::MULTI_FINGER_SWIPE: + case MotionClassification::PINCH: + // Most gestures can be performed on an unfocused window, so they should not + // not affect window focus. + return true; + case MotionClassification::NONE: + case MotionClassification::AMBIGUOUS_GESTURE: + case MotionClassification::DEEP_PRESS: + return false; + } +} + } // namespace GestureConverter::GestureConverter(InputReaderContext& readerContext, const InputDeviceContext& deviceContext, int32_t deviceId) : mDeviceId(deviceId), mReaderContext(readerContext), - mEnableFlingStop(input_flags::enable_touchpad_fling_stop()) { - deviceContext.getAbsoluteAxisInfo(ABS_MT_POSITION_X, &mXAxisInfo); - deviceContext.getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &mYAxisInfo); -} + mEnableFlingStop(input_flags::enable_touchpad_fling_stop()), + mEnableNoFocusChange(input_flags::enable_touchpad_no_focus_change()), + // We can safely assume that ABS_MT_POSITION_X and _Y axes will be available, as EventHub + // won't classify a device as a touchpad if they're not present. + mXAxisInfo(deviceContext.getAbsoluteAxisInfo(ABS_MT_POSITION_X).value()), + mYAxisInfo(deviceContext.getAbsoluteAxisInfo(ABS_MT_POSITION_Y).value()) {} std::string GestureConverter::dump() const { std::stringstream out; @@ -337,7 +354,6 @@ std::list<NotifyArgs> GestureConverter::handleScroll(nsecs_t when, nsecs_t readT NotifyMotionArgs args = makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN, /* actionButton= */ 0, mButtonState, /* pointerCount= */ 1, mFakeFingerCoords.data()); - args.flags |= AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE; out.push_back(args); } float deltaX = gesture.details.scroll.dx; @@ -352,7 +368,6 @@ std::list<NotifyArgs> GestureConverter::handleScroll(nsecs_t when, nsecs_t readT NotifyMotionArgs args = makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_MOVE, /* actionButton= */ 0, mButtonState, /* pointerCount= */ 1, mFakeFingerCoords.data()); - args.flags |= AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE; out.push_back(args); return out; } @@ -426,7 +441,6 @@ std::list<NotifyArgs> GestureConverter::endScroll(nsecs_t when, nsecs_t readTime NotifyMotionArgs args = makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /* actionButton= */ 0, mButtonState, /* pointerCount= */ 1, mFakeFingerCoords.data()); - args.flags |= AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE; out.push_back(args); mCurrentClassification = MotionClassification::NONE; out += enterHover(when, readTime); @@ -623,6 +637,18 @@ NotifyMotionArgs GestureConverter::makeMotionArgs(nsecs_t when, nsecs_t readTime int32_t actionButton, int32_t buttonState, uint32_t pointerCount, const PointerCoords* pointerCoords) { + int32_t flags = 0; + if (action == AMOTION_EVENT_ACTION_CANCEL) { + flags |= AMOTION_EVENT_FLAG_CANCELED; + } + if (mEnableNoFocusChange && isGestureNoFocusChange(mCurrentClassification)) { + flags |= AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE; + } + if (mCurrentClassification == MotionClassification::TWO_FINGER_SWIPE) { + // This helps to make GestureDetector responsive. + flags |= AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE; + } + return {mReaderContext.getNextId(), when, readTime, @@ -632,7 +658,7 @@ NotifyMotionArgs GestureConverter::makeMotionArgs(nsecs_t when, nsecs_t readTime /* policyFlags= */ POLICY_FLAG_WAKE, action, /* actionButton= */ actionButton, - /* flags= */ action == AMOTION_EVENT_ACTION_CANCEL ? AMOTION_EVENT_FLAG_CANCELED : 0, + flags, mReaderContext.getGlobalMetaState(), buttonState, mCurrentClassification, diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.h b/services/inputflinger/reader/mapper/gestures/GestureConverter.h index 829fb9289b..c9a35c151f 100644 --- a/services/inputflinger/reader/mapper/gestures/GestureConverter.h +++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.h @@ -99,6 +99,7 @@ private: const int32_t mDeviceId; InputReaderContext& mReaderContext; const bool mEnableFlingStop; + const bool mEnableNoFocusChange; std::optional<ui::LogicalDisplayId> mDisplayId; FloatRect mBoundsInLogicalDisplay{}; diff --git a/services/inputflinger/reader/mapper/gestures/HardwareProperties.cpp b/services/inputflinger/reader/mapper/gestures/HardwareProperties.cpp index 04655dc439..d8a1f501d1 100644 --- a/services/inputflinger/reader/mapper/gestures/HardwareProperties.cpp +++ b/services/inputflinger/reader/mapper/gestures/HardwareProperties.cpp @@ -16,6 +16,8 @@ #include "HardwareProperties.h" +#include <optional> + namespace android { namespace { @@ -33,26 +35,34 @@ unsigned short getMaxTouchCount(const InputDeviceContext& context) { HardwareProperties createHardwareProperties(const InputDeviceContext& context) { HardwareProperties props; - RawAbsoluteAxisInfo absMtPositionX; - context.getAbsoluteAxisInfo(ABS_MT_POSITION_X, &absMtPositionX); + // We can safely assume that ABS_MT_POSITION_X and _Y axes will be available, as EventHub won't + // classify a device as a touchpad if they're not present. + RawAbsoluteAxisInfo absMtPositionX = context.getAbsoluteAxisInfo(ABS_MT_POSITION_X).value(); props.left = absMtPositionX.minValue; props.right = absMtPositionX.maxValue; props.res_x = absMtPositionX.resolution; - RawAbsoluteAxisInfo absMtPositionY; - context.getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &absMtPositionY); + RawAbsoluteAxisInfo absMtPositionY = context.getAbsoluteAxisInfo(ABS_MT_POSITION_Y).value(); props.top = absMtPositionY.minValue; props.bottom = absMtPositionY.maxValue; props.res_y = absMtPositionY.resolution; - RawAbsoluteAxisInfo absMtOrientation; - context.getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &absMtOrientation); - props.orientation_minimum = absMtOrientation.minValue; - props.orientation_maximum = absMtOrientation.maxValue; + if (std::optional<RawAbsoluteAxisInfo> absMtOrientation = + context.getAbsoluteAxisInfo(ABS_MT_ORIENTATION); + absMtOrientation) { + props.orientation_minimum = absMtOrientation->minValue; + props.orientation_maximum = absMtOrientation->maxValue; + } else { + props.orientation_minimum = 0; + props.orientation_maximum = 0; + } - RawAbsoluteAxisInfo absMtSlot; - context.getAbsoluteAxisInfo(ABS_MT_SLOT, &absMtSlot); - props.max_finger_cnt = absMtSlot.maxValue - absMtSlot.minValue + 1; + if (std::optional<RawAbsoluteAxisInfo> absMtSlot = context.getAbsoluteAxisInfo(ABS_MT_SLOT); + absMtSlot) { + props.max_finger_cnt = absMtSlot->maxValue - absMtSlot->minValue + 1; + } else { + props.max_finger_cnt = 1; + } props.max_touch_cnt = getMaxTouchCount(context); // T5R2 ("Track 5, Report 2") is a feature of some old Synaptics touchpads that could track 5 @@ -71,9 +81,7 @@ HardwareProperties createHardwareProperties(const InputDeviceContext& context) { // are haptic. props.is_haptic_pad = false; - RawAbsoluteAxisInfo absMtPressure; - context.getAbsoluteAxisInfo(ABS_MT_PRESSURE, &absMtPressure); - props.reports_pressure = absMtPressure.valid; + props.reports_pressure = context.hasAbsoluteAxis(ABS_MT_PRESSURE); return props; } diff --git a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h index 07e62c6eba..148ca5aa9e 100644 --- a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h +++ b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h @@ -26,18 +26,13 @@ #include "accumulator/CursorButtonAccumulator.h" #include "accumulator/MultiTouchMotionAccumulator.h" #include "accumulator/TouchButtonAccumulator.h" +#include "include/TouchpadHardwareState.h" +#include "TouchpadHardwareState.h" #include "include/gestures.h" namespace android { -// A HardwareState struct, but bundled with a vector to contain its FingerStates, so you don't have -// to worry about where that memory is allocated. -struct SelfContainedHardwareState { - HardwareState state; - std::vector<FingerState> fingers; -}; - // Converts RawEvents into the HardwareState structs used by the gestures library. class HardwareStateConverter { public: diff --git a/services/inputflinger/rust/bounce_keys_filter.rs b/services/inputflinger/rust/bounce_keys_filter.rs index 2d5039a1b1..e05e8e512f 100644 --- a/services/inputflinger/rust/bounce_keys_filter.rs +++ b/services/inputflinger/rust/bounce_keys_filter.rs @@ -17,12 +17,13 @@ //! Bounce keys input filter implementation. //! Bounce keys is an accessibility feature to aid users who have physical disabilities, that //! allows the user to configure the device to ignore rapid, repeated key presses of the same key. -use crate::input_filter::Filter; +use crate::input_filter::{Filter, VIRTUAL_KEYBOARD_DEVICE_ID}; use android_hardware_input_common::aidl::android::hardware::input::common::Source::Source; use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{ DeviceInfo::DeviceInfo, KeyEvent::KeyEvent, KeyEventAction::KeyEventAction, }; +use input::KeyboardType; use log::debug; use std::collections::{HashMap, HashSet}; @@ -42,7 +43,7 @@ pub struct BounceKeysFilter { next: Box<dyn Filter + Send + Sync>, key_event_map: HashMap<i32, LastUpKeyEvent>, blocked_events: Vec<BlockedEvent>, - external_devices: HashSet<i32>, + supported_devices: HashSet<i32>, bounce_key_threshold_ns: i64, } @@ -56,7 +57,7 @@ impl BounceKeysFilter { next, key_event_map: HashMap::new(), blocked_events: Vec::new(), - external_devices: HashSet::new(), + supported_devices: HashSet::new(), bounce_key_threshold_ns, } } @@ -64,7 +65,10 @@ impl BounceKeysFilter { impl Filter for BounceKeysFilter { fn notify_key(&mut self, event: &KeyEvent) { - if !(self.external_devices.contains(&event.deviceId) && event.source == Source::KEYBOARD) { + // Check if it is a supported device and event source contains Source::KEYBOARD + if !(self.supported_devices.contains(&event.deviceId) + && event.source.0 & Source::KEYBOARD.0 != 0) + { self.next.notify_key(event); return; } @@ -110,10 +114,17 @@ impl Filter for BounceKeysFilter { self.blocked_events.retain(|blocked_event| { device_infos.iter().any(|x| blocked_event.device_id == x.deviceId) }); - self.external_devices.clear(); + self.supported_devices.clear(); for device_info in device_infos { - if device_info.external { - self.external_devices.insert(device_info.deviceId); + if device_info.deviceId == VIRTUAL_KEYBOARD_DEVICE_ID { + continue; + } + if device_info.keyboardType == KeyboardType::None as i32 { + continue; + } + // Support Alphabetic keyboards and Non-alphabetic external keyboards + if device_info.external || device_info.keyboardType == KeyboardType::Alphabetic as i32 { + self.supported_devices.insert(device_info.deviceId); } } self.next.notify_devices_changed(device_infos); @@ -122,16 +133,26 @@ impl Filter for BounceKeysFilter { fn destroy(&mut self) { self.next.destroy(); } + + fn dump(&mut self, dump_str: String) -> String { + let mut result = "Bounce Keys filter: \n".to_string(); + result += &format!("\tthreshold = {:?}ns\n", self.bounce_key_threshold_ns); + result += &format!("\tkey_event_map = {:?}\n", self.key_event_map); + result += &format!("\tblocked_events = {:?}\n", self.blocked_events); + result += &format!("\tsupported_devices = {:?}\n", self.supported_devices); + self.next.dump(dump_str + &result) + } } #[cfg(test)] mod tests { use crate::bounce_keys_filter::BounceKeysFilter; - use crate::input_filter::{test_filter::TestFilter, Filter}; + use crate::input_filter::{test_filter::TestFilter, Filter, VIRTUAL_KEYBOARD_DEVICE_ID}; use android_hardware_input_common::aidl::android::hardware::input::common::Source::Source; use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{ DeviceInfo::DeviceInfo, KeyEvent::KeyEvent, KeyEventAction::KeyEventAction, }; + use input::KeyboardType; static BASE_KEY_EVENT: KeyEvent = KeyEvent { id: 1, @@ -156,6 +177,7 @@ mod tests { Box::new(next.clone()), 1, /* device_id */ 100, /* threshold */ + KeyboardType::Alphabetic, ); let event = KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT }; @@ -181,12 +203,103 @@ mod tests { } #[test] - fn test_is_notify_key_doesnt_block_for_internal_keyboard() { + fn test_is_notify_key_for_tv_remote() { + let mut next = TestFilter::new(); + let mut filter = setup_filter_with_external_device( + Box::new(next.clone()), + 1, /* device_id */ + 100, /* threshold */ + KeyboardType::NonAlphabetic, + ); + + let source = Source(Source::KEYBOARD.0 | Source::DPAD.0); + let event = KeyEvent { action: KeyEventAction::DOWN, source, ..BASE_KEY_EVENT }; + filter.notify_key(&event); + assert_eq!(next.last_event().unwrap(), event); + + let event = KeyEvent { action: KeyEventAction::UP, source, ..BASE_KEY_EVENT }; + filter.notify_key(&event); + assert_eq!(next.last_event().unwrap(), event); + + next.clear(); + let event = KeyEvent { action: KeyEventAction::DOWN, source, ..BASE_KEY_EVENT }; + filter.notify_key(&event); + assert!(next.last_event().is_none()); + + let event = + KeyEvent { eventTime: 100, action: KeyEventAction::UP, source, ..BASE_KEY_EVENT }; + filter.notify_key(&event); + assert!(next.last_event().is_none()); + + let event = + KeyEvent { eventTime: 200, action: KeyEventAction::DOWN, source, ..BASE_KEY_EVENT }; + filter.notify_key(&event); + assert_eq!(next.last_event().unwrap(), event); + } + + #[test] + fn test_is_notify_key_blocks_for_internal_keyboard() { + let mut next = TestFilter::new(); + let mut filter = setup_filter_with_internal_device( + Box::new(next.clone()), + 1, /* device_id */ + 100, /* threshold */ + KeyboardType::Alphabetic, + ); + + let event = KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT }; + filter.notify_key(&event); + assert_eq!(next.last_event().unwrap(), event); + + let event = KeyEvent { action: KeyEventAction::UP, ..BASE_KEY_EVENT }; + filter.notify_key(&event); + assert_eq!(next.last_event().unwrap(), event); + + next.clear(); + let event = KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT }; + filter.notify_key(&event); + assert!(next.last_event().is_none()); + + let event = KeyEvent { eventTime: 100, action: KeyEventAction::UP, ..BASE_KEY_EVENT }; + filter.notify_key(&event); + assert!(next.last_event().is_none()); + + let event = KeyEvent { eventTime: 200, action: KeyEventAction::DOWN, ..BASE_KEY_EVENT }; + filter.notify_key(&event); + assert_eq!(next.last_event().unwrap(), event); + } + + #[test] + fn test_is_notify_key_doesnt_block_for_internal_non_alphabetic_keyboard() { let next = TestFilter::new(); let mut filter = setup_filter_with_internal_device( Box::new(next.clone()), 1, /* device_id */ 100, /* threshold */ + KeyboardType::NonAlphabetic, + ); + + let event = KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT }; + filter.notify_key(&event); + assert_eq!(next.last_event().unwrap(), event); + + let event = KeyEvent { action: KeyEventAction::UP, ..BASE_KEY_EVENT }; + filter.notify_key(&event); + assert_eq!(next.last_event().unwrap(), event); + + let event = KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT }; + filter.notify_key(&event); + assert_eq!(next.last_event().unwrap(), event); + } + + #[test] + fn test_is_notify_key_doesnt_block_for_virtual_keyboard() { + let next = TestFilter::new(); + let mut filter = setup_filter_with_internal_device( + Box::new(next.clone()), + VIRTUAL_KEYBOARD_DEVICE_ID, /* device_id */ + 100, /* threshold */ + KeyboardType::Alphabetic, ); let event = KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT }; @@ -209,6 +322,7 @@ mod tests { Box::new(next.clone()), 1, /* device_id */ 100, /* threshold */ + KeyboardType::NonAlphabetic, ); let event = @@ -233,12 +347,60 @@ mod tests { let mut filter = setup_filter_with_devices( Box::new(next.clone()), &[ - DeviceInfo { deviceId: 1, external: true }, - DeviceInfo { deviceId: 2, external: true }, + DeviceInfo { + deviceId: 1, + external: true, + keyboardType: KeyboardType::Alphabetic as i32, + }, + DeviceInfo { + deviceId: 2, + external: true, + keyboardType: KeyboardType::Alphabetic as i32, + }, + ], + 100, /* threshold */ + ); + + // Bounce key scenario on the external keyboard + let event = KeyEvent { deviceId: 1, action: KeyEventAction::DOWN, ..BASE_KEY_EVENT }; + filter.notify_key(&event); + assert_eq!(next.last_event().unwrap(), event); + + let event = KeyEvent { deviceId: 1, action: KeyEventAction::UP, ..BASE_KEY_EVENT }; + filter.notify_key(&event); + assert_eq!(next.last_event().unwrap(), event); + + next.clear(); + let event = KeyEvent { deviceId: 1, action: KeyEventAction::DOWN, ..BASE_KEY_EVENT }; + filter.notify_key(&event); + assert!(next.last_event().is_none()); + + let event = KeyEvent { deviceId: 2, action: KeyEventAction::DOWN, ..BASE_KEY_EVENT }; + filter.notify_key(&event); + assert_eq!(next.last_event().unwrap(), event); + } + + #[test] + fn test_is_notify_key_for_external_and_internal_alphabetic_keyboards() { + let mut next = TestFilter::new(); + let mut filter = setup_filter_with_devices( + Box::new(next.clone()), + &[ + DeviceInfo { + deviceId: 1, + external: false, + keyboardType: KeyboardType::Alphabetic as i32, + }, + DeviceInfo { + deviceId: 2, + external: true, + keyboardType: KeyboardType::Alphabetic as i32, + }, ], 100, /* threshold */ ); + // Bounce key scenario on the internal keyboard let event = KeyEvent { deviceId: 1, action: KeyEventAction::DOWN, ..BASE_KEY_EVENT }; filter.notify_key(&event); assert_eq!(next.last_event().unwrap(), event); @@ -261,10 +423,15 @@ mod tests { next: Box<dyn Filter + Send + Sync>, device_id: i32, threshold: i64, + keyboard_type: KeyboardType, ) -> BounceKeysFilter { setup_filter_with_devices( next, - &[DeviceInfo { deviceId: device_id, external: true }], + &[DeviceInfo { + deviceId: device_id, + external: true, + keyboardType: keyboard_type as i32, + }], threshold, ) } @@ -273,10 +440,15 @@ mod tests { next: Box<dyn Filter + Send + Sync>, device_id: i32, threshold: i64, + keyboard_type: KeyboardType, ) -> BounceKeysFilter { setup_filter_with_devices( next, - &[DeviceInfo { deviceId: device_id, external: false }], + &[DeviceInfo { + deviceId: device_id, + external: false, + keyboardType: keyboard_type as i32, + }], threshold, ) } diff --git a/services/inputflinger/rust/input_filter.rs b/services/inputflinger/rust/input_filter.rs index 8b44af38db..e2212449aa 100644 --- a/services/inputflinger/rust/input_filter.rs +++ b/services/inputflinger/rust/input_filter.rs @@ -35,11 +35,15 @@ use input::ModifierState; use log::{error, info}; use std::sync::{Arc, Mutex, RwLock}; +/// Virtual keyboard device ID +pub const VIRTUAL_KEYBOARD_DEVICE_ID: i32 = -1; + /// Interface for all the sub input filters pub trait Filter { fn notify_key(&mut self, event: &KeyEvent); fn notify_devices_changed(&mut self, device_infos: &[DeviceInfo]); fn destroy(&mut self); + fn dump(&mut self, dump_str: String) -> String; } struct InputFilterState { @@ -119,18 +123,30 @@ impl IInputFilter for InputFilter { self.input_filter_thread.clone(), )); state.enabled = true; - info!("Slow keys filter is installed"); + info!( + "Slow keys filter is installed, threshold = {:?}ns", + config.slowKeysThresholdNs + ); } if config.bounceKeysThresholdNs > 0 { first_filter = Box::new(BounceKeysFilter::new(first_filter, config.bounceKeysThresholdNs)); state.enabled = true; - info!("Bounce keys filter is installed"); + info!( + "Bounce keys filter is installed, threshold = {:?}ns", + config.bounceKeysThresholdNs + ); } state.first_filter = first_filter; } Result::Ok(()) } + + fn dumpFilter(&self) -> binder::Result<String> { + let first_filter = &mut self.state.lock().unwrap().first_filter; + let dump_str = first_filter.dump(String::new()); + Result::Ok(dump_str) + } } struct BaseFilter { @@ -158,6 +174,11 @@ impl Filter for BaseFilter { fn destroy(&mut self) { // do nothing } + + fn dump(&mut self, dump_str: String) -> String { + // do nothing + dump_str + } } /// This struct wraps around IInputFilterCallbacks restricting access to only @@ -214,6 +235,7 @@ mod tests { InputFilterConfiguration::InputFilterConfiguration, KeyEvent::KeyEvent, KeyEventAction::KeyEventAction, }; + use input::KeyboardType; use std::sync::{Arc, RwLock}; #[test] @@ -256,7 +278,11 @@ mod tests { Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks)))), ); assert!(input_filter - .notifyInputDevicesChanged(&[DeviceInfo { deviceId: 0, external: true }]) + .notifyInputDevicesChanged(&[DeviceInfo { + deviceId: 0, + external: true, + keyboardType: KeyboardType::None as i32 + }]) .is_ok()); assert!(test_filter.is_device_changed_called()); } @@ -389,6 +415,10 @@ pub mod test_filter { fn destroy(&mut self) { self.inner().is_destroy_called = true; } + fn dump(&mut self, dump_str: String) -> String { + // do nothing + dump_str + } } } diff --git a/services/inputflinger/rust/slow_keys_filter.rs b/services/inputflinger/rust/slow_keys_filter.rs index 0f18a2f395..8830aac529 100644 --- a/services/inputflinger/rust/slow_keys_filter.rs +++ b/services/inputflinger/rust/slow_keys_filter.rs @@ -18,12 +18,13 @@ //! Slow keys is an accessibility feature to aid users who have physical disabilities, that allows //! the user to specify the duration for which one must press-and-hold a key before the system //! accepts the keypress. -use crate::input_filter::Filter; +use crate::input_filter::{Filter, VIRTUAL_KEYBOARD_DEVICE_ID}; use crate::input_filter_thread::{InputFilterThread, ThreadCallback}; use android_hardware_input_common::aidl::android::hardware::input::common::Source::Source; use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{ DeviceInfo::DeviceInfo, KeyEvent::KeyEvent, KeyEventAction::KeyEventAction, }; +use input::KeyboardType; use log::debug; use std::collections::HashSet; use std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard}; @@ -41,7 +42,7 @@ struct OngoingKeyDown { struct SlowKeysFilterInner { next: Box<dyn Filter + Send + Sync>, slow_key_threshold_ns: i64, - external_devices: HashSet<i32>, + supported_devices: HashSet<i32>, // This tracks KeyEvents that are blocked by Slow keys filter and will be passed through if the // press duration exceeds the slow keys threshold. pending_down_events: Vec<KeyEvent>, @@ -65,7 +66,7 @@ impl SlowKeysFilter { let filter = Self(Arc::new(RwLock::new(SlowKeysFilterInner { next, slow_key_threshold_ns, - external_devices: HashSet::new(), + supported_devices: HashSet::new(), pending_down_events: Vec::new(), ongoing_down_events: Vec::new(), input_filter_thread: input_filter_thread.clone(), @@ -98,8 +99,8 @@ impl Filter for SlowKeysFilter { { // acquire write lock let mut slow_filter = self.write_inner(); - if !(slow_filter.external_devices.contains(&event.deviceId) - && event.source == Source::KEYBOARD) + if !(slow_filter.supported_devices.contains(&event.deviceId) + && event.source.0 & Source::KEYBOARD.0 != 0) { slow_filter.next.notify_key(event); return; @@ -164,10 +165,17 @@ impl Filter for SlowKeysFilter { slow_filter .ongoing_down_events .retain(|event| device_infos.iter().any(|x| event.device_id == x.deviceId)); - slow_filter.external_devices.clear(); + slow_filter.supported_devices.clear(); for device_info in device_infos { - if device_info.external { - slow_filter.external_devices.insert(device_info.deviceId); + if device_info.deviceId == VIRTUAL_KEYBOARD_DEVICE_ID { + continue; + } + if device_info.keyboardType == KeyboardType::None as i32 { + continue; + } + // Support Alphabetic keyboards and Non-alphabetic external keyboards + if device_info.external || device_info.keyboardType == KeyboardType::Alphabetic as i32 { + slow_filter.supported_devices.insert(device_info.deviceId); } } slow_filter.next.notify_devices_changed(device_infos); @@ -178,6 +186,16 @@ impl Filter for SlowKeysFilter { slow_filter.input_filter_thread.unregister_thread_callback(Box::new(self.clone())); slow_filter.next.destroy(); } + + fn dump(&mut self, dump_str: String) -> String { + let mut slow_filter = self.write_inner(); + let mut result = "Slow Keys filter: \n".to_string(); + result += &format!("\tthreshold = {:?}ns\n", slow_filter.slow_key_threshold_ns); + result += &format!("\tongoing_down_events = {:?}\n", slow_filter.ongoing_down_events); + result += &format!("\tpending_down_events = {:?}\n", slow_filter.pending_down_events); + result += &format!("\tsupported_devices = {:?}\n", slow_filter.supported_devices); + slow_filter.next.dump(dump_str + &result) + } } impl ThreadCallback for SlowKeysFilter { @@ -217,6 +235,7 @@ mod tests { use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{ DeviceInfo::DeviceInfo, KeyEvent::KeyEvent, KeyEventAction::KeyEventAction, }; + use input::KeyboardType; use nix::{sys::time::TimeValLike, time::clock_gettime, time::ClockId}; use std::sync::{Arc, RwLock}; use std::time::Duration; @@ -240,7 +259,7 @@ mod tests { static SLOW_KEYS_THRESHOLD_NS: i64 = 100 * 1000000; // 100 ms #[test] - fn test_is_notify_key_for_internal_keyboard_not_blocked() { + fn test_is_notify_key_for_internal_non_alphabetic_keyboard_not_blocked() { let test_callbacks = TestCallbacks::new(); let test_thread = get_thread(test_callbacks.clone()); let next = TestFilter::new(); @@ -249,6 +268,7 @@ mod tests { test_thread.clone(), 1, /* device_id */ SLOW_KEYS_THRESHOLD_NS, + KeyboardType::NonAlphabetic, ); let event = KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT }; @@ -266,6 +286,7 @@ mod tests { test_thread.clone(), 1, /* device_id */ SLOW_KEYS_THRESHOLD_NS, + KeyboardType::NonAlphabetic, ); let event = @@ -275,6 +296,115 @@ mod tests { } #[test] + fn test_notify_key_for_tv_remote_when_key_pressed_for_threshold_time() { + let test_callbacks = TestCallbacks::new(); + let test_thread = get_thread(test_callbacks.clone()); + let next = TestFilter::new(); + let mut filter = setup_filter_with_external_device( + Box::new(next.clone()), + test_thread.clone(), + 1, /* device_id */ + SLOW_KEYS_THRESHOLD_NS, + KeyboardType::NonAlphabetic, + ); + let down_time = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap().num_nanoseconds(); + let source = Source(Source::KEYBOARD.0 | Source::DPAD.0); + filter.notify_key(&KeyEvent { + action: KeyEventAction::DOWN, + downTime: down_time, + eventTime: down_time, + source, + ..BASE_KEY_EVENT + }); + assert!(next.last_event().is_none()); + + std::thread::sleep(Duration::from_nanos(2 * SLOW_KEYS_THRESHOLD_NS as u64)); + assert_eq!( + next.last_event().unwrap(), + KeyEvent { + action: KeyEventAction::DOWN, + downTime: down_time + SLOW_KEYS_THRESHOLD_NS, + eventTime: down_time + SLOW_KEYS_THRESHOLD_NS, + source, + policyFlags: POLICY_FLAG_DISABLE_KEY_REPEAT, + ..BASE_KEY_EVENT + } + ); + + let up_time = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap().num_nanoseconds(); + filter.notify_key(&KeyEvent { + action: KeyEventAction::UP, + downTime: down_time, + eventTime: up_time, + source, + ..BASE_KEY_EVENT + }); + + assert_eq!( + next.last_event().unwrap(), + KeyEvent { + action: KeyEventAction::UP, + downTime: down_time + SLOW_KEYS_THRESHOLD_NS, + eventTime: up_time, + source, + ..BASE_KEY_EVENT + } + ); + } + + #[test] + fn test_notify_key_for_internal_alphabetic_keyboard_when_key_pressed_for_threshold_time() { + let test_callbacks = TestCallbacks::new(); + let test_thread = get_thread(test_callbacks.clone()); + let next = TestFilter::new(); + let mut filter = setup_filter_with_internal_device( + Box::new(next.clone()), + test_thread.clone(), + 1, /* device_id */ + SLOW_KEYS_THRESHOLD_NS, + KeyboardType::Alphabetic, + ); + let down_time = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap().num_nanoseconds(); + filter.notify_key(&KeyEvent { + action: KeyEventAction::DOWN, + downTime: down_time, + eventTime: down_time, + ..BASE_KEY_EVENT + }); + assert!(next.last_event().is_none()); + + std::thread::sleep(Duration::from_nanos(2 * SLOW_KEYS_THRESHOLD_NS as u64)); + assert_eq!( + next.last_event().unwrap(), + KeyEvent { + action: KeyEventAction::DOWN, + downTime: down_time + SLOW_KEYS_THRESHOLD_NS, + eventTime: down_time + SLOW_KEYS_THRESHOLD_NS, + policyFlags: POLICY_FLAG_DISABLE_KEY_REPEAT, + ..BASE_KEY_EVENT + } + ); + + let up_time = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap().num_nanoseconds(); + filter.notify_key(&KeyEvent { + action: KeyEventAction::UP, + downTime: down_time, + eventTime: up_time, + ..BASE_KEY_EVENT + }); + + assert_eq!( + next.last_event().unwrap(), + KeyEvent { + action: KeyEventAction::UP, + downTime: down_time + SLOW_KEYS_THRESHOLD_NS, + eventTime: up_time, + ..BASE_KEY_EVENT + } + ); + } + + #[test] fn test_notify_key_for_external_keyboard_when_key_pressed_for_threshold_time() { let test_callbacks = TestCallbacks::new(); let test_thread = get_thread(test_callbacks.clone()); @@ -284,6 +414,7 @@ mod tests { test_thread.clone(), 1, /* device_id */ SLOW_KEYS_THRESHOLD_NS, + KeyboardType::Alphabetic, ); let down_time = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap().num_nanoseconds(); filter.notify_key(&KeyEvent { @@ -335,6 +466,7 @@ mod tests { test_thread.clone(), 1, /* device_id */ SLOW_KEYS_THRESHOLD_NS, + KeyboardType::Alphabetic, ); let mut now = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap().num_nanoseconds(); filter.notify_key(&KeyEvent { @@ -367,6 +499,7 @@ mod tests { test_thread.clone(), 1, /* device_id */ SLOW_KEYS_THRESHOLD_NS, + KeyboardType::Alphabetic, ); let now = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap().num_nanoseconds(); @@ -388,11 +521,16 @@ mod tests { test_thread: InputFilterThread, device_id: i32, threshold: i64, + keyboard_type: KeyboardType, ) -> SlowKeysFilter { setup_filter_with_devices( next, test_thread, - &[DeviceInfo { deviceId: device_id, external: true }], + &[DeviceInfo { + deviceId: device_id, + external: true, + keyboardType: keyboard_type as i32, + }], threshold, ) } @@ -402,11 +540,16 @@ mod tests { test_thread: InputFilterThread, device_id: i32, threshold: i64, + keyboard_type: KeyboardType, ) -> SlowKeysFilter { setup_filter_with_devices( next, test_thread, - &[DeviceInfo { deviceId: device_id, external: false }], + &[DeviceInfo { + deviceId: device_id, + external: false, + keyboardType: keyboard_type as i32, + }], threshold, ) } diff --git a/services/inputflinger/rust/sticky_keys_filter.rs b/services/inputflinger/rust/sticky_keys_filter.rs index 6c7c7fba39..161a5fca60 100644 --- a/services/inputflinger/rust/sticky_keys_filter.rs +++ b/services/inputflinger/rust/sticky_keys_filter.rs @@ -134,6 +134,14 @@ impl Filter for StickyKeysFilter { fn destroy(&mut self) { self.next.destroy(); } + + fn dump(&mut self, dump_str: String) -> String { + let mut result = "Sticky Keys filter: \n".to_string(); + result += &format!("\tmodifier_state = {:?}\n", self.modifier_state); + result += &format!("\tlocked_modifier_state = {:?}\n", self.locked_modifier_state); + result += &format!("\tcontributing_devices = {:?}\n", self.contributing_devices); + self.next.dump(dump_str + &result) + } } fn is_modifier_key(keycode: i32) -> bool { @@ -235,6 +243,7 @@ mod tests { DeviceInfo::DeviceInfo, IInputFilter::IInputFilterCallbacks::IInputFilterCallbacks, KeyEvent::KeyEvent, KeyEventAction::KeyEventAction, }; + use input::KeyboardType; use input::ModifierState; use std::sync::{Arc, RwLock}; @@ -496,7 +505,11 @@ mod tests { ..BASE_KEY_UP }); - sticky_keys_filter.notify_devices_changed(&[DeviceInfo { deviceId: 2, external: true }]); + sticky_keys_filter.notify_devices_changed(&[DeviceInfo { + deviceId: 2, + external: true, + keyboardType: KeyboardType::Alphabetic as i32, + }]); assert_eq!( test_callbacks.get_last_modifier_state(), ModifierState::CtrlLeftOn | ModifierState::CtrlOn diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp index 18b6c5e47b..744cf4a514 100644 --- a/services/inputflinger/tests/Android.bp +++ b/services/inputflinger/tests/Android.bp @@ -70,17 +70,21 @@ cc_test { "InputTraceSession.cpp", "InputTracingTest.cpp", "InstrumentedInputReader.cpp", + "JoystickInputMapper_test.cpp", "LatencyTracker_test.cpp", "MultiTouchMotionAccumulator_test.cpp", "NotifyArgs_test.cpp", "PointerChoreographer_test.cpp", "PreferStylusOverTouch_test.cpp", "PropertyProvider_test.cpp", + "RotaryEncoderInputMapper_test.cpp", "SlopController_test.cpp", + "SwitchInputMapper_test.cpp", "SyncQueue_test.cpp", "TimerProvider_test.cpp", "TestInputListener.cpp", "TouchpadInputMapper_test.cpp", + "VibratorInputMapper_test.cpp", "MultiTouchInputMapper_test.cpp", "KeyboardInputMapper_test.cpp", "UinputDevice.cpp", @@ -115,6 +119,9 @@ cc_test { test_options: { unit_test: true, }, - test_suites: ["device-tests"], + test_suites: [ + "device-tests", + "device-platinum-tests", + ], native_coverage: false, } diff --git a/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp b/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp index b738abf6d2..353011aa5c 100644 --- a/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp +++ b/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp @@ -20,6 +20,7 @@ #include <memory> #include <EventHub.h> +#include <com_android_input_flags.h> #include <gtest/gtest.h> #include <linux/input-event-codes.h> #include <linux/input.h> @@ -32,9 +33,14 @@ #include "TestEventMatchers.h" #include "TestInputListener.h" +namespace input_flags = com::android::input::flags; + namespace android { using testing::AllOf; +using testing::Each; +using testing::ElementsAre; +using testing::VariantWith; class CapturedTouchpadEventConverterTest : public testing::Test { public: @@ -44,6 +50,8 @@ public: mReader(mFakeEventHub, mFakePolicy, mFakeListener), mDevice(newDevice()), mDeviceContext(*mDevice, EVENTHUB_ID) { + input_flags::include_relative_axis_values_for_captured_touchpads(true); + const size_t slotCount = 8; mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_SLOT, 0, slotCount - 1, 0, 0, 0); mAccumulator.configure(mDeviceContext, slotCount, /*usingSlotsProtocol=*/true); @@ -123,7 +131,7 @@ protected: TEST_F(CapturedTouchpadEventConverterTest, MotionRanges_allAxesPresent_populatedCorrectly) { mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_X, 0, 4000, 0, 0, 45); - mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_Y, 0, 2500, 0, 0, 40); + mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_Y, -500, 2000, 0, 0, 40); mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_TOUCH_MAJOR, 0, 1100, 0, 0, 35); mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_TOUCH_MINOR, 0, 1000, 0, 0, 30); mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_WIDTH_MAJOR, 0, 900, 0, 0, 25); @@ -147,8 +155,8 @@ TEST_F(CapturedTouchpadEventConverterTest, MotionRanges_allAxesPresent_populated const InputDeviceInfo::MotionRange* posY = info.getMotionRange(AMOTION_EVENT_AXIS_Y, AINPUT_SOURCE_TOUCHPAD); ASSERT_NE(nullptr, posY); - EXPECT_NEAR(0, posY->min, EPSILON); - EXPECT_NEAR(2500, posY->max, EPSILON); + EXPECT_NEAR(-500, posY->min, EPSILON); + EXPECT_NEAR(2000, posY->max, EPSILON); EXPECT_NEAR(40, posY->resolution, EPSILON); const InputDeviceInfo::MotionRange* touchMajor = @@ -179,8 +187,22 @@ TEST_F(CapturedTouchpadEventConverterTest, MotionRanges_allAxesPresent_populated EXPECT_NEAR(800, toolMinor->max, EPSILON); EXPECT_NEAR(20, toolMinor->resolution, EPSILON); - // ...except orientation and pressure, which get scaled, and size, which is generated from other - // values. + // ...except for the relative motion axes, derived from the corresponding absolute ones: + const InputDeviceInfo::MotionRange* relX = + info.getMotionRange(AMOTION_EVENT_AXIS_RELATIVE_X, AINPUT_SOURCE_TOUCHPAD); + ASSERT_NE(nullptr, relX); + EXPECT_NEAR(-4000, relX->min, EPSILON); + EXPECT_NEAR(4000, relX->max, EPSILON); + EXPECT_NEAR(45, relX->resolution, EPSILON); + + const InputDeviceInfo::MotionRange* relY = + info.getMotionRange(AMOTION_EVENT_AXIS_RELATIVE_Y, AINPUT_SOURCE_TOUCHPAD); + ASSERT_NE(nullptr, relY); + EXPECT_NEAR(-2500, relY->min, EPSILON); + EXPECT_NEAR(2500, relY->max, EPSILON); + EXPECT_NEAR(40, relY->resolution, EPSILON); + + // ...orientation and pressure, which get scaled: const InputDeviceInfo::MotionRange* orientation = info.getMotionRange(AMOTION_EVENT_AXIS_ORIENTATION, AINPUT_SOURCE_TOUCHPAD); ASSERT_NE(nullptr, orientation); @@ -195,6 +217,7 @@ TEST_F(CapturedTouchpadEventConverterTest, MotionRanges_allAxesPresent_populated EXPECT_NEAR(1, pressure->max, EPSILON); EXPECT_NEAR(0, pressure->resolution, EPSILON); + // ... and size, which is generated from other values. const InputDeviceInfo::MotionRange* size = info.getMotionRange(AMOTION_EVENT_AXIS_SIZE, AINPUT_SOURCE_TOUCHPAD); ASSERT_NE(nullptr, size); @@ -216,7 +239,9 @@ TEST_F(CapturedTouchpadEventConverterTest, MotionRanges_bareMinimumAxesPresent_p // present, since it's generated from axes that aren't provided by this device). EXPECT_NE(nullptr, info.getMotionRange(AMOTION_EVENT_AXIS_X, AINPUT_SOURCE_TOUCHPAD)); EXPECT_NE(nullptr, info.getMotionRange(AMOTION_EVENT_AXIS_Y, AINPUT_SOURCE_TOUCHPAD)); - EXPECT_EQ(2u, info.getMotionRanges().size()); + EXPECT_NE(nullptr, info.getMotionRange(AMOTION_EVENT_AXIS_RELATIVE_X, AINPUT_SOURCE_TOUCHPAD)); + EXPECT_NE(nullptr, info.getMotionRange(AMOTION_EVENT_AXIS_RELATIVE_Y, AINPUT_SOURCE_TOUCHPAD)); + EXPECT_EQ(4u, info.getMotionRanges().size()); } TEST_F(CapturedTouchpadEventConverterTest, OneFinger_motionReportedCorrectly) { @@ -232,28 +257,31 @@ TEST_F(CapturedTouchpadEventConverterTest, OneFinger_motionReportedCorrectly) { EXPECT_THAT(processSyncAndExpectSingleMotionArg(conv), AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithPointerCount(1u), - WithCoords(50, 100), WithToolType(ToolType::FINGER))); + WithCoords(50, 100), WithRelativeMotion(0, 0), + WithToolType(ToolType::FINGER))); processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 52); processAxis(conv, EV_ABS, ABS_MT_POSITION_Y, 99); EXPECT_THAT(processSyncAndExpectSingleMotionArg(conv), AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithPointerCount(1u), - WithCoords(52, 99), WithToolType(ToolType::FINGER))); + WithCoords(52, 99), WithRelativeMotion(2, -1), + WithToolType(ToolType::FINGER))); processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, -1); processAxis(conv, EV_KEY, BTN_TOUCH, 0); processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 0); std::list<NotifyArgs> args = processSync(conv); - ASSERT_EQ(2u, args.size()); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithPointerCount(1u), - WithCoords(52, 99), WithToolType(ToolType::FINGER))); - args.pop_front(); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithPointerCount(1u), - WithCoords(52, 99), WithToolType(ToolType::FINGER))); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_MOVE)), + VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_UP)))); + EXPECT_THAT(args, + Each(VariantWith<NotifyMotionArgs>( + AllOf(WithCoords(52, 99), WithRelativeMotion(0, 0), WithPointerCount(1u), + WithToolType(ToolType::FINGER))))); } TEST_F(CapturedTouchpadEventConverterTest, OneFinger_touchDimensionsPassedThrough) { @@ -504,13 +532,13 @@ TEST_F(CapturedTouchpadEventConverterTest, PalmTurningIntoFinger_reported) { EXPECT_THAT(processSyncAndExpectSingleMotionArg(conv), AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithPointerCount(1u), - WithCoords(51, 100))); + WithCoords(51, 100), WithRelativeMotion(0, 0))); processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 52); EXPECT_THAT(processSyncAndExpectSingleMotionArg(conv), AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithPointerCount(1u), - WithCoords(52, 100))); + WithCoords(52, 100), WithRelativeMotion(1, 0))); } TEST_F(CapturedTouchpadEventConverterTest, FingerArrivingAfterPalm_onlyFingerReported) { @@ -550,7 +578,7 @@ TEST_F(CapturedTouchpadEventConverterTest, FingerArrivingAfterPalm_onlyFingerRep EXPECT_THAT(processSyncAndExpectSingleMotionArg(conv), AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithPointerCount(1u), - WithCoords(98, 148))); + WithCoords(98, 148), WithRelativeMotion(-2, -2))); } TEST_F(CapturedTouchpadEventConverterTest, FingerAndFingerTurningIntoPalm_partiallyCancelled) { @@ -572,17 +600,17 @@ TEST_F(CapturedTouchpadEventConverterTest, FingerAndFingerTurningIntoPalm_partia processAxis(conv, EV_KEY, BTN_TOUCH, 1); processAxis(conv, EV_KEY, BTN_TOOL_DOUBLETAP, 1); - std::list<NotifyArgs> args = processSync(conv); - ASSERT_EQ(2u, args.size()); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithPointerCount(1u), - WithToolType(ToolType::FINGER))); - args.pop_front(); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN | - 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithPointerCount(2u), WithPointerToolType(0, ToolType::FINGER), - WithPointerToolType(1, ToolType::FINGER))); + EXPECT_THAT(processSync(conv), + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithPointerCount(1u), WithToolType(ToolType::FINGER))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_DOWN | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithPointerCount(2u), + WithPointerToolType(0, ToolType::FINGER), + WithPointerToolType(1, ToolType::FINGER))))); processAxis(conv, EV_ABS, ABS_MT_SLOT, 0); processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 51); @@ -591,15 +619,16 @@ TEST_F(CapturedTouchpadEventConverterTest, FingerAndFingerTurningIntoPalm_partia processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 251); processAxis(conv, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_PALM); - args = processSync(conv); - ASSERT_EQ(2u, args.size()); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithPointerCount(2u))); - args.pop_front(); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | - 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithFlags(AMOTION_EVENT_FLAG_CANCELED), WithPointerCount(2u))); + std::list<NotifyArgs> args = processSync(conv); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_MOVE)), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_UP | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithFlags(AMOTION_EVENT_FLAG_CANCELED))))); + EXPECT_THAT(args, Each(VariantWith<NotifyMotionArgs>(WithPointerCount(2u)))); } TEST_F(CapturedTouchpadEventConverterTest, FingerAndPalmTurningIntoFinger_reported) { @@ -632,15 +661,15 @@ TEST_F(CapturedTouchpadEventConverterTest, FingerAndPalmTurningIntoFinger_report processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 251); processAxis(conv, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER); - std::list<NotifyArgs> args = processSync(conv); - ASSERT_EQ(2u, args.size()); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithPointerCount(1u))); - args.pop_front(); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN | - 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithPointerCount(2u))); + EXPECT_THAT(processSync(conv), + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithPointerCount(1u))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_DOWN | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithPointerCount(2u))))); } TEST_F(CapturedTouchpadEventConverterTest, TwoFingers_motionReportedCorrectly) { @@ -656,7 +685,8 @@ TEST_F(CapturedTouchpadEventConverterTest, TwoFingers_motionReportedCorrectly) { EXPECT_THAT(processSyncAndExpectSingleMotionArg(conv), AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithPointerCount(1u), - WithCoords(50, 100), WithToolType(ToolType::FINGER))); + WithCoords(50, 100), WithRelativeMotion(0, 0), + WithToolType(ToolType::FINGER))); processAxis(conv, EV_ABS, ABS_MT_SLOT, 0); processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 52); @@ -670,18 +700,22 @@ TEST_F(CapturedTouchpadEventConverterTest, TwoFingers_motionReportedCorrectly) { processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 0); processAxis(conv, EV_KEY, BTN_TOOL_DOUBLETAP, 1); - std::list<NotifyArgs> args = processSync(conv); - ASSERT_EQ(2u, args.size()); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithPointerCount(1u), - WithCoords(52, 99), WithToolType(ToolType::FINGER))); - args.pop_front(); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN | - 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithPointerCount(2u), WithPointerCoords(0, 52, 99), - WithPointerCoords(1, 250, 200), WithPointerToolType(0, ToolType::FINGER), - WithPointerToolType(1, ToolType::FINGER))); + EXPECT_THAT(processSync(conv), + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithPointerCount(1u), WithCoords(52, 99), + WithRelativeMotion(2, -1), + WithToolType(ToolType::FINGER))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_DOWN | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithPointerCount(2u), WithPointerCoords(0, 52, 99), + WithPointerRelativeMotion(0, 0, 0), + WithPointerCoords(1, 250, 200), + WithPointerRelativeMotion(1, 0, 0), + WithPointerToolType(0, ToolType::FINGER), + WithPointerToolType(1, ToolType::FINGER))))); processAxis(conv, EV_ABS, ABS_MT_SLOT, 0); processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, -1); @@ -692,34 +726,96 @@ TEST_F(CapturedTouchpadEventConverterTest, TwoFingers_motionReportedCorrectly) { processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 1); processAxis(conv, EV_KEY, BTN_TOOL_DOUBLETAP, 0); - args = processSync(conv); - ASSERT_EQ(2u, args.size()); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithPointerCount(2u), - WithPointerCoords(0, 52, 99), WithPointerCoords(1, 255, 202), - WithPointerToolType(1, ToolType::FINGER), - WithPointerToolType(0, ToolType::FINGER))); - args.pop_front(); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | - 0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithPointerCount(2u), WithPointerCoords(0, 52, 99), - WithPointerCoords(1, 255, 202), WithPointerToolType(0, ToolType::FINGER), - WithPointerToolType(1, ToolType::FINGER))); + std::list<NotifyArgs> args = processSync(conv); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithPointerRelativeMotion(1, 5, 2))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_UP | + 0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithPointerRelativeMotion(1, 0, 0))))); + EXPECT_THAT(args, + Each(VariantWith<NotifyMotionArgs>( + AllOf(WithPointerCount(2u), WithPointerCoords(0, 52, 99), + WithPointerRelativeMotion(0, 0, 0), WithPointerCoords(1, 255, 202), + WithPointerToolType(1, ToolType::FINGER), + WithPointerToolType(0, ToolType::FINGER))))); processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, -1); processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 0); processAxis(conv, EV_KEY, BTN_TOUCH, 0); args = processSync(conv); - ASSERT_EQ(2u, args.size()); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_MOVE)), + VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_UP)))); + EXPECT_THAT(args, + Each(VariantWith<NotifyMotionArgs>(AllOf(WithPointerCount(1u), WithCoords(255, 202), + WithRelativeMotion(0, 0), + WithToolType(ToolType::FINGER))))); +} + +TEST_F(CapturedTouchpadEventConverterTest, RelativeMotionAxesClearedForNewFingerInSlot) { + CapturedTouchpadEventConverter conv = createConverter(); + // Put down one finger. + processAxis(conv, EV_ABS, ABS_MT_SLOT, 0); + processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 1); + processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 50); + processAxis(conv, EV_ABS, ABS_MT_POSITION_Y, 100); + + processAxis(conv, EV_KEY, BTN_TOUCH, 1); + processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 1); + + EXPECT_THAT(processSyncAndExpectSingleMotionArg(conv), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithPointerCount(1u), + WithCoords(50, 100), WithRelativeMotion(0, 0))); + + // Move it in negative X and Y directions. + processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 47); + processAxis(conv, EV_ABS, ABS_MT_POSITION_Y, 97); + + EXPECT_THAT(processSyncAndExpectSingleMotionArg(conv), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithCoords(47, 97), + WithRelativeMotion(-3, -3))); + + // Lift it. + processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, -1); + processAxis(conv, EV_KEY, BTN_TOUCH, 0); + processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 0); + + std::list<NotifyArgs> args = processSync(conv); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_MOVE)), + VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_UP)))); + EXPECT_THAT(args, + Each(VariantWith<NotifyMotionArgs>(AllOf(WithCoords(47, 97), + WithRelativeMotion(0, 0), + WithPointerCount(1u))))); + + // Put down another finger using the same slot. Relative axis values should be cleared. + processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 2); + processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 60); + processAxis(conv, EV_ABS, ABS_MT_POSITION_Y, 60); + + processAxis(conv, EV_KEY, BTN_TOUCH, 1); + processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 1); + + EXPECT_THAT(processSyncAndExpectSingleMotionArg(conv), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithPointerCount(1u), + WithCoords(60, 60), WithRelativeMotion(0, 0))); + + processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 64); + processAxis(conv, EV_ABS, ABS_MT_POSITION_Y, 58); + + EXPECT_THAT(processSyncAndExpectSingleMotionArg(conv), AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithPointerCount(1u), - WithCoords(255, 202), WithToolType(ToolType::FINGER))); - args.pop_front(); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithPointerCount(1u), - WithCoords(255, 202), WithToolType(ToolType::FINGER))); + WithCoords(64, 58), WithRelativeMotion(4, -2))); } // Pointer IDs max out at 31, and so must be reused once a touch is lifted to avoid running out. @@ -737,17 +833,18 @@ TEST_F(CapturedTouchpadEventConverterTest, PointerIdsReusedAfterLift) { processAxis(conv, EV_KEY, BTN_TOUCH, 1); processAxis(conv, EV_KEY, BTN_TOOL_DOUBLETAP, 1); - std::list<NotifyArgs> args = processSync(conv); - ASSERT_EQ(2u, args.size()); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithPointerCount(1u), - WithPointerId(/*index=*/0, /*id=*/0))); - args.pop_front(); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN | - 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithPointerCount(2u), WithPointerId(/*index=*/0, /*id=*/0), - WithPointerId(/*index=*/1, /*id=*/1))); + EXPECT_THAT(processSync(conv), + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithPointerCount(1u), + WithPointerId(/*index=*/0, /*id=*/0))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_DOWN | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithPointerCount(2u), + WithPointerId(/*index=*/0, /*id=*/0), + WithPointerId(/*index=*/1, /*id=*/1))))); // Lift the finger in slot 0, freeing up pointer ID 0... processAxis(conv, EV_ABS, ABS_MT_SLOT, 0); @@ -758,27 +855,30 @@ TEST_F(CapturedTouchpadEventConverterTest, PointerIdsReusedAfterLift) { processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 3); processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 30); - args = processSync(conv); - ASSERT_EQ(3u, args.size()); + std::list<NotifyArgs> args = processSync(conv); // Slot 1 being present will result in a MOVE event, even though it hasn't actually moved (see // comments in CapturedTouchpadEventConverter::sync). - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithPointerCount(2u), - WithPointerId(/*index=*/0, /*id=*/0), WithPointerId(/*index=*/1, /*id=*/1))); - args.pop_front(); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | - 0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithPointerCount(2u), WithPointerId(/*index=*/0, /*id=*/0), - WithPointerId(/*index=*/1, /*id=*/1))); - args.pop_front(); - // Slot 0 being lifted causes the finger from slot 1 to move up to index 0, but keep its - // previous ID. The new finger in slot 2 should take ID 0, which was just freed up. - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN | - 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithPointerCount(2u), WithPointerId(/*index=*/0, /*id=*/1), - WithPointerId(/*index=*/1, /*id=*/0))); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithPointerId(/*index=*/0, /*id=*/0), + WithPointerId(/*index=*/1, /*id=*/1))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_UP | + 0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithPointerId(/*index=*/0, /*id=*/0), + WithPointerId(/*index=*/1, /*id=*/1))), + // Slot 0 being lifted causes the finger from slot 1 to move up to index + // 0, but keep its previous ID. The new finger in slot 2 should take ID + // 0, which was just freed up. + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_DOWN | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithPointerId(/*index=*/0, /*id=*/1), + WithPointerId(/*index=*/1, /*id=*/0))))); + EXPECT_THAT(args, Each(VariantWith<NotifyMotionArgs>(WithPointerCount(2u)))); } // Motion events without any pointers are invalid, so when a button press is reported in the same @@ -797,33 +897,30 @@ TEST_F(CapturedTouchpadEventConverterTest, processAxis(conv, EV_KEY, BTN_LEFT, 1); - std::list<NotifyArgs> args = processSync(conv); - ASSERT_EQ(2u, args.size()); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - WithMotionAction(AMOTION_EVENT_ACTION_DOWN)); - args.pop_front(); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), WithPointerCount(1u), - WithCoords(50, 100), WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY))); + EXPECT_THAT(processSync(conv), + ElementsAre(VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_DOWN)), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithPointerCount(1u), WithCoords(50, 100), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY))))); processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, -1); processAxis(conv, EV_KEY, BTN_TOUCH, 0); processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 0); processAxis(conv, EV_KEY, BTN_LEFT, 0); - args = processSync(conv); - ASSERT_EQ(3u, args.size()); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - WithMotionAction(AMOTION_EVENT_ACTION_MOVE)); - args.pop_front(); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), WithPointerCount(1u), - WithCoords(50, 100), WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), - WithButtonState(0))); - args.pop_front(); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - WithMotionAction(AMOTION_EVENT_ACTION_UP)); + EXPECT_THAT(processSync(conv), + ElementsAre(VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_MOVE)), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithPointerCount(1u), WithCoords(50, 100), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(0))), + VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_UP)))); } // Some touchpads sometimes report a button press before they report the finger touching the pad. In @@ -841,15 +938,14 @@ TEST_F(CapturedTouchpadEventConverterTest, ButtonPressedBeforeTouch_ReportedOnce processAxis(conv, EV_KEY, BTN_TOUCH, 1); processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 1); - std::list<NotifyArgs> args = processSync(conv); - ASSERT_EQ(2u, args.size()); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - WithMotionAction(AMOTION_EVENT_ACTION_DOWN)); - args.pop_front(); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), WithPointerCount(1u), - WithCoords(50, 100), WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY))); + EXPECT_THAT(processSync(conv), + ElementsAre(VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_DOWN)), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithPointerCount(1u), WithCoords(50, 100), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY))))); } // When all fingers are lifted from a touchpad, we should release any buttons that are down, since @@ -866,29 +962,25 @@ TEST_F(CapturedTouchpadEventConverterTest, ButtonReleasedAfterTouchLifts_Reporte processAxis(conv, EV_KEY, BTN_LEFT, 1); - std::list<NotifyArgs> args = processSync(conv); - ASSERT_EQ(2u, args.size()); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - WithMotionAction(AMOTION_EVENT_ACTION_DOWN)); - args.pop_front(); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS)); + EXPECT_THAT(processSync(conv), + ElementsAre(VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_DOWN)), + VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS)))); processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, -1); processAxis(conv, EV_KEY, BTN_TOUCH, 0); processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 0); - args = processSync(conv); - ASSERT_EQ(3u, args.size()); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - WithMotionAction(AMOTION_EVENT_ACTION_MOVE)); - args.pop_front(); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), WithPointerCount(1u), - WithCoords(50, 100), WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), - WithButtonState(0))); - args.pop_front(); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - WithMotionAction(AMOTION_EVENT_ACTION_UP)); + EXPECT_THAT(processSync(conv), + ElementsAre(VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_MOVE)), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithPointerCount(1u), WithCoords(50, 100), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(0))), + VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_UP)))); processAxis(conv, EV_KEY, BTN_LEFT, 0); ASSERT_EQ(0u, processSync(conv).size()); @@ -908,48 +1000,41 @@ TEST_F(CapturedTouchpadEventConverterTest, MultipleButtonsPressedDuringTouch_Rep WithMotionAction(AMOTION_EVENT_ACTION_DOWN)); processAxis(conv, EV_KEY, BTN_LEFT, 1); - std::list<NotifyArgs> args = processSync(conv); - ASSERT_EQ(2u, args.size()); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - WithMotionAction(AMOTION_EVENT_ACTION_MOVE)); - args.pop_front(); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), - WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY))); + EXPECT_THAT(processSync(conv), + ElementsAre(VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_MOVE)), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY))))); processAxis(conv, EV_KEY, BTN_RIGHT, 1); - args = processSync(conv); - ASSERT_EQ(2u, args.size()); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - WithMotionAction(AMOTION_EVENT_ACTION_MOVE)); - args.pop_front(); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), - WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY | - AMOTION_EVENT_BUTTON_SECONDARY))); + EXPECT_THAT(processSync(conv), + ElementsAre(VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_MOVE)), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY | + AMOTION_EVENT_BUTTON_SECONDARY))))); processAxis(conv, EV_KEY, BTN_LEFT, 0); - args = processSync(conv); - ASSERT_EQ(2u, args.size()); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - WithMotionAction(AMOTION_EVENT_ACTION_MOVE)); - args.pop_front(); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), - WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), - WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY))); + EXPECT_THAT(processSync(conv), + ElementsAre(VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_MOVE)), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY))))); processAxis(conv, EV_KEY, BTN_RIGHT, 0); - args = processSync(conv); - ASSERT_EQ(2u, args.size()); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - WithMotionAction(AMOTION_EVENT_ACTION_MOVE)); - args.pop_front(); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), - WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY), WithButtonState(0))); + EXPECT_THAT(processSync(conv), + ElementsAre(VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_MOVE)), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY), + WithButtonState(0))))); } } // namespace android diff --git a/services/inputflinger/tests/CursorInputMapper_test.cpp b/services/inputflinger/tests/CursorInputMapper_test.cpp index cda067f03e..b27d02d77c 100644 --- a/services/inputflinger/tests/CursorInputMapper_test.cpp +++ b/services/inputflinger/tests/CursorInputMapper_test.cpp @@ -17,11 +17,13 @@ #include "CursorInputMapper.h" #include <list> +#include <optional> #include <string> #include <tuple> #include <variant> #include <android-base/logging.h> +#include <android_companion_virtualdevice_flags.h> #include <com_android_input_flags.h> #include <gtest/gtest.h> #include <input/DisplayViewport.h> @@ -92,41 +94,10 @@ DisplayViewport createSecondaryViewport() { return v; } -/** - * A fake InputDeviceContext that allows the associated viewport to be specified for the mapper. - * - * This is currently necessary because InputMapperUnitTest doesn't register the mappers it creates - * with the InputDevice object, meaning that InputDevice::isIgnored becomes true, and the input - * device doesn't set its associated viewport when it's configured. - * - * TODO(b/319217713): work out a way to avoid this fake. - */ -class ViewportFakingInputDeviceContext : public InputDeviceContext { -public: - ViewportFakingInputDeviceContext(InputDevice& device, int32_t eventHubId, - std::optional<DisplayViewport> viewport) - : InputDeviceContext(device, eventHubId), mAssociatedViewport(viewport) {} - - ViewportFakingInputDeviceContext(InputDevice& device, int32_t eventHubId, - ui::Rotation orientation) - : ViewportFakingInputDeviceContext(device, eventHubId, - createPrimaryViewport(orientation)) {} - - std::optional<DisplayViewport> getAssociatedViewport() const override { - return mAssociatedViewport; - } - - void setViewport(const std::optional<DisplayViewport>& viewport) { - mAssociatedViewport = viewport; - } - -private: - std::optional<DisplayViewport> mAssociatedViewport; -}; - } // namespace namespace input_flags = com::android::input::flags; +namespace vd_flags = android::companion::virtualdevice::flags; /** * Unit tests for CursorInputMapper. @@ -151,6 +122,10 @@ protected: .WillRepeatedly(Return(false)); EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_HWHEEL)) .WillRepeatedly(Return(false)); + EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_WHEEL_HI_RES)) + .WillRepeatedly(Return(false)); + EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_HWHEEL_HI_RES)) + .WillRepeatedly(Return(false)); mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID); mFakePolicy->addDisplayViewport(createPrimaryViewport(ui::Rotation::Rotation0)); @@ -193,6 +168,7 @@ class CursorInputMapperUnitTest : public CursorInputMapperUnitTestBase { protected: void SetUp() override { input_flags::enable_new_mouse_pointer_ballistics(false); + vd_flags::high_resolution_scroll(false); CursorInputMapperUnitTestBase::SetUp(); } }; @@ -534,8 +510,9 @@ TEST_F(CursorInputMapperUnitTest, ProcessShouldNotRotateMotionsWhenOrientationAw // need to be rotated. mPropertyMap.addProperty("cursor.mode", "navigation"); mPropertyMap.addProperty("cursor.orientationAware", "1"); - ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, ui::Rotation::Rotation90); - mMapper = createInputMapper<CursorInputMapper>(deviceContext, mReaderConfiguration); + EXPECT_CALL((*mDevice), getAssociatedViewport) + .WillRepeatedly(Return(createPrimaryViewport(ui::Rotation::Rotation90))); + mMapper = createInputMapper<CursorInputMapper>(*mDeviceContext, mReaderConfiguration); ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, 1, 0, 1)); ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 1, 1, 1)); @@ -551,8 +528,9 @@ TEST_F(CursorInputMapperUnitTest, ProcessShouldRotateMotionsWhenNotOrientationAw // Since InputReader works in the un-rotated coordinate space, only devices that are not // orientation-aware are affected by display rotation. mPropertyMap.addProperty("cursor.mode", "navigation"); - ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, ui::Rotation::Rotation0); - mMapper = createInputMapper<CursorInputMapper>(deviceContext, mReaderConfiguration); + EXPECT_CALL((*mDevice), getAssociatedViewport) + .WillRepeatedly(Return(createPrimaryViewport(ui::Rotation::Rotation0))); + mMapper = createInputMapper<CursorInputMapper>(*mDeviceContext, mReaderConfiguration); ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, 1, 0, 1)); ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 1, 1, 1)); @@ -563,7 +541,8 @@ TEST_F(CursorInputMapperUnitTest, ProcessShouldRotateMotionsWhenNotOrientationAw ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 0, -1, 0)); ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 1, -1, 1)); - deviceContext.setViewport(createPrimaryViewport(ui::Rotation::Rotation90)); + EXPECT_CALL((*mDevice), getAssociatedViewport) + .WillRepeatedly(Return(createPrimaryViewport(ui::Rotation::Rotation90))); std::list<NotifyArgs> args = mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration, InputReaderConfiguration::Change::DISPLAY_INFO); @@ -576,7 +555,8 @@ TEST_F(CursorInputMapperUnitTest, ProcessShouldRotateMotionsWhenNotOrientationAw ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 0, 0, -1)); ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 1, -1, -1)); - deviceContext.setViewport(createPrimaryViewport(ui::Rotation::Rotation180)); + EXPECT_CALL((*mDevice), getAssociatedViewport) + .WillRepeatedly(Return(createPrimaryViewport(ui::Rotation::Rotation180))); args = mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration, InputReaderConfiguration::Change::DISPLAY_INFO); ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, 1, 0, -1)); @@ -588,7 +568,8 @@ TEST_F(CursorInputMapperUnitTest, ProcessShouldRotateMotionsWhenNotOrientationAw ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 0, 1, 0)); ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 1, 1, -1)); - deviceContext.setViewport(createPrimaryViewport(ui::Rotation::Rotation270)); + EXPECT_CALL((*mDevice), getAssociatedViewport) + .WillRepeatedly(Return(createPrimaryViewport(ui::Rotation::Rotation270))); args = mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration, InputReaderConfiguration::Change::DISPLAY_INFO); ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, 1, 1, 0)); @@ -642,8 +623,8 @@ TEST_F(CursorInputMapperUnitTest, ConfigureDisplayIdWithAssociatedViewport) { mReaderConfiguration.setDisplayViewports({primaryViewport, secondaryViewport}); // Set up the secondary display as the display on which the pointer should be shown. // The InputDevice is not associated with any display. - ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, secondaryViewport); - mMapper = createInputMapper<CursorInputMapper>(deviceContext, mReaderConfiguration); + EXPECT_CALL((*mDevice), getAssociatedViewport).WillRepeatedly(Return(secondaryViewport)); + mMapper = createInputMapper<CursorInputMapper>(*mDeviceContext, mReaderConfiguration); std::list<NotifyArgs> args; // Ensure input events are generated for the secondary display. @@ -663,8 +644,8 @@ TEST_F(CursorInputMapperUnitTest, mReaderConfiguration.setDisplayViewports({primaryViewport, secondaryViewport}); // Set up the primary display as the display on which the pointer should be shown. // Associate the InputDevice with the secondary display. - ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, secondaryViewport); - mMapper = createInputMapper<CursorInputMapper>(deviceContext, mReaderConfiguration); + EXPECT_CALL((*mDevice), getAssociatedViewport).WillRepeatedly(Return(secondaryViewport)); + mMapper = createInputMapper<CursorInputMapper>(*mDeviceContext, mReaderConfiguration); // With PointerChoreographer enabled, there could be a PointerController for the associated // display even if it is different from the pointer display. So the mapper should generate an @@ -835,6 +816,72 @@ TEST_F(CursorInputMapperUnitTest, ProcessWhenModeIsPointerShouldKeepZeroCoords) WithOrientation(0.0f), WithDistance(0.0f))))); } +TEST_F(CursorInputMapperUnitTest, ProcessRegularScroll) { + createMapper(); + + std::list<NotifyArgs> args; + args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 1); + args += process(ARBITRARY_TIME, EV_REL, REL_HWHEEL, 1); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithSource(AINPUT_SOURCE_MOUSE), + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))), + VariantWith<NotifyMotionArgs>( + AllOf(WithSource(AINPUT_SOURCE_MOUSE), + WithMotionAction(AMOTION_EVENT_ACTION_SCROLL), + WithScroll(1.0f, 1.0f))))); +} + +TEST_F(CursorInputMapperUnitTest, ProcessHighResScroll) { + vd_flags::high_resolution_scroll(true); + EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_WHEEL_HI_RES)) + .WillRepeatedly(Return(true)); + EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_HWHEEL_HI_RES)) + .WillRepeatedly(Return(true)); + createMapper(); + + std::list<NotifyArgs> args; + args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL_HI_RES, 60); + args += process(ARBITRARY_TIME, EV_REL, REL_HWHEEL_HI_RES, 60); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithSource(AINPUT_SOURCE_MOUSE), + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))), + VariantWith<NotifyMotionArgs>( + AllOf(WithSource(AINPUT_SOURCE_MOUSE), + WithMotionAction(AMOTION_EVENT_ACTION_SCROLL), + WithScroll(0.5f, 0.5f))))); +} + +TEST_F(CursorInputMapperUnitTest, HighResScrollIgnoresRegularScroll) { + vd_flags::high_resolution_scroll(true); + EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_WHEEL_HI_RES)) + .WillRepeatedly(Return(true)); + EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_HWHEEL_HI_RES)) + .WillRepeatedly(Return(true)); + createMapper(); + + std::list<NotifyArgs> args; + args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL_HI_RES, 60); + args += process(ARBITRARY_TIME, EV_REL, REL_HWHEEL_HI_RES, 60); + args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 1); + args += process(ARBITRARY_TIME, EV_REL, REL_HWHEEL, 1); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithSource(AINPUT_SOURCE_MOUSE), + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))), + VariantWith<NotifyMotionArgs>( + AllOf(WithSource(AINPUT_SOURCE_MOUSE), + WithMotionAction(AMOTION_EVENT_ACTION_SCROLL), + WithScroll(0.5f, 0.5f))))); +} + /** * When Pointer Capture is enabled, we expect to report unprocessed relative movements, so any * pointer acceleration or speed processing should not be applied. @@ -954,8 +1001,8 @@ TEST_F(CursorInputMapperUnitTestWithNewBallistics, ConfigureAccelerationWithAsso mPropertyMap.addProperty("cursor.mode", "pointer"); DisplayViewport primaryViewport = createPrimaryViewport(ui::Rotation::Rotation0); mReaderConfiguration.setDisplayViewports({primaryViewport}); - ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, primaryViewport); - mMapper = createInputMapper<CursorInputMapper>(deviceContext, mReaderConfiguration); + EXPECT_CALL((*mDevice), getAssociatedViewport).WillRepeatedly(Return(primaryViewport)); + mMapper = createInputMapper<CursorInputMapper>(*mDeviceContext, mReaderConfiguration); std::list<NotifyArgs> args; @@ -993,9 +1040,8 @@ TEST_F(CursorInputMapperUnitTestWithNewBallistics, ConfigureAccelerationOnDispla mReaderConfiguration.displaysWithMousePointerAccelerationDisabled.emplace(DISPLAY_ID); // Don't associate the device with the display yet. - ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, - /*viewport=*/std::nullopt); - mMapper = createInputMapper<CursorInputMapper>(deviceContext, mReaderConfiguration); + EXPECT_CALL((*mDevice), getAssociatedViewport).WillRepeatedly(Return(std::nullopt)); + mMapper = createInputMapper<CursorInputMapper>(*mDeviceContext, mReaderConfiguration); std::list<NotifyArgs> args; @@ -1009,7 +1055,7 @@ TEST_F(CursorInputMapperUnitTestWithNewBallistics, ConfigureAccelerationOnDispla ASSERT_GT(coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y), 20.f); // Now associate the device with the display, and verify that acceleration is disabled. - deviceContext.setViewport(primaryViewport); + EXPECT_CALL((*mDevice), getAssociatedViewport).WillRepeatedly(Return(primaryViewport)); args += mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration, InputReaderConfiguration::Change::DISPLAY_INFO); args.clear(); @@ -1023,6 +1069,72 @@ TEST_F(CursorInputMapperUnitTestWithNewBallistics, ConfigureAccelerationOnDispla WithRelativeMotion(10, 20))))); } +TEST_F(CursorInputMapperUnitTestWithNewBallistics, ProcessRegularScroll) { + createMapper(); + + std::list<NotifyArgs> args; + args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 1); + args += process(ARBITRARY_TIME, EV_REL, REL_HWHEEL, 1); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithSource(AINPUT_SOURCE_MOUSE), + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))), + VariantWith<NotifyMotionArgs>( + AllOf(WithSource(AINPUT_SOURCE_MOUSE), + WithMotionAction(AMOTION_EVENT_ACTION_SCROLL), + WithScroll(1.0f, 1.0f))))); +} + +TEST_F(CursorInputMapperUnitTestWithNewBallistics, ProcessHighResScroll) { + vd_flags::high_resolution_scroll(true); + EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_WHEEL_HI_RES)) + .WillRepeatedly(Return(true)); + EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_HWHEEL_HI_RES)) + .WillRepeatedly(Return(true)); + createMapper(); + + std::list<NotifyArgs> args; + args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL_HI_RES, 60); + args += process(ARBITRARY_TIME, EV_REL, REL_HWHEEL_HI_RES, 60); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithSource(AINPUT_SOURCE_MOUSE), + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))), + VariantWith<NotifyMotionArgs>( + AllOf(WithSource(AINPUT_SOURCE_MOUSE), + WithMotionAction(AMOTION_EVENT_ACTION_SCROLL), + WithScroll(0.5f, 0.5f))))); +} + +TEST_F(CursorInputMapperUnitTestWithNewBallistics, HighResScrollIgnoresRegularScroll) { + vd_flags::high_resolution_scroll(true); + EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_WHEEL_HI_RES)) + .WillRepeatedly(Return(true)); + EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_HWHEEL_HI_RES)) + .WillRepeatedly(Return(true)); + createMapper(); + + std::list<NotifyArgs> args; + args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL_HI_RES, 60); + args += process(ARBITRARY_TIME, EV_REL, REL_HWHEEL_HI_RES, 60); + args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 1); + args += process(ARBITRARY_TIME, EV_REL, REL_HWHEEL, 1); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithSource(AINPUT_SOURCE_MOUSE), + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))), + VariantWith<NotifyMotionArgs>( + AllOf(WithSource(AINPUT_SOURCE_MOUSE), + WithMotionAction(AMOTION_EVENT_ACTION_SCROLL), + WithScroll(0.5f, 0.5f))))); +} + namespace { // Minimum timestamp separation between subsequent input events from a Bluetooth device. diff --git a/services/inputflinger/tests/EventHub_test.cpp b/services/inputflinger/tests/EventHub_test.cpp index 2e296daa22..0e3d15a1ac 100644 --- a/services/inputflinger/tests/EventHub_test.cpp +++ b/services/inputflinger/tests/EventHub_test.cpp @@ -48,9 +48,6 @@ static void dumpEvents(const std::vector<RawEvent>& events) { case EventHubInterface::DEVICE_REMOVED: ALOGI("Device removed: %i", event.deviceId); break; - case EventHubInterface::FINISHED_DEVICE_SCAN: - ALOGI("Finished device scan."); - break; } } else { ALOGI("Device %" PRId32 " : time = %" PRId64 ", type %i, code %i, value %i", @@ -145,15 +142,13 @@ void EventHubTest::consumeInitialDeviceAddedEvents() { // None of the existing system devices should be changing while this test is run. // Check that the returned device ids are unique for all of the existing devices. EXPECT_EQ(existingDevices.size(), events.size() - 1); - // The last event should be "finished device scan" - EXPECT_EQ(EventHubInterface::FINISHED_DEVICE_SCAN, events[events.size() - 1].type); } int32_t EventHubTest::waitForDeviceCreation() { // Wait a little longer than usual, to ensure input device has time to be created std::vector<RawEvent> events = getEvents(2); - if (events.size() != 2) { - ADD_FAILURE() << "Instead of 2 events, received " << events.size(); + if (events.size() != 1) { + ADD_FAILURE() << "Instead of 1 event, received " << events.size(); return 0; // this value is unused } const RawEvent& deviceAddedEvent = events[0]; @@ -161,21 +156,15 @@ int32_t EventHubTest::waitForDeviceCreation() { InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(deviceAddedEvent.deviceId); const int32_t deviceId = deviceAddedEvent.deviceId; EXPECT_EQ(identifier.name, mKeyboard->getName()); - const RawEvent& finishedDeviceScanEvent = events[1]; - EXPECT_EQ(static_cast<int32_t>(EventHubInterface::FINISHED_DEVICE_SCAN), - finishedDeviceScanEvent.type); return deviceId; } void EventHubTest::waitForDeviceClose(int32_t deviceId) { std::vector<RawEvent> events = getEvents(2); - ASSERT_EQ(2U, events.size()); + ASSERT_EQ(1U, events.size()); const RawEvent& deviceRemovedEvent = events[0]; EXPECT_EQ(static_cast<int32_t>(EventHubInterface::DEVICE_REMOVED), deviceRemovedEvent.type); EXPECT_EQ(deviceId, deviceRemovedEvent.deviceId); - const RawEvent& finishedDeviceScanEvent = events[1]; - EXPECT_EQ(static_cast<int32_t>(EventHubInterface::FINISHED_DEVICE_SCAN), - finishedDeviceScanEvent.type); } void EventHubTest::assertNoMoreEvents() { diff --git a/services/inputflinger/tests/FakeEventHub.cpp b/services/inputflinger/tests/FakeEventHub.cpp index daa000f2ce..943de6e3cf 100644 --- a/services/inputflinger/tests/FakeEventHub.cpp +++ b/services/inputflinger/tests/FakeEventHub.cpp @@ -16,6 +16,8 @@ #include "FakeEventHub.h" +#include <optional> + #include <android-base/thread_annotations.h> #include <gtest/gtest.h> #include <linux/input-event-codes.h> @@ -86,10 +88,6 @@ status_t FakeEventHub::disableDevice(int32_t deviceId) { return device->disable(); } -void FakeEventHub::finishDeviceScan() { - enqueueEvent(ARBITRARY_TIME, READ_TIME, 0, EventHubInterface::FINISHED_DEVICE_SCAN, 0, 0); -} - void FakeEventHub::addConfigurationProperty(int32_t deviceId, const char* key, const char* value) { getDevice(deviceId)->configuration.addProperty(key, value); } @@ -103,7 +101,6 @@ void FakeEventHub::addAbsoluteAxis(int32_t deviceId, int axis, int32_t minValue, Device* device = getDevice(deviceId); RawAbsoluteAxisInfo info; - info.valid = true; info.minValue = minValue; info.maxValue = maxValue; info.flat = flat; @@ -154,9 +151,10 @@ void FakeEventHub::addKeyCodeMapping(int32_t deviceId, int32_t fromKeyCode, int3 getDevice(deviceId)->keyCodeMapping.insert_or_assign(fromKeyCode, toKeyCode); } -void FakeEventHub::addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const { +void FakeEventHub::setKeyRemapping(int32_t deviceId, + const std::map<int32_t, int32_t>& keyRemapping) const { Device* device = getDevice(deviceId); - device->keyRemapping.insert_or_assign(fromKeyCode, toKeyCode); + device->keyRemapping = keyRemapping; } void FakeEventHub::addLed(int32_t deviceId, int32_t led, bool initialState) { @@ -263,18 +261,16 @@ std::optional<PropertyMap> FakeEventHub::getConfiguration(int32_t deviceId) cons return device->configuration; } -status_t FakeEventHub::getAbsoluteAxisInfo(int32_t deviceId, int axis, - RawAbsoluteAxisInfo* outAxisInfo) const { +std::optional<RawAbsoluteAxisInfo> FakeEventHub::getAbsoluteAxisInfo(int32_t deviceId, + int axis) const { Device* device = getDevice(deviceId); if (device) { ssize_t index = device->absoluteAxes.indexOfKey(axis); if (index >= 0) { - *outAxisInfo = device->absoluteAxes.valueAt(index); - return OK; + return device->absoluteAxes.valueAt(index); } } - outAxisInfo->clear(); - return -1; + return std::nullopt; } bool FakeEventHub::hasRelativeAxis(int32_t deviceId, int axis) const { @@ -417,18 +413,15 @@ int32_t FakeEventHub::getSwitchState(int32_t deviceId, int32_t sw) const { return AKEY_STATE_UNKNOWN; } -status_t FakeEventHub::getAbsoluteAxisValue(int32_t deviceId, int32_t axis, - int32_t* outValue) const { +std::optional<int32_t> FakeEventHub::getAbsoluteAxisValue(int32_t deviceId, int32_t axis) const { Device* device = getDevice(deviceId); if (device) { ssize_t index = device->absoluteAxisValue.indexOfKey(axis); if (index >= 0) { - *outValue = device->absoluteAxisValue.valueAt(index); - return OK; + return device->absoluteAxisValue.valueAt(index); } } - *outValue = 0; - return -1; + return std::nullopt; } void FakeEventHub::setMtSlotValues(int32_t deviceId, int32_t axis, diff --git a/services/inputflinger/tests/FakeEventHub.h b/services/inputflinger/tests/FakeEventHub.h index f07b3441c2..2dfbb2388a 100644 --- a/services/inputflinger/tests/FakeEventHub.h +++ b/services/inputflinger/tests/FakeEventHub.h @@ -55,7 +55,7 @@ class FakeEventHub : public EventHubInterface { KeyedVector<int32_t, int32_t> absoluteAxisValue; KeyedVector<int32_t, KeyInfo> keysByScanCode; KeyedVector<int32_t, KeyInfo> keysByUsageCode; - std::unordered_map<int32_t, int32_t> keyRemapping; + std::map<int32_t, int32_t> keyRemapping; KeyedVector<int32_t, bool> leds; // fake mapping which would normally come from keyCharacterMap std::unordered_map<int32_t, int32_t> keyCodeMapping; @@ -112,8 +112,6 @@ public: status_t enableDevice(int32_t deviceId) override; status_t disableDevice(int32_t deviceId) override; - void finishDeviceScan(); - void addConfigurationProperty(int32_t deviceId, const char* key, const char* value); void addConfigurationMap(int32_t deviceId, const PropertyMap* configuration); @@ -131,7 +129,7 @@ public: void addKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t keyCode, uint32_t flags); void addKeyCodeMapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode); - void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const; + void setKeyRemapping(int32_t deviceId, const std::map<int32_t, int32_t>& keyRemapping) const; void addVirtualKeyDefinition(int32_t deviceId, const VirtualKeyDefinition& definition); void addSensorAxis(int32_t deviceId, int32_t absCode, InputDeviceSensorType sensorType, @@ -168,8 +166,8 @@ private: InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const override; int32_t getDeviceControllerNumber(int32_t) const override; std::optional<PropertyMap> getConfiguration(int32_t deviceId) const override; - status_t getAbsoluteAxisInfo(int32_t deviceId, int axis, - RawAbsoluteAxisInfo* outAxisInfo) const override; + std::optional<RawAbsoluteAxisInfo> getAbsoluteAxisInfo(int32_t deviceId, + int axis) const override; bool hasRelativeAxis(int32_t deviceId, int axis) const override; bool hasInputProperty(int32_t, int) const override; bool hasMscEvent(int32_t deviceId, int mscEvent) const override final; @@ -187,7 +185,7 @@ private: std::optional<RawLayoutInfo> getRawLayoutInfo(int32_t deviceId) const override; int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const override; int32_t getSwitchState(int32_t deviceId, int32_t sw) const override; - status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t* outValue) const override; + std::optional<int32_t> getAbsoluteAxisValue(int32_t deviceId, int32_t axis) const override; int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const override; // Return true if the device has non-empty key layout. diff --git a/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp b/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp index 3df05f4bae..db68d8a14a 100644 --- a/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp +++ b/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp @@ -54,13 +54,6 @@ void FakeInputDispatcherPolicy::assertFilterInputEventWasNotCalled() { ASSERT_EQ(nullptr, mFilteredEvent); } -void FakeInputDispatcherPolicy::assertNotifyConfigurationChangedWasCalled(nsecs_t when) { - std::scoped_lock lock(mLock); - ASSERT_TRUE(mConfigurationChangedTime) << "Timed out waiting for configuration changed call"; - ASSERT_EQ(*mConfigurationChangedTime, when); - mConfigurationChangedTime = std::nullopt; -} - void FakeInputDispatcherPolicy::assertNotifySwitchWasCalled(const NotifySwitchArgs& args) { std::scoped_lock lock(mLock); ASSERT_TRUE(mLastNotifySwitch); @@ -342,11 +335,6 @@ std::optional<T> FakeInputDispatcherPolicy::getItemFromStorageLockedInterruptibl return std::make_optional(item); } -void FakeInputDispatcherPolicy::notifyConfigurationChanged(nsecs_t when) { - std::scoped_lock lock(mLock); - mConfigurationChangedTime = when; -} - void FakeInputDispatcherPolicy::notifyWindowUnresponsive(const sp<IBinder>& connectionToken, std::optional<gui::Pid> pid, const std::string&) { diff --git a/services/inputflinger/tests/FakeInputDispatcherPolicy.h b/services/inputflinger/tests/FakeInputDispatcherPolicy.h index a0f3ea9008..a9e39d1630 100644 --- a/services/inputflinger/tests/FakeInputDispatcherPolicy.h +++ b/services/inputflinger/tests/FakeInputDispatcherPolicy.h @@ -66,7 +66,6 @@ public: void assertFilterInputEventWasCalled(const NotifyKeyArgs& args); void assertFilterInputEventWasCalled(const NotifyMotionArgs& args, vec2 point); void assertFilterInputEventWasNotCalled(); - void assertNotifyConfigurationChangedWasCalled(nsecs_t when); void assertNotifySwitchWasCalled(const NotifySwitchArgs& args); void assertOnPointerDownEquals(const sp<IBinder>& touchedToken); void assertOnPointerDownWasNotCalled(); @@ -121,7 +120,6 @@ public: private: std::mutex mLock; std::unique_ptr<InputEvent> mFilteredEvent GUARDED_BY(mLock); - std::optional<nsecs_t> mConfigurationChangedTime GUARDED_BY(mLock); sp<IBinder> mOnPointerDownToken GUARDED_BY(mLock); std::optional<NotifySwitchArgs> mLastNotifySwitch GUARDED_BY(mLock); @@ -173,7 +171,6 @@ private: std::condition_variable& condition) REQUIRES(mLock); - void notifyConfigurationChanged(nsecs_t when) override; void notifyWindowUnresponsive(const sp<IBinder>& connectionToken, std::optional<gui::Pid> pid, const std::string&) override; void notifyWindowResponsive(const sp<IBinder>& connectionToken, diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.cpp b/services/inputflinger/tests/FakeInputReaderPolicy.cpp index d2cb0ac3df..f373cac085 100644 --- a/services/inputflinger/tests/FakeInputReaderPolicy.cpp +++ b/services/inputflinger/tests/FakeInputReaderPolicy.cpp @@ -16,6 +16,7 @@ #include "FakeInputReaderPolicy.h" +#include <android-base/properties.h> #include <android-base/thread_annotations.h> #include <gtest/gtest.h> @@ -24,20 +25,30 @@ namespace android { +namespace { + +static const int HW_TIMEOUT_MULTIPLIER = base::GetIntProperty("ro.hw_timeout_multiplier", 1); + +} // namespace + void FakeInputReaderPolicy::assertInputDevicesChanged() { - waitForInputDevices([](bool devicesChanged) { - if (!devicesChanged) { - FAIL() << "Timed out waiting for notifyInputDevicesChanged() to be called."; - } - }); + waitForInputDevices( + [](bool devicesChanged) { + if (!devicesChanged) { + FAIL() << "Timed out waiting for notifyInputDevicesChanged() to be called."; + } + }, + ADD_INPUT_DEVICE_TIMEOUT); } void FakeInputReaderPolicy::assertInputDevicesNotChanged() { - waitForInputDevices([](bool devicesChanged) { - if (devicesChanged) { - FAIL() << "Expected notifyInputDevicesChanged() to not be called."; - } - }); + waitForInputDevices( + [](bool devicesChanged) { + if (devicesChanged) { + FAIL() << "Expected notifyInputDevicesChanged() to not be called."; + } + }, + INPUT_DEVICES_DIDNT_CHANGE_TIMEOUT); } void FakeInputReaderPolicy::assertStylusGestureNotified(int32_t deviceId) { @@ -58,6 +69,17 @@ void FakeInputReaderPolicy::assertStylusGestureNotNotified() { ASSERT_FALSE(mDeviceIdOfNotifiedStylusGesture); } +void FakeInputReaderPolicy::assertTouchpadHardwareStateNotified() { + std::unique_lock lock(mLock); + base::ScopedLockAssertion assumeLocked(mLock); + + const bool success = + mTouchpadHardwareStateNotified.wait_for(lock, WAIT_TIMEOUT, [this]() REQUIRES(mLock) { + return mTouchpadHardwareState.has_value(); + }); + ASSERT_TRUE(success) << "Timed out waiting for hardware state to be notified"; +} + void FakeInputReaderPolicy::clearViewports() { mViewports.clear(); mConfig.setDisplayViewports(mViewports); @@ -227,6 +249,17 @@ void FakeInputReaderPolicy::notifyInputDevicesChanged( mDevicesChangedCondition.notify_all(); } +void FakeInputReaderPolicy::notifyTouchpadHardwareState(const SelfContainedHardwareState& schs, + int32_t deviceId) { + std::scoped_lock lock(mLock); + mTouchpadHardwareState = schs; + mTouchpadHardwareStateNotified.notify_all(); +} + +void FakeInputReaderPolicy::notifyTouchpadGestureInfo(GestureType type, int32_t deviceId) { + std::scoped_lock lock(mLock); +} + std::shared_ptr<KeyCharacterMap> FakeInputReaderPolicy::getKeyboardLayoutOverlay( const InputDeviceIdentifier&, const std::optional<KeyboardLayoutInfo>) { return nullptr; @@ -236,14 +269,16 @@ std::string FakeInputReaderPolicy::getDeviceAlias(const InputDeviceIdentifier&) return ""; } -void FakeInputReaderPolicy::waitForInputDevices(std::function<void(bool)> processDevicesChanged) { +void FakeInputReaderPolicy::waitForInputDevices(std::function<void(bool)> processDevicesChanged, + std::chrono::milliseconds timeout) { std::unique_lock<std::mutex> lock(mLock); base::ScopedLockAssertion assumeLocked(mLock); const bool devicesChanged = - mDevicesChangedCondition.wait_for(lock, WAIT_TIMEOUT, [this]() REQUIRES(mLock) { - return mInputDevicesChanged; - }); + mDevicesChangedCondition.wait_for(lock, timeout * HW_TIMEOUT_MULTIPLIER, + [this]() REQUIRES(mLock) { + return mInputDevicesChanged; + }); ASSERT_NO_FATAL_FAILURE(processDevicesChanged(devicesChanged)); mInputDevicesChanged = false; } diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.h b/services/inputflinger/tests/FakeInputReaderPolicy.h index 94f1311a1e..3a2b4e9ed9 100644 --- a/services/inputflinger/tests/FakeInputReaderPolicy.h +++ b/services/inputflinger/tests/FakeInputReaderPolicy.h @@ -42,6 +42,7 @@ public: void assertInputDevicesNotChanged(); void assertStylusGestureNotified(int32_t deviceId); void assertStylusGestureNotNotified(); + void assertTouchpadHardwareStateNotified(); virtual void clearViewports(); std::optional<DisplayViewport> getDisplayViewportByUniqueId(const std::string& uniqueId) const; @@ -82,10 +83,14 @@ public: private: void getReaderConfiguration(InputReaderConfiguration* outConfig) override; void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) override; + void notifyTouchpadHardwareState(const SelfContainedHardwareState& schs, + int32_t deviceId) override; + void notifyTouchpadGestureInfo(GestureType type, int32_t deviceId) override; std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay( const InputDeviceIdentifier&, const std::optional<KeyboardLayoutInfo>) override; std::string getDeviceAlias(const InputDeviceIdentifier&) override; - void waitForInputDevices(std::function<void(bool)> processDevicesChanged); + void waitForInputDevices(std::function<void(bool)> processDevicesChanged, + std::chrono::milliseconds timeout); void notifyStylusGestureStarted(int32_t deviceId, nsecs_t eventTime) override; mutable std::mutex mLock; @@ -101,6 +106,9 @@ private: std::condition_variable mStylusGestureNotifiedCondition; std::optional<DeviceId> mDeviceIdOfNotifiedStylusGesture GUARDED_BY(mLock){}; + std::condition_variable mTouchpadHardwareStateNotified; + std::optional<SelfContainedHardwareState> mTouchpadHardwareState GUARDED_BY(mLock){}; + uint32_t mNextPointerCaptureSequenceNumber{0}; }; diff --git a/services/inputflinger/tests/FakePointerController.cpp b/services/inputflinger/tests/FakePointerController.cpp index d0998ba851..887a939e09 100644 --- a/services/inputflinger/tests/FakePointerController.cpp +++ b/services/inputflinger/tests/FakePointerController.cpp @@ -148,12 +148,6 @@ bool FakePointerController::isPointerShown() { return mIsPointerShown; } -std::optional<FloatRect> FakePointerController::getBounds() const { - if (!mEnabled) return std::nullopt; - - return mHaveBounds ? std::make_optional<FloatRect>(mMinX, mMinY, mMaxX, mMaxY) : std::nullopt; -} - void FakePointerController::move(float deltaX, float deltaY) { if (!mEnabled) return; diff --git a/services/inputflinger/tests/FakePointerController.h b/services/inputflinger/tests/FakePointerController.h index 2c76c6214c..9b773a7715 100644 --- a/services/inputflinger/tests/FakePointerController.h +++ b/services/inputflinger/tests/FakePointerController.h @@ -65,7 +65,6 @@ public: private: std::string dump() override { return ""; } - std::optional<FloatRect> getBounds() const override; void move(float deltaX, float deltaY) override; void unfade(Transition) override; void setPresentation(Presentation) override {} diff --git a/services/inputflinger/tests/GestureConverter_test.cpp b/services/inputflinger/tests/GestureConverter_test.cpp index d0cd677ade..225ae0f67c 100644 --- a/services/inputflinger/tests/GestureConverter_test.cpp +++ b/services/inputflinger/tests/GestureConverter_test.cpp @@ -279,6 +279,8 @@ TEST_F(GestureConverterTest, DragWithButton) { } TEST_F(GestureConverterTest, Scroll) { + input_flags::enable_touchpad_no_focus_change(true); + const nsecs_t downTime = 12345; InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); @@ -300,7 +302,8 @@ TEST_F(GestureConverterTest, Scroll) { ASSERT_THAT(args, Each(VariantWith<NotifyMotionArgs>( AllOf(WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), - WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), + WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE | + AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE), WithToolType(ToolType::FINGER), WithDisplayId(ui::LogicalDisplayId::DEFAULT))))); @@ -312,7 +315,8 @@ TEST_F(GestureConverterTest, Scroll) { WithGestureScrollDistance(0, 5, EPSILON), WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), WithToolType(ToolType::FINGER), - WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), + WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE | + AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE), WithDisplayId(ui::LogicalDisplayId::DEFAULT))))); Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1, @@ -325,7 +329,8 @@ TEST_F(GestureConverterTest, Scroll) { WithGestureScrollDistance(0, 0, EPSILON), WithMotionClassification( MotionClassification::TWO_FINGER_SWIPE), - WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE))), + WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE | + AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE))), VariantWith<NotifyMotionArgs>( AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), WithCoords(0, 0), @@ -845,6 +850,8 @@ TEST_F(GestureConverterTest, FourFingerSwipe_Horizontal) { } TEST_F(GestureConverterTest, Pinch_Inwards) { + input_flags::enable_touchpad_no_focus_change(true); + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); converter.setDisplayId(ui::LogicalDisplayId::DEFAULT); @@ -867,7 +874,8 @@ TEST_F(GestureConverterTest, Pinch_Inwards) { AllOf(WithMotionClassification(MotionClassification::PINCH), WithGesturePinchScaleFactor(1.0f, EPSILON), WithToolType(ToolType::FINGER), - WithDisplayId(ui::LogicalDisplayId::DEFAULT))))); + WithDisplayId(ui::LogicalDisplayId::DEFAULT), + WithFlags(AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE))))); Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 0.8, GESTURES_ZOOM_UPDATE); @@ -879,7 +887,8 @@ TEST_F(GestureConverterTest, Pinch_Inwards) { WithGesturePinchScaleFactor(0.8f, EPSILON), WithPointerCoords(0, -80, 0), WithPointerCoords(1, 80, 0), WithPointerCount(2u), WithToolType(ToolType::FINGER), - WithDisplayId(ui::LogicalDisplayId::DEFAULT))))); + WithDisplayId(ui::LogicalDisplayId::DEFAULT), + WithFlags(AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE))))); Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1, GESTURES_ZOOM_END); @@ -891,12 +900,14 @@ TEST_F(GestureConverterTest, Pinch_Inwards) { 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), WithMotionClassification(MotionClassification::PINCH), WithGesturePinchScaleFactor(1.0f, EPSILON), - WithPointerCount(2u))), + WithPointerCount(2u), + WithFlags(AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE))), VariantWith<NotifyMotionArgs>( AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithMotionClassification(MotionClassification::PINCH), WithGesturePinchScaleFactor(1.0f, EPSILON), - WithPointerCount(1u))), + WithPointerCount(1u), + WithFlags(AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE))), VariantWith<NotifyMotionArgs>( AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), WithCoords(0, 0), @@ -908,6 +919,8 @@ TEST_F(GestureConverterTest, Pinch_Inwards) { } TEST_F(GestureConverterTest, Pinch_Outwards) { + input_flags::enable_touchpad_no_focus_change(true); + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); converter.setDisplayId(ui::LogicalDisplayId::DEFAULT); @@ -930,7 +943,8 @@ TEST_F(GestureConverterTest, Pinch_Outwards) { AllOf(WithMotionClassification(MotionClassification::PINCH), WithGesturePinchScaleFactor(1.0f, EPSILON), WithToolType(ToolType::FINGER), - WithDisplayId(ui::LogicalDisplayId::DEFAULT))))); + WithDisplayId(ui::LogicalDisplayId::DEFAULT), + WithFlags(AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE))))); Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1.1, GESTURES_ZOOM_UPDATE); @@ -942,7 +956,8 @@ TEST_F(GestureConverterTest, Pinch_Outwards) { WithGesturePinchScaleFactor(1.1f, EPSILON), WithPointerCoords(0, -110, 0), WithPointerCoords(1, 110, 0), WithPointerCount(2u), WithToolType(ToolType::FINGER), - WithDisplayId(ui::LogicalDisplayId::DEFAULT))))); + WithDisplayId(ui::LogicalDisplayId::DEFAULT), + WithFlags(AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE))))); Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1, GESTURES_ZOOM_END); @@ -954,12 +969,14 @@ TEST_F(GestureConverterTest, Pinch_Outwards) { 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), WithMotionClassification(MotionClassification::PINCH), WithGesturePinchScaleFactor(1.0f, EPSILON), - WithPointerCount(2u))), + WithPointerCount(2u), + WithFlags(AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE))), VariantWith<NotifyMotionArgs>( AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithMotionClassification(MotionClassification::PINCH), WithGesturePinchScaleFactor(1.0f, EPSILON), - WithPointerCount(1u))), + WithPointerCount(1u), + WithFlags(AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE))), VariantWith<NotifyMotionArgs>( AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), WithCoords(0, 0), @@ -1055,6 +1072,8 @@ TEST_F(GestureConverterTest, ResetWithButtonPressed) { } TEST_F(GestureConverterTest, ResetDuringScroll) { + input_flags::enable_touchpad_no_focus_change(true); + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); converter.setDisplayId(ui::LogicalDisplayId::DEFAULT); @@ -1070,7 +1089,8 @@ TEST_F(GestureConverterTest, ResetDuringScroll) { WithGestureScrollDistance(0, 0, EPSILON), WithMotionClassification( MotionClassification::TWO_FINGER_SWIPE), - WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE))), + WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE | + AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE))), VariantWith<NotifyMotionArgs>( AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), WithCoords(0, 0), diff --git a/services/inputflinger/tests/HardwareProperties_test.cpp b/services/inputflinger/tests/HardwareProperties_test.cpp index 8dfa8c8e0c..e87f8228c8 100644 --- a/services/inputflinger/tests/HardwareProperties_test.cpp +++ b/services/inputflinger/tests/HardwareProperties_test.cpp @@ -48,24 +48,19 @@ protected: static constexpr int32_t EVENTHUB_ID = 1; void setupValidAxis(int axis, int32_t min, int32_t max, int32_t resolution) { - EXPECT_CALL(mMockEventHub, getAbsoluteAxisInfo(EVENTHUB_ID, axis, testing::_)) - .WillRepeatedly([=](int32_t, int32_t, RawAbsoluteAxisInfo* outAxisInfo) { - outAxisInfo->valid = true; - outAxisInfo->minValue = min; - outAxisInfo->maxValue = max; - outAxisInfo->flat = 0; - outAxisInfo->fuzz = 0; - outAxisInfo->resolution = resolution; - return OK; - }); + EXPECT_CALL(mMockEventHub, getAbsoluteAxisInfo(EVENTHUB_ID, axis)) + .WillRepeatedly(Return(std::optional<RawAbsoluteAxisInfo>{{ + .minValue = min, + .maxValue = max, + .flat = 0, + .fuzz = 0, + .resolution = resolution, + }})); } void setupInvalidAxis(int axis) { - EXPECT_CALL(mMockEventHub, getAbsoluteAxisInfo(EVENTHUB_ID, axis, testing::_)) - .WillRepeatedly([=](int32_t, int32_t, RawAbsoluteAxisInfo* outAxisInfo) { - outAxisInfo->valid = false; - return -1; - }); + EXPECT_CALL(mMockEventHub, getAbsoluteAxisInfo(EVENTHUB_ID, axis)) + .WillRepeatedly(Return(std::nullopt)); } void setProperty(int property, bool value) { diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 48930ef444..7b5c47b1ac 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -411,16 +411,6 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { << "Should reject motion events with duplicate pointer ids."; } -/* Test InputDispatcher for notifyConfigurationChanged and notifySwitch events */ - -TEST_F(InputDispatcherTest, NotifyConfigurationChanged_CallsPolicy) { - constexpr nsecs_t eventTime = 20; - mDispatcher->notifyConfigurationChanged({/*id=*/10, eventTime}); - ASSERT_TRUE(mDispatcher->waitForIdle()); - - mFakePolicy->assertNotifyConfigurationChangedWasCalled(eventTime); -} - TEST_F(InputDispatcherTest, NotifySwitch_CallsPolicy) { NotifySwitchArgs args(InputEvent::nextId(), /*eventTime=*/20, /*policyFlags=*/0, /*switchValues=*/1, @@ -4265,6 +4255,123 @@ TEST_F(InputDispatcherTest, SplitTouchesSendCorrectActionDownTime) { window2->consumeMotionEvent(WithDownTime(secondDown->getDownTime())); } +/** + * When events are not split, the downTime should be adjusted such that the downTime corresponds + * to the event time of the first ACTION_DOWN. If a new window appears, it should not affect + * the event routing because the first window prevents splitting. + */ +TEST_F(InputDispatcherTest, SplitTouchesSendCorrectActionDownTimeForNewWindow) { + SCOPED_FLAG_OVERRIDE(split_all_touches, false); + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> window1 = + sp<FakeWindowHandle>::make(application, mDispatcher, "Window1", DISPLAY_ID); + window1->setTouchableRegion(Region{{0, 0, 100, 100}}); + window1->setPreventSplitting(true); + + sp<FakeWindowHandle> window2 = + sp<FakeWindowHandle>::make(application, mDispatcher, "Window2", DISPLAY_ID); + window2->setTouchableRegion(Region{{100, 0, 200, 100}}); + + mDispatcher->onWindowInfosChanged({{*window1->getInfo()}, {}, 0, 0}); + + // Touch down on the first window + NotifyMotionArgs downArgs = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50)) + .build(); + mDispatcher->notifyMotion(downArgs); + + window1->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_DOWN), WithDownTime(downArgs.downTime))); + + // Second window is added + mDispatcher->onWindowInfosChanged({{*window1->getInfo(), *window2->getInfo()}, {}, 0, 0}); + + // Now touch down on the window with another 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(150).y(50)) + .downTime(downArgs.downTime) + .build()); + window1->consumeMotionPointerDown(1, AllOf(WithDownTime(downArgs.downTime))); + + // Finish the gesture + mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50)) + .pointer(PointerBuilder(1, ToolType::FINGER).x(150).y(50)) + .downTime(downArgs.downTime) + .build()); + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50)) + .downTime(downArgs.downTime) + .build()); + window1->consumeMotionPointerUp(1, AllOf(WithDownTime(downArgs.downTime))); + window1->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_UP), WithDownTime(downArgs.downTime))); + window2->assertNoEvents(); +} + +/** + * When splitting touch events, the downTime should be adjusted such that the downTime corresponds + * to the event time of the first ACTION_DOWN sent to the new window. + * If a new window that does not support split appears on the screen and gets touched with the + * second finger, it should not get any events because it doesn't want split touches. At the same + * time, the first window should not get the pointer_down event because it supports split touches + * (and the touch occurred outside of the bounds of window1). + */ +TEST_F(InputDispatcherTest, SplitTouchesDropsEventForNonSplittableSecondWindow) { + SCOPED_FLAG_OVERRIDE(split_all_touches, false); + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> window1 = + sp<FakeWindowHandle>::make(application, mDispatcher, "Window1", DISPLAY_ID); + window1->setTouchableRegion(Region{{0, 0, 100, 100}}); + + sp<FakeWindowHandle> window2 = + sp<FakeWindowHandle>::make(application, mDispatcher, "Window2", DISPLAY_ID); + window2->setTouchableRegion(Region{{100, 0, 200, 100}}); + + mDispatcher->onWindowInfosChanged({{*window1->getInfo()}, {}, 0, 0}); + + // Touch down on the first window + NotifyMotionArgs downArgs = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50)) + .build(); + mDispatcher->notifyMotion(downArgs); + + window1->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_DOWN), WithDownTime(downArgs.downTime))); + + // Second window is added + window2->setPreventSplitting(true); + mDispatcher->onWindowInfosChanged({{*window1->getInfo(), *window2->getInfo()}, {}, 0, 0}); + + // Now touch down on the window with another 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(150).y(50)) + .downTime(downArgs.downTime) + .build()); + // Event is dropped because window2 doesn't support split touch, and window1 does. + + // Complete the gesture + mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50)) + .pointer(PointerBuilder(1, ToolType::FINGER).x(150).y(50)) + .downTime(downArgs.downTime) + .build()); + // A redundant MOVE event is generated that doesn't carry any new information + window1->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_MOVE), WithDownTime(downArgs.downTime))); + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50)) + .downTime(downArgs.downTime) + .build()); + + window1->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_UP), WithDownTime(downArgs.downTime))); + window1->assertNoEvents(); + window2->assertNoEvents(); +} + TEST_F(InputDispatcherTest, HoverMoveEnterMouseClickAndHoverMoveExit) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> windowLeft = sp<FakeWindowHandle>::make(application, mDispatcher, "Left", @@ -4469,6 +4576,202 @@ TEST_F(InputDispatcherTest, TwoPointersDownMouseClick) { window->assertNoEvents(); } +/** + * A spy window sits above a window with NO_INPUT_CHANNEL. Ensure that the spy receives events even + * though the window underneath should not get any events. + */ +TEST_F(InputDispatcherTest, NonSplittableSpyAboveNoInputChannelWindowSinglePointer) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + + sp<FakeWindowHandle> spyWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Spy", + ui::LogicalDisplayId::DEFAULT); + spyWindow->setFrame(Rect(0, 0, 100, 100)); + spyWindow->setTrustedOverlay(true); + spyWindow->setPreventSplitting(true); + spyWindow->setSpy(true); + // Another window below spy that has both NO_INPUT_CHANNEL and PREVENT_SPLITTING + sp<FakeWindowHandle> inputSinkWindow = + sp<FakeWindowHandle>::make(application, mDispatcher, "Input sink below spy", + ui::LogicalDisplayId::DEFAULT); + inputSinkWindow->setFrame(Rect(0, 0, 100, 100)); + inputSinkWindow->setTrustedOverlay(true); + inputSinkWindow->setPreventSplitting(true); + inputSinkWindow->setNoInputChannel(true); + + mDispatcher->onWindowInfosChanged( + {{*spyWindow->getInfo(), *inputSinkWindow->getInfo()}, {}, 0, 0}); + + // Tap the spy window + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(51)) + .build()); + mDispatcher->notifyMotion( + MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(50).y(51)) + .build()); + + spyWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN))); + spyWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_UP))); + inputSinkWindow->assertNoEvents(); +} + +/** + * A spy window sits above a window with NO_INPUT_CHANNEL. Ensure that the spy receives events even + * though the window underneath should not get any events. + * Same test as above, but with two pointers touching instead of one. + */ +TEST_F(InputDispatcherTest, NonSplittableSpyAboveNoInputChannelWindowTwoPointers) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + + sp<FakeWindowHandle> spyWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Spy", + ui::LogicalDisplayId::DEFAULT); + spyWindow->setFrame(Rect(0, 0, 100, 100)); + spyWindow->setTrustedOverlay(true); + spyWindow->setPreventSplitting(true); + spyWindow->setSpy(true); + // Another window below spy that would have both NO_INPUT_CHANNEL and PREVENT_SPLITTING + sp<FakeWindowHandle> inputSinkWindow = + sp<FakeWindowHandle>::make(application, mDispatcher, "Input sink below spy", + ui::LogicalDisplayId::DEFAULT); + inputSinkWindow->setFrame(Rect(0, 0, 100, 100)); + inputSinkWindow->setTrustedOverlay(true); + inputSinkWindow->setPreventSplitting(true); + inputSinkWindow->setNoInputChannel(true); + + mDispatcher->onWindowInfosChanged( + {{*spyWindow->getInfo(), *inputSinkWindow->getInfo()}, {}, 0, 0}); + + // Both fingers land into the spy window + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(51)) + .build()); + mDispatcher->notifyMotion( + MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(50).y(51)) + .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(10).y(11)) + .build()); + mDispatcher->notifyMotion( + MotionArgsBuilder(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(50).y(51)) + .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(10).y(11)) + .build()); + mDispatcher->notifyMotion( + MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(50).y(51)) + .build()); + + spyWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN))); + spyWindow->consumeMotionPointerDown(1, WithPointerCount(2)); + spyWindow->consumeMotionPointerUp(1, WithPointerCount(2)); + spyWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_UP))); + inputSinkWindow->assertNoEvents(); +} + +/** Check the behaviour for cases where input sink prevents or doesn't prevent splitting. */ +class SpyThatPreventsSplittingWithApplicationFixture : public InputDispatcherTest, + public ::testing::WithParamInterface<bool> { +}; + +/** + * Three windows: + * - An application window (app window) + * - A spy window that does not overlap the app window. Has PREVENT_SPLITTING flag + * - A window below the spy that has NO_INPUT_CHANNEL (call it 'inputSink') + * + * The spy window is side-by-side with the app window. The inputSink is below the spy. + * We first touch the area outside of the appWindow, but inside spyWindow. + * Only the SPY window should get the DOWN event. + * The spy pilfers after receiving the first DOWN event. + * Next, we touch the app window. + * The spy should receive POINTER_DOWN(1) (since spy is preventing splits). + * Also, since the spy is already pilfering the first pointer, it will be sent the remaining new + * pointers automatically, as well. + * Next, the first pointer (from the spy) is lifted. + * Spy should get POINTER_UP(0). + * This event should not go to the app because the app never received this pointer to begin with. + * Now, lift the remaining pointer and check that the spy receives UP event. + * + * Finally, send a new ACTION_DOWN event to the spy and check that it's received. + * This test attempts to reproduce a crash in the dispatcher. + */ +TEST_P(SpyThatPreventsSplittingWithApplicationFixture, SpyThatPreventsSplittingWithApplication) { + SCOPED_FLAG_OVERRIDE(split_all_touches, false); + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + + sp<FakeWindowHandle> spyWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Spy", + ui::LogicalDisplayId::DEFAULT); + spyWindow->setFrame(Rect(100, 100, 200, 200)); + spyWindow->setTrustedOverlay(true); + spyWindow->setPreventSplitting(true); + spyWindow->setSpy(true); + // Another window below spy that has both NO_INPUT_CHANNEL and PREVENT_SPLITTING + sp<FakeWindowHandle> inputSinkWindow = + sp<FakeWindowHandle>::make(application, mDispatcher, "Input sink below spy", + ui::LogicalDisplayId::DEFAULT); + inputSinkWindow->setFrame(Rect(100, 100, 200, 200)); // directly below the spy + inputSinkWindow->setTrustedOverlay(true); + inputSinkWindow->setPreventSplitting(GetParam()); + inputSinkWindow->setNoInputChannel(true); + + sp<FakeWindowHandle> appWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "App", + ui::LogicalDisplayId::DEFAULT); + appWindow->setFrame(Rect(0, 0, 100, 100)); + + mDispatcher->setFocusedApplication(ui::LogicalDisplayId::DEFAULT, application); + mDispatcher->onWindowInfosChanged( + {{*spyWindow->getInfo(), *inputSinkWindow->getInfo(), *appWindow->getInfo()}, + {}, + 0, + 0}); + + // First finger lands outside of the appWindow, but inside of the spy window + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(150)) + .build()); + spyWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN))); + + mDispatcher->pilferPointers(spyWindow->getToken()); + + // Second finger lands in the app, and goes to the spy window. It doesn't go to the app because + // the spy is already pilfering the first pointer, and this automatically grants the remaining + // new pointers to the spy, as well. + mDispatcher->notifyMotion( + MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(150).y(150)) + .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(50).y(50)) + .build()); + + spyWindow->consumeMotionPointerDown(1, WithPointerCount(2)); + + // Now lift up the first pointer + mDispatcher->notifyMotion( + MotionArgsBuilder(POINTER_0_UP, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(150).y(150)) + .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(50).y(50)) + .build()); + spyWindow->consumeMotionPointerUp(0, WithPointerCount(2)); + + // And lift the remaining pointer! + mDispatcher->notifyMotion( + MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(50).y(50)) + .build()); + spyWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_UP), WithPointerCount(1))); + + // Now send a new DOWN, which should again go to spy. + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(150)) + .build()); + spyWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN))); + // The app window doesn't get any events this entire time because the spy received the events + // first and pilfered, which makes all new pointers go to it as well. + appWindow->assertNoEvents(); +} + +// Behaviour should be the same regardless of whether inputSink supports splitting. +INSTANTIATE_TEST_SUITE_P(SpyThatPreventsSplittingWithApplication, + SpyThatPreventsSplittingWithApplicationFixture, testing::Bool()); + TEST_F(InputDispatcherTest, HoverWithSpyWindows) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); @@ -4827,6 +5130,54 @@ TEST_F_WITH_FLAGS(InputDispatcherTest, InvalidA11yHoverStreamDoesNotCrash, } /** + * Invalid events injected by input filter are rejected. + */ +TEST_F(InputDispatcherTest, InvalidA11yEventsGetRejected) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Window", + ui::LogicalDisplayId::DEFAULT); + + mDispatcher->setFocusedApplication(ui::LogicalDisplayId::DEFAULT, application); + + mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0}); + + // a11y sets 'POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY' policy flag during injection, so define + // a custom injection function here for convenience. + auto injectFromAccessibility = [&](int32_t action, float x, float y) { + MotionEvent event = MotionEventBuilder(action, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(x).y(y)) + .addFlag(AMOTION_EVENT_FLAG_IS_ACCESSIBILITY_EVENT) + .build(); + return injectMotionEvent(*mDispatcher, event, 100ms, + InputEventInjectionSync::WAIT_FOR_RESULT, /*targetUid=*/{}, + POLICY_FLAG_PASS_TO_USER | POLICY_FLAG_FILTERED | + POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY); + }; + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectFromAccessibility(ACTION_DOWN, /*x=*/300, /*y=*/400)); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectFromAccessibility(ACTION_MOVE, /*x=*/310, /*y=*/420)); + window->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); + window->consumeMotionEvent(WithMotionAction(ACTION_MOVE)); + // finger is still down, so a new DOWN event should be rejected! + ASSERT_EQ(InputEventInjectionResult::FAILED, + injectFromAccessibility(ACTION_DOWN, /*x=*/340, /*y=*/410)); + + // if the gesture is correctly finished, new down event will succeed + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectFromAccessibility(ACTION_MOVE, /*x=*/320, /*y=*/430)); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectFromAccessibility(ACTION_UP, /*x=*/320, /*y=*/430)); + window->consumeMotionEvent(WithMotionAction(ACTION_MOVE)); + window->consumeMotionEvent(WithMotionAction(ACTION_UP)); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectFromAccessibility(ACTION_DOWN, /*x=*/350, /*y=*/460)); + window->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); +} + +/** * If mouse is hovering when the touch goes down, the hovering should be stopped via HOVER_EXIT. */ TEST_F(InputDispatcherTest, TouchDownAfterMouseHover_legacy) { @@ -5376,6 +5727,7 @@ TEST_F(InputDispatcherTest, OnWindowInfosChanged_RemoveAllWindowsOnDisplay) { } TEST_F(InputDispatcherTest, NonSplitTouchableWindowReceivesMultiTouch) { + SCOPED_FLAG_OVERRIDE(split_all_touches, false); std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Fake Window", @@ -5421,6 +5773,7 @@ TEST_F(InputDispatcherTest, NonSplitTouchableWindowReceivesMultiTouch) { * "incomplete" gestures. */ TEST_F(InputDispatcherTest, SplittableAndNonSplittableWindows) { + SCOPED_FLAG_OVERRIDE(split_all_touches, false); std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> leftWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Left splittable Window", @@ -5451,6 +5804,273 @@ TEST_F(InputDispatcherTest, SplittableAndNonSplittableWindows) { } /** + * Three windows: + * 1) A window on the left, with flag dup_to_wallpaper + * 2) A window on the right, with flag slippery + * 3) A wallpaper window under the left window + * When touch slips from right window to left, the wallpaper should receive a similar slippery + * enter event. Later on, when another device becomes active, the wallpaper should receive + * consistent streams from the new device, and also from the old device. + * This test attempts to reproduce a crash in the dispatcher where the wallpaper target's downTime + * was not getting set during slippery entrance. + */ +TEST_F(InputDispatcherTest, WallpaperWindowWhenSlipperyAndMultiWindowMultiTouch) { + SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, true); + std::shared_ptr<FakeApplicationHandle> application1 = std::make_shared<FakeApplicationHandle>(); + std::shared_ptr<FakeApplicationHandle> application2 = std::make_shared<FakeApplicationHandle>(); + std::shared_ptr<FakeApplicationHandle> application3 = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> wallpaper = + sp<FakeWindowHandle>::make(application1, mDispatcher, "wallpaper", + ui::LogicalDisplayId::DEFAULT); + wallpaper->setIsWallpaper(true); + wallpaper->setPreventSplitting(true); + wallpaper->setTouchable(false); + + sp<FakeWindowHandle> leftWindow = sp<FakeWindowHandle>::make(application2, mDispatcher, "Left", + ui::LogicalDisplayId::DEFAULT); + leftWindow->setTouchableRegion(Region{{0, 0, 100, 100}}); + leftWindow->setDupTouchToWallpaper(true); + + sp<FakeWindowHandle> rightWindow = + sp<FakeWindowHandle>::make(application3, mDispatcher, "Right", + ui::LogicalDisplayId::DEFAULT); + rightWindow->setTouchableRegion(Region{{100, 0, 200, 100}}); + rightWindow->setSlippery(true); + rightWindow->setWatchOutsideTouch(true); + rightWindow->setTrustedOverlay(true); + + mDispatcher->onWindowInfosChanged( + {{*rightWindow->getInfo(), *leftWindow->getInfo(), *wallpaper->getInfo()}, {}, 0, 0}); + + const DeviceId deviceA = 3; + const DeviceId deviceB = 9; + + // First finger from device A into right window + NotifyMotionArgs deviceADownArgs = + MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(50)) + .deviceId(deviceA) + .build(); + + mDispatcher->notifyMotion(deviceADownArgs); + rightWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); + + // Move the finger of device A from right window into left window. It should slip. + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(80).y(50)) + .deviceId(deviceA) + .downTime(deviceADownArgs.downTime) + .build()); + + leftWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); + rightWindow->consumeMotionEvent(WithMotionAction(ACTION_CANCEL)); + wallpaper->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); + + // Finger from device B down into left window + NotifyMotionArgs deviceBDownArgs = + MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(40).y(40)) + .deviceId(deviceB) + .build(); + mDispatcher->notifyMotion(deviceBDownArgs); + leftWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_DOWN))); + wallpaper->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_DOWN))); + + rightWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_OUTSIDE))); + + // Move finger from device B, still keeping it in the left window + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(40).y(50)) + .deviceId(deviceB) + .downTime(deviceBDownArgs.downTime) + .build()); + leftWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_MOVE))); + wallpaper->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_MOVE))); + + // Lift the finger from device B + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(40).y(50)) + .deviceId(deviceB) + .downTime(deviceBDownArgs.downTime) + .build()); + leftWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_UP))); + wallpaper->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_UP))); + + // Move the finger of device A, keeping it in the left window + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(70).y(50)) + .deviceId(deviceA) + .downTime(deviceADownArgs.downTime) + .build()); + + leftWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_MOVE))); + wallpaper->consumeMotionEvent(AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_MOVE))); + + // Second finger down from device A, into the right window. It should be split into: + // MOVE for the left window (due to existing implementation) + a DOWN into the right window + // Wallpaper will not receive this new pointer, and it will only get the MOVE event. + mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(70).y(50)) + .pointer(PointerBuilder(1, ToolType::FINGER).x(140).y(50)) + .deviceId(deviceA) + .downTime(deviceADownArgs.downTime) + .build()); + auto firstFingerMoveFromDeviceA = AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_MOVE), + WithPointerCount(1), WithPointerId(0, 0)); + leftWindow->consumeMotionEvent(firstFingerMoveFromDeviceA); + wallpaper->consumeMotionEvent(firstFingerMoveFromDeviceA); + rightWindow->consumeMotionEvent( + AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_DOWN), WithPointerId(0, 1))); + + // Lift up the second finger. + mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(70).y(50)) + .pointer(PointerBuilder(1, ToolType::FINGER).x(140).y(50)) + .deviceId(deviceA) + .downTime(deviceADownArgs.downTime) + .build()); + + rightWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_UP))); + leftWindow->consumeMotionEvent(firstFingerMoveFromDeviceA); + wallpaper->consumeMotionEvent(firstFingerMoveFromDeviceA); + + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(70).y(50)) + .deviceId(deviceA) + .downTime(deviceADownArgs.downTime) + .build()); + + leftWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_UP))); + wallpaper->consumeMotionEvent(AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_UP))); + rightWindow->assertNoEvents(); +} + +/** + * Same test as above, but with enable_multi_device_same_window_stream flag set to false. + */ +TEST_F(InputDispatcherTest, WallpaperWindowWhenSlipperyAndMultiWindowMultiTouch_legacy) { + SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, false); + std::shared_ptr<FakeApplicationHandle> application1 = std::make_shared<FakeApplicationHandle>(); + std::shared_ptr<FakeApplicationHandle> application2 = std::make_shared<FakeApplicationHandle>(); + std::shared_ptr<FakeApplicationHandle> application3 = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> wallpaper = + sp<FakeWindowHandle>::make(application1, mDispatcher, "wallpaper", + ui::LogicalDisplayId::DEFAULT); + wallpaper->setIsWallpaper(true); + wallpaper->setPreventSplitting(true); + wallpaper->setTouchable(false); + + sp<FakeWindowHandle> leftWindow = sp<FakeWindowHandle>::make(application2, mDispatcher, "Left", + ui::LogicalDisplayId::DEFAULT); + leftWindow->setTouchableRegion(Region{{0, 0, 100, 100}}); + leftWindow->setDupTouchToWallpaper(true); + + sp<FakeWindowHandle> rightWindow = + sp<FakeWindowHandle>::make(application3, mDispatcher, "Right", + ui::LogicalDisplayId::DEFAULT); + rightWindow->setTouchableRegion(Region{{100, 0, 200, 100}}); + rightWindow->setSlippery(true); + rightWindow->setWatchOutsideTouch(true); + rightWindow->setTrustedOverlay(true); + + mDispatcher->onWindowInfosChanged( + {{*rightWindow->getInfo(), *leftWindow->getInfo(), *wallpaper->getInfo()}, {}, 0, 0}); + + const DeviceId deviceA = 3; + const DeviceId deviceB = 9; + + // First finger from device A into right window + NotifyMotionArgs deviceADownArgs = + MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(50)) + .deviceId(deviceA) + .build(); + + mDispatcher->notifyMotion(deviceADownArgs); + rightWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); + + // Move the finger of device A from right window into left window. It should slip. + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(80).y(50)) + .deviceId(deviceA) + .downTime(deviceADownArgs.downTime) + .build()); + + leftWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); + rightWindow->consumeMotionEvent(WithMotionAction(ACTION_CANCEL)); + wallpaper->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); + + // Finger from device B down into left window + NotifyMotionArgs deviceBDownArgs = + MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(40).y(40)) + .deviceId(deviceB) + .build(); + mDispatcher->notifyMotion(deviceBDownArgs); + leftWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_CANCEL))); + leftWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_DOWN))); + wallpaper->consumeMotionEvent(AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_CANCEL))); + wallpaper->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_DOWN))); + + rightWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_OUTSIDE))); + + // Move finger from device B, still keeping it in the left window + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(40).y(50)) + .deviceId(deviceB) + .downTime(deviceBDownArgs.downTime) + .build()); + leftWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_MOVE))); + wallpaper->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_MOVE))); + + // Lift the finger from device B + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(40).y(50)) + .deviceId(deviceB) + .downTime(deviceBDownArgs.downTime) + .build()); + leftWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_UP))); + wallpaper->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_UP))); + + // Move the finger of device A, keeping it in the left window + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(70).y(50)) + .deviceId(deviceA) + .downTime(deviceADownArgs.downTime) + .build()); + // This device was already canceled, so MOVE events will not be arriving to the windows from it. + + // Second finger down from device A, into the right window. It should be split into: + // MOVE for the left window (due to existing implementation) + a DOWN into the right window + // Wallpaper will not receive this new pointer, and it will only get the MOVE event. + mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(70).y(50)) + .pointer(PointerBuilder(1, ToolType::FINGER).x(140).y(50)) + .deviceId(deviceA) + .downTime(deviceADownArgs.downTime) + .build()); + rightWindow->consumeMotionEvent( + AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_DOWN), WithPointerId(0, 1))); + + // Lift up the second finger. + mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(70).y(50)) + .pointer(PointerBuilder(1, ToolType::FINGER).x(140).y(50)) + .deviceId(deviceA) + .downTime(deviceADownArgs.downTime) + .build()); + + rightWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_UP))); + + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(70).y(50)) + .deviceId(deviceA) + .downTime(deviceADownArgs.downTime) + .build()); + rightWindow->assertNoEvents(); +} + +/** * Two windows: left and right. The left window has PREVENT_SPLITTING input config. Device A sends a * down event to the right window. Device B sends a down event to the left window, and then a * POINTER_DOWN event to the right window. However, since the left window prevents splitting, the @@ -5458,6 +6078,7 @@ TEST_F(InputDispatcherTest, SplittableAndNonSplittableWindows) { * This test attempts to reproduce a crash. */ TEST_F(InputDispatcherTest, MultiDeviceTwoWindowsPreventSplitting) { + SCOPED_FLAG_OVERRIDE(split_all_touches, false); std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> leftWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Left window (prevent splitting)", @@ -8204,6 +8825,7 @@ TEST_F(InputDispatcherTest, HoverEnterExitSynthesisUsesNewEventId) { * the previous window should receive this event and not be dropped. */ TEST_F(InputDispatcherMultiDeviceTest, SingleDevicePointerDownEventRetentionWithoutWindowTarget) { + SCOPED_FLAG_OVERRIDE(split_all_touches, false); std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ui::LogicalDisplayId::DEFAULT); @@ -8562,6 +9184,7 @@ class InputDispatcherKeyRepeatTest : public InputDispatcherTest { protected: static constexpr std::chrono::nanoseconds KEY_REPEAT_TIMEOUT = 40ms; static constexpr std::chrono::nanoseconds KEY_REPEAT_DELAY = 40ms; + static constexpr bool KEY_REPEAT_ENABLED = true; std::shared_ptr<FakeApplicationHandle> mApp; sp<FakeWindowHandle> mWindow; @@ -8569,7 +9192,8 @@ protected: virtual void SetUp() override { InputDispatcherTest::SetUp(); - mDispatcher->setKeyRepeatConfiguration(KEY_REPEAT_TIMEOUT, KEY_REPEAT_DELAY); + mDispatcher->setKeyRepeatConfiguration(KEY_REPEAT_TIMEOUT, KEY_REPEAT_DELAY, + KEY_REPEAT_ENABLED); setUpWindow(); } @@ -8718,6 +9342,24 @@ TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_CorrectRepeatCountWhenInjectK expectKeyRepeatOnce(3); } +TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_NoRepeatWhenKeyRepeatDisabled) { + SCOPED_FLAG_OVERRIDE(keyboard_repeat_keys, true); + static constexpr std::chrono::milliseconds KEY_NO_REPEAT_ASSERTION_TIMEOUT = 100ms; + + mDispatcher->setKeyRepeatConfiguration(KEY_REPEAT_TIMEOUT, KEY_REPEAT_DELAY, + /*repeatKeyEnabled=*/false); + sendAndConsumeKeyDown(/*deviceId=*/1); + + ASSERT_GT(KEY_NO_REPEAT_ASSERTION_TIMEOUT, KEY_REPEAT_TIMEOUT) + << "Ensure the check for no key repeats extends beyond the repeat timeout duration."; + ASSERT_GT(KEY_NO_REPEAT_ASSERTION_TIMEOUT, KEY_REPEAT_DELAY) + << "Ensure the check for no key repeats extends beyond the repeat delay duration."; + + // No events should be returned if key repeat is turned off. + // Wait for KEY_NO_REPEAT_ASSERTION_TIMEOUT to return no events to ensure key repeat disabled. + mWindow->assertNoEvents(KEY_NO_REPEAT_ASSERTION_TIMEOUT); +} + /* Test InputDispatcher for MultiDisplay */ class InputDispatcherFocusOnTwoDisplaysTest : public InputDispatcherTest { public: diff --git a/services/inputflinger/tests/InputMapperTest.cpp b/services/inputflinger/tests/InputMapperTest.cpp index ff0de83fb3..7dff144f87 100644 --- a/services/inputflinger/tests/InputMapperTest.cpp +++ b/services/inputflinger/tests/InputMapperTest.cpp @@ -54,16 +54,15 @@ void InputMapperUnitTest::SetUpWithBus(int bus) { void InputMapperUnitTest::setupAxis(int axis, bool valid, int32_t min, int32_t max, int32_t resolution) { - EXPECT_CALL(mMockEventHub, getAbsoluteAxisInfo(EVENTHUB_ID, axis, _)) - .WillRepeatedly([=](int32_t, int32_t, RawAbsoluteAxisInfo* outAxisInfo) { - outAxisInfo->valid = valid; - outAxisInfo->minValue = min; - outAxisInfo->maxValue = max; - outAxisInfo->flat = 0; - outAxisInfo->fuzz = 0; - outAxisInfo->resolution = resolution; - return valid ? OK : -1; - }); + EXPECT_CALL(mMockEventHub, getAbsoluteAxisInfo(EVENTHUB_ID, axis)) + .WillRepeatedly(Return(valid ? std::optional<RawAbsoluteAxisInfo>{{ + .minValue = min, + .maxValue = max, + .flat = 0, + .fuzz = 0, + .resolution = resolution, + }} + : std::nullopt)); } void InputMapperUnitTest::expectScanCodes(bool present, std::set<int> scanCodes) { @@ -87,6 +86,13 @@ void InputMapperUnitTest::setKeyCodeState(KeyState state, std::set<int> keyCodes } } +void InputMapperUnitTest::setSwitchState(int32_t state, std::set<int32_t> switchCodes) { + for (const auto& switchCode : switchCodes) { + EXPECT_CALL(mMockEventHub, getSwitchState(EVENTHUB_ID, switchCode)) + .WillRepeatedly(testing::Return(static_cast<int>(state))); + } +} + std::list<NotifyArgs> InputMapperUnitTest::process(int32_t type, int32_t code, int32_t value) { nsecs_t when = systemTime(SYSTEM_TIME_MONOTONIC); return process(when, type, code, value); diff --git a/services/inputflinger/tests/InputMapperTest.h b/services/inputflinger/tests/InputMapperTest.h index 4271a700fa..fc27e4fefd 100644 --- a/services/inputflinger/tests/InputMapperTest.h +++ b/services/inputflinger/tests/InputMapperTest.h @@ -51,6 +51,8 @@ protected: void setKeyCodeState(KeyState state, std::set<int> keyCodes); + void setSwitchState(int32_t state, std::set<int32_t> switchCodes); + std::list<NotifyArgs> process(int32_t type, int32_t code, int32_t value); std::list<NotifyArgs> process(nsecs_t when, int32_t type, int32_t code, int32_t value); diff --git a/services/inputflinger/tests/InputProcessor_test.cpp b/services/inputflinger/tests/InputProcessor_test.cpp index f7e5e6783b..d4c5a009a6 100644 --- a/services/inputflinger/tests/InputProcessor_test.cpp +++ b/services/inputflinger/tests/InputProcessor_test.cpp @@ -63,20 +63,6 @@ protected: void SetUp() override { mProcessor = std::make_unique<InputProcessor>(mTestListener); } }; -/** - * Create a basic configuration change and send it to input processor. - * Expect that the event is received by the next input stage, unmodified. - */ -TEST_F(InputProcessorTest, SendToNextStage_NotifyConfigurationChangedArgs) { - // Create a basic configuration change and send to processor - NotifyConfigurationChangedArgs args(/*sequenceNum=*/1, /*eventTime=*/2); - - mProcessor->notifyConfigurationChanged(args); - NotifyConfigurationChangedArgs outArgs; - ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyConfigurationChangedWasCalled(&outArgs)); - ASSERT_EQ(args, outArgs); -} - TEST_F(InputProcessorTest, SendToNextStage_NotifyKeyArgs) { // Create a basic key event and send to processor NotifyKeyArgs args(/*sequenceNum=*/1, /*eventTime=*/2, /*readTime=*/21, /*deviceId=*/3, diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 93fae9b4ac..17c37d5a41 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -24,19 +24,16 @@ #include <InputReader.h> #include <InputReaderBase.h> #include <InputReaderFactory.h> -#include <JoystickInputMapper.h> #include <KeyboardInputMapper.h> #include <MultiTouchInputMapper.h> #include <NotifyArgsBuilders.h> #include <PeripheralController.h> #include <SensorInputMapper.h> #include <SingleTouchInputMapper.h> -#include <SwitchInputMapper.h> #include <TestEventMatchers.h> #include <TestInputListener.h> #include <TouchInputMapper.h> #include <UinputDevice.h> -#include <VibratorInputMapper.h> #include <android-base/thread_annotations.h> #include <com_android_input_flags.h> #include <ftl/enum.h> @@ -624,7 +621,6 @@ protected: if (configuration) { mFakeEventHub->addConfigurationMap(eventHubId, configuration); } - mFakeEventHub->finishDeviceScan(); mReader->loopOnce(); mReader->loopOnce(); ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); @@ -758,8 +754,6 @@ TEST_F(InputReaderTest, WhenEnabledChanges_SendsDeviceResetNotification) { mReader->pushNextDevice(device); ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr)); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled(nullptr)); - NotifyDeviceResetArgs resetArgs; ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); ASSERT_EQ(deviceId, resetArgs.deviceId); @@ -775,7 +769,6 @@ TEST_F(InputReaderTest, WhenEnabledChanges_SendsDeviceResetNotification) { disableDevice(deviceId); mReader->loopOnce(); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasNotCalled()); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasNotCalled()); ASSERT_EQ(device->isEnabled(), false); enableDevice(deviceId); @@ -960,16 +953,6 @@ TEST_F(InputReaderTest, MarkSupportedKeyCodes_ForwardsRequestsToMappers) { ASSERT_TRUE(flags[0] && flags[1] && !flags[2] && !flags[3]); } -TEST_F(InputReaderTest, LoopOnce_WhenDeviceScanFinished_SendsConfigurationChanged) { - constexpr int32_t eventHubId = 1; - addDevice(eventHubId, "ignored", InputDeviceClass::KEYBOARD, nullptr); - - NotifyConfigurationChangedArgs args; - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled(&args)); - ASSERT_EQ(ARBITRARY_TIME, args.eventTime); -} - TEST_F(InputReaderTest, LoopOnce_ForwardsRawEventsToMappers) { constexpr int32_t deviceId = END_RESERVED_ID + 1000; constexpr ftl::Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD; @@ -1074,7 +1057,6 @@ TEST_F(InputReaderTest, Device_CanDispatchToDisplay) { // The device is added after the input port associations are processed since // we do not yet support dynamic device-to-display associations. ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr)); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled()); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled()); ASSERT_NO_FATAL_FAILURE(mapper.assertConfigureWasCalled()); @@ -1104,8 +1086,6 @@ TEST_F(InputReaderTest, WhenEnabledChanges_AllSubdevicesAreUpdated) { ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[0], "fake1", deviceClass, nullptr)); ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[1], "fake2", deviceClass, nullptr)); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled(nullptr)); - NotifyDeviceResetArgs resetArgs; ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); ASSERT_EQ(deviceId, resetArgs.deviceId); @@ -1477,9 +1457,8 @@ protected: // Since this test is run on a real device, all the input devices connected // to the test device will show up in mReader. We wait for those input devices to // show up before beginning the tests. - ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyInputDevicesChangedWasCalled()); - ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled()); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); } }; @@ -1499,12 +1478,10 @@ TEST_F(InputReaderIntegrationTest, TestInvalidDevice) { // consider it as a valid device. std::unique_ptr<UinputDevice> invalidDevice = createUinputDevice<InvalidUinputDevice>(); ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesNotChanged()); - ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasNotCalled()); ASSERT_EQ(numDevices, mFakePolicy->getInputDevices().size()); invalidDevice.reset(); ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesNotChanged()); - ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasNotCalled()); ASSERT_EQ(numDevices, mFakePolicy->getInputDevices().size()); } @@ -1513,7 +1490,6 @@ TEST_F(InputReaderIntegrationTest, AddNewDevice) { std::unique_ptr<UinputHomeKey> keyboard = createUinputDevice<UinputHomeKey>(); ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); - ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled()); ASSERT_EQ(initialNumDevices + 1, mFakePolicy->getInputDevices().size()); const auto device = waitForDevice(keyboard->getName()); @@ -1524,7 +1500,6 @@ TEST_F(InputReaderIntegrationTest, AddNewDevice) { keyboard.reset(); ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); - ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled()); ASSERT_EQ(initialNumDevices, mFakePolicy->getInputDevices().size()); } @@ -1532,21 +1507,14 @@ TEST_F(InputReaderIntegrationTest, SendsEventsToInputListener) { std::unique_ptr<UinputHomeKey> keyboard = createUinputDevice<UinputHomeKey>(); ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); - NotifyConfigurationChangedArgs configChangedArgs; - ASSERT_NO_FATAL_FAILURE( - mTestListener->assertNotifyConfigurationChangedWasCalled(&configChangedArgs)); - int32_t prevId = configChangedArgs.id; - nsecs_t prevTimestamp = configChangedArgs.eventTime; - NotifyKeyArgs keyArgs; keyboard->pressAndReleaseHomeKey(); ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(&keyArgs)); ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action); - ASSERT_NE(prevId, keyArgs.id); - prevId = keyArgs.id; - ASSERT_LE(prevTimestamp, keyArgs.eventTime); ASSERT_LE(keyArgs.eventTime, keyArgs.readTime); - prevTimestamp = keyArgs.eventTime; + + int32_t prevId = keyArgs.id; + nsecs_t prevTimestamp = keyArgs.eventTime; ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(&keyArgs)); ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action); @@ -1669,7 +1637,6 @@ protected: mDevice = createUinputDevice<UinputTouchScreen>(Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT)); ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); - ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled()); const auto info = waitForDevice(mDevice->getName()); ASSERT_TRUE(info); mDeviceInfo = *info; @@ -1738,7 +1705,6 @@ protected: UNIQUE_ID, isInputPortAssociation ? DISPLAY_PORT : NO_PORT, ViewportType::INTERNAL); ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); - ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled()); const auto info = waitForDevice(mDevice->getName()); ASSERT_TRUE(info); mDeviceInfo = *info; @@ -2071,7 +2037,6 @@ TEST_P(TouchIntegrationTest, ExternalStylusConnectedDuringTouchGesture) { // Connecting an external stylus mid-gesture should not interrupt the ongoing gesture stream. auto externalStylus = createUinputDevice<UinputExternalStylus>(); ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); - ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled()); const auto stylusInfo = waitForDevice(externalStylus->getName()); ASSERT_TRUE(stylusInfo); @@ -2084,7 +2049,6 @@ TEST_P(TouchIntegrationTest, ExternalStylusConnectedDuringTouchGesture) { // Disconnecting an external stylus mid-gesture should not interrupt the ongoing gesture stream. externalStylus.reset(); ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); - ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled()); ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled()); // Up @@ -2142,7 +2106,6 @@ private: mStylusDeviceLifecycleTracker = createUinputDevice<T>(); mStylus = mStylusDeviceLifecycleTracker.get(); ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); - ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled()); const auto info = waitForDevice(mStylus->getName()); ASSERT_TRUE(info); mStylusInfo = *info; @@ -2412,7 +2375,6 @@ TEST_F(ExternalStylusIntegrationTest, ExternalStylusConnectionChangesTouchscreen std::unique_ptr<UinputExternalStylusWithPressure> stylus = createUinputDevice<UinputExternalStylusWithPressure>(); ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); - ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled()); const auto stylusInfo = waitForDevice(stylus->getName()); ASSERT_TRUE(stylusInfo); @@ -2430,7 +2392,6 @@ TEST_F(ExternalStylusIntegrationTest, FusedExternalStylusPressureReported) { std::unique_ptr<UinputExternalStylusWithPressure> stylus = createUinputDevice<UinputExternalStylusWithPressure>(); ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); - ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled()); const auto stylusInfo = waitForDevice(stylus->getName()); ASSERT_TRUE(stylusInfo); @@ -2476,7 +2437,6 @@ TEST_F(ExternalStylusIntegrationTest, FusedExternalStylusPressureNotReported) { std::unique_ptr<UinputExternalStylusWithPressure> stylus = createUinputDevice<UinputExternalStylusWithPressure>(); ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); - ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled()); const auto stylusInfo = waitForDevice(stylus->getName()); ASSERT_TRUE(stylusInfo); @@ -2556,7 +2516,6 @@ TEST_F(ExternalStylusIntegrationTest, UnfusedExternalStylus) { // touch pointers. std::unique_ptr<UinputExternalStylus> stylus = createUinputDevice<UinputExternalStylus>(); ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); - ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled()); const auto stylusInfo = waitForDevice(stylus->getName()); ASSERT_TRUE(stylusInfo); @@ -3058,106 +3017,6 @@ TEST_F(InputDeviceTest, KernelBufferOverflowResetsMappers) { mapper.assertProcessWasCalled(); } -// --- SwitchInputMapperTest --- - -class SwitchInputMapperTest : public InputMapperTest { -protected: -}; - -TEST_F(SwitchInputMapperTest, GetSources) { - SwitchInputMapper& mapper = constructAndAddMapper<SwitchInputMapper>(); - - ASSERT_EQ(uint32_t(AINPUT_SOURCE_SWITCH), mapper.getSources()); -} - -TEST_F(SwitchInputMapperTest, GetSwitchState) { - SwitchInputMapper& mapper = constructAndAddMapper<SwitchInputMapper>(); - - mFakeEventHub->setSwitchState(EVENTHUB_ID, SW_LID, 1); - ASSERT_EQ(1, mapper.getSwitchState(AINPUT_SOURCE_ANY, SW_LID)); - - mFakeEventHub->setSwitchState(EVENTHUB_ID, SW_LID, 0); - ASSERT_EQ(0, mapper.getSwitchState(AINPUT_SOURCE_ANY, SW_LID)); -} - -TEST_F(SwitchInputMapperTest, Process) { - SwitchInputMapper& mapper = constructAndAddMapper<SwitchInputMapper>(); - std::list<NotifyArgs> out; - out = process(mapper, ARBITRARY_TIME, READ_TIME, EV_SW, SW_LID, 1); - ASSERT_TRUE(out.empty()); - out = process(mapper, ARBITRARY_TIME, READ_TIME, EV_SW, SW_JACK_PHYSICAL_INSERT, 1); - ASSERT_TRUE(out.empty()); - out = process(mapper, ARBITRARY_TIME, READ_TIME, EV_SW, SW_HEADPHONE_INSERT, 0); - ASSERT_TRUE(out.empty()); - out = process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - - ASSERT_EQ(1u, out.size()); - const NotifySwitchArgs& args = std::get<NotifySwitchArgs>(*out.begin()); - ASSERT_EQ(ARBITRARY_TIME, args.eventTime); - ASSERT_EQ((1U << SW_LID) | (1U << SW_JACK_PHYSICAL_INSERT), args.switchValues); - ASSERT_EQ((1U << SW_LID) | (1U << SW_JACK_PHYSICAL_INSERT) | (1 << SW_HEADPHONE_INSERT), - args.switchMask); - ASSERT_EQ(uint32_t(0), args.policyFlags); -} - -// --- VibratorInputMapperTest --- -class VibratorInputMapperTest : public InputMapperTest { -protected: - void SetUp() override { InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::VIBRATOR); } -}; - -TEST_F(VibratorInputMapperTest, GetSources) { - VibratorInputMapper& mapper = constructAndAddMapper<VibratorInputMapper>(); - - ASSERT_EQ(AINPUT_SOURCE_UNKNOWN, mapper.getSources()); -} - -TEST_F(VibratorInputMapperTest, GetVibratorIds) { - VibratorInputMapper& mapper = constructAndAddMapper<VibratorInputMapper>(); - - ASSERT_EQ(mapper.getVibratorIds().size(), 2U); -} - -TEST_F(VibratorInputMapperTest, Vibrate) { - constexpr uint8_t DEFAULT_AMPLITUDE = 192; - constexpr int32_t VIBRATION_TOKEN = 100; - VibratorInputMapper& mapper = constructAndAddMapper<VibratorInputMapper>(); - - VibrationElement pattern(2); - VibrationSequence sequence(2); - pattern.duration = std::chrono::milliseconds(200); - pattern.channels = {{/*vibratorId=*/0, DEFAULT_AMPLITUDE / 2}, - {/*vibratorId=*/1, DEFAULT_AMPLITUDE}}; - sequence.addElement(pattern); - pattern.duration = std::chrono::milliseconds(500); - pattern.channels = {{/*vibratorId=*/0, DEFAULT_AMPLITUDE / 4}, - {/*vibratorId=*/1, DEFAULT_AMPLITUDE}}; - sequence.addElement(pattern); - - std::vector<int64_t> timings = {0, 1}; - std::vector<uint8_t> amplitudes = {DEFAULT_AMPLITUDE, DEFAULT_AMPLITUDE / 2}; - - ASSERT_FALSE(mapper.isVibrating()); - // Start vibrating - std::list<NotifyArgs> out = mapper.vibrate(sequence, /*repeat=*/-1, VIBRATION_TOKEN); - ASSERT_TRUE(mapper.isVibrating()); - // Verify vibrator state listener was notified. - mReader->loopOnce(); - ASSERT_EQ(1u, out.size()); - const NotifyVibratorStateArgs& vibrateArgs = std::get<NotifyVibratorStateArgs>(*out.begin()); - ASSERT_EQ(DEVICE_ID, vibrateArgs.deviceId); - ASSERT_TRUE(vibrateArgs.isOn); - // Stop vibrating - out = mapper.cancelVibrate(VIBRATION_TOKEN); - ASSERT_FALSE(mapper.isVibrating()); - // Verify vibrator state listener was notified. - mReader->loopOnce(); - ASSERT_EQ(1u, out.size()); - const NotifyVibratorStateArgs& cancelArgs = std::get<NotifyVibratorStateArgs>(*out.begin()); - ASSERT_EQ(DEVICE_ID, cancelArgs.deviceId); - ASSERT_FALSE(cancelArgs.isOn); -} - // --- SensorInputMapperTest --- class SensorInputMapperTest : public InputMapperTest { @@ -3471,11 +3330,11 @@ TEST_F(KeyboardInputMapperTest, Process_SimpleKeyPress) { TEST_F(KeyboardInputMapperTest, Process_KeyRemapping) { mFakeEventHub->addKey(EVENTHUB_ID, KEY_A, 0, AKEYCODE_A, 0); mFakeEventHub->addKey(EVENTHUB_ID, KEY_B, 0, AKEYCODE_B, 0); - mFakeEventHub->addKeyRemapping(EVENTHUB_ID, AKEYCODE_A, AKEYCODE_B); KeyboardInputMapper& mapper = constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); + mFakeEventHub->setKeyRemapping(EVENTHUB_ID, {{AKEYCODE_A, AKEYCODE_B}}); // Key down by scan code. process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_A, 1); NotifyKeyArgs args; @@ -6337,7 +6196,7 @@ TEST_F(SingleTouchInputMapperTest, WhenDeviceTypeIsSetToTouchNavigation_setsCorr SingleTouchInputMapper& mapper = constructAndAddMapper<SingleTouchInputMapper>(); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled()); - ASSERT_EQ(AINPUT_SOURCE_TOUCH_NAVIGATION, mapper.getSources()); + ASSERT_EQ(AINPUT_SOURCE_TOUCH_NAVIGATION | AINPUT_SOURCE_TOUCHPAD, mapper.getSources()); } TEST_F(SingleTouchInputMapperTest, WhenDeviceTypeIsChangedToTouchNavigation_updatesDeviceType) { @@ -6360,7 +6219,7 @@ TEST_F(SingleTouchInputMapperTest, WhenDeviceTypeIsChangedToTouchNavigation_upda InputReaderConfiguration::Change::DEVICE_TYPE /*changes*/); // Check whether device type update was successful. - ASSERT_EQ(AINPUT_SOURCE_TOUCH_NAVIGATION, mDevice->getSources()); + ASSERT_EQ(AINPUT_SOURCE_TOUCH_NAVIGATION | AINPUT_SOURCE_TOUCHPAD, mDevice->getSources()); } TEST_F(SingleTouchInputMapperTest, HoverEventsOutsidePhysicalFrameAreIgnored) { @@ -6816,15 +6675,27 @@ INSTANTIATE_TEST_SUITE_P(TouchscreenPrecisionTests, TouchscreenPrecisionTestsFix class ExternalStylusFusionTest : public SingleTouchInputMapperTest { public: - SingleTouchInputMapper& initializeInputMapperWithExternalStylus() { + void SetUp() override { + SingleTouchInputMapperTest::SetUp(); + mExternalStylusDeviceInfo = {}; + mStylusState = {}; + } + + SingleTouchInputMapper& initializeInputMapperWithExternalStylus(bool supportsPressure = true) { addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(ui::ROTATION_0); prepareButtons(); prepareAxes(POSITION); auto& mapper = constructAndAddMapper<SingleTouchInputMapper>(); + if (supportsPressure) { + mExternalStylusDeviceInfo.addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, + AINPUT_SOURCE_STYLUS, 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f); + mStylusState.pressure = 0.f; + } + mStylusState.when = ARBITRARY_TIME; - mStylusState.pressure = 0.f; mStylusState.toolType = ToolType::STYLUS; mReader->getContext()->setExternalStylusDevices({mExternalStylusDeviceInfo}); configureDevice(InputReaderConfiguration::Change::EXTERNAL_STYLUS_PRESENCE); @@ -6932,11 +6803,17 @@ private: InputDeviceInfo mExternalStylusDeviceInfo{}; }; -TEST_F(ExternalStylusFusionTest, UsesBluetoothStylusSource) { +TEST_F(ExternalStylusFusionTest, UsesBluetoothStylusSourceWithPressure) { SingleTouchInputMapper& mapper = initializeInputMapperWithExternalStylus(); ASSERT_EQ(STYLUS_FUSION_SOURCE, mapper.getSources()); } +TEST_F(ExternalStylusFusionTest, DoesNotUseBluetoothStylusSourceWithoutPressure) { + SingleTouchInputMapper& mapper = + initializeInputMapperWithExternalStylus(/*supportsPressure=*/false); + ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper.getSources()); +} + TEST_F(ExternalStylusFusionTest, UnsuccessfulFusion) { SingleTouchInputMapper& mapper = initializeInputMapperWithExternalStylus(); ASSERT_NO_FATAL_FAILURE(testUnsuccessfulFusionGesture(mapper)); @@ -10203,67 +10080,6 @@ TEST_F(MultiTouchPointerModeTest, WhenViewportActiveStatusChanged_PointerGesture ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); } -// --- JoystickInputMapperTest --- - -class JoystickInputMapperTest : public InputMapperTest { -protected: - static const int32_t RAW_X_MIN; - static const int32_t RAW_X_MAX; - static const int32_t RAW_Y_MIN; - static const int32_t RAW_Y_MAX; - - void SetUp() override { - InputMapperTest::SetUp(InputDeviceClass::JOYSTICK | InputDeviceClass::EXTERNAL); - } - void prepareAxes() { - mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_X, RAW_X_MIN, RAW_X_MAX, 0, 0); - mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_Y, RAW_Y_MIN, RAW_Y_MAX, 0, 0); - } - - void processAxis(JoystickInputMapper& mapper, int32_t axis, int32_t value) { - process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, axis, value); - } - - void processSync(JoystickInputMapper& mapper) { - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - } - - void prepareVirtualDisplay(ui::Rotation orientation) { - setDisplayInfoAndReconfigure(VIRTUAL_DISPLAY_ID, VIRTUAL_DISPLAY_WIDTH, - VIRTUAL_DISPLAY_HEIGHT, orientation, VIRTUAL_DISPLAY_UNIQUE_ID, - NO_PORT, ViewportType::VIRTUAL); - } -}; - -const int32_t JoystickInputMapperTest::RAW_X_MIN = -32767; -const int32_t JoystickInputMapperTest::RAW_X_MAX = 32767; -const int32_t JoystickInputMapperTest::RAW_Y_MIN = -32767; -const int32_t JoystickInputMapperTest::RAW_Y_MAX = 32767; - -TEST_F(JoystickInputMapperTest, Configure_AssignsDisplayUniqueId) { - prepareAxes(); - JoystickInputMapper& mapper = constructAndAddMapper<JoystickInputMapper>(); - - mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, VIRTUAL_DISPLAY_UNIQUE_ID); - - prepareVirtualDisplay(ui::ROTATION_0); - - // Send an axis event - processAxis(mapper, ABS_X, 100); - processSync(mapper); - - NotifyMotionArgs args; - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(VIRTUAL_DISPLAY_ID, args.displayId); - - // Send another axis event - processAxis(mapper, ABS_Y, 100); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(VIRTUAL_DISPLAY_ID, args.displayId); -} - // --- PeripheralControllerTest --- class PeripheralControllerTest : public testing::Test { diff --git a/services/inputflinger/tests/InterfaceMocks.h b/services/inputflinger/tests/InterfaceMocks.h index 8a15d077c6..f41b39ac8e 100644 --- a/services/inputflinger/tests/InterfaceMocks.h +++ b/services/inputflinger/tests/InterfaceMocks.h @@ -92,12 +92,13 @@ public: MOCK_METHOD(InputDeviceIdentifier, getDeviceIdentifier, (int32_t deviceId), (const)); MOCK_METHOD(int32_t, getDeviceControllerNumber, (int32_t deviceId), (const)); MOCK_METHOD(std::optional<PropertyMap>, getConfiguration, (int32_t deviceId), (const)); - MOCK_METHOD(status_t, getAbsoluteAxisInfo, - (int32_t deviceId, int axis, RawAbsoluteAxisInfo* outAxisInfo), (const)); + MOCK_METHOD(std::optional<RawAbsoluteAxisInfo>, getAbsoluteAxisInfo, + (int32_t deviceId, int axis), (const)); MOCK_METHOD(bool, hasRelativeAxis, (int32_t deviceId, int axis), (const)); MOCK_METHOD(bool, hasInputProperty, (int32_t deviceId, int property), (const)); MOCK_METHOD(bool, hasMscEvent, (int32_t deviceId, int mscEvent), (const)); - MOCK_METHOD(void, addKeyRemapping, (int32_t deviceId, int fromKeyCode, int toKeyCode), (const)); + MOCK_METHOD(void, setKeyRemapping, + (int32_t deviceId, (const std::map<int32_t, int32_t>& keyRemapping)), (const)); MOCK_METHOD(status_t, mapKey, (int32_t deviceId, int scanCode, int usageCode, int32_t metaState, int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags), @@ -132,7 +133,7 @@ public: MOCK_METHOD(int32_t, getKeyCodeState, (int32_t deviceId, int32_t keyCode), (const, override)); MOCK_METHOD(int32_t, getSwitchState, (int32_t deviceId, int32_t sw), (const, override)); - MOCK_METHOD(status_t, getAbsoluteAxisValue, (int32_t deviceId, int32_t axis, int32_t* outValue), + MOCK_METHOD(std::optional<int32_t>, getAbsoluteAxisValue, (int32_t deviceId, int32_t axis), (const, override)); MOCK_METHOD(base::Result<std::vector<int32_t>>, getMtSlotValues, (int32_t deviceId, int32_t axis, size_t slotCount), (const, override)); @@ -188,6 +189,7 @@ public: MOCK_METHOD(void, notifyPointerDisplayIdChanged, (ui::LogicalDisplayId displayId, const FloatPoint& position), (override)); MOCK_METHOD(bool, isInputMethodConnectionActive, (), (override)); + MOCK_METHOD(void, notifyMouseCursorFadedOnTyping, (), (override)); }; class MockInputDevice : public InputDevice { @@ -197,6 +199,7 @@ public: : InputDevice(context, id, generation, identifier) {} MOCK_METHOD(uint32_t, getSources, (), (const, override)); + MOCK_METHOD(std::optional<DisplayViewport>, getAssociatedViewport, (), (const)); MOCK_METHOD(bool, isEnabled, (), ()); MOCK_METHOD(void, dump, (std::string& dump, const std::string& eventHubDevStr), ()); @@ -245,8 +248,6 @@ public: MOCK_METHOD(int32_t, getMetaState, (), ()); MOCK_METHOD(void, updateMetaState, (int32_t keyCode), ()); - MOCK_METHOD(void, addKeyRemapping, (int32_t fromKeyCode, int32_t toKeyCode), ()); - MOCK_METHOD(void, setKeyboardType, (KeyboardType keyboardType), ()); MOCK_METHOD(void, bumpGeneration, (), ()); diff --git a/services/inputflinger/tests/JoystickInputMapper_test.cpp b/services/inputflinger/tests/JoystickInputMapper_test.cpp new file mode 100644 index 0000000000..adebd72a03 --- /dev/null +++ b/services/inputflinger/tests/JoystickInputMapper_test.cpp @@ -0,0 +1,83 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "JoystickInputMapper.h" + +#include <list> +#include <optional> + +#include <EventHub.h> +#include <NotifyArgs.h> +#include <ftl/flags.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <input/DisplayViewport.h> +#include <linux/input-event-codes.h> +#include <ui/LogicalDisplayId.h> + +#include "InputMapperTest.h" +#include "TestConstants.h" +#include "TestEventMatchers.h" + +namespace android { + +using namespace ftl::flag_operators; +using testing::ElementsAre; +using testing::IsEmpty; +using testing::Return; +using testing::VariantWith; + +class JoystickInputMapperTest : public InputMapperUnitTest { +protected: + void SetUp() override { + InputMapperUnitTest::SetUp(); + EXPECT_CALL(mMockEventHub, getDeviceClasses(EVENTHUB_ID)) + .WillRepeatedly(Return(InputDeviceClass::JOYSTICK | InputDeviceClass::EXTERNAL)); + + // The mapper requests info on all ABS axis IDs, including ones which aren't actually used + // (e.g. in the range from 0x0b (ABS_BRAKE) to 0x0f (ABS_HAT0X)), so just return nullopt for + // all axes we don't explicitly set up below. + EXPECT_CALL(mMockEventHub, getAbsoluteAxisInfo(EVENTHUB_ID, testing::_)) + .WillRepeatedly(Return(std::nullopt)); + + setupAxis(ABS_X, /*valid=*/true, /*min=*/-32767, /*max=*/32767, /*resolution=*/0); + setupAxis(ABS_Y, /*valid=*/true, /*min=*/-32767, /*max=*/32767, /*resolution=*/0); + } +}; + +TEST_F(JoystickInputMapperTest, Configure_AssignsDisplayUniqueId) { + DisplayViewport viewport; + viewport.displayId = ui::LogicalDisplayId{1}; + EXPECT_CALL((*mDevice), getAssociatedViewport).WillRepeatedly(Return(viewport)); + mMapper = createInputMapper<JoystickInputMapper>(*mDeviceContext, + mFakePolicy->getReaderConfiguration()); + + std::list<NotifyArgs> out; + + // Send an axis event + out = process(EV_ABS, ABS_X, 100); + ASSERT_THAT(out, IsEmpty()); + out = process(EV_SYN, SYN_REPORT, 0); + ASSERT_THAT(out, ElementsAre(VariantWith<NotifyMotionArgs>(WithDisplayId(viewport.displayId)))); + + // Send another axis event + out = process(EV_ABS, ABS_Y, 100); + ASSERT_THAT(out, IsEmpty()); + out = process(EV_SYN, SYN_REPORT, 0); + ASSERT_THAT(out, ElementsAre(VariantWith<NotifyMotionArgs>(WithDisplayId(viewport.displayId)))); +} + +} // namespace android diff --git a/services/inputflinger/tests/KeyboardInputMapper_test.cpp b/services/inputflinger/tests/KeyboardInputMapper_test.cpp index 4d322e90ef..88c25d302a 100644 --- a/services/inputflinger/tests/KeyboardInputMapper_test.cpp +++ b/services/inputflinger/tests/KeyboardInputMapper_test.cpp @@ -70,48 +70,8 @@ protected: mMapper = createInputMapper<KeyboardInputMapper>(*mDeviceContext, mReaderConfiguration, AINPUT_SOURCE_KEYBOARD); } - - void testTouchpadTapStateForKeys(const std::vector<int32_t>& keyCodes, - const bool expectPrevent) { - if (expectPrevent) { - EXPECT_CALL(mMockInputReaderContext, setPreventingTouchpadTaps(true)) - .Times(keyCodes.size()); - } - for (int32_t keyCode : keyCodes) { - process(EV_KEY, keyCode, 1); - process(EV_SYN, SYN_REPORT, 0); - process(EV_KEY, keyCode, 0); - process(EV_SYN, SYN_REPORT, 0); - } - } }; -/** - * Touchpad tap should not be disabled if there is no active Input Method Connection - */ -TEST_F(KeyboardInputMapperUnitTest, KeystrokesWithoutIMeConnectionDontDisableTouchpadTap) { - testTouchpadTapStateForKeys({KEY_0, KEY_A, KEY_LEFTCTRL}, /* expectPrevent= */ false); -} - -/** - * Touchpad tap should be disabled if there is a active Input Method Connection - */ -TEST_F(KeyboardInputMapperUnitTest, AlphanumericKeystrokesWithIMeConnectionDisableTouchpadTap) { - mFakePolicy->setIsInputMethodConnectionActive(true); - testTouchpadTapStateForKeys({KEY_0, KEY_A}, /* expectPrevent= */ true); -} - -/** - * Touchpad tap should not be disabled by meta keys even if Input Method Connection is active - */ -TEST_F(KeyboardInputMapperUnitTest, MetaKeystrokesWithIMeConnectionDontDisableTouchpadTap) { - mFakePolicy->setIsInputMethodConnectionActive(true); - std::vector<int32_t> metaKeys{KEY_LEFTALT, KEY_RIGHTALT, KEY_LEFTSHIFT, KEY_RIGHTSHIFT, - KEY_FN, KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTMETA, - KEY_RIGHTMETA, KEY_CAPSLOCK, KEY_NUMLOCK, KEY_SCROLLLOCK}; - testTouchpadTapStateForKeys(metaKeys, /* expectPrevent= */ false); -} - TEST_F(KeyboardInputMapperUnitTest, KeyPressTimestampRecorded) { nsecs_t when = ARBITRARY_TIME; std::vector<int32_t> keyCodes{KEY_0, KEY_A, KEY_LEFTCTRL, KEY_RIGHTALT, KEY_LEFTSHIFT}; diff --git a/services/inputflinger/tests/LatencyTracker_test.cpp b/services/inputflinger/tests/LatencyTracker_test.cpp index 4fcffddee2..0f92833e64 100644 --- a/services/inputflinger/tests/LatencyTracker_test.cpp +++ b/services/inputflinger/tests/LatencyTracker_test.cpp @@ -61,12 +61,12 @@ const std::chrono::duration ANR_TIMEOUT = std::chrono::milliseconds( InputEventTimeline getTestTimeline() { InputEventTimeline t( - /*isDown=*/true, /*eventTime=*/2, /*readTime=*/3, /*vendorId=*/0, /*productId=*/0, - /*sources=*/{InputDeviceUsageSource::UNKNOWN}); + /*sources=*/{InputDeviceUsageSource::UNKNOWN}, + /*inputEventActionType=*/InputEventActionType::UNKNOWN_INPUT_EVENT); ConnectionTimeline expectedCT(/*deliveryTime=*/6, /*consumeTime=*/7, /*finishTime=*/8); std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline; graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME] = 9; @@ -116,9 +116,10 @@ private: void LatencyTrackerTest::triggerEventReporting(nsecs_t lastEventTime) { const nsecs_t triggerEventTime = lastEventTime + std::chrono::nanoseconds(ANR_TIMEOUT).count() + 1; - mTracker->trackListener(/*inputEventId=*/1, /*isDown=*/true, triggerEventTime, + mTracker->trackListener(/*inputEventId=*/1, triggerEventTime, /*readTime=*/3, DEVICE_ID, - /*sources=*/{InputDeviceUsageSource::UNKNOWN}); + /*sources=*/{InputDeviceUsageSource::UNKNOWN}, + AMOTION_EVENT_ACTION_CANCEL, InputEventType::MOTION); } void LatencyTrackerTest::assertReceivedTimeline(const InputEventTimeline& timeline) { @@ -167,12 +168,15 @@ void LatencyTrackerTest::assertReceivedTimelines(const std::vector<InputEventTim * any additional ConnectionTimeline's. */ TEST_F(LatencyTrackerTest, TrackListener_DoesNotTriggerReporting) { - mTracker->trackListener(/*inputEventId=*/1, /*isDown=*/false, /*eventTime=*/2, - /*readTime=*/3, DEVICE_ID, {InputDeviceUsageSource::UNKNOWN}); + mTracker->trackListener(/*inputEventId=*/1, /*eventTime=*/2, + /*readTime=*/3, DEVICE_ID, {InputDeviceUsageSource::UNKNOWN}, + AMOTION_EVENT_ACTION_CANCEL, InputEventType::MOTION); triggerEventReporting(/*eventTime=*/2); - assertReceivedTimeline(InputEventTimeline{/*isDown=*/false, /*eventTime=*/2, - /*readTime=*/3, /*vendorId=*/0, /*productID=*/0, - /*sources=*/{InputDeviceUsageSource::UNKNOWN}}); + assertReceivedTimeline( + InputEventTimeline{/*eventTime=*/2, + /*readTime=*/3, /*vendorId=*/0, /*productID=*/0, + /*sources=*/{InputDeviceUsageSource::UNKNOWN}, + /*inputEventActionType=*/InputEventActionType::UNKNOWN_INPUT_EVENT}); } /** @@ -203,8 +207,9 @@ TEST_F(LatencyTrackerTest, TrackAllParameters_ReportsFullTimeline) { const auto& [connectionToken, expectedCT] = *expected.connectionTimelines.begin(); - mTracker->trackListener(inputEventId, expected.isDown, expected.eventTime, expected.readTime, - DEVICE_ID, {InputDeviceUsageSource::UNKNOWN}); + mTracker->trackListener(inputEventId, expected.eventTime, expected.readTime, DEVICE_ID, + {InputDeviceUsageSource::UNKNOWN}, AMOTION_EVENT_ACTION_CANCEL, + InputEventType::MOTION); mTracker->trackFinishedEvent(inputEventId, connectionToken, expectedCT.deliveryTime, expectedCT.consumeTime, expectedCT.finishTime); mTracker->trackGraphicsLatency(inputEventId, connectionToken, expectedCT.graphicsTimeline); @@ -220,14 +225,15 @@ TEST_F(LatencyTrackerTest, TrackAllParameters_ReportsFullTimeline) { TEST_F(LatencyTrackerTest, WhenDuplicateEventsAreReported_DoesNotCrash) { constexpr nsecs_t inputEventId = 1; constexpr nsecs_t readTime = 3; // does not matter for this test - constexpr bool isDown = true; // does not matter for this test // In the following 2 calls to trackListener, the inputEventId's are the same, but event times // are different. - mTracker->trackListener(inputEventId, isDown, /*eventTime=*/1, readTime, DEVICE_ID, - {InputDeviceUsageSource::UNKNOWN}); - mTracker->trackListener(inputEventId, isDown, /*eventTime=*/2, readTime, DEVICE_ID, - {InputDeviceUsageSource::UNKNOWN}); + mTracker->trackListener(inputEventId, /*eventTime=*/1, readTime, DEVICE_ID, + {InputDeviceUsageSource::UNKNOWN}, AMOTION_EVENT_ACTION_CANCEL, + InputEventType::MOTION); + mTracker->trackListener(inputEventId, /*eventTime=*/2, readTime, DEVICE_ID, + {InputDeviceUsageSource::UNKNOWN}, AMOTION_EVENT_ACTION_CANCEL, + InputEventType::MOTION); triggerEventReporting(/*eventTime=*/2); // Since we sent duplicate input events, the tracker should just delete all of them, because it @@ -238,12 +244,12 @@ TEST_F(LatencyTrackerTest, WhenDuplicateEventsAreReported_DoesNotCrash) { TEST_F(LatencyTrackerTest, MultipleEvents_AreReportedConsistently) { constexpr int32_t inputEventId1 = 1; InputEventTimeline timeline1( - /*isDown*/ true, /*eventTime*/ 2, /*readTime*/ 3, /*vendorId=*/0, /*productId=*/0, - /*sources=*/{InputDeviceUsageSource::UNKNOWN}); + /*sources=*/{InputDeviceUsageSource::UNKNOWN}, + /*inputEventType=*/InputEventActionType::UNKNOWN_INPUT_EVENT); timeline1.connectionTimelines.emplace(connection1, ConnectionTimeline(/*deliveryTime*/ 6, /*consumeTime*/ 7, /*finishTime*/ 8)); @@ -255,12 +261,12 @@ TEST_F(LatencyTrackerTest, MultipleEvents_AreReportedConsistently) { constexpr int32_t inputEventId2 = 10; InputEventTimeline timeline2( - /*isDown=*/false, /*eventTime=*/20, /*readTime=*/30, /*vendorId=*/0, /*productId=*/0, - /*sources=*/{InputDeviceUsageSource::UNKNOWN}); + /*sources=*/{InputDeviceUsageSource::UNKNOWN}, + /*inputEventActionType=*/InputEventActionType::UNKNOWN_INPUT_EVENT); timeline2.connectionTimelines.emplace(connection2, ConnectionTimeline(/*deliveryTime=*/60, /*consumeTime=*/70, @@ -272,11 +278,13 @@ TEST_F(LatencyTrackerTest, MultipleEvents_AreReportedConsistently) { connectionTimeline2.setGraphicsTimeline(std::move(graphicsTimeline2)); // Start processing first event - mTracker->trackListener(inputEventId1, timeline1.isDown, timeline1.eventTime, - timeline1.readTime, DEVICE_ID, {InputDeviceUsageSource::UNKNOWN}); + mTracker->trackListener(inputEventId1, timeline1.eventTime, timeline1.readTime, DEVICE_ID, + {InputDeviceUsageSource::UNKNOWN}, AMOTION_EVENT_ACTION_CANCEL, + InputEventType::MOTION); // Start processing second event - mTracker->trackListener(inputEventId2, timeline2.isDown, timeline2.eventTime, - timeline2.readTime, DEVICE_ID, {InputDeviceUsageSource::UNKNOWN}); + mTracker->trackListener(inputEventId2, timeline2.eventTime, timeline2.readTime, DEVICE_ID, + {InputDeviceUsageSource::UNKNOWN}, AMOTION_EVENT_ACTION_CANCEL, + InputEventType::MOTION); mTracker->trackFinishedEvent(inputEventId1, connection1, connectionTimeline1.deliveryTime, connectionTimeline1.consumeTime, connectionTimeline1.finishTime); @@ -301,12 +309,14 @@ TEST_F(LatencyTrackerTest, IncompleteEvents_AreHandledConsistently) { const sp<IBinder>& token = timeline.connectionTimelines.begin()->first; for (size_t i = 1; i <= 100; i++) { - mTracker->trackListener(/*inputEventId=*/i, timeline.isDown, timeline.eventTime, - timeline.readTime, /*deviceId=*/DEVICE_ID, - /*sources=*/{InputDeviceUsageSource::UNKNOWN}); - expectedTimelines.push_back(InputEventTimeline{timeline.isDown, timeline.eventTime, - timeline.readTime, timeline.vendorId, - timeline.productId, timeline.sources}); + mTracker->trackListener(/*inputEventId=*/i, timeline.eventTime, timeline.readTime, + /*deviceId=*/DEVICE_ID, + /*sources=*/{InputDeviceUsageSource::UNKNOWN}, + AMOTION_EVENT_ACTION_CANCEL, InputEventType::MOTION); + expectedTimelines.push_back(InputEventTimeline{timeline.eventTime, timeline.readTime, + timeline.vendorId, timeline.productId, + timeline.sources, + timeline.inputEventActionType}); } // Now, complete the first event that was sent. mTracker->trackFinishedEvent(/*inputEventId=*/1, token, expectedCT.deliveryTime, @@ -332,12 +342,13 @@ TEST_F(LatencyTrackerTest, EventsAreTracked_WhenTrackListenerIsCalledFirst) { expectedCT.consumeTime, expectedCT.finishTime); mTracker->trackGraphicsLatency(inputEventId, connection1, expectedCT.graphicsTimeline); - mTracker->trackListener(inputEventId, expected.isDown, expected.eventTime, expected.readTime, - DEVICE_ID, {InputDeviceUsageSource::UNKNOWN}); + mTracker->trackListener(inputEventId, expected.eventTime, expected.readTime, DEVICE_ID, + {InputDeviceUsageSource::UNKNOWN}, AMOTION_EVENT_ACTION_CANCEL, + InputEventType::MOTION); triggerEventReporting(expected.eventTime); - assertReceivedTimeline(InputEventTimeline{expected.isDown, expected.eventTime, - expected.readTime, expected.vendorId, - expected.productId, expected.sources}); + assertReceivedTimeline(InputEventTimeline{expected.eventTime, expected.readTime, + expected.vendorId, expected.productId, + expected.sources, expected.inputEventActionType}); } /** @@ -348,22 +359,92 @@ TEST_F(LatencyTrackerTest, EventsAreTracked_WhenTrackListenerIsCalledFirst) { TEST_F(LatencyTrackerTest, TrackListenerCheck_DeviceInfoFieldsInputEventTimeline) { constexpr int32_t inputEventId = 1; InputEventTimeline timeline( - /*isDown*/ true, /*eventTime*/ 2, /*readTime*/ 3, + /*eventTime*/ 2, /*readTime*/ 3, /*vendorId=*/50, /*productId=*/60, /*sources=*/ - {InputDeviceUsageSource::TOUCHSCREEN, InputDeviceUsageSource::STYLUS_DIRECT}); + {InputDeviceUsageSource::TOUCHSCREEN, InputDeviceUsageSource::STYLUS_DIRECT}, + /*inputEventActionType=*/InputEventActionType::UNKNOWN_INPUT_EVENT); InputDeviceInfo deviceInfo1 = generateTestDeviceInfo( /*vendorId=*/5, /*productId=*/6, /*deviceId=*/DEVICE_ID + 1); InputDeviceInfo deviceInfo2 = generateTestDeviceInfo( /*vendorId=*/50, /*productId=*/60, /*deviceId=*/DEVICE_ID); mTracker->setInputDevices({deviceInfo1, deviceInfo2}); - mTracker->trackListener(inputEventId, timeline.isDown, timeline.eventTime, timeline.readTime, - DEVICE_ID, + mTracker->trackListener(inputEventId, timeline.eventTime, timeline.readTime, DEVICE_ID, {InputDeviceUsageSource::TOUCHSCREEN, - InputDeviceUsageSource::STYLUS_DIRECT}); + InputDeviceUsageSource::STYLUS_DIRECT}, + AMOTION_EVENT_ACTION_CANCEL, InputEventType::MOTION); triggerEventReporting(timeline.eventTime); assertReceivedTimeline(timeline); } +/** + * Check that InputEventActionType is correctly assigned to InputEventTimeline in trackListener. + */ +TEST_F(LatencyTrackerTest, TrackListenerCheck_InputEventActionTypeFieldInputEventTimeline) { + constexpr int32_t inputEventId = 1; + // Create timelines for different event types (Motion, Key) + InputEventTimeline motionDownTimeline( + /*eventTime*/ 2, /*readTime*/ 3, + /*vendorId*/ 0, /*productId*/ 0, + /*sources*/ {InputDeviceUsageSource::UNKNOWN}, + /*inputEventActionType*/ InputEventActionType::MOTION_ACTION_DOWN); + + InputEventTimeline motionMoveTimeline( + /*eventTime*/ 4, /*readTime*/ 5, + /*vendorId*/ 0, /*productId*/ 0, + /*sources*/ {InputDeviceUsageSource::UNKNOWN}, + /*inputEventActionType*/ InputEventActionType::MOTION_ACTION_MOVE); + + InputEventTimeline motionUpTimeline( + /*eventTime*/ 6, /*readTime*/ 7, + /*vendorId*/ 0, /*productId*/ 0, + /*sources*/ {InputDeviceUsageSource::UNKNOWN}, + /*inputEventActionType*/ InputEventActionType::MOTION_ACTION_UP); + + InputEventTimeline keyDownTimeline( + /*eventTime*/ 8, /*readTime*/ 9, + /*vendorId*/ 0, /*productId*/ 0, + /*sources*/ {InputDeviceUsageSource::UNKNOWN}, + /*inputEventActionType*/ InputEventActionType::KEY); + + InputEventTimeline keyUpTimeline( + /*eventTime*/ 10, /*readTime*/ 11, + /*vendorId*/ 0, /*productId*/ 0, + /*sources*/ {InputDeviceUsageSource::UNKNOWN}, + /*inputEventActionType*/ InputEventActionType::KEY); + + InputEventTimeline unknownTimeline( + /*eventTime*/ 12, /*readTime*/ 13, + /*vendorId*/ 0, /*productId*/ 0, + /*sources*/ {InputDeviceUsageSource::UNKNOWN}, + /*inputEventActionType*/ InputEventActionType::UNKNOWN_INPUT_EVENT); + + mTracker->trackListener(inputEventId, motionDownTimeline.eventTime, motionDownTimeline.readTime, + DEVICE_ID, motionDownTimeline.sources, AMOTION_EVENT_ACTION_DOWN, + InputEventType::MOTION); + mTracker->trackListener(inputEventId + 1, motionMoveTimeline.eventTime, + motionMoveTimeline.readTime, DEVICE_ID, motionMoveTimeline.sources, + AMOTION_EVENT_ACTION_MOVE, InputEventType::MOTION); + mTracker->trackListener(inputEventId + 2, motionUpTimeline.eventTime, motionUpTimeline.readTime, + DEVICE_ID, motionUpTimeline.sources, AMOTION_EVENT_ACTION_UP, + InputEventType::MOTION); + mTracker->trackListener(inputEventId + 3, keyDownTimeline.eventTime, keyDownTimeline.readTime, + DEVICE_ID, keyDownTimeline.sources, AKEY_EVENT_ACTION_DOWN, + InputEventType::KEY); + mTracker->trackListener(inputEventId + 4, keyUpTimeline.eventTime, keyUpTimeline.readTime, + DEVICE_ID, keyUpTimeline.sources, AKEY_EVENT_ACTION_UP, + InputEventType::KEY); + mTracker->trackListener(inputEventId + 5, unknownTimeline.eventTime, unknownTimeline.readTime, + DEVICE_ID, unknownTimeline.sources, AMOTION_EVENT_ACTION_POINTER_DOWN, + InputEventType::MOTION); + + triggerEventReporting(unknownTimeline.eventTime); + + std::vector<InputEventTimeline> expectedTimelines = {motionDownTimeline, motionMoveTimeline, + motionUpTimeline, keyDownTimeline, + keyUpTimeline, unknownTimeline}; + assertReceivedTimelines(expectedTimelines); +} + } // namespace android::inputdispatcher diff --git a/services/inputflinger/tests/MultiTouchInputMapper_test.cpp b/services/inputflinger/tests/MultiTouchInputMapper_test.cpp index c57c251b38..9a6b266b21 100644 --- a/services/inputflinger/tests/MultiTouchInputMapper_test.cpp +++ b/services/inputflinger/tests/MultiTouchInputMapper_test.cpp @@ -99,11 +99,8 @@ protected: setupAxis(ABS_MT_TOOL_TYPE, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0); // reset current slot at the beginning - EXPECT_CALL(mMockEventHub, getAbsoluteAxisValue(EVENTHUB_ID, ABS_MT_SLOT, _)) - .WillRepeatedly([](int32_t, int32_t, int32_t* outValue) { - *outValue = 0; - return OK; - }); + EXPECT_CALL(mMockEventHub, getAbsoluteAxisValue(EVENTHUB_ID, ABS_MT_SLOT)) + .WillRepeatedly(Return(0)); // mark all slots not in use mockSlotValues({}); @@ -210,11 +207,8 @@ TEST_F(MultiTouchInputMapperUnitTest, MultiFingerGestureWithUnexpectedReset) { const auto pointerCoordsBeforeReset = std::get<NotifyMotionArgs>(args.back()).pointerCoords; // On buffer overflow mapper will be reset and MT slots data will be repopulated - EXPECT_CALL(mMockEventHub, getAbsoluteAxisValue(EVENTHUB_ID, ABS_MT_SLOT, _)) - .WillRepeatedly([=](int32_t, int32_t, int32_t* outValue) { - *outValue = 1; - return OK; - }); + EXPECT_CALL(mMockEventHub, getAbsoluteAxisValue(EVENTHUB_ID, ABS_MT_SLOT)) + .WillRepeatedly(Return(1)); mockSlotValues( {{1, {Point{x1, y1}, FIRST_TRACKING_ID}}, {2, {Point{x2, y2}, SECOND_TRACKING_ID}}}); diff --git a/services/inputflinger/tests/PointerChoreographer_test.cpp b/services/inputflinger/tests/PointerChoreographer_test.cpp index a1279ff61b..411c7baf77 100644 --- a/services/inputflinger/tests/PointerChoreographer_test.cpp +++ b/services/inputflinger/tests/PointerChoreographer_test.cpp @@ -196,7 +196,6 @@ private: TEST_F(PointerChoreographerTest, ForwardsArgsToInnerListener) { const std::vector<NotifyArgs> allArgs{NotifyInputDevicesChangedArgs{}, - NotifyConfigurationChangedArgs{}, KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).build(), MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) .pointer(FIRST_TOUCH_POINTER) @@ -214,9 +213,6 @@ TEST_F(PointerChoreographerTest, ForwardsArgsToInnerListener) { [&](const NotifyInputDevicesChangedArgs& args) { mTestListener.assertNotifyInputDevicesChangedWasCalled(); }, - [&](const NotifyConfigurationChangedArgs& args) { - mTestListener.assertNotifyConfigurationChangedWasCalled(); - }, [&](const NotifyKeyArgs& args) { mTestListener.assertNotifyKeyWasCalled(); }, @@ -982,6 +978,36 @@ TEST_F(PointerChoreographerTest, WhenTouchDeviceIsResetClearsSpots) { assertPointerControllerRemoved(pc); } +/** + * When both "show touches" and "stylus hover icons" are enabled, if the app doesn't specify an + * icon for the hovering stylus, fall back to using the spot hover icon. + */ +TEST_F(PointerChoreographerTest, ShowTouchesOverridesUnspecifiedStylusIcon) { + mChoreographer.setShowTouchesEnabled(true); + mChoreographer.setStylusPointerIconEnabled(true); + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, + {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS, + DISPLAY_ID)}}); + + mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, + AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS) + .pointer(STYLUS_POINTER) + .deviceId(DEVICE_ID) + .displayId(DISPLAY_ID) + .build()); + auto pc = assertPointerControllerCreated(ControllerType::STYLUS); + + mChoreographer.setPointerIcon(PointerIconStyle::TYPE_NOT_SPECIFIED, DISPLAY_ID, DEVICE_ID); + pc->assertPointerIconSet(PointerIconStyle::TYPE_SPOT_HOVER); + + mChoreographer.setPointerIcon(PointerIconStyle::TYPE_ARROW, DISPLAY_ID, DEVICE_ID); + pc->assertPointerIconSet(PointerIconStyle::TYPE_ARROW); + + mChoreographer.setPointerIcon(PointerIconStyle::TYPE_NOT_SPECIFIED, DISPLAY_ID, DEVICE_ID); + pc->assertPointerIconSet(PointerIconStyle::TYPE_SPOT_HOVER); +} + using StylusFixtureParam = std::tuple</*name*/ std::string_view, /*source*/ uint32_t, ControllerType>; @@ -2365,7 +2391,13 @@ TEST_F(PointerChoreographerTest, MouseAndDrawingTabletReportMouseEvents) { assertPointerControllerRemoved(pc); } -class PointerVisibilityOnKeyPressTest : public PointerChoreographerTest { +using PointerVisibilityAndTouchpadTapStateOnKeyPressTestFixtureParam = + std::tuple<std::string_view /*name*/, uint32_t /*source*/>; + +class PointerVisibilityAndTouchpadTapStateOnKeyPressTestFixture + : public PointerChoreographerTest, + public testing::WithParamInterface< + PointerVisibilityAndTouchpadTapStateOnKeyPressTestFixtureParam> { protected: const std::unordered_map<int32_t, int32_t> mMetaKeyStates{{AKEYCODE_ALT_LEFT, AMETA_ALT_LEFT_ON}, @@ -2429,15 +2461,28 @@ protected: } }; -TEST_F(PointerVisibilityOnKeyPressTest, KeystrokesWithoutImeConnectionDoesNotHidePointer) { +INSTANTIATE_TEST_SUITE_P( + PointerChoreographerTest, PointerVisibilityAndTouchpadTapStateOnKeyPressTestFixture, + testing::Values(std::make_tuple("Mouse", AINPUT_SOURCE_MOUSE), + std::make_tuple("Touchpad", AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD)), + [](const testing::TestParamInfo< + PointerVisibilityAndTouchpadTapStateOnKeyPressTestFixtureParam>& p) { + return std::string{std::get<0>(p.param)}; + }); + +TEST_P(PointerVisibilityAndTouchpadTapStateOnKeyPressTestFixture, + KeystrokesWithoutImeConnectionDoesNotHidePointerOrDisablesTouchpadTap) { + const auto& [_, source] = GetParam(); mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); // Mouse connected mChoreographer.notifyInputDevicesChanged( - {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, DISPLAY_ID)}}); + {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, source, DISPLAY_ID)}}); auto pc = assertPointerControllerCreated(ControllerType::MOUSE); ASSERT_TRUE(pc->isPointerShown()); + EXPECT_CALL(mMockPolicy, notifyMouseCursorFadedOnTyping).Times(0); + notifyKey(ui::LogicalDisplayId::INVALID, AKEYCODE_0); notifyKey(ui::LogicalDisplayId::INVALID, AKEYCODE_A); notifyKey(ui::LogicalDisplayId::INVALID, AKEYCODE_CTRL_LEFT); @@ -2445,16 +2490,19 @@ TEST_F(PointerVisibilityOnKeyPressTest, KeystrokesWithoutImeConnectionDoesNotHid ASSERT_TRUE(pc->isPointerShown()); } -TEST_F(PointerVisibilityOnKeyPressTest, AlphanumericKeystrokesWithImeConnectionHidePointer) { +TEST_P(PointerVisibilityAndTouchpadTapStateOnKeyPressTestFixture, + AlphanumericKeystrokesWithImeConnectionHidePointerAndDisablesTouchpadTap) { + const auto& [_, source] = GetParam(); mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); // Mouse connected mChoreographer.notifyInputDevicesChanged( - {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, DISPLAY_ID)}}); + {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, source, DISPLAY_ID)}}); auto pc = assertPointerControllerCreated(ControllerType::MOUSE); ASSERT_TRUE(pc->isPointerShown()); EXPECT_CALL(mMockPolicy, isInputMethodConnectionActive).WillRepeatedly(testing::Return(true)); + EXPECT_CALL(mMockPolicy, notifyMouseCursorFadedOnTyping).Times(2); notifyKey(DISPLAY_ID, AKEYCODE_0); ASSERT_FALSE(pc->isPointerShown()); @@ -2465,17 +2513,19 @@ TEST_F(PointerVisibilityOnKeyPressTest, AlphanumericKeystrokesWithImeConnectionH ASSERT_FALSE(pc->isPointerShown()); } -TEST_F(PointerVisibilityOnKeyPressTest, MetaKeystrokesDoNotHidePointer) { +TEST_P(PointerVisibilityAndTouchpadTapStateOnKeyPressTestFixture, + MetaKeystrokesDoNotHidePointerOrDisablesTouchpadTap) { + const auto& [_, source] = GetParam(); mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); // Mouse connected mChoreographer.notifyInputDevicesChanged( - {/*id=*/0, - {generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_MOUSE, DISPLAY_ID)}}); + {/*id=*/0, {generateTestDeviceInfo(SECOND_DEVICE_ID, source, DISPLAY_ID)}}); auto pc = assertPointerControllerCreated(ControllerType::MOUSE); ASSERT_TRUE(pc->isPointerShown()); EXPECT_CALL(mMockPolicy, isInputMethodConnectionActive).WillRepeatedly(testing::Return(true)); + EXPECT_CALL(mMockPolicy, notifyMouseCursorFadedOnTyping).Times(0); const std::vector<int32_t> metaKeyCodes{AKEYCODE_ALT_LEFT, AKEYCODE_ALT_RIGHT, AKEYCODE_SHIFT_LEFT, AKEYCODE_SHIFT_RIGHT, @@ -2491,14 +2541,16 @@ TEST_F(PointerVisibilityOnKeyPressTest, MetaKeystrokesDoNotHidePointer) { ASSERT_TRUE(pc->isPointerShown()); } -TEST_F(PointerVisibilityOnKeyPressTest, KeystrokesWithoutTargetHidePointerOnlyOnFocusedDisplay) { +TEST_P(PointerVisibilityAndTouchpadTapStateOnKeyPressTestFixture, + KeystrokesWithoutTargetHidePointerOnlyOnFocusedDisplayAndDisablesTouchpadTap) { + const auto& [_, source] = GetParam(); mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID})); mChoreographer.setFocusedDisplay(DISPLAY_ID); // Mouse connected mChoreographer.notifyInputDevicesChanged( {/*id=*/0, - {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, DISPLAY_ID), + {generateTestDeviceInfo(DEVICE_ID, source, DISPLAY_ID), generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_MOUSE, ANOTHER_DISPLAY_ID)}}); auto pc1 = assertPointerControllerCreated(ControllerType::MOUSE); auto pc2 = assertPointerControllerCreated(ControllerType::MOUSE); @@ -2506,6 +2558,7 @@ TEST_F(PointerVisibilityOnKeyPressTest, KeystrokesWithoutTargetHidePointerOnlyOn ASSERT_TRUE(pc2->isPointerShown()); EXPECT_CALL(mMockPolicy, isInputMethodConnectionActive).WillRepeatedly(testing::Return(true)); + EXPECT_CALL(mMockPolicy, notifyMouseCursorFadedOnTyping).Times(2); notifyKey(ui::LogicalDisplayId::INVALID, AKEYCODE_0); ASSERT_FALSE(pc1->isPointerShown()); @@ -2517,16 +2570,19 @@ TEST_F(PointerVisibilityOnKeyPressTest, KeystrokesWithoutTargetHidePointerOnlyOn ASSERT_TRUE(pc2->isPointerShown()); } -TEST_F(PointerVisibilityOnKeyPressTest, TestMetaKeyCombinations) { +TEST_P(PointerVisibilityAndTouchpadTapStateOnKeyPressTestFixture, TestMetaKeyCombinations) { + const auto& [_, source] = GetParam(); mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); // Mouse connected mChoreographer.notifyInputDevicesChanged( - {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, DISPLAY_ID)}}); + {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, source, DISPLAY_ID)}}); auto pc = assertPointerControllerCreated(ControllerType::MOUSE); + EXPECT_CALL(mMockPolicy, isInputMethodConnectionActive).WillRepeatedly(testing::Return(true)); - // meta key combinations that should hide pointer + // meta key combinations that should hide pointer and disable touchpad taps + EXPECT_CALL(mMockPolicy, notifyMouseCursorFadedOnTyping).Times(5); metaKeyCombinationHidesPointer(*pc, AKEYCODE_A, AKEYCODE_SHIFT_LEFT); metaKeyCombinationHidesPointer(*pc, AKEYCODE_A, AKEYCODE_SHIFT_RIGHT); metaKeyCombinationHidesPointer(*pc, AKEYCODE_A, AKEYCODE_CAPS_LOCK); @@ -2534,6 +2590,7 @@ TEST_F(PointerVisibilityOnKeyPressTest, TestMetaKeyCombinations) { metaKeyCombinationHidesPointer(*pc, AKEYCODE_A, AKEYCODE_SCROLL_LOCK); // meta key combinations that should not hide pointer + EXPECT_CALL(mMockPolicy, notifyMouseCursorFadedOnTyping).Times(0); metaKeyCombinationDoesNotHidePointer(*pc, AKEYCODE_A, AKEYCODE_ALT_LEFT); metaKeyCombinationDoesNotHidePointer(*pc, AKEYCODE_A, AKEYCODE_ALT_RIGHT); metaKeyCombinationDoesNotHidePointer(*pc, AKEYCODE_A, AKEYCODE_CTRL_LEFT); diff --git a/services/inputflinger/tests/RotaryEncoderInputMapper_test.cpp b/services/inputflinger/tests/RotaryEncoderInputMapper_test.cpp new file mode 100644 index 0000000000..6607bc7972 --- /dev/null +++ b/services/inputflinger/tests/RotaryEncoderInputMapper_test.cpp @@ -0,0 +1,190 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "RotaryEncoderInputMapper.h" + +#include <list> +#include <string> +#include <tuple> +#include <variant> + +#include <android-base/logging.h> +#include <android_companion_virtualdevice_flags.h> +#include <gtest/gtest.h> +#include <input/DisplayViewport.h> +#include <linux/input-event-codes.h> +#include <linux/input.h> +#include <utils/Timers.h> + +#include "InputMapperTest.h" +#include "InputReaderBase.h" +#include "InterfaceMocks.h" +#include "NotifyArgs.h" +#include "TestEventMatchers.h" +#include "ui/Rotation.h" + +#define TAG "RotaryEncoderInputMapper_test" + +namespace android { + +using testing::AllOf; +using testing::Return; +using testing::VariantWith; +constexpr ui::LogicalDisplayId DISPLAY_ID = ui::LogicalDisplayId::DEFAULT; +constexpr ui::LogicalDisplayId SECONDARY_DISPLAY_ID = ui::LogicalDisplayId{DISPLAY_ID.val() + 1}; +constexpr int32_t DISPLAY_WIDTH = 480; +constexpr int32_t DISPLAY_HEIGHT = 800; + +namespace { + +DisplayViewport createViewport() { + DisplayViewport v; + v.orientation = ui::Rotation::Rotation0; + v.logicalRight = DISPLAY_HEIGHT; + v.logicalBottom = DISPLAY_WIDTH; + v.physicalRight = DISPLAY_HEIGHT; + v.physicalBottom = DISPLAY_WIDTH; + v.deviceWidth = DISPLAY_HEIGHT; + v.deviceHeight = DISPLAY_WIDTH; + v.isActive = true; + return v; +} + +DisplayViewport createPrimaryViewport() { + DisplayViewport v = createViewport(); + v.displayId = DISPLAY_ID; + v.uniqueId = "local:1"; + return v; +} + +DisplayViewport createSecondaryViewport() { + DisplayViewport v = createViewport(); + v.displayId = SECONDARY_DISPLAY_ID; + v.uniqueId = "local:2"; + v.type = ViewportType::EXTERNAL; + return v; +} + +} // namespace + +namespace vd_flags = android::companion::virtualdevice::flags; + +/** + * Unit tests for RotaryEncoderInputMapper. + */ +class RotaryEncoderInputMapperTest : public InputMapperUnitTest { +protected: + void SetUp() override { SetUpWithBus(BUS_USB); } + void SetUpWithBus(int bus) override { + InputMapperUnitTest::SetUpWithBus(bus); + + EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_WHEEL)) + .WillRepeatedly(Return(true)); + EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_HWHEEL)) + .WillRepeatedly(Return(false)); + EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_WHEEL_HI_RES)) + .WillRepeatedly(Return(false)); + EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_HWHEEL_HI_RES)) + .WillRepeatedly(Return(false)); + } +}; + +TEST_F(RotaryEncoderInputMapperTest, ConfigureDisplayIdWithAssociatedViewport) { + DisplayViewport primaryViewport = createPrimaryViewport(); + DisplayViewport secondaryViewport = createSecondaryViewport(); + mReaderConfiguration.setDisplayViewports({primaryViewport, secondaryViewport}); + + // Set up the secondary display as the associated viewport of the mapper. + EXPECT_CALL((*mDevice), getAssociatedViewport).WillRepeatedly(Return(secondaryViewport)); + mMapper = createInputMapper<RotaryEncoderInputMapper>(*mDeviceContext, mReaderConfiguration); + + std::list<NotifyArgs> args; + // Ensure input events are generated for the secondary display. + args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 1); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_SCROLL), + WithSource(AINPUT_SOURCE_ROTARY_ENCODER), + WithDisplayId(SECONDARY_DISPLAY_ID))))); +} + +TEST_F(RotaryEncoderInputMapperTest, ConfigureDisplayIdNoAssociatedViewport) { + // Set up the default display. + mFakePolicy->clearViewports(); + mFakePolicy->addDisplayViewport(createPrimaryViewport()); + + // Set up the mapper with no associated viewport. + mMapper = createInputMapper<RotaryEncoderInputMapper>(*mDeviceContext, mReaderConfiguration); + + // Ensure input events are generated without display ID + std::list<NotifyArgs> args; + args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 1); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_SCROLL), + WithSource(AINPUT_SOURCE_ROTARY_ENCODER), + WithDisplayId(ui::LogicalDisplayId::INVALID))))); +} + +TEST_F(RotaryEncoderInputMapperTest, ProcessRegularScroll) { + mMapper = createInputMapper<RotaryEncoderInputMapper>(*mDeviceContext, mReaderConfiguration); + + std::list<NotifyArgs> args; + args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 1); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithSource(AINPUT_SOURCE_ROTARY_ENCODER), + WithMotionAction(AMOTION_EVENT_ACTION_SCROLL), WithScroll(1.0f))))); +} + +TEST_F(RotaryEncoderInputMapperTest, ProcessHighResScroll) { + vd_flags::high_resolution_scroll(true); + EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_WHEEL_HI_RES)) + .WillRepeatedly(Return(true)); + mMapper = createInputMapper<RotaryEncoderInputMapper>(*mDeviceContext, mReaderConfiguration); + + std::list<NotifyArgs> args; + args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL_HI_RES, 60); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithSource(AINPUT_SOURCE_ROTARY_ENCODER), + WithMotionAction(AMOTION_EVENT_ACTION_SCROLL), WithScroll(0.5f))))); +} + +TEST_F(RotaryEncoderInputMapperTest, HighResScrollIgnoresRegularScroll) { + vd_flags::high_resolution_scroll(true); + EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_WHEEL_HI_RES)) + .WillRepeatedly(Return(true)); + mMapper = createInputMapper<RotaryEncoderInputMapper>(*mDeviceContext, mReaderConfiguration); + + std::list<NotifyArgs> args; + args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL_HI_RES, 60); + args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 1); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithSource(AINPUT_SOURCE_ROTARY_ENCODER), + WithMotionAction(AMOTION_EVENT_ACTION_SCROLL), WithScroll(0.5f))))); +} + +} // namespace android
\ No newline at end of file diff --git a/services/inputflinger/tests/SwitchInputMapper_test.cpp b/services/inputflinger/tests/SwitchInputMapper_test.cpp new file mode 100644 index 0000000000..ebbf10b8db --- /dev/null +++ b/services/inputflinger/tests/SwitchInputMapper_test.cpp @@ -0,0 +1,72 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SwitchInputMapper.h" + +#include <list> +#include <variant> + +#include <NotifyArgs.h> +#include <gtest/gtest.h> +#include <input/Input.h> +#include <linux/input-event-codes.h> + +#include "InputMapperTest.h" +#include "TestConstants.h" + +namespace android { + +class SwitchInputMapperTest : public InputMapperUnitTest { +protected: + void SetUp() override { + InputMapperUnitTest::SetUp(); + mMapper = createInputMapper<SwitchInputMapper>(*mDeviceContext, + mFakePolicy->getReaderConfiguration()); + } +}; + +TEST_F(SwitchInputMapperTest, GetSources) { + ASSERT_EQ(uint32_t(AINPUT_SOURCE_SWITCH), mMapper->getSources()); +} + +TEST_F(SwitchInputMapperTest, GetSwitchState) { + setSwitchState(1, {SW_LID}); + ASSERT_EQ(1, mMapper->getSwitchState(AINPUT_SOURCE_ANY, SW_LID)); + + setSwitchState(0, {SW_LID}); + ASSERT_EQ(0, mMapper->getSwitchState(AINPUT_SOURCE_ANY, SW_LID)); +} + +TEST_F(SwitchInputMapperTest, Process) { + std::list<NotifyArgs> out; + out = process(ARBITRARY_TIME, EV_SW, SW_LID, 1); + ASSERT_TRUE(out.empty()); + out = process(ARBITRARY_TIME, EV_SW, SW_JACK_PHYSICAL_INSERT, 1); + ASSERT_TRUE(out.empty()); + out = process(ARBITRARY_TIME, EV_SW, SW_HEADPHONE_INSERT, 0); + ASSERT_TRUE(out.empty()); + out = process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + + ASSERT_EQ(1u, out.size()); + const NotifySwitchArgs& args = std::get<NotifySwitchArgs>(*out.begin()); + ASSERT_EQ(ARBITRARY_TIME, args.eventTime); + ASSERT_EQ((1U << SW_LID) | (1U << SW_JACK_PHYSICAL_INSERT), args.switchValues); + ASSERT_EQ((1U << SW_LID) | (1U << SW_JACK_PHYSICAL_INSERT) | (1 << SW_HEADPHONE_INSERT), + args.switchMask); + ASSERT_EQ(uint32_t(0), args.policyFlags); +} + +} // namespace android
\ No newline at end of file diff --git a/services/inputflinger/tests/TestConstants.h b/services/inputflinger/tests/TestConstants.h index ad48b0fbe0..d2337dd92c 100644 --- a/services/inputflinger/tests/TestConstants.h +++ b/services/inputflinger/tests/TestConstants.h @@ -24,6 +24,12 @@ namespace android { using std::chrono_literals::operator""ms; +// Timeout for waiting for an input device to be added and processed +static constexpr std::chrono::duration ADD_INPUT_DEVICE_TIMEOUT = 5000ms; + +// Timeout for asserting that an input device change did not occur +static constexpr std::chrono::duration INPUT_DEVICES_DIDNT_CHANGE_TIMEOUT = 100ms; + // Timeout for waiting for an expected event static constexpr std::chrono::duration WAIT_TIMEOUT = 100ms; diff --git a/services/inputflinger/tests/TestEventMatchers.h b/services/inputflinger/tests/TestEventMatchers.h index a6d9d5b0b2..6fa3365faa 100644 --- a/services/inputflinger/tests/TestEventMatchers.h +++ b/services/inputflinger/tests/TestEventMatchers.h @@ -615,7 +615,12 @@ public: explicit WithPointerIdMatcher(size_t index, int32_t pointerId) : mIndex(index), mPointerId(pointerId) {} - bool MatchAndExplain(const NotifyMotionArgs& args, std::ostream*) const { + bool MatchAndExplain(const NotifyMotionArgs& args, std::ostream* os) const { + if (mIndex >= args.pointerCoords.size()) { + *os << "Pointer index " << mIndex << " is out of bounds"; + return false; + } + return args.pointerProperties[mIndex].id == mPointerId; } @@ -646,12 +651,51 @@ MATCHER_P2(WithCursorPosition, x, y, "InputEvent with specified cursor position" return (isnan(x) ? isnan(argX) : x == argX) && (isnan(y) ? isnan(argY) : y == argY); } -MATCHER_P2(WithRelativeMotion, x, y, "InputEvent with specified relative motion") { - const auto argX = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X); - const auto argY = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y); - *result_listener << "expected relative motion (" << x << ", " << y << "), but got (" << argX - << ", " << argY << ")"; - return argX == x && argY == y; +/// Relative motion matcher +class WithRelativeMotionMatcher { +public: + using is_gtest_matcher = void; + explicit WithRelativeMotionMatcher(size_t pointerIndex, float relX, float relY) + : mPointerIndex(pointerIndex), mRelX(relX), mRelY(relY) {} + + bool MatchAndExplain(const NotifyMotionArgs& event, std::ostream* os) const { + if (mPointerIndex >= event.pointerCoords.size()) { + *os << "Pointer index " << mPointerIndex << " is out of bounds"; + return false; + } + + const PointerCoords& coords = event.pointerCoords[mPointerIndex]; + bool matches = mRelX == coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X) && + mRelY == coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y); + if (!matches) { + *os << "expected relative motion (" << mRelX << ", " << mRelY << ") at pointer index " + << mPointerIndex << ", but got (" + << coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X) << ", " + << coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y) << ")"; + } + return matches; + } + + void DescribeTo(std::ostream* os) const { + *os << "with relative motion (" << mRelX << ", " << mRelY << ") at pointer index " + << mPointerIndex; + } + + void DescribeNegationTo(std::ostream* os) const { *os << "wrong relative motion"; } + +private: + const size_t mPointerIndex; + const float mRelX; + const float mRelY; +}; + +inline WithRelativeMotionMatcher WithRelativeMotion(float relX, float relY) { + return WithRelativeMotionMatcher(0, relX, relY); +} + +inline WithRelativeMotionMatcher WithPointerRelativeMotion(size_t pointerIndex, float relX, + float relY) { + return WithRelativeMotionMatcher(pointerIndex, relX, relY); } MATCHER_P3(WithGestureOffset, dx, dy, epsilon, @@ -720,6 +764,21 @@ MATCHER_P(WithDistance, distance, "MotionEvent with specified distance") { return argDistance == distance; } +MATCHER_P(WithScroll, scroll, "InputEvent with specified scroll value") { + const auto argScroll = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_SCROLL); + *result_listener << "expected scroll value " << scroll << ", but got " << argScroll; + return argScroll == scroll; +} + +MATCHER_P2(WithScroll, scrollX, scrollY, "InputEvent with specified scroll values") { + const auto argScrollX = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_HSCROLL); + const auto argScrollY = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_VSCROLL); + *result_listener << "expected scroll values " << scrollX << " scroll x " << scrollY + << " scroll y, but got " << argScrollX << " scroll x " << argScrollY + << " scroll y"; + return argScrollX == scrollX && argScrollY == scrollY; +} + MATCHER_P2(WithTouchDimensions, maj, min, "InputEvent with specified touch dimensions") { const auto argMajor = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR); const auto argMinor = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR); @@ -743,10 +802,14 @@ MATCHER_P(WithToolType, toolType, "InputEvent with specified tool type") { return argToolType == toolType; } -MATCHER_P2(WithPointerToolType, pointer, toolType, +MATCHER_P2(WithPointerToolType, pointerIndex, toolType, "InputEvent with specified tool type for pointer") { - const auto argToolType = arg.pointerProperties[pointer].toolType; - *result_listener << "expected pointer " << pointer << " to have tool type " + if (std::cmp_greater_equal(pointerIndex, arg.getPointerCount())) { + *result_listener << "Pointer index " << pointerIndex << " is out of bounds"; + return false; + } + const auto argToolType = arg.pointerProperties[pointerIndex].toolType; + *result_listener << "expected pointer " << pointerIndex << " to have tool type " << ftl::enum_string(toolType) << ", but got " << ftl::enum_string(argToolType); return argToolType == toolType; } diff --git a/services/inputflinger/tests/TestInputListener.cpp b/services/inputflinger/tests/TestInputListener.cpp index 41e250f789..369f9cc7cb 100644 --- a/services/inputflinger/tests/TestInputListener.cpp +++ b/services/inputflinger/tests/TestInputListener.cpp @@ -37,19 +37,6 @@ void TestInputListener::assertNotifyInputDevicesChangedWasCalled( "to have been called.")); } -void TestInputListener::assertNotifyConfigurationChangedWasCalled( - NotifyConfigurationChangedArgs* outEventArgs) { - ASSERT_NO_FATAL_FAILURE( - assertCalled<NotifyConfigurationChangedArgs>(outEventArgs, - "Expected notifyConfigurationChanged() " - "to have been called.")); -} - -void TestInputListener::assertNotifyConfigurationChangedWasNotCalled() { - ASSERT_NO_FATAL_FAILURE(assertNotCalled<NotifyConfigurationChangedArgs>( - "notifyConfigurationChanged() should not be called.")); -} - void TestInputListener::assertNotifyDeviceResetWasCalled(NotifyDeviceResetArgs* outEventArgs) { ASSERT_NO_FATAL_FAILURE( assertCalled< @@ -192,10 +179,6 @@ void TestInputListener::notifyInputDevicesChanged(const NotifyInputDevicesChange addToQueue<NotifyInputDevicesChangedArgs>(args); } -void TestInputListener::notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) { - addToQueue<NotifyConfigurationChangedArgs>(args); -} - void TestInputListener::notifyDeviceReset(const NotifyDeviceResetArgs& args) { addToQueue<NotifyDeviceResetArgs>(args); } diff --git a/services/inputflinger/tests/TestInputListener.h b/services/inputflinger/tests/TestInputListener.h index 3c5e0146d6..47eae4dd8f 100644 --- a/services/inputflinger/tests/TestInputListener.h +++ b/services/inputflinger/tests/TestInputListener.h @@ -38,11 +38,6 @@ public: void assertNotifyInputDevicesChangedWasCalled( NotifyInputDevicesChangedArgs* outEventArgs = nullptr); - void assertNotifyConfigurationChangedWasCalled( - NotifyConfigurationChangedArgs* outEventArgs = nullptr); - - void assertNotifyConfigurationChangedWasNotCalled(); - void clearNotifyDeviceResetCalls(); void assertNotifyDeviceResetWasCalled(const ::testing::Matcher<NotifyDeviceResetArgs>& matcher); @@ -85,8 +80,6 @@ private: virtual void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override; - virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override; - virtual void notifyDeviceReset(const NotifyDeviceResetArgs& args) override; virtual void notifyKey(const NotifyKeyArgs& args) override; @@ -107,7 +100,6 @@ private: const std::chrono::milliseconds mEventDidNotHappenTimeout; std::tuple<std::vector<NotifyInputDevicesChangedArgs>, // - std::vector<NotifyConfigurationChangedArgs>, // std::vector<NotifyDeviceResetArgs>, // std::vector<NotifyKeyArgs>, // std::vector<NotifyMotionArgs>, // diff --git a/services/inputflinger/tests/TouchpadInputMapper_test.cpp b/services/inputflinger/tests/TouchpadInputMapper_test.cpp index 12fa835e8c..ea69fffeaa 100644 --- a/services/inputflinger/tests/TouchpadInputMapper_test.cpp +++ b/services/inputflinger/tests/TouchpadInputMapper_test.cpp @@ -103,11 +103,8 @@ protected: setupAxis(ABS_MT_DISTANCE, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0); setupAxis(ABS_MT_TOOL_TYPE, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0); - EXPECT_CALL(mMockEventHub, getAbsoluteAxisValue(EVENTHUB_ID, ABS_MT_SLOT, testing::_)) - .WillRepeatedly([](int32_t eventHubId, int32_t, int32_t* outValue) { - *outValue = 0; - return OK; - }); + EXPECT_CALL(mMockEventHub, getAbsoluteAxisValue(EVENTHUB_ID, ABS_MT_SLOT)) + .WillRepeatedly(Return(0)); EXPECT_CALL(mMockEventHub, getMtSlotValues(EVENTHUB_ID, testing::_, testing::_)) .WillRepeatedly([]() -> base::Result<std::vector<int32_t>> { return base::ResultError("Axis not supported", NAME_NOT_FOUND); @@ -175,4 +172,22 @@ TEST_F(TouchpadInputMapperTest, HoverAndLeftButtonPress) { ASSERT_THAT(args, testing::IsEmpty()); } +TEST_F(TouchpadInputMapperTest, TouchpadHardwareState) { + mReaderConfiguration.shouldNotifyTouchpadHardwareState = true; + std::list<NotifyArgs> args = + mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration, + InputReaderConfiguration::Change::TOUCHPAD_SETTINGS); + + args += process(EV_ABS, ABS_MT_TRACKING_ID, 1); + args += process(EV_KEY, BTN_TOUCH, 1); + setScanCodeState(KeyState::DOWN, {BTN_TOOL_FINGER}); + args += process(EV_KEY, BTN_TOOL_FINGER, 1); + args += process(EV_ABS, ABS_MT_POSITION_X, 50); + args += process(EV_ABS, ABS_MT_POSITION_Y, 50); + args += process(EV_ABS, ABS_MT_PRESSURE, 1); + args += process(EV_SYN, SYN_REPORT, 0); + + mFakePolicy->assertTouchpadHardwareStateNotified(); +} + } // namespace android diff --git a/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp b/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp index 853f628a13..bbb2fc8d38 100644 --- a/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp +++ b/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp @@ -414,20 +414,6 @@ protected: }; /** - * Create a basic configuration change and send it to input processor. - * Expect that the event is received by the next input stage, unmodified. - */ -TEST_F(UnwantedInteractionBlockerTest, ConfigurationChangedIsPassedToNextListener) { - // Create a basic configuration change and send to blocker - NotifyConfigurationChangedArgs args(/*sequenceNum=*/1, /*eventTime=*/2); - - mBlocker->notifyConfigurationChanged(args); - NotifyConfigurationChangedArgs outArgs; - ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyConfigurationChangedWasCalled(&outArgs)); - ASSERT_EQ(args, outArgs); -} - -/** * Keys are not handled in 'UnwantedInteractionBlocker' and should be passed * to next stage unmodified. */ diff --git a/services/inputflinger/tests/VibratorInputMapper_test.cpp b/services/inputflinger/tests/VibratorInputMapper_test.cpp new file mode 100644 index 0000000000..6e3344c345 --- /dev/null +++ b/services/inputflinger/tests/VibratorInputMapper_test.cpp @@ -0,0 +1,93 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "VibratorInputMapper.h" + +#include <chrono> +#include <list> +#include <variant> +#include <vector> + +#include <EventHub.h> +#include <NotifyArgs.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <input/Input.h> + +#include "InputMapperTest.h" +#include "VibrationElement.h" + +namespace android { + +class VibratorInputMapperTest : public InputMapperUnitTest { +protected: + void SetUp() override { + InputMapperUnitTest::SetUp(); + EXPECT_CALL(mMockEventHub, getDeviceClasses(EVENTHUB_ID)) + .WillRepeatedly(testing::Return(InputDeviceClass::VIBRATOR)); + EXPECT_CALL(mMockEventHub, getVibratorIds(EVENTHUB_ID)) + .WillRepeatedly(testing::Return<std::vector<int32_t>>({0, 1})); + mMapper = createInputMapper<VibratorInputMapper>(*mDeviceContext, + mFakePolicy->getReaderConfiguration()); + } +}; + +TEST_F(VibratorInputMapperTest, GetSources) { + ASSERT_EQ(AINPUT_SOURCE_UNKNOWN, mMapper->getSources()); +} + +TEST_F(VibratorInputMapperTest, GetVibratorIds) { + ASSERT_EQ(mMapper->getVibratorIds().size(), 2U); +} + +TEST_F(VibratorInputMapperTest, Vibrate) { + constexpr uint8_t DEFAULT_AMPLITUDE = 192; + constexpr int32_t VIBRATION_TOKEN = 100; + + VibrationElement pattern(2); + VibrationSequence sequence(2); + pattern.duration = std::chrono::milliseconds(200); + pattern.channels = {{/*vibratorId=*/0, DEFAULT_AMPLITUDE / 2}, + {/*vibratorId=*/1, DEFAULT_AMPLITUDE}}; + sequence.addElement(pattern); + pattern.duration = std::chrono::milliseconds(500); + pattern.channels = {{/*vibratorId=*/0, DEFAULT_AMPLITUDE / 4}, + {/*vibratorId=*/1, DEFAULT_AMPLITUDE}}; + sequence.addElement(pattern); + + std::vector<int64_t> timings = {0, 1}; + std::vector<uint8_t> amplitudes = {DEFAULT_AMPLITUDE, DEFAULT_AMPLITUDE / 2}; + + ASSERT_FALSE(mMapper->isVibrating()); + // Start vibrating + std::list<NotifyArgs> out = mMapper->vibrate(sequence, /*repeat=*/-1, VIBRATION_TOKEN); + ASSERT_TRUE(mMapper->isVibrating()); + // Verify vibrator state listener was notified. + ASSERT_EQ(1u, out.size()); + const NotifyVibratorStateArgs& vibrateArgs = std::get<NotifyVibratorStateArgs>(*out.begin()); + ASSERT_EQ(DEVICE_ID, vibrateArgs.deviceId); + ASSERT_TRUE(vibrateArgs.isOn); + // Stop vibrating + out = mMapper->cancelVibrate(VIBRATION_TOKEN); + ASSERT_FALSE(mMapper->isVibrating()); + // Verify vibrator state listener was notified. + ASSERT_EQ(1u, out.size()); + const NotifyVibratorStateArgs& cancelArgs = std::get<NotifyVibratorStateArgs>(*out.begin()); + ASSERT_EQ(DEVICE_ID, cancelArgs.deviceId); + ASSERT_FALSE(cancelArgs.isOn); +} + +} // namespace android
\ No newline at end of file diff --git a/services/inputflinger/tests/fuzzers/InputClassifierFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputClassifierFuzzer.cpp index 0b4ac1fe86..46a6189d0f 100644 --- a/services/inputflinger/tests/fuzzers/InputClassifierFuzzer.cpp +++ b/services/inputflinger/tests/fuzzers/InputClassifierFuzzer.cpp @@ -39,12 +39,6 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t *data, size_t size) { while (fdp.remaining_bytes() > 0) { fdp.PickValueInArray<std::function<void()>>({ [&]() -> void { - // SendToNextStage_NotifyConfigurationChangedArgs - mClassifier->notifyConfigurationChanged( - {/*sequenceNum=*/fdp.ConsumeIntegral<int32_t>(), - /*eventTime=*/fdp.ConsumeIntegral<nsecs_t>()}); - }, - [&]() -> void { // SendToNextStage_NotifyKeyArgs const nsecs_t eventTime = fdp.ConsumeIntegralInRange<nsecs_t>(0, diff --git a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp index 7d26a43440..5442a65f2f 100644 --- a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp +++ b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp @@ -117,6 +117,10 @@ public: return reader->getSensors(deviceId); } + std::optional<HardwareProperties> getTouchpadHardwareProperties(int32_t deviceId) { + return reader->getTouchpadHardwareProperties(deviceId); + } + bool canDispatchToDisplay(int32_t deviceId, ui::LogicalDisplayId displayId) { return reader->canDispatchToDisplay(deviceId, displayId); } @@ -151,10 +155,6 @@ public: return reader->getLightPlayerId(deviceId, lightId); } - void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const { - reader->addKeyRemapping(deviceId, fromKeyCode, toKeyCode); - } - int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const { return reader->getKeyCodeForKeyLocation(deviceId, locationKeyCode); } @@ -169,6 +169,8 @@ public: DeviceId getLastUsedInputDeviceId() override { return reader->getLastUsedInputDeviceId(); } + void notifyMouseCursorFadedOnTyping() override { reader->notifyMouseCursorFadedOnTyping(); } + private: std::unique_ptr<InputReaderInterface> reader; }; diff --git a/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp b/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp index 6daeaafbb3..695eb3c5dd 100644 --- a/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp +++ b/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp @@ -18,6 +18,7 @@ #include <linux/input.h> #include "../../InputDeviceMetricsSource.h" +#include "../InputEventTimeline.h" #include "dispatcher/LatencyTracker.h" namespace android { @@ -65,14 +66,15 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) { fdp.PickValueInArray<std::function<void()>>({ [&]() -> void { int32_t inputEventId = fdp.ConsumeIntegral<int32_t>(); - int32_t isDown = fdp.ConsumeBool(); nsecs_t eventTime = fdp.ConsumeIntegral<nsecs_t>(); nsecs_t readTime = fdp.ConsumeIntegral<nsecs_t>(); const DeviceId deviceId = fdp.ConsumeIntegral<int32_t>(); std::set<InputDeviceUsageSource> sources = { fdp.ConsumeEnum<InputDeviceUsageSource>()}; - tracker.trackListener(inputEventId, isDown, eventTime, readTime, deviceId, - sources); + const int32_t inputEventActionType = fdp.ConsumeIntegral<int32_t>(); + const InputEventType inputEventType = fdp.ConsumeEnum<InputEventType>(); + tracker.trackListener(inputEventId, eventTime, readTime, deviceId, sources, + inputEventActionType, inputEventType); }, [&]() -> void { int32_t inputEventId = fdp.ConsumeIntegral<int32_t>(); diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h index ff425ddfb7..fa8270a3d9 100644 --- a/services/inputflinger/tests/fuzzers/MapperHelpers.h +++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h @@ -17,6 +17,7 @@ #include <map> #include <memory> +#include <optional> #include <EventHub.h> #include <InputDevice.h> @@ -31,8 +32,7 @@ constexpr size_t kValidTypes[] = {EV_SW, EV_MSC, EV_REL, android::EventHubInterface::DEVICE_ADDED, - android::EventHubInterface::DEVICE_REMOVED, - android::EventHubInterface::FINISHED_DEVICE_SCAN}; + android::EventHubInterface::DEVICE_REMOVED}; constexpr size_t kValidCodes[] = { SYN_REPORT, @@ -119,16 +119,25 @@ public: void setAbsoluteAxisInfo(int32_t deviceId, int axis, const RawAbsoluteAxisInfo& axisInfo) { mAxes[deviceId][axis] = axisInfo; } - status_t getAbsoluteAxisInfo(int32_t deviceId, int axis, - RawAbsoluteAxisInfo* outAxisInfo) const override { + std::optional<RawAbsoluteAxisInfo> getAbsoluteAxisInfo(int32_t deviceId, + int axis) const override { if (auto deviceAxesIt = mAxes.find(deviceId); deviceAxesIt != mAxes.end()) { const std::map<int, RawAbsoluteAxisInfo>& deviceAxes = deviceAxesIt->second; if (auto axisInfoIt = deviceAxes.find(axis); axisInfoIt != deviceAxes.end()) { - *outAxisInfo = axisInfoIt->second; - return OK; + return axisInfoIt->second; } } - return mFdp->ConsumeIntegral<status_t>(); + if (mFdp->ConsumeBool()) { + return std::optional<RawAbsoluteAxisInfo>({ + .minValue = mFdp->ConsumeIntegral<int32_t>(), + .maxValue = mFdp->ConsumeIntegral<int32_t>(), + .flat = mFdp->ConsumeIntegral<int32_t>(), + .fuzz = mFdp->ConsumeIntegral<int32_t>(), + .resolution = mFdp->ConsumeIntegral<int32_t>(), + }); + } else { + return std::nullopt; + } } bool hasRelativeAxis(int32_t deviceId, int axis) const override { return mFdp->ConsumeBool(); } bool hasInputProperty(int32_t deviceId, int property) const override { @@ -193,13 +202,17 @@ public: int32_t getSwitchState(int32_t deviceId, int32_t sw) const override { return mFdp->ConsumeIntegral<int32_t>(); } - void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const override {} + void setKeyRemapping(int32_t deviceId, + const std::map<int32_t, int32_t>& keyRemapping) const override {} int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const override { return mFdp->ConsumeIntegral<int32_t>(); } - status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis, - int32_t* outValue) const override { - return mFdp->ConsumeIntegral<status_t>(); + std::optional<int32_t> getAbsoluteAxisValue(int32_t deviceId, int32_t axis) const override { + if (mFdp->ConsumeBool()) { + return mFdp->ConsumeIntegral<int32_t>(); + } else { + return std::nullopt; + } } base::Result<std::vector<int32_t>> getMtSlotValues(int32_t deviceId, int32_t axis, size_t slotCount) const override { @@ -269,6 +282,9 @@ public: FuzzInputReaderPolicy(std::shared_ptr<ThreadSafeFuzzedDataProvider> mFdp) : mFdp(mFdp) {} void getReaderConfiguration(InputReaderConfiguration* outConfig) override {} void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) override {} + void notifyTouchpadHardwareState(const SelfContainedHardwareState& schs, + int32_t deviceId) override {} + void notifyTouchpadGestureInfo(GestureType type, int32_t deviceId) override {} std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay( const InputDeviceIdentifier& identifier, const std::optional<KeyboardLayoutInfo> layoutInfo) override { @@ -293,7 +309,6 @@ public: class FuzzInputListener : public virtual InputListenerInterface { public: void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override {} - void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override {} void notifyKey(const NotifyKeyArgs& args) override {} void notifyMotion(const NotifyMotionArgs& args) override {} void notifySwitch(const NotifySwitchArgs& args) override {} @@ -333,8 +348,8 @@ public: int32_t getLedMetaState() override { return mFdp->ConsumeIntegral<int32_t>(); }; void notifyStylusGestureStarted(int32_t, nsecs_t) {} - void setPreventingTouchpadTaps(bool prevent) {} - bool isPreventingTouchpadTaps() { return mFdp->ConsumeBool(); }; + void setPreventingTouchpadTaps(bool prevent) override {} + bool isPreventingTouchpadTaps() override { return mFdp->ConsumeBool(); }; void setLastKeyDownTimestamp(nsecs_t when) { mLastKeyDownTimestamp = when; }; nsecs_t getLastKeyDownTimestamp() { return mLastKeyDownTimestamp; }; diff --git a/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp index c620032eef..ebbb311512 100644 --- a/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp +++ b/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp @@ -34,7 +34,6 @@ void setAxisInfo(ThreadSafeFuzzedDataProvider& fdp, FuzzEventHub& eventHub, int3 if (fdp.ConsumeBool()) { eventHub.setAbsoluteAxisInfo(id, axis, RawAbsoluteAxisInfo{ - .valid = fdp.ConsumeBool(), .minValue = fdp.ConsumeIntegral<int32_t>(), .maxValue = fdp.ConsumeIntegral<int32_t>(), .flat = fdp.ConsumeIntegral<int32_t>(), diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp index f4b0265afb..7b2596adca 100644 --- a/services/sensorservice/Android.bp +++ b/services/sensorservice/Android.bp @@ -52,6 +52,7 @@ cc_library { "-Wall", "-Werror", "-Wextra", + "-Wthread-safety", "-fvisibility=hidden", ], diff --git a/services/sensorservice/SensorDirectConnection.cpp b/services/sensorservice/SensorDirectConnection.cpp index 555b80aed3..33724a93b5 100644 --- a/services/sensorservice/SensorDirectConnection.cpp +++ b/services/sensorservice/SensorDirectConnection.cpp @@ -26,12 +26,17 @@ namespace android { using util::ProtoOutputStream; -SensorService::SensorDirectConnection::SensorDirectConnection(const sp<SensorService>& service, - uid_t uid, const sensors_direct_mem_t *mem, int32_t halChannelHandle, - const String16& opPackageName, int deviceId) - : mService(service), mUid(uid), mMem(*mem), +SensorService::SensorDirectConnection::SensorDirectConnection( + const sp<SensorService>& service, uid_t uid, pid_t pid, const sensors_direct_mem_t* mem, + int32_t halChannelHandle, const String16& opPackageName, int deviceId) + : mService(service), + mUid(uid), + mPid(pid), + mMem(*mem), mHalChannelHandle(halChannelHandle), - mOpPackageName(opPackageName), mDeviceId(deviceId), mDestroyed(false) { + mOpPackageName(opPackageName), + mDeviceId(deviceId), + mDestroyed(false) { mUserId = multiuser_get_user_id(mUid); ALOGD_IF(DEBUG_CONNECTIONS, "Created SensorDirectConnection"); } @@ -62,10 +67,21 @@ void SensorService::SensorDirectConnection::onFirstRef() { void SensorService::SensorDirectConnection::dump(String8& result) const { Mutex::Autolock _l(mConnectionLock); - result.appendFormat("\tPackage %s, HAL channel handle %d, total sensor activated %zu\n", - String8(mOpPackageName).c_str(), getHalChannelHandle(), mActivated.size()); - for (auto &i : mActivated) { - result.appendFormat("\t\tSensor %#08x, rate %d\n", i.first, i.second); + result.appendFormat("\t%s | HAL channel handle %d | uid %d | pid %d\n", + String8(mOpPackageName).c_str(), getHalChannelHandle(), mUid, mPid); + result.appendFormat("\tActivated sensor count: %zu\n", mActivated.size()); + dumpSensorInfoWithLock(result, mActivated); + + result.appendFormat("\tBackup sensor (opened but UID idle) count: %zu\n", + mActivatedBackup.size()); + dumpSensorInfoWithLock(result, mActivatedBackup); +} + +void SensorService::SensorDirectConnection::dumpSensorInfoWithLock( + String8& result, std::unordered_map<int, int> sensors) const { + for (auto& i : sensors) { + result.appendFormat("\t\t%s 0x%08x | rate %d\n", mService->getSensorName(i.first).c_str(), + i.first, i.second); } } diff --git a/services/sensorservice/SensorDirectConnection.h b/services/sensorservice/SensorDirectConnection.h index bfaf811330..9f21731fa9 100644 --- a/services/sensorservice/SensorDirectConnection.h +++ b/services/sensorservice/SensorDirectConnection.h @@ -17,9 +17,10 @@ #ifndef ANDROID_SENSOR_DIRECT_CONNECTION_H #define ANDROID_SENSOR_DIRECT_CONNECTION_H -#include <optional> +#include <android-base/thread_annotations.h> #include <stdint.h> #include <sys/types.h> +#include <optional> #include <binder/BinderService.h> @@ -37,15 +38,15 @@ class BitTube; class SensorService::SensorDirectConnection: public BnSensorEventConnection { public: - SensorDirectConnection(const sp<SensorService>& service, uid_t uid, - const sensors_direct_mem_t *mem, int32_t halChannelHandle, - const String16& opPackageName, int deviceId); + SensorDirectConnection(const sp<SensorService>& service, uid_t uid, pid_t pid, + const sensors_direct_mem_t* mem, int32_t halChannelHandle, + const String16& opPackageName, int deviceId); void dump(String8& result) const; void dump(util::ProtoOutputStream* proto) const; uid_t getUid() const { return mUid; } const String16& getOpPackageName() const { return mOpPackageName; } int32_t getHalChannelHandle() const; - bool isEquivalent(const sensors_direct_mem_t *mem) const; + bool isEquivalent(const sensors_direct_mem_t* mem) const; // Invoked when access to sensors for this connection has changed, e.g. lost or // regained due to changes in the sensor restricted/privacy mode or the @@ -94,8 +95,13 @@ private: // Recover sensor requests previously capped by capRates(). void uncapRates(); + // Dumps a set of sensor infos. + void dumpSensorInfoWithLock(String8& result, std::unordered_map<int, int> sensors) const + EXCLUSIVE_LOCKS_REQUIRED(mConnectionLock); + const sp<SensorService> mService; const uid_t mUid; + const pid_t mPid; const sensors_direct_mem_t mMem; const int32_t mHalChannelHandle; const String16 mOpPackageName; diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp index 760cc8f26c..0d00642688 100644 --- a/services/sensorservice/SensorEventConnection.cpp +++ b/services/sensorservice/SensorEventConnection.cpp @@ -90,15 +90,14 @@ void SensorService::SensorEventConnection::dump(String8& result) { result.append("NORMAL\n"); } result.appendFormat("\t %s | WakeLockRefCount %d | uid %d | cache size %d | " - "max cache size %d\n", mPackageName.c_str(), mWakeLockRefCount, mUid, mCacheSize, - mMaxCacheSize); + "max cache size %d | has sensor access: %s\n", + mPackageName.c_str(), mWakeLockRefCount, mUid, mCacheSize, mMaxCacheSize, + hasSensorAccess() ? "true" : "false"); for (auto& it : mSensorInfo) { const FlushInfo& flushInfo = it.second; - result.appendFormat("\t %s 0x%08x | status: %s | pending flush events %d \n", - mService->getSensorName(it.first).c_str(), - it.first, - flushInfo.mFirstFlushPending ? "First flush pending" : - "active", + result.appendFormat("\t %s 0x%08x | first flush pending: %s | pending flush events %d \n", + mService->getSensorName(it.first).c_str(), it.first, + flushInfo.mFirstFlushPending ? "true" : "false", flushInfo.mPendingFlushEventsToSend); } #if DEBUG_CONNECTIONS @@ -712,14 +711,17 @@ status_t SensorService::SensorEventConnection::enableDisable( if (err == OK && isSensorCapped) { if ((requestedSamplingPeriodNs >= SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS) || !isRateCappedBasedOnPermission()) { + Mutex::Autolock _l(mConnectionLock); mMicSamplingPeriodBackup[handle] = requestedSamplingPeriodNs; } else { + Mutex::Autolock _l(mConnectionLock); mMicSamplingPeriodBackup[handle] = SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS; } } } else { err = mService->disable(this, handle); + Mutex::Autolock _l(mConnectionLock); mMicSamplingPeriodBackup.erase(handle); } return err; @@ -751,8 +753,10 @@ status_t SensorService::SensorEventConnection::setEventRate(int handle, nsecs_t if (ret == OK && isSensorCapped) { if ((requestedSamplingPeriodNs >= SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS) || !isRateCappedBasedOnPermission()) { + Mutex::Autolock _l(mConnectionLock); mMicSamplingPeriodBackup[handle] = requestedSamplingPeriodNs; } else { + Mutex::Autolock _l(mConnectionLock); mMicSamplingPeriodBackup[handle] = SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS; } } diff --git a/services/sensorservice/SensorEventConnection.h b/services/sensorservice/SensorEventConnection.h index 6a98a40686..bb8733dc3a 100644 --- a/services/sensorservice/SensorEventConnection.h +++ b/services/sensorservice/SensorEventConnection.h @@ -199,7 +199,8 @@ private: // valid mapping for sensors that require a permission in order to reduce the lookup time. std::unordered_map<int32_t, int32_t> mHandleToAppOp; // Mapping of sensor handles to its rate before being capped by the mic toggle. - std::unordered_map<int, nsecs_t> mMicSamplingPeriodBackup; + std::unordered_map<int, nsecs_t> mMicSamplingPeriodBackup + GUARDED_BY(mConnectionLock); userid_t mUserId; std::optional<bool> mIsRateCappedBasedOnPermission; diff --git a/services/sensorservice/SensorRegistrationInfo.h b/services/sensorservice/SensorRegistrationInfo.h index dc9e8215e7..a8a773ad6b 100644 --- a/services/sensorservice/SensorRegistrationInfo.h +++ b/services/sensorservice/SensorRegistrationInfo.h @@ -38,10 +38,11 @@ public: mActivated = false; } - SensorRegistrationInfo(int32_t handle, const String8 &packageName, - int64_t samplingRateNs, int64_t maxReportLatencyNs, bool activate) { + SensorRegistrationInfo(int32_t handle, const String8& packageName, int64_t samplingRateNs, + int64_t maxReportLatencyNs, bool activate, status_t registerStatus) { mSensorHandle = handle; mPackageName = packageName; + mRegisteredStatus = registerStatus; mSamplingRateUs = static_cast<int64_t>(samplingRateNs/1000); mMaxReportLatencyUs = static_cast<int64_t>(maxReportLatencyNs/1000); @@ -60,28 +61,43 @@ public: return (info.mSensorHandle == INT32_MIN && info.mRealtimeSec == 0); } - // Dumpable interface - virtual std::string dump() const override { + std::string dump(SensorService* sensorService) const { struct tm* timeinfo = localtime(&mRealtimeSec); const int8_t hour = static_cast<int8_t>(timeinfo->tm_hour); const int8_t min = static_cast<int8_t>(timeinfo->tm_min); const int8_t sec = static_cast<int8_t>(timeinfo->tm_sec); std::ostringstream ss; - ss << std::setfill('0') << std::setw(2) << static_cast<int>(hour) << ":" - << std::setw(2) << static_cast<int>(min) << ":" - << std::setw(2) << static_cast<int>(sec) - << (mActivated ? " +" : " -") - << " 0x" << std::hex << std::setw(8) << mSensorHandle << std::dec - << std::setfill(' ') << " pid=" << std::setw(5) << mPid - << " uid=" << std::setw(5) << mUid << " package=" << mPackageName; - if (mActivated) { - ss << " samplingPeriod=" << mSamplingRateUs << "us" - << " batchingPeriod=" << mMaxReportLatencyUs << "us"; - }; + ss << std::setfill('0') << std::setw(2) << static_cast<int>(hour) << ":" << std::setw(2) + << static_cast<int>(min) << ":" << std::setw(2) << static_cast<int>(sec) + << (mActivated ? " +" : " -") << " 0x" << std::hex << std::setw(8) << mSensorHandle; + ss << std::dec << std::setfill(' ') << " pid=" << std::setw(5) << mPid + << " uid=" << std::setw(5) << mUid; + + std::string samplingRate = + mActivated ? std::to_string(mSamplingRateUs) : " N/A "; + std::string batchingInterval = + mActivated ? std::to_string(mMaxReportLatencyUs) : " N/A "; + ss << " samplingPeriod=" << std::setfill(' ') << std::setw(8) + << samplingRate << "us" << " batchingPeriod=" << std::setfill(' ') + << std::setw(8) << batchingInterval << "us" + << " result=" << statusToString(mRegisteredStatus) + << " (sensor, package): (" << std::setfill(' ') << std::left + << std::setw(27); + + if (sensorService != nullptr) { + ss << sensorService->getSensorName(mSensorHandle); + } else { + ss << "null"; + } + ss << ", " << mPackageName << ")"; + return ss.str(); } + // Dumpable interface + virtual std::string dump() const override { return dump(static_cast<SensorService*>(nullptr)); } + /** * Dump debugging information as android.service.SensorRegistrationInfoProto protobuf message * using ProtoOutputStream. @@ -110,6 +126,7 @@ private: int64_t mMaxReportLatencyUs; bool mActivated; time_t mRealtimeSec; + status_t mRegisteredStatus; }; } // namespace android; diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp index 31b7f8886c..060508ca25 100644 --- a/services/sensorservice/SensorService.cpp +++ b/services/sensorservice/SensorService.cpp @@ -682,14 +682,14 @@ status_t SensorService::dump(int fd, const Vector<String16>& args) { mSensorPrivacyPolicy->isSensorPrivacyEnabled() ? "enabled" : "disabled"); const auto& activeConnections = connLock.getActiveConnections(); - result.appendFormat("%zd active connections\n", activeConnections.size()); + result.appendFormat("%zd open event connections\n", activeConnections.size()); for (size_t i=0 ; i < activeConnections.size() ; i++) { result.appendFormat("Connection Number: %zu \n", i); activeConnections[i]->dump(result); } const auto& directConnections = connLock.getDirectConnections(); - result.appendFormat("%zd direct connections\n", directConnections.size()); + result.appendFormat("%zd open direct connections\n", directConnections.size()); for (size_t i = 0 ; i < directConnections.size() ; i++) { result.appendFormat("Direct connection %zu:\n", i); directConnections[i]->dump(result); @@ -708,7 +708,7 @@ status_t SensorService::dump(int fd, const Vector<String16>& args) { SENSOR_REGISTRATIONS_BUF_SIZE; continue; } - result.appendFormat("%s\n", reg_info.dump().c_str()); + result.appendFormat("%s\n", reg_info.dump(this).c_str()); currentIndex = (currentIndex - 1 + SENSOR_REGISTRATIONS_BUF_SIZE) % SENSOR_REGISTRATIONS_BUF_SIZE; } while(startIndex != currentIndex); @@ -1583,7 +1583,11 @@ sp<ISensorEventConnection> SensorService::createSensorEventConnection(const Stri // Only 4 modes supported for a SensorEventConnection ... NORMAL, DATA_INJECTION, // REPLAY_DATA_INJECTION and HAL_BYPASS_REPLAY_DATA_INJECTION if (requestedMode != NORMAL && !isInjectionMode(requestedMode)) { - return nullptr; + ALOGE( + "Failed to create sensor event connection: invalid request mode. " + "requestMode: %d", + requestedMode); + return nullptr; } resetTargetSdkVersionCache(opPackageName); @@ -1591,8 +1595,19 @@ sp<ISensorEventConnection> SensorService::createSensorEventConnection(const Stri // To create a client in DATA_INJECTION mode to inject data, SensorService should already be // operating in DI mode. if (requestedMode == DATA_INJECTION) { - if (mCurrentOperatingMode != DATA_INJECTION) return nullptr; - if (!isAllowListedPackage(packageName)) return nullptr; + if (mCurrentOperatingMode != DATA_INJECTION) { + ALOGE( + "Failed to create sensor event connection: sensor service not in " + "DI mode when creating a client in DATA_INJECTION mode"); + return nullptr; + } + if (!isAllowListedPackage(packageName)) { + ALOGE( + "Failed to create sensor event connection: package %s not in " + "allowed list for DATA_INJECTION mode", + packageName.c_str()); + return nullptr; + } } uid_t uid = IPCThreadState::self()->getCallingUid(); @@ -1729,7 +1744,10 @@ sp<ISensorEventConnection> SensorService::createSensorDirectConnection( ALOGE("SensorDevice::registerDirectChannel returns %d", channelHandle); } else { mem.handle = clone; - conn = new SensorDirectConnection(this, uid, &mem, channelHandle, opPackageName, deviceId); + IPCThreadState* thread = IPCThreadState::self(); + pid_t pid = (thread != nullptr) ? thread->getCallingPid() : -1; + conn = new SensorDirectConnection(this, uid, pid, &mem, channelHandle, opPackageName, + deviceId); } if (conn == nullptr) { @@ -2149,17 +2167,17 @@ status_t SensorService::enable(const sp<SensorEventConnection>& connection, sensor->getSensor().getRequiredAppOp() >= 0) { connection->mHandleToAppOp[handle] = sensor->getSensor().getRequiredAppOp(); } - - mLastNSensorRegistrations.editItemAt(mNextSensorRegIndex) = - SensorRegistrationInfo(handle, connection->getPackageName(), - samplingPeriodNs, maxBatchReportLatencyNs, true); - mNextSensorRegIndex = (mNextSensorRegIndex + 1) % SENSOR_REGISTRATIONS_BUF_SIZE; } if (err != NO_ERROR) { // batch/activate has failed, reset our state. cleanupWithoutDisableLocked(connection, handle); } + + mLastNSensorRegistrations.editItemAt(mNextSensorRegIndex) = + SensorRegistrationInfo(handle, connection->getPackageName(), samplingPeriodNs, + maxBatchReportLatencyNs, /*activate=*/ true, err); + mNextSensorRegIndex = (mNextSensorRegIndex + 1) % SENSOR_REGISTRATIONS_BUF_SIZE; return err; } @@ -2172,13 +2190,10 @@ status_t SensorService::disable(const sp<SensorEventConnection>& connection, int if (err == NO_ERROR) { std::shared_ptr<SensorInterface> sensor = getSensorInterfaceFromHandle(handle); err = sensor != nullptr ? sensor->activate(connection.get(), false) : status_t(BAD_VALUE); - - } - if (err == NO_ERROR) { - mLastNSensorRegistrations.editItemAt(mNextSensorRegIndex) = - SensorRegistrationInfo(handle, connection->getPackageName(), 0, 0, false); - mNextSensorRegIndex = (mNextSensorRegIndex + 1) % SENSOR_REGISTRATIONS_BUF_SIZE; } + mLastNSensorRegistrations.editItemAt(mNextSensorRegIndex) = + SensorRegistrationInfo(handle, connection->getPackageName(), 0, 0, /*activate=*/ false, err); + mNextSensorRegIndex = (mNextSensorRegIndex + 1) % SENSOR_REGISTRATIONS_BUF_SIZE; return err; } diff --git a/services/sensorservice/aidl/fuzzer/Android.bp b/services/sensorservice/aidl/fuzzer/Android.bp index f6f104ee88..b2dc89bfd0 100644 --- a/services/sensorservice/aidl/fuzzer/Android.bp +++ b/services/sensorservice/aidl/fuzzer/Android.bp @@ -26,6 +26,11 @@ cc_fuzz { "libfakeservicemanager", "libcutils", "liblog", + "libsensor_flags_c_lib", + ], + shared_libs: [ + "libaconfig_storage_read_api_cc", + "server_configurable_flags", ], srcs: [ "fuzzer.cpp", diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp index 1b6c598372..c2a9880d87 100644 --- a/services/surfaceflinger/Android.bp +++ b/services/surfaceflinger/Android.bp @@ -47,6 +47,7 @@ cc_defaults { "libtimestats_deps", "libsurfaceflinger_common_deps", "surfaceflinger_defaults", + "libsurfaceflinger_proto_deps", ], cflags: [ "-DLOG_TAG=\"SurfaceFlinger\"", @@ -85,7 +86,7 @@ cc_defaults { "libui", "libutils", "libSurfaceFlingerProp", - "libaconfig_storage_read_api_cc" + "libaconfig_storage_read_api_cc", ], static_libs: [ "iinputflinger_aidl_lib_static", @@ -93,7 +94,6 @@ cc_defaults { "libcompositionengine", "libframetimeline", "libgui_aidl_static", - "liblayers_proto", "libperfetto_client_experimental", "librenderengine", "libscheduler", @@ -187,6 +187,7 @@ filegroup { "FrameTracker.cpp", "HdrLayerInfoReporter.cpp", "HdrSdrRatioOverlay.cpp", + "Jank/JankTracker.cpp", "WindowInfosListenerInvoker.cpp", "Layer.cpp", "LayerFE.cpp", diff --git a/services/surfaceflinger/Client.cpp b/services/surfaceflinger/Client.cpp index 6b4215e2f4..abeb2a92eb 100644 --- a/services/surfaceflinger/Client.cpp +++ b/services/surfaceflinger/Client.cpp @@ -21,7 +21,7 @@ #include <private/android_filesystem_config.h> -#include <gui/AidlStatusUtil.h> +#include <gui/AidlUtil.h> #include <gui/SchedulingPolicy.h> #include "Client.h" diff --git a/services/surfaceflinger/ClientCache.cpp b/services/surfaceflinger/ClientCache.cpp index 09e41ffede..40ea8d387a 100644 --- a/services/surfaceflinger/ClientCache.cpp +++ b/services/surfaceflinger/ClientCache.cpp @@ -22,7 +22,7 @@ #include <cinttypes> #include <android-base/stringprintf.h> -#include <gui/TraceUtils.h> +#include <common/trace.h> #include <renderengine/impl/ExternalTexture.h> #include "ClientCache.h" diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp index d4f152d0c5..141a228b13 100644 --- a/services/surfaceflinger/CompositionEngine/Android.bp +++ b/services/surfaceflinger/CompositionEngine/Android.bp @@ -17,6 +17,7 @@ cc_defaults { "librenderengine_deps", "libtimestats_deps", "surfaceflinger_defaults", + "libsurfaceflinger_proto_deps", ], cflags: [ "-DLOG_TAG=\"CompositionEngine\"", @@ -41,7 +42,6 @@ cc_defaults { "libutils", ], static_libs: [ - "liblayers_proto", "libmath", "librenderengine", "libtimestats", @@ -147,6 +147,7 @@ cc_test { "tests/CompositionEngineTest.cpp", "tests/DisplayColorProfileTest.cpp", "tests/DisplayTest.cpp", + "tests/HwcAsyncWorkerTest.cpp", "tests/HwcBufferCacheTest.cpp", "tests/MockHWC2.cpp", "tests/MockHWComposer.cpp", diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h index 11759b855f..d1429a2ec6 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h @@ -35,6 +35,7 @@ #pragma clang diagnostic ignored "-Wextra" #include <gui/BufferQueue.h> +#include <ui/EdgeExtensionEffect.h> #include <ui/GraphicBuffer.h> #include <ui/GraphicTypes.h> #include <ui/StretchEffect.h> @@ -133,12 +134,16 @@ struct LayerFECompositionState { // The bounds of the layer in layer local coordinates FloatRect geomLayerBounds; + // The crop to apply to the layer in layer local coordinates + FloatRect geomLayerCrop; + ShadowSettings shadowSettings; // List of regions that require blur std::vector<BlurRegion> blurRegions; StretchEffect stretchEffect; + EdgeExtensionEffect edgeExtensionEffect; /* * Geometry state diff --git a/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp b/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp index bdaa1d0ae1..d9018bc3ab 100644 --- a/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp +++ b/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp @@ -37,7 +37,8 @@ inline bool equalIgnoringSource(const renderengine::LayerSettings& lhs, lhs.colorTransform == rhs.colorTransform && lhs.disableBlending == rhs.disableBlending && lhs.shadow == rhs.shadow && lhs.backgroundBlurRadius == rhs.backgroundBlurRadius && - lhs.stretchEffect == rhs.stretchEffect; + lhs.stretchEffect == rhs.stretchEffect && + lhs.edgeExtensionEffect == rhs.edgeExtensionEffect; } inline bool equalIgnoringBuffer(const renderengine::Buffer& lhs, const renderengine::Buffer& rhs) { diff --git a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp index 4c776879f0..5c5d0cd74d 100644 --- a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp +++ b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include <common/trace.h> #include <compositionengine/CompositionRefreshArgs.h> #include <compositionengine/LayerFE.h> #include <compositionengine/LayerFECompositionState.h> @@ -23,7 +24,6 @@ #include <ui/DisplayMap.h> #include <renderengine/RenderEngine.h> -#include <utils/Trace.h> // TODO(b/129481165): remove the #pragma below and fix conversion issues #pragma clang diagnostic push @@ -128,7 +128,7 @@ void offloadOutputs(Outputs& outputs) { } // namespace void CompositionEngine::present(CompositionRefreshArgs& args) { - ATRACE_CALL(); + SFTRACE_CALL(); ALOGV(__FUNCTION__); preComposition(args); @@ -155,7 +155,7 @@ void CompositionEngine::present(CompositionRefreshArgs& args) { } { - ATRACE_NAME("Waiting on HWC"); + SFTRACE_NAME("Waiting on HWC"); for (auto& future : presentFutures) { // TODO(b/185536303): Call ftl::Future::wait() once it exists, since // we do not need the return value of get(). @@ -177,7 +177,7 @@ void CompositionEngine::updateCursorAsync(CompositionRefreshArgs& args) { } void CompositionEngine::preComposition(CompositionRefreshArgs& args) { - ATRACE_CALL(); + SFTRACE_CALL(); ALOGV(__FUNCTION__); bool needsAnotherUpdate = false; @@ -199,7 +199,7 @@ void CompositionEngine::preComposition(CompositionRefreshArgs& args) { // promises for buffer releases are fulfilled at the end of composition. void CompositionEngine::postComposition(CompositionRefreshArgs& args) { if (FlagManager::getInstance().ce_fence_promise()) { - ATRACE_CALL(); + SFTRACE_CALL(); ALOGV(__FUNCTION__); for (auto& layerFE : args.layers) { diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp index c1617d787b..77b1940e23 100644 --- a/services/surfaceflinger/CompositionEngine/src/Display.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp @@ -15,6 +15,7 @@ */ #include <android-base/stringprintf.h> +#include <common/trace.h> #include <compositionengine/CompositionEngine.h> #include <compositionengine/CompositionRefreshArgs.h> #include <compositionengine/DisplayCreationArgs.h> @@ -25,9 +26,6 @@ #include <compositionengine/impl/DumpHelpers.h> #include <compositionengine/impl/OutputLayer.h> #include <compositionengine/impl/RenderSurface.h> -#include <gui/TraceUtils.h> - -#include <utils/Trace.h> // TODO(b/129481165): remove the #pragma below and fix conversion issues #pragma clang diagnostic push @@ -235,7 +233,7 @@ void Display::beginFrame() { bool Display::chooseCompositionStrategy( std::optional<android::HWComposer::DeviceRequestedChanges>* outChanges) { - ATRACE_FORMAT("%s for %s", __func__, getNamePlusId().c_str()); + SFTRACE_FORMAT("%s for %s", __func__, getNamePlusId().c_str()); ALOGV(__FUNCTION__); if (mIsDisconnected) { diff --git a/services/surfaceflinger/CompositionEngine/src/HwcAsyncWorker.cpp b/services/surfaceflinger/CompositionEngine/src/HwcAsyncWorker.cpp index 6086f0be3f..91385b4ff3 100644 --- a/services/surfaceflinger/CompositionEngine/src/HwcAsyncWorker.cpp +++ b/services/surfaceflinger/CompositionEngine/src/HwcAsyncWorker.cpp @@ -24,6 +24,7 @@ #include <android-base/thread_annotations.h> #include <cutils/sched_policy.h> +#include <ftl/fake_guard.h> namespace android::compositionengine::impl { @@ -60,7 +61,7 @@ void HwcAsyncWorker::run() { std::unique_lock<std::mutex> lock(mMutex); android::base::ScopedLockAssertion assumeLock(mMutex); while (!mDone) { - mCv.wait(lock); + mCv.wait(lock, [this]() FTL_FAKE_GUARD(mMutex) { return mTaskRequested || mDone; }); if (mTaskRequested && mTask.valid()) { mTask(); mTaskRequested = false; diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp index b40aea4210..2d8f98f2a4 100644 --- a/services/surfaceflinger/CompositionEngine/src/Output.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp @@ -17,6 +17,7 @@ #include <SurfaceFlingerProperties.sysprop.h> #include <android-base/stringprintf.h> #include <common/FlagManager.h> +#include <common/trace.h> #include <compositionengine/CompositionEngine.h> #include <compositionengine/CompositionRefreshArgs.h> #include <compositionengine/DisplayColorProfile.h> @@ -31,7 +32,6 @@ #include <compositionengine/impl/planner/Planner.h> #include <ftl/algorithm.h> #include <ftl/future.h> -#include <gui/TraceUtils.h> #include <scheduler/FrameTargeter.h> #include <scheduler/Time.h> @@ -53,7 +53,6 @@ #include <android-base/properties.h> #include <ui/DebugUtils.h> #include <ui/HdrCapabilities.h> -#include <utils/Trace.h> #include "TracedOrdinal.h" @@ -424,7 +423,7 @@ void Output::setReleasedLayers(Output::ReleasedLayers&& layers) { void Output::prepare(const compositionengine::CompositionRefreshArgs& refreshArgs, LayerFESet& geomSnapshots) { - ATRACE_CALL(); + SFTRACE_CALL(); ALOGV(__FUNCTION__); rebuildLayerStacks(refreshArgs, geomSnapshots); @@ -453,8 +452,8 @@ ftl::Future<std::monostate> Output::present( }) .value(); }; - ATRACE_FORMAT("%s for %s%s", __func__, mNamePlusId.c_str(), - stringifyExpectedPresentTime().c_str()); + SFTRACE_FORMAT("%s for %s%s", __func__, mNamePlusId.c_str(), + stringifyExpectedPresentTime().c_str()); ALOGV(__FUNCTION__); updateColorProfile(refreshArgs); @@ -518,7 +517,7 @@ void Output::rebuildLayerStacks(const compositionengine::CompositionRefreshArgs& if (!outputState.isEnabled || CC_LIKELY(!refreshArgs.updatingOutputGeometryThisFrame)) { return; } - ATRACE_CALL(); + SFTRACE_CALL(); ALOGV(__FUNCTION__); // Process the layers to determine visibility and coverage @@ -804,7 +803,7 @@ void Output::setReleasedLayers(const compositionengine::CompositionRefreshArgs&) } void Output::updateCompositionState(const compositionengine::CompositionRefreshArgs& refreshArgs) { - ATRACE_CALL(); + SFTRACE_CALL(); ALOGV(__FUNCTION__); if (!getState().isEnabled) { @@ -831,14 +830,14 @@ void Output::planComposition() { return; } - ATRACE_CALL(); + SFTRACE_CALL(); ALOGV(__FUNCTION__); mPlanner->plan(getOutputLayersOrderedByZ()); } void Output::writeCompositionState(const compositionengine::CompositionRefreshArgs& refreshArgs) { - ATRACE_CALL(); + SFTRACE_CALL(); ALOGV(__FUNCTION__); if (!getState().isEnabled) { @@ -1081,7 +1080,7 @@ void Output::beginFrame() { } void Output::prepareFrame() { - ATRACE_CALL(); + SFTRACE_CALL(); ALOGV(__FUNCTION__); auto& outputState = editState(); @@ -1102,10 +1101,10 @@ void Output::prepareFrame() { } ftl::Future<std::monostate> Output::presentFrameAndReleaseLayersAsync(bool flushEvenWhenDisabled) { - return ftl::Future<bool>(std::move(mHwComposerAsyncWorker->send([this, flushEvenWhenDisabled]() { + return ftl::Future<bool>(mHwComposerAsyncWorker->send([this, flushEvenWhenDisabled]() { presentFrameAndReleaseLayers(flushEvenWhenDisabled); return true; - }))) + })) .then([](bool) { return std::monostate{}; }); } @@ -1116,7 +1115,7 @@ std::future<bool> Output::chooseCompositionStrategyAsync( } GpuCompositionResult Output::prepareFrameAsync() { - ATRACE_CALL(); + SFTRACE_CALL(); ALOGV(__FUNCTION__); auto& state = editState(); const auto& previousChanges = state.previousDeviceRequestedChanges; @@ -1146,7 +1145,7 @@ GpuCompositionResult Output::prepareFrameAsync() { state.strategyPrediction = predictionSucceeded ? CompositionStrategyPredictionState::SUCCESS : CompositionStrategyPredictionState::FAIL; if (!predictionSucceeded) { - ATRACE_NAME("CompositionStrategyPredictionMiss"); + SFTRACE_NAME("CompositionStrategyPredictionMiss"); resetCompositionStrategy(); if (chooseCompositionSuccess) { applyCompositionStrategy(changes); @@ -1155,7 +1154,7 @@ GpuCompositionResult Output::prepareFrameAsync() { // Track the dequeued buffer to reuse so we don't need to dequeue another one. compositionResult.buffer = buffer; } else { - ATRACE_NAME("CompositionStrategyPredictionHit"); + SFTRACE_NAME("CompositionStrategyPredictionHit"); } state.previousDeviceRequestedChanges = std::move(changes); state.previousDeviceRequestedSuccess = chooseCompositionSuccess; @@ -1187,7 +1186,7 @@ void Output::devOptRepaintFlash(const compositionengine::CompositionRefreshArgs& } void Output::finishFrame(GpuCompositionResult&& result) { - ATRACE_CALL(); + SFTRACE_CALL(); ALOGV(__FUNCTION__); const auto& outputState = getState(); if (!outputState.isEnabled) { @@ -1276,7 +1275,7 @@ bool Output::dequeueRenderBuffer(base::unique_fd* bufferFence, std::optional<base::unique_fd> Output::composeSurfaces( const Region& debugRegion, std::shared_ptr<renderengine::ExternalTexture> tex, base::unique_fd& fd) { - ATRACE_CALL(); + SFTRACE_CALL(); ALOGV(__FUNCTION__); const auto& outputState = getState(); @@ -1317,13 +1316,13 @@ std::optional<base::unique_fd> Output::composeSurfaces( if (mClientCompositionRequestCache->exists(tex->getBuffer()->getId(), clientCompositionDisplay, clientCompositionLayers)) { - ATRACE_NAME("ClientCompositionCacheHit"); + SFTRACE_NAME("ClientCompositionCacheHit"); outputCompositionState.reusedClientComposition = true; setExpensiveRenderingExpected(false); // b/239944175 pass the fence associated with the buffer. return base::unique_fd(std::move(fd)); } - ATRACE_NAME("ClientCompositionCacheMiss"); + SFTRACE_NAME("ClientCompositionCacheMiss"); mClientCompositionRequestCache->add(tex->getBuffer()->getId(), clientCompositionDisplay, clientCompositionLayers); } @@ -1570,7 +1569,7 @@ bool Output::isPowerHintSessionGpuReportingEnabled() { } void Output::presentFrameAndReleaseLayers(bool flushEvenWhenDisabled) { - ATRACE_FORMAT("%s for %s", __func__, mNamePlusId.c_str()); + SFTRACE_FORMAT("%s for %s", __func__, mNamePlusId.c_str()); ALOGV(__FUNCTION__); if (!getState().isEnabled) { diff --git a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp index c0b23d97d4..d6028bf4cf 100644 --- a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp +++ b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp @@ -18,6 +18,7 @@ #include <android-base/stringprintf.h> #include <android/native_window.h> +#include <common/trace.h> #include <compositionengine/CompositionEngine.h> #include <compositionengine/Display.h> #include <compositionengine/DisplaySurface.h> @@ -32,7 +33,6 @@ #include <system/window.h> #include <ui/GraphicBuffer.h> #include <ui/Rect.h> -#include <utils/Trace.h> // TODO(b/129481165): remove the #pragma below and fix conversion issues #pragma clang diagnostic push @@ -149,7 +149,7 @@ void RenderSurface::prepareFrame(bool usesClientComposition, bool usesDeviceComp std::shared_ptr<renderengine::ExternalTexture> RenderSurface::dequeueBuffer( base::unique_fd* bufferFence) { - ATRACE_CALL(); + SFTRACE_CALL(); int fd = -1; ANativeWindowBuffer* buffer = nullptr; diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp index ea9442da06..409a206ace 100644 --- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp +++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp @@ -21,6 +21,7 @@ #include <android-base/properties.h> #include <android-base/stringprintf.h> +#include <common/trace.h> #include <compositionengine/impl/OutputCompositionState.h> #include <compositionengine/impl/planner/CachedSet.h> #include <math/HashCombine.h> @@ -28,7 +29,6 @@ #include <renderengine/RenderEngine.h> #include <ui/DebugUtils.h> #include <ui/HdrRenderTypeUtils.h> -#include <utils/Trace.h> namespace android::compositionengine::impl::planner { @@ -160,7 +160,7 @@ void CachedSet::updateAge(std::chrono::steady_clock::time_point now) { void CachedSet::render(renderengine::RenderEngine& renderEngine, TexturePool& texturePool, const OutputCompositionState& outputState, bool deviceHandlesColorTransform) { - ATRACE_CALL(); + SFTRACE_CALL(); if (outputState.powerCallback) { outputState.powerCallback->notifyCpuLoadUp(); } diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp index 4bafed2c8e..783209c26a 100644 --- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp +++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp @@ -21,11 +21,10 @@ #include <android-base/properties.h> #include <common/FlagManager.h> +#include <common/trace.h> #include <compositionengine/impl/planner/Flattener.h> #include <compositionengine/impl/planner/LayerState.h> -#include <gui/TraceUtils.h> - using time_point = std::chrono::steady_clock::time_point; using namespace std::chrono_literals; @@ -77,7 +76,7 @@ Flattener::Flattener(renderengine::RenderEngine& renderEngine, const Tunables& t NonBufferHash Flattener::flattenLayers(const std::vector<const LayerState*>& layers, NonBufferHash hash, time_point now) { - ATRACE_CALL(); + SFTRACE_CALL(); const size_t unflattenedDisplayCost = calculateDisplayCost(layers); mUnflattenedDisplayCost += unflattenedDisplayCost; @@ -113,7 +112,7 @@ void Flattener::renderCachedSets( const OutputCompositionState& outputState, std::optional<std::chrono::steady_clock::time_point> renderDeadline, bool deviceHandlesColorTransform) { - ATRACE_CALL(); + SFTRACE_CALL(); if (!mNewCachedSet) { return; @@ -121,7 +120,7 @@ void Flattener::renderCachedSets( // Ensure that a cached set has a valid buffer first if (mNewCachedSet->hasRenderedBuffer()) { - ATRACE_NAME("mNewCachedSet->hasRenderedBuffer()"); + SFTRACE_NAME("mNewCachedSet->hasRenderedBuffer()"); return; } @@ -138,13 +137,13 @@ void Flattener::renderCachedSets( if (mNewCachedSet->getSkipCount() <= mTunables.mRenderScheduling->maxDeferRenderAttempts) { - ATRACE_FORMAT("DeadlinePassed: exceeded deadline by: %d us", - std::chrono::duration_cast<std::chrono::microseconds>( - estimatedRenderFinish - *renderDeadline) - .count()); + SFTRACE_FORMAT("DeadlinePassed: exceeded deadline by: %d us", + std::chrono::duration_cast<std::chrono::microseconds>( + estimatedRenderFinish - *renderDeadline) + .count()); return; } else { - ATRACE_NAME("DeadlinePassed: exceeded max skips"); + SFTRACE_NAME("DeadlinePassed: exceeded max skips"); } } } @@ -271,7 +270,7 @@ NonBufferHash Flattener::computeLayersHash() const{ // was already populated with these layers, i.e. on the second and following // calls with the same geometry. bool Flattener::mergeWithCachedSets(const std::vector<const LayerState*>& layers, time_point now) { - ATRACE_CALL(); + SFTRACE_CALL(); std::vector<CachedSet> merged; if (mLayers.empty()) { @@ -415,7 +414,7 @@ bool Flattener::mergeWithCachedSets(const std::vector<const LayerState*>& layers } std::vector<Flattener::Run> Flattener::findCandidateRuns(time_point now) const { - ATRACE_CALL(); + SFTRACE_CALL(); std::vector<Run> runs; bool isPartOfRun = false; Run::Builder builder; @@ -431,8 +430,8 @@ std::vector<Flattener::Run> Flattener::findCandidateRuns(time_point now) const { if (!layerIsInactive && currentSet->getLayerCount() == kNumLayersFpsConsideration) { auto layerFps = currentSet->getFirstLayer().getState()->getFps(); if (layerFps > 0 && layerFps <= kFpsActiveThreshold) { - ATRACE_FORMAT("layer is considered inactive due to low FPS [%s] %f", - currentSet->getFirstLayer().getName().c_str(), layerFps); + SFTRACE_FORMAT("layer is considered inactive due to low FPS [%s] %f", + currentSet->getFirstLayer().getName().c_str(), layerFps); layerIsInactive = true; } } @@ -494,7 +493,7 @@ std::optional<Flattener::Run> Flattener::findBestRun(std::vector<Flattener::Run> } void Flattener::buildCachedSets(time_point now) { - ATRACE_CALL(); + SFTRACE_CALL(); if (mLayers.empty()) { ALOGV("[%s] No layers found, returning", __func__); return; @@ -508,7 +507,7 @@ void Flattener::buildCachedSets(time_point now) { for (const CachedSet& layer : mLayers) { // TODO (b/191997217): make it less aggressive, and sync with findCandidateRuns if (layer.hasProtectedLayers()) { - ATRACE_NAME("layer->hasProtectedLayers()"); + SFTRACE_NAME("layer->hasProtectedLayers()"); return; } } diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp index 5e6cade56f..d114ff79a1 100644 --- a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp +++ b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp @@ -21,11 +21,11 @@ #define ATRACE_TAG ATRACE_TAG_GRAPHICS #include <android-base/properties.h> +#include <common/trace.h> #include <compositionengine/LayerFECompositionState.h> #include <compositionengine/impl/OutputLayerCompositionState.h> #include <compositionengine/impl/planner/Planner.h> -#include <utils/Trace.h> #include <chrono> namespace android::compositionengine::impl::planner { @@ -83,7 +83,7 @@ void Planner::setDisplaySize(ui::Size size) { void Planner::plan( compositionengine::Output::OutputLayersEnumerator<compositionengine::Output>&& layers) { - ATRACE_CALL(); + SFTRACE_CALL(); std::unordered_set<LayerId> removedLayers; removedLayers.reserve(mPreviousLayers.size()); @@ -165,7 +165,7 @@ void Planner::plan( void Planner::reportFinalPlan( compositionengine::Output::OutputLayersEnumerator<compositionengine::Output>&& layers) { - ATRACE_CALL(); + SFTRACE_CALL(); if (!mPredictorEnabled) { return; } @@ -204,7 +204,7 @@ void Planner::reportFinalPlan( void Planner::renderCachedSets(const OutputCompositionState& outputState, std::optional<std::chrono::steady_clock::time_point> renderDeadline, bool deviceHandlesColorTransform) { - ATRACE_CALL(); + SFTRACE_CALL(); mFlattener.renderCachedSets(outputState, renderDeadline, deviceHandlesColorTransform); } diff --git a/services/surfaceflinger/CompositionEngine/tests/HwcAsyncWorkerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/HwcAsyncWorkerTest.cpp new file mode 100644 index 0000000000..dd04df6d6b --- /dev/null +++ b/services/surfaceflinger/CompositionEngine/tests/HwcAsyncWorkerTest.cpp @@ -0,0 +1,71 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <future> + +#include <compositionengine/impl/HwcAsyncWorker.h> +#include <gtest/gtest.h> + +namespace android::compositionengine { +namespace { + +using namespace std::chrono_literals; + +// For the edge case tests below, how much real time should be spent trying to reproduce edge cases +// problems in a loop. +// +// Larger values mean problems are more likely to be detected, at the cost of making the unit test +// run slower. +// +// As we expect the tests to be run continuously, even a short loop will eventually catch +// problems, though not necessarily from changes in the same build that introduce them. +constexpr auto kWallTimeForEdgeCaseTests = 5ms; + +TEST(HwcAsyncWorker, continuousTasksEdgeCase) { + // Ensures that a single worker that is given multiple tasks in short succession will run them. + + impl::HwcAsyncWorker worker; + const auto endTime = std::chrono::steady_clock::now() + kWallTimeForEdgeCaseTests; + while (std::chrono::steady_clock::now() < endTime) { + auto f1 = worker.send([] { return false; }); + EXPECT_FALSE(f1.get()); + auto f2 = worker.send([] { return true; }); + EXPECT_TRUE(f2.get()); + } +} + +TEST(HwcAsyncWorker, constructAndDestroyEdgeCase) { + // Ensures that newly created HwcAsyncWorkers can be immediately destroyed. + + const auto endTime = std::chrono::steady_clock::now() + kWallTimeForEdgeCaseTests; + while (std::chrono::steady_clock::now() < endTime) { + impl::HwcAsyncWorker worker; + } +} + +TEST(HwcAsyncWorker, newlyCreatedRunsTasksEdgeCase) { + // Ensures that newly created HwcAsyncWorkers will run a task if given one immediately. + + const auto endTime = std::chrono::steady_clock::now() + kWallTimeForEdgeCaseTests; + while (std::chrono::steady_clock::now() < endTime) { + impl::HwcAsyncWorker worker; + auto f = worker.send([] { return true; }); + f.get(); + } +} + +} // namespace +} // namespace android::compositionengine diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h index b0b1a02164..eb6e677117 100644 --- a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h +++ b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h @@ -33,6 +33,7 @@ #include "DisplayHardware/HWC2.h" #include <aidl/android/hardware/graphics/composer3/Composition.h> +#include <aidl/android/hardware/graphics/composer3/Lut.h> // TODO(b/129481165): remove the #pragma below and fix conversion issues #pragma clang diagnostic pop // ignored "-Wconversion -Wextra" @@ -77,6 +78,7 @@ public: Error(const std::string&, bool, const std::vector<uint8_t>&)); MOCK_METHOD1(setBrightness, Error(float)); MOCK_METHOD1(setBlockingRegion, Error(const android::Region&)); + MOCK_METHOD(Error, setLuts, (std::vector<aidl::android::hardware::graphics::composer3::Lut>&)); }; } // namespace mock diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h index 629d9f23ff..e910c72e2e 100644 --- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h +++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h @@ -51,7 +51,8 @@ public: MOCK_CONST_METHOD0(getMaxVirtualDisplayCount, size_t()); MOCK_CONST_METHOD0(getMaxVirtualDisplayDimension, size_t()); MOCK_METHOD3(allocateVirtualDisplay, bool(HalVirtualDisplayId, ui::Size, ui::PixelFormat*)); - MOCK_METHOD2(allocatePhysicalDisplay, void(hal::HWDisplayId, PhysicalDisplayId)); + MOCK_METHOD3(allocatePhysicalDisplay, + void(hal::HWDisplayId, PhysicalDisplayId, std::optional<ui::Size>)); MOCK_METHOD1(createLayer, std::shared_ptr<HWC2::Layer>(HalDisplayId)); MOCK_METHOD(status_t, getDeviceCompositionChanges, @@ -151,6 +152,10 @@ public: getOverlaySupport, (), (const, override)); MOCK_METHOD(status_t, setRefreshRateChangedCallbackDebugEnabled, (PhysicalDisplayId, bool)); MOCK_METHOD(status_t, notifyExpectedPresent, (PhysicalDisplayId, TimePoint, Fps)); + MOCK_METHOD(status_t, getRequestedLuts, + (PhysicalDisplayId, + std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*), + (override)); }; } // namespace mock diff --git a/services/surfaceflinger/Display/DisplayModeController.cpp b/services/surfaceflinger/Display/DisplayModeController.cpp index a6a9bec3c3..0e9218cb93 100644 --- a/services/surfaceflinger/Display/DisplayModeController.cpp +++ b/services/surfaceflinger/Display/DisplayModeController.cpp @@ -22,7 +22,9 @@ #include "Display/DisplaySnapshot.h" #include "DisplayHardware/HWComposer.h" +#include <android-base/properties.h> #include <common/FlagManager.h> +#include <common/trace.h> #include <ftl/concat.h> #include <ftl/expected.h> #include <log/log.h> @@ -83,7 +85,7 @@ auto DisplayModeController::setDesiredMode(PhysicalDisplayId displayId, FTL_EXPECT(mDisplays.get(displayId).ok_or(DesiredModeAction::None)).get(); { - ATRACE_NAME(displayPtr->concatId(__func__).c_str()); + SFTRACE_NAME(displayPtr->concatId(__func__).c_str()); ALOGD("%s %s", displayPtr->concatId(__func__).c_str(), to_string(desiredMode).c_str()); std::scoped_lock lock(displayPtr->desiredModeLock); @@ -204,7 +206,7 @@ bool DisplayModeController::initiateModeChange(PhysicalDisplayId displayId, return false; } - ATRACE_INT(displayPtr->pendingModeFpsTrace.c_str(), mode.getVsyncRate().getIntValue()); + SFTRACE_INT(displayPtr->pendingModeFpsTrace.c_str(), mode.getVsyncRate().getIntValue()); return true; } @@ -227,8 +229,8 @@ void DisplayModeController::setActiveModeLocked(PhysicalDisplayId displayId, Dis Fps vsyncRate, Fps renderFps) { const auto& displayPtr = FTL_TRY(mDisplays.get(displayId).ok_or(ftl::Unit())).get(); - ATRACE_INT(displayPtr->activeModeFpsTrace.c_str(), vsyncRate.getIntValue()); - ATRACE_INT(displayPtr->renderRateFpsTrace.c_str(), renderFps.getIntValue()); + SFTRACE_INT(displayPtr->activeModeFpsTrace.c_str(), vsyncRate.getIntValue()); + SFTRACE_INT(displayPtr->renderRateFpsTrace.c_str(), renderFps.getIntValue()); displayPtr->selectorPtr->setActiveMode(modeId, renderFps); @@ -237,4 +239,63 @@ void DisplayModeController::setActiveModeLocked(PhysicalDisplayId displayId, Dis } } +void DisplayModeController::updateKernelIdleTimer(PhysicalDisplayId displayId) { + std::lock_guard lock(mDisplayLock); + const auto& displayPtr = FTL_TRY(mDisplays.get(displayId).ok_or(ftl::Unit())).get(); + + const auto controllerOpt = displayPtr->selectorPtr->kernelIdleTimerController(); + if (!controllerOpt) return; + + using KernelIdleTimerAction = scheduler::RefreshRateSelector::KernelIdleTimerAction; + + switch (displayPtr->selectorPtr->getIdleTimerAction()) { + case KernelIdleTimerAction::TurnOff: + if (displayPtr->isKernelIdleTimerEnabled) { + SFTRACE_INT("KernelIdleTimer", 0); + updateKernelIdleTimer(displayId, std::chrono::milliseconds::zero(), *controllerOpt); + displayPtr->isKernelIdleTimerEnabled = false; + } + break; + case KernelIdleTimerAction::TurnOn: + if (!displayPtr->isKernelIdleTimerEnabled) { + SFTRACE_INT("KernelIdleTimer", 1); + const auto timeout = displayPtr->selectorPtr->getIdleTimerTimeout(); + updateKernelIdleTimer(displayId, timeout, *controllerOpt); + displayPtr->isKernelIdleTimerEnabled = true; + } + break; + } +} + +void DisplayModeController::updateKernelIdleTimer(PhysicalDisplayId displayId, + std::chrono::milliseconds timeout, + KernelIdleTimerController controller) { + switch (controller) { + case KernelIdleTimerController::HwcApi: + mComposerPtr->setIdleTimerEnabled(displayId, timeout); + break; + + case KernelIdleTimerController::Sysprop: + using namespace std::string_literals; + base::SetProperty("graphics.display.kernel_idle_timer.enabled"s, + timeout > std::chrono::milliseconds::zero() ? "true"s : "false"s); + break; + } +} + +auto DisplayModeController::getKernelIdleTimerState(PhysicalDisplayId displayId) const + -> KernelIdleTimerState { + std::lock_guard lock(mDisplayLock); + const auto& displayPtr = + FTL_EXPECT(mDisplays.get(displayId).ok_or(KernelIdleTimerState())).get(); + + const auto desiredModeIdOpt = + (std::scoped_lock(displayPtr->desiredModeLock), displayPtr->desiredModeOpt) + .transform([](const display::DisplayModeRequest& request) { + return request.mode.modePtr->getId(); + }); + + return {desiredModeIdOpt, displayPtr->isKernelIdleTimerEnabled}; +} + } // namespace android::display diff --git a/services/surfaceflinger/Display/DisplayModeController.h b/services/surfaceflinger/Display/DisplayModeController.h index 258b04b876..9ec603d5f6 100644 --- a/services/surfaceflinger/Display/DisplayModeController.h +++ b/services/surfaceflinger/Display/DisplayModeController.h @@ -97,6 +97,17 @@ public: void setActiveMode(PhysicalDisplayId, DisplayModeId, Fps vsyncRate, Fps renderFps) EXCLUDES(mDisplayLock); + void updateKernelIdleTimer(PhysicalDisplayId) REQUIRES(kMainThreadContext) + EXCLUDES(mDisplayLock); + + struct KernelIdleTimerState { + std::optional<DisplayModeId> desiredModeIdOpt = std::nullopt; + bool isEnabled = false; + }; + + KernelIdleTimerState getKernelIdleTimerState(PhysicalDisplayId) const + REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock); + private: struct Display { template <size_t N> @@ -121,6 +132,8 @@ private: DisplayModeRequestOpt pendingModeOpt GUARDED_BY(kMainThreadContext); bool isModeSetPending GUARDED_BY(kMainThreadContext) = false; + + bool isKernelIdleTimerEnabled GUARDED_BY(kMainThreadContext) = false; }; using DisplayPtr = std::unique_ptr<Display>; @@ -128,6 +141,10 @@ private: void setActiveModeLocked(PhysicalDisplayId, DisplayModeId, Fps vsyncRate, Fps renderFps) REQUIRES(mDisplayLock); + using KernelIdleTimerController = scheduler::RefreshRateSelector::KernelIdleTimerController; + void updateKernelIdleTimer(PhysicalDisplayId, std::chrono::milliseconds timeout, + KernelIdleTimerController) REQUIRES(mDisplayLock); + // Set once when initializing the DisplayModeController, which the HWComposer must outlive. HWComposer* mComposerPtr = nullptr; diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp index 05f4da26be..402a3d2e2f 100644 --- a/services/surfaceflinger/DisplayDevice.cpp +++ b/services/surfaceflinger/DisplayDevice.cpp @@ -24,6 +24,7 @@ #define ATRACE_TAG ATRACE_TAG_GRAPHICS +#include <common/trace.h> #include <compositionengine/CompositionEngine.h> #include <compositionengine/Display.h> #include <compositionengine/DisplayColorProfile.h> @@ -200,19 +201,6 @@ bool DisplayDevice::isPoweredOn() const { return mPowerMode != hal::PowerMode::OFF; } -nsecs_t DisplayDevice::getVsyncPeriodFromHWC() const { - const auto physicalId = getPhysicalId(); - if (!mHwComposer.isConnected(physicalId)) { - return 0; - } - - if (const auto vsyncPeriodOpt = mHwComposer.getDisplayVsyncPeriod(physicalId).value_opt()) { - return *vsyncPeriodOpt; - } - - return refreshRateSelector().getActiveMode().modePtr->getVsyncRate().getPeriodNsecs(); -} - ui::Dataspace DisplayDevice::getCompositionDataSpace() const { return mCompositionDisplay->getState().dataspace; } @@ -398,7 +386,7 @@ void DisplayDevice::enableHdrSdrRatioOverlay(bool enable) { } void DisplayDevice::updateHdrSdrRatioOverlayRatio(float currentHdrSdrRatio) { - ATRACE_CALL(); + SFTRACE_CALL(); mHdrSdrRatio = currentHdrSdrRatio; if (mHdrSdrRatioOverlay) { mHdrSdrRatioOverlay->changeHdrSdrRatio(currentHdrSdrRatio); @@ -440,7 +428,7 @@ void DisplayDevice::enableRefreshRateOverlay(bool enable, bool setByHwc, Fps ref } void DisplayDevice::updateRefreshRateOverlayRate(Fps refreshRate, Fps renderFps, bool setByHwc) { - ATRACE_CALL(); + SFTRACE_CALL(); if (mRefreshRateOverlay) { if (!mRefreshRateOverlay->isSetByHwc() || setByHwc) { if (mRefreshRateSelector->isVrrDevice() && !mRefreshRateOverlay->isSetByHwc()) { diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h index 1b8a3a8f54..3e3f558cee 100644 --- a/services/surfaceflinger/DisplayDevice.h +++ b/services/surfaceflinger/DisplayDevice.h @@ -203,8 +203,6 @@ public: void updateHdrSdrRatioOverlayRatio(float currentHdrSdrRatio); bool isHdrSdrRatioOverlayEnabled() const { return mHdrSdrRatioOverlay != nullptr; } - nsecs_t getVsyncPeriodFromHWC() const; - Fps getAdjustedRefreshRate() const { return mAdjustedRefreshRate; } // Round the requested refresh rate to match a divisor of the pacesetter diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp index 362ab9c39e..66237b9d8a 100644 --- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp @@ -25,9 +25,8 @@ #include <android/binder_ibinder_platform.h> #include <android/binder_manager.h> #include <common/FlagManager.h> -#include <gui/TraceUtils.h> +#include <common/trace.h> #include <log/log.h> -#include <utils/Trace.h> #include <aidl/android/hardware/graphics/composer3/BnComposerCallback.h> @@ -45,6 +44,7 @@ using hardware::Return; using aidl::android::hardware::graphics::composer3::BnComposerCallback; using aidl::android::hardware::graphics::composer3::Capability; using aidl::android::hardware::graphics::composer3::ClientTargetPropertyWithBrightness; +using aidl::android::hardware::graphics::composer3::Lut; using aidl::android::hardware::graphics::composer3::PowerMode; using aidl::android::hardware::graphics::composer3::VirtualDisplay; @@ -60,6 +60,7 @@ using AidlDisplayCapability = aidl::android::hardware::graphics::composer3::Disp using AidlHdrCapabilities = aidl::android::hardware::graphics::composer3::HdrCapabilities; using AidlHdrConversionCapability = aidl::android::hardware::graphics::common::HdrConversionCapability; +using AidlHdcpLevels = aidl::android::hardware::drm::HdcpLevels; using AidlHdrConversionStrategy = aidl::android::hardware::graphics::common::HdrConversionStrategy; using AidlOverlayProperties = aidl::android::hardware::graphics::composer3::OverlayProperties; using AidlPerFrameMetadata = aidl::android::hardware::graphics::composer3::PerFrameMetadata; @@ -223,6 +224,12 @@ public: return ::ndk::ScopedAStatus::ok(); } + ::ndk::ScopedAStatus onHdcpLevelsChanged(int64_t in_display, + const AidlHdcpLevels& levels) override { + mCallback.onComposerHalHdcpLevelsChanged(translate<Display>(in_display), levels); + return ::ndk::ScopedAStatus::ok(); + } + private: HWC2::ComposerCallback& mCallback; }; @@ -677,7 +684,7 @@ Error AidlComposer::getReleaseFences(Display display, std::vector<Layer>* outLay Error AidlComposer::presentDisplay(Display display, int* outPresentFence) { const auto displayId = translate<int64_t>(display); - ATRACE_FORMAT("HwcPresentDisplay %" PRId64, displayId); + SFTRACE_FORMAT("HwcPresentDisplay %" PRId64, displayId); Error error = Error::NONE; mMutex.lock_shared(); @@ -810,7 +817,7 @@ Error AidlComposer::validateDisplay(Display display, nsecs_t expectedPresentTime int32_t frameIntervalNs, uint32_t* outNumTypes, uint32_t* outNumRequests) { const auto displayId = translate<int64_t>(display); - ATRACE_FORMAT("HwcValidateDisplay %" PRId64, displayId); + SFTRACE_FORMAT("HwcValidateDisplay %" PRId64, displayId); Error error = Error::NONE; mMutex.lock_shared(); @@ -840,7 +847,7 @@ Error AidlComposer::presentOrValidateDisplay(Display display, nsecs_t expectedPr uint32_t* outNumRequests, int* outPresentFence, uint32_t* state) { const auto displayId = translate<int64_t>(display); - ATRACE_FORMAT("HwcPresentOrValidateDisplay %" PRId64, displayId); + SFTRACE_FORMAT("HwcPresentOrValidateDisplay %" PRId64, displayId); Error error = Error::NONE; mMutex.lock_shared(); @@ -1540,6 +1547,30 @@ Error AidlComposer::getClientTargetProperty( return error; } +Error AidlComposer::getRequestedLuts(Display display, std::vector<DisplayLuts::LayerLut>* outLuts) { + Error error = Error::NONE; + mMutex.lock_shared(); + if (auto reader = getReader(display)) { + *outLuts = reader->get().takeDisplayLuts(translate<int64_t>(display)); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; +} + +Error AidlComposer::setLayerLuts(Display display, Layer layer, std::vector<Lut>& luts) { + Error error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().setLayerLuts(translate<int64_t>(display), translate<int64_t>(layer), luts); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; +} + Error AidlComposer::setLayerBrightness(Display display, Layer layer, float brightness) { Error error = Error::NONE; mMutex.lock_shared(); diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h index ea0e53a202..246223a668 100644 --- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h @@ -244,6 +244,13 @@ public: Error setRefreshRateChangedCallbackDebugEnabled(Display, bool) override; Error notifyExpectedPresent(Display, nsecs_t expectedPresentTime, int32_t frameIntervalNs) override; + Error getRequestedLuts( + Display display, + std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>* + outLuts) override; + Error setLayerLuts( + Display display, Layer layer, + std::vector<aidl::android::hardware::graphics::composer3::Lut>& luts) override; private: // Many public functions above simply write a command into the command diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h index bc067a0e5d..7db9a94e54 100644 --- a/services/surfaceflinger/DisplayHardware/ComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h @@ -40,7 +40,9 @@ #include <aidl/android/hardware/graphics/composer3/Composition.h> #include <aidl/android/hardware/graphics/composer3/DisplayCapability.h> #include <aidl/android/hardware/graphics/composer3/DisplayConfiguration.h> +#include <aidl/android/hardware/graphics/composer3/DisplayLuts.h> #include <aidl/android/hardware/graphics/composer3/IComposerCallback.h> +#include <aidl/android/hardware/graphics/composer3/Lut.h> #include <aidl/android/hardware/graphics/composer3/OverlayProperties.h> #include <aidl/android/hardware/graphics/common/Transform.h> @@ -303,6 +305,9 @@ public: virtual Error setRefreshRateChangedCallbackDebugEnabled(Display, bool) = 0; virtual Error notifyExpectedPresent(Display, nsecs_t expectedPresentTime, int32_t frameIntervalNs) = 0; + virtual Error getRequestedLuts(Display display, + std::vector<V3_0::DisplayLuts::LayerLut>* outLuts) = 0; + virtual Error setLayerLuts(Display display, Layer layer, std::vector<V3_0::Lut>& luts) = 0; }; } // namespace Hwc2 diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp index c77cdd4432..748765a4e4 100644 --- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp +++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp @@ -31,10 +31,11 @@ #include <utils/String8.h> #include <log/log.h> -#include <hardware/hardware.h> +#include <com_android_graphics_libgui_flags.h> #include <gui/BufferItem.h> #include <gui/BufferQueue.h> #include <gui/Surface.h> +#include <hardware/hardware.h> #include <ui/DebugUtils.h> #include <ui/GraphicBuffer.h> @@ -48,10 +49,18 @@ namespace android { using ui::Dataspace; +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) +FramebufferSurface::FramebufferSurface(HWComposer& hwc, PhysicalDisplayId displayId, + const sp<IGraphicBufferProducer>& producer, + const sp<IGraphicBufferConsumer>& consumer, + const ui::Size& size, const ui::Size& maxSize) + : ConsumerBase(producer, consumer), +#else FramebufferSurface::FramebufferSurface(HWComposer& hwc, PhysicalDisplayId displayId, const sp<IGraphicBufferConsumer>& consumer, const ui::Size& size, const ui::Size& maxSize) : ConsumerBase(consumer), +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) mDisplayId(displayId), mMaxSize(maxSize), mCurrentBufferSlot(-1), diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h index 2728cf637e..6ca64a2437 100644 --- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h +++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h @@ -20,6 +20,7 @@ #include <stdint.h> #include <sys/types.h> +#include <com_android_graphics_libgui_flags.h> #include <compositionengine/DisplaySurface.h> #include <gui/BufferQueue.h> #include <gui/ConsumerBase.h> @@ -40,9 +41,16 @@ class HWComposer; class FramebufferSurface : public ConsumerBase, public compositionengine::DisplaySurface { public: +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) FramebufferSurface(HWComposer& hwc, PhysicalDisplayId displayId, + const sp<IGraphicBufferProducer>& producer, const sp<IGraphicBufferConsumer>& consumer, const ui::Size& size, const ui::Size& maxSize); +#else + FramebufferSurface(HWComposer& hwc, PhysicalDisplayId displayId, + const sp<IGraphicBufferConsumer>& consumer, const ui::Size& size, + const ui::Size& maxSize); +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) virtual status_t beginFrame(bool mustRecompose); virtual status_t prepareFrame(CompositionType compositionType); diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp index 8c0f81e051..f1fa9389eb 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.cpp +++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp @@ -41,6 +41,8 @@ using aidl::android::hardware::graphics::composer3::Color; using aidl::android::hardware::graphics::composer3::Composition; using AidlCapability = aidl::android::hardware::graphics::composer3::Capability; using aidl::android::hardware::graphics::composer3::DisplayCapability; +using aidl::android::hardware::graphics::composer3::DisplayLuts; +using aidl::android::hardware::graphics::composer3::Lut; using aidl::android::hardware::graphics::composer3::OverlayProperties; namespace android { @@ -607,6 +609,19 @@ Error Display::getClientTargetProperty( return static_cast<Error>(error); } +Error Display::getRequestedLuts(std::vector<DisplayLuts::LayerLut>* outLayerLuts) { + std::vector<DisplayLuts::LayerLut> tmpLayerLuts; + const auto error = mComposer.getRequestedLuts(mId, &tmpLayerLuts); + for (DisplayLuts::LayerLut& layerLut : tmpLayerLuts) { + if (layerLut.lut.pfd.get() >= 0) { + outLayerLuts->push_back({layerLut.layer, + Lut{ndk::ScopedFileDescriptor(layerLut.lut.pfd.release()), + layerLut.lut.lutProperties}}); + } + } + return static_cast<Error>(error); +} + Error Display::getDisplayDecorationSupport( std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>* support) { @@ -660,6 +675,11 @@ void Display::loadDisplayCapabilities() { } }); } + +void Display::setPhysicalSizeInMm(std::optional<ui::Size> size) { + mPhysicalSize = size; +} + } // namespace impl // Layer methods @@ -1037,6 +1057,14 @@ Error Layer::setBlockingRegion(const Region& region) { return static_cast<Error>(intError); } +Error Layer::setLuts(std::vector<Lut>& luts) { + if (CC_UNLIKELY(!mDisplay)) { + return Error::BAD_DISPLAY; + } + const auto intError = mComposer.setLayerLuts(mDisplay->getId(), mId, luts); + return static_cast<Error>(intError); +} + } // namespace impl } // namespace HWC2 } // namespace android diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h index 5b948318ae..8e2aeaf234 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.h +++ b/services/surfaceflinger/DisplayHardware/HWC2.h @@ -45,6 +45,7 @@ #include <aidl/android/hardware/graphics/composer3/Color.h> #include <aidl/android/hardware/graphics/composer3/Composition.h> #include <aidl/android/hardware/graphics/composer3/DisplayCapability.h> +#include <aidl/android/hardware/graphics/composer3/Lut.h> #include <aidl/android/hardware/graphics/composer3/OverlayProperties.h> #include <aidl/android/hardware/graphics/composer3/RefreshRateChangedDebugData.h> @@ -66,6 +67,7 @@ class Layer; namespace hal = android::hardware::graphics::composer::hal; +using aidl::android::hardware::drm::HdcpLevels; using aidl::android::hardware::graphics::common::DisplayHotplugEvent; using aidl::android::hardware::graphics::composer3::RefreshRateChangedDebugData; @@ -84,6 +86,7 @@ struct ComposerCallback { virtual void onComposerHalSeamlessPossible(hal::HWDisplayId) = 0; virtual void onComposerHalVsyncIdle(hal::HWDisplayId) = 0; virtual void onRefreshRateChangedDebug(const RefreshRateChangedDebugData&) = 0; + virtual void onComposerHalHdcpLevelsChanged(hal::HWDisplayId, const HdcpLevels& levels) = 0; protected: ~ComposerCallback() = default; @@ -102,6 +105,7 @@ public: virtual bool isVsyncPeriodSwitchSupported() const = 0; virtual bool hasDisplayIdleTimerCapability() const = 0; virtual void onLayerDestroyed(hal::HWLayerId layerId) = 0; + virtual std::optional<ui::Size> getPhysicalSizeInMm() const = 0; [[nodiscard]] virtual hal::Error acceptChanges() = 0; [[nodiscard]] virtual base::expected<std::shared_ptr<HWC2::Layer>, hal::Error> @@ -178,6 +182,9 @@ public: [[nodiscard]] virtual hal::Error getClientTargetProperty( aidl::android::hardware::graphics::composer3::ClientTargetPropertyWithBrightness* outClientTargetProperty) = 0; + [[nodiscard]] virtual hal::Error getRequestedLuts( + std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>* + outLuts) = 0; [[nodiscard]] virtual hal::Error getDisplayDecorationSupport( std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>* support) = 0; @@ -261,6 +268,9 @@ public: hal::Error getClientTargetProperty( aidl::android::hardware::graphics::composer3::ClientTargetPropertyWithBrightness* outClientTargetProperty) override; + hal::Error getRequestedLuts( + std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>* + outLuts) override; hal::Error getDisplayDecorationSupport( std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>* support) override; @@ -276,6 +286,8 @@ public: bool hasDisplayIdleTimerCapability() const override; void onLayerDestroyed(hal::HWLayerId layerId) override; hal::Error getPhysicalDisplayOrientation(Hwc2::AidlTransform* outTransform) const override; + void setPhysicalSizeInMm(std::optional<ui::Size> size); + std::optional<ui::Size> getPhysicalSizeInMm() const override { return mPhysicalSize; } private: void loadDisplayCapabilities(); @@ -309,6 +321,8 @@ private: std::optional< std::unordered_set<aidl::android::hardware::graphics::composer3::DisplayCapability>> mDisplayCapabilities GUARDED_BY(mDisplayCapabilitiesMutex); + // Physical size in mm. + std::optional<ui::Size> mPhysicalSize; }; } // namespace impl @@ -354,6 +368,8 @@ public: // AIDL HAL [[nodiscard]] virtual hal::Error setBrightness(float brightness) = 0; [[nodiscard]] virtual hal::Error setBlockingRegion(const android::Region& region) = 0; + [[nodiscard]] virtual hal::Error setLuts( + std::vector<aidl::android::hardware::graphics::composer3::Lut>& luts) = 0; }; namespace impl { @@ -404,6 +420,8 @@ public: // AIDL HAL hal::Error setBrightness(float brightness) override; hal::Error setBlockingRegion(const android::Region& region) override; + hal::Error setLuts( + std::vector<aidl::android::hardware::graphics::composer3::Lut>& luts) override; private: // These are references to data owned by HWComposer, which will outlive diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index 3d285a84ef..d08e261e6c 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -28,16 +28,15 @@ #include "HWComposer.h" #include <android-base/properties.h> +#include <common/trace.h> #include <compositionengine/Output.h> #include <compositionengine/OutputLayer.h> #include <compositionengine/impl/OutputLayerCompositionState.h> #include <ftl/concat.h> -#include <gui/TraceUtils.h> #include <log/log.h> #include <ui/DebugUtils.h> #include <ui/GraphicBuffer.h> #include <utils/Errors.h> -#include <utils/Trace.h> #include "../Layer.h" // needed only for debugging #include "../SurfaceFlingerProperties.h" @@ -77,6 +76,7 @@ using aidl::android::hardware::graphics::common::HdrConversionCapability; using aidl::android::hardware::graphics::common::HdrConversionStrategy; using aidl::android::hardware::graphics::composer3::Capability; using aidl::android::hardware::graphics::composer3::DisplayCapability; +using aidl::android::hardware::graphics::composer3::DisplayConfiguration; using namespace std::string_literals; namespace android { @@ -178,8 +178,8 @@ std::optional<PhysicalDisplayId> HWComposer::onVsync(hal::HWDisplayId hwcDisplay displayData.lastPresentTimestamp = timestamp; } - ATRACE_INT(ftl::Concat("HW_VSYNC_", displayIdOpt->value).c_str(), - displayData.vsyncTraceToggle); + SFTRACE_INT(ftl::Concat("HW_VSYNC_", displayIdOpt->value).c_str(), + displayData.vsyncTraceToggle); displayData.vsyncTraceToggle = !displayData.vsyncTraceToggle; return displayIdOpt; @@ -223,8 +223,8 @@ bool HWComposer::allocateVirtualDisplay(HalVirtualDisplayId displayId, ui::Size return true; } -void HWComposer::allocatePhysicalDisplay(hal::HWDisplayId hwcDisplayId, - PhysicalDisplayId displayId) { +void HWComposer::allocatePhysicalDisplay(hal::HWDisplayId hwcDisplayId, PhysicalDisplayId displayId, + std::optional<ui::Size> physicalSize) { mPhysicalDisplayIdMap[hwcDisplayId] = displayId; if (!mPrimaryHwcDisplayId) { @@ -236,6 +236,7 @@ void HWComposer::allocatePhysicalDisplay(hal::HWDisplayId hwcDisplayId, std::make_unique<HWC2::impl::Display>(*mComposer.get(), mCapabilities, hwcDisplayId, hal::DisplayType::PHYSICAL); newDisplay->setConnected(true); + newDisplay->setPhysicalSizeInMm(physicalSize); displayData.hwcDisplay = std::move(newDisplay); } @@ -277,6 +278,47 @@ std::vector<HWComposer::HWCDisplayMode> HWComposer::getModes(PhysicalDisplayId d return getModesFromLegacyDisplayConfigs(hwcDisplayId); } +DisplayConfiguration::Dpi HWComposer::getEstimatedDotsPerInchFromSize( + uint64_t hwcDisplayId, const HWCDisplayMode& hwcMode) const { + if (!FlagManager::getInstance().correct_dpi_with_display_size()) { + return {-1, -1}; + } + if (const auto displayId = toPhysicalDisplayId(hwcDisplayId)) { + if (const auto it = mDisplayData.find(displayId.value()); + it != mDisplayData.end() && it->second.hwcDisplay->getPhysicalSizeInMm()) { + ui::Size size = it->second.hwcDisplay->getPhysicalSizeInMm().value(); + if (hwcMode.width > 0 && hwcMode.height > 0 && size.width > 0 && size.height > 0) { + static constexpr float kMmPerInch = 25.4f; + return {hwcMode.width * kMmPerInch / size.width, + hwcMode.height * kMmPerInch / size.height}; + } + } + } + return {-1, -1}; +} + +DisplayConfiguration::Dpi HWComposer::correctedDpiIfneeded( + DisplayConfiguration::Dpi dpi, DisplayConfiguration::Dpi estimatedDpi) const { + // hwc can be unreliable when it comes to dpi. A rough estimated dpi may yield better + // results. For instance, libdrm and bad edid may result in a dpi of {350, 290} for a + // 16:9 3840x2160 display, which would match a 4:3 aspect ratio. + // The logic here checks if hwc was able to provide some dpi, and if so if the dpi + // disparity between the axes is more reasonable than a rough estimate, otherwise use + // the estimated dpi as a corrected value. + if (estimatedDpi.x == -1 || estimatedDpi.y == -1) { + return dpi; + } + if (dpi.x == -1 || dpi.y == -1) { + return estimatedDpi; + } + if (std::min(dpi.x, dpi.y) != 0 && std::min(estimatedDpi.x, estimatedDpi.y) != 0 && + abs(dpi.x - dpi.y) / std::min(dpi.x, dpi.y) > + abs(estimatedDpi.x - estimatedDpi.y) / std::min(estimatedDpi.x, estimatedDpi.y)) { + return estimatedDpi; + } + return dpi; +} + std::vector<HWComposer::HWCDisplayMode> HWComposer::getModesFromDisplayConfigurations( uint64_t hwcDisplayId, int32_t maxFrameIntervalNs) const { std::vector<hal::DisplayConfiguration> configs; @@ -295,9 +337,16 @@ std::vector<HWComposer::HWCDisplayMode> HWComposer::getModesFromDisplayConfigura .configGroup = config.configGroup, .vrrConfig = config.vrrConfig}; + const DisplayConfiguration::Dpi estimatedDPI = + getEstimatedDotsPerInchFromSize(hwcDisplayId, hwcMode); if (config.dpi) { - hwcMode.dpiX = config.dpi->x; - hwcMode.dpiY = config.dpi->y; + const DisplayConfiguration::Dpi dpi = + correctedDpiIfneeded(config.dpi.value(), estimatedDPI); + hwcMode.dpiX = dpi.x; + hwcMode.dpiY = dpi.y; + } else if (estimatedDPI.x != -1 && estimatedDPI.y != -1) { + hwcMode.dpiX = estimatedDPI.x; + hwcMode.dpiY = estimatedDPI.y; } if (!mEnableVrrTimeout) { @@ -329,12 +378,14 @@ std::vector<HWComposer::HWCDisplayMode> HWComposer::getModesFromLegacyDisplayCon const int32_t dpiX = getAttribute(hwcDisplayId, configId, hal::Attribute::DPI_X); const int32_t dpiY = getAttribute(hwcDisplayId, configId, hal::Attribute::DPI_Y); - if (dpiX != -1) { - hwcMode.dpiX = static_cast<float>(dpiX) / 1000.f; - } - if (dpiY != -1) { - hwcMode.dpiY = static_cast<float>(dpiY) / 1000.f; - } + const DisplayConfiguration::Dpi hwcDpi = + DisplayConfiguration::Dpi{dpiX == -1 ? dpiY : dpiX / 1000.f, + dpiY == -1 ? dpiY : dpiY / 1000.f}; + const DisplayConfiguration::Dpi estimatedDPI = + getEstimatedDotsPerInchFromSize(hwcDisplayId, hwcMode); + const DisplayConfiguration::Dpi dpi = correctedDpiIfneeded(hwcDpi, estimatedDPI); + hwcMode.dpiX = dpi.x; + hwcMode.dpiY = dpi.y; modes.push_back(hwcMode); } @@ -428,14 +479,14 @@ void HWComposer::setVsyncEnabled(PhysicalDisplayId displayId, hal::Vsync enabled return; } - ATRACE_CALL(); + SFTRACE_CALL(); auto error = displayData.hwcDisplay->setVsyncEnabled(enabled); RETURN_IF_HWC_ERROR(error, displayId); displayData.vsyncEnabled = enabled; - ATRACE_INT(ftl::Concat("HW_VSYNC_ON_", displayId.value).c_str(), - enabled == hal::Vsync::ENABLE ? 1 : 0); + SFTRACE_INT(ftl::Concat("HW_VSYNC_ON_", displayId.value).c_str(), + enabled == hal::Vsync::ENABLE ? 1 : 0); } status_t HWComposer::setClientTarget(HalDisplayId displayId, uint32_t slot, @@ -455,7 +506,7 @@ status_t HWComposer::getDeviceCompositionChanges( std::optional<std::chrono::steady_clock::time_point> earliestPresentTime, nsecs_t expectedPresentTime, Fps frameInterval, std::optional<android::HWComposer::DeviceRequestedChanges>* outChanges) { - ATRACE_CALL(); + SFTRACE_CALL(); RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); @@ -493,7 +544,7 @@ status_t HWComposer::getDeviceCompositionChanges( }(); displayData.validateWasSkipped = false; - ATRACE_FORMAT("NextFrameInterval %d_Hz", frameInterval.getIntValue()); + SFTRACE_FORMAT("NextFrameInterval %d_Hz", frameInterval.getIntValue()); if (canSkipValidate) { sp<Fence> outPresentFence = Fence::NO_FENCE; uint32_t state = UINT32_MAX; @@ -534,6 +585,7 @@ status_t HWComposer::getDeviceCompositionChanges( DeviceRequestedChanges::ClientTargetProperty clientTargetProperty; error = hwcDisplay->getClientTargetProperty(&clientTargetProperty); + RETURN_IF_HWC_ERROR_FOR("getClientTargetProperty", error, displayId, BAD_INDEX); outChanges->emplace(DeviceRequestedChanges{std::move(changedTypes), std::move(displayRequests), std::move(layerRequests), @@ -568,7 +620,7 @@ sp<Fence> HWComposer::getLayerReleaseFence(HalDisplayId displayId, HWC2::Layer* status_t HWComposer::presentAndGetReleaseFences( HalDisplayId displayId, std::optional<std::chrono::steady_clock::time_point> earliestPresentTime) { - ATRACE_CALL(); + SFTRACE_CALL(); RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); @@ -584,7 +636,7 @@ status_t HWComposer::presentAndGetReleaseFences( } if (earliestPresentTime) { - ATRACE_NAME("wait for earliest present time"); + SFTRACE_NAME("wait for earliest present time"); std::this_thread::sleep_until(*earliestPresentTime); } @@ -897,9 +949,9 @@ status_t HWComposer::setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId status_t HWComposer::notifyExpectedPresent(PhysicalDisplayId displayId, TimePoint expectedPresentTime, Fps frameInterval) { RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); - ATRACE_FORMAT("%s ExpectedPresentTime in %.2fms frameInterval %.2fms", __func__, - ticks<std::milli, float>(expectedPresentTime - TimePoint::now()), - ticks<std::milli, float>(Duration::fromNs(frameInterval.getPeriodNsecs()))); + SFTRACE_FORMAT("%s ExpectedPresentTime in %.2fms frameInterval %.2fms", __func__, + ticks<std::milli, float>(expectedPresentTime - TimePoint::now()), + ticks<std::milli, float>(Duration::fromNs(frameInterval.getPeriodNsecs()))); const auto error = mComposer->notifyExpectedPresent(mDisplayData[displayId].hwcDisplay->getId(), expectedPresentTime.ns(), frameInterval.getPeriodNsecs()); @@ -926,6 +978,21 @@ status_t HWComposer::getDisplayDecorationSupport( return NO_ERROR; } +status_t HWComposer::getRequestedLuts( + PhysicalDisplayId displayId, + std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>* outLuts) { + RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); + const auto error = mDisplayData[displayId].hwcDisplay->getRequestedLuts(outLuts); + if (error == hal::Error::UNSUPPORTED) { + RETURN_IF_HWC_ERROR(error, displayId, INVALID_OPERATION); + } + if (error == hal::Error::BAD_PARAMETER) { + RETURN_IF_HWC_ERROR(error, displayId, BAD_VALUE); + } + RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR); + return NO_ERROR; +} + status_t HWComposer::setAutoLowLatencyMode(PhysicalDisplayId displayId, bool on) { RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); const auto error = mDisplayData[displayId].hwcDisplay->setAutoLowLatencyMode(on); @@ -1057,6 +1124,8 @@ std::optional<DisplayIdentificationInfo> HWComposer::onHotplugConnect( getDisplayIdentificationData(hwcDisplayId, &port, &data); if (auto newInfo = parseDisplayIdentificationData(port, data)) { info->deviceProductInfo = std::move(newInfo->deviceProductInfo); + info->preferredDetailedTimingDescriptor = + std::move(newInfo->preferredDetailedTimingDescriptor); } else { ALOGE("Failed to parse identification data for display %" PRIu64, hwcDisplayId); } @@ -1099,7 +1168,11 @@ std::optional<DisplayIdentificationInfo> HWComposer::onHotplugConnect( } if (!isConnected(info->id)) { - allocatePhysicalDisplay(hwcDisplayId, info->id); + std::optional<ui::Size> size = std::nullopt; + if (info->preferredDetailedTimingDescriptor) { + size = info->preferredDetailedTimingDescriptor->physicalSizeInMm; + } + allocatePhysicalDisplay(hwcDisplayId, info->id, size); } return info; } @@ -1149,7 +1222,7 @@ void HWComposer::loadHdrConversionCapabilities() { status_t HWComposer::setIdleTimerEnabled(PhysicalDisplayId displayId, std::chrono::milliseconds timeout) { - ATRACE_CALL(); + SFTRACE_CALL(); RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); const auto error = mDisplayData[displayId].hwcDisplay->setIdleTimerEnabled(timeout); if (error == hal::Error::UNSUPPORTED) { @@ -1168,7 +1241,7 @@ bool HWComposer::hasDisplayIdleTimerCapability(PhysicalDisplayId displayId) cons } Hwc2::AidlTransform HWComposer::getPhysicalDisplayOrientation(PhysicalDisplayId displayId) const { - ATRACE_CALL(); + SFTRACE_CALL(); RETURN_IF_INVALID_DISPLAY(displayId, Hwc2::AidlTransform::NONE); Hwc2::AidlTransform outTransform; const auto& hwcDisplay = mDisplayData.at(displayId).hwcDisplay; diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h index 9368b7b6dd..b95c619f75 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.h +++ b/services/surfaceflinger/DisplayHardware/HWComposer.h @@ -52,6 +52,7 @@ #include <aidl/android/hardware/graphics/composer3/ClientTargetPropertyWithBrightness.h> #include <aidl/android/hardware/graphics/composer3/Composition.h> #include <aidl/android/hardware/graphics/composer3/DisplayCapability.h> +#include <aidl/android/hardware/graphics/composer3/DisplayLuts.h> #include <aidl/android/hardware/graphics/composer3/OverlayProperties.h> namespace android { @@ -133,7 +134,8 @@ public: // supported by the HWC can be queried in advance, but allocation may fail for other reasons. virtual bool allocateVirtualDisplay(HalVirtualDisplayId, ui::Size, ui::PixelFormat*) = 0; - virtual void allocatePhysicalDisplay(hal::HWDisplayId, PhysicalDisplayId) = 0; + virtual void allocatePhysicalDisplay(hal::HWDisplayId, PhysicalDisplayId, + std::optional<ui::Size> physicalSize) = 0; // Attempts to create a new layer on this display virtual std::shared_ptr<HWC2::Layer> createLayer(HalDisplayId) = 0; @@ -309,6 +311,11 @@ public: virtual status_t setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId, bool enabled) = 0; virtual status_t notifyExpectedPresent(PhysicalDisplayId, TimePoint expectedPresentTime, Fps frameInterval) = 0; + + // Composer 4.0 + virtual status_t getRequestedLuts( + PhysicalDisplayId, + std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*) = 0; }; static inline bool operator==(const android::HWComposer::DeviceRequestedChanges& lhs, @@ -343,7 +350,8 @@ public: bool allocateVirtualDisplay(HalVirtualDisplayId, ui::Size, ui::PixelFormat*) override; // Called from SurfaceFlinger, when the state for a new physical display needs to be recreated. - void allocatePhysicalDisplay(hal::HWDisplayId, PhysicalDisplayId) override; + void allocatePhysicalDisplay(hal::HWDisplayId, PhysicalDisplayId, + std::optional<ui::Size> physicalSize) override; // Attempts to create a new layer on this display std::shared_ptr<HWC2::Layer> createLayer(HalDisplayId) override; @@ -472,6 +480,12 @@ public: status_t notifyExpectedPresent(PhysicalDisplayId, TimePoint expectedPresentTime, Fps frameInterval) override; + // Composer 4.0 + status_t getRequestedLuts( + PhysicalDisplayId, + std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*) + override; + // for debugging ---------------------------------------------------------- void dump(std::string& out) const override; void dumpOverlayProperties(std::string& out) const override; @@ -520,6 +534,13 @@ private: std::optional<DisplayIdentificationInfo> onHotplugDisconnect(hal::HWDisplayId); bool shouldIgnoreHotplugConnect(hal::HWDisplayId, bool hasDisplayIdentificationData) const; + aidl::android::hardware::graphics::composer3::DisplayConfiguration::Dpi + getEstimatedDotsPerInchFromSize(uint64_t hwcDisplayId, const HWCDisplayMode& hwcMode) const; + + aidl::android::hardware::graphics::composer3::DisplayConfiguration::Dpi correctedDpiIfneeded( + aidl::android::hardware::graphics::composer3::DisplayConfiguration::Dpi dpi, + aidl::android::hardware::graphics::composer3::DisplayConfiguration::Dpi estimatedDpi) + const; std::vector<HWCDisplayMode> getModesFromDisplayConfigurations(uint64_t hwcDisplayId, int32_t maxFrameIntervalNs) const; std::vector<HWCDisplayMode> getModesFromLegacyDisplayConfigs(uint64_t hwcDisplayId) const; diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp index be95913b89..ee1e07ae7d 100644 --- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp @@ -27,11 +27,11 @@ #include <SurfaceFlingerProperties.h> #include <aidl/android/hardware/graphics/common/DisplayHotplugEvent.h> #include <android/binder_manager.h> +#include <common/trace.h> #include <composer-command-buffer/2.2/ComposerCommandBuffer.h> #include <hidl/HidlTransportSupport.h> #include <hidl/HidlTransportUtils.h> #include <log/log.h> -#include <utils/Trace.h> #include "HWC2.h" #include "Hal.h" @@ -46,6 +46,8 @@ using aidl::android::hardware::graphics::composer3::Capability; using aidl::android::hardware::graphics::composer3::ClientTargetPropertyWithBrightness; using aidl::android::hardware::graphics::composer3::DimmingStage; using aidl::android::hardware::graphics::composer3::DisplayCapability; +using aidl::android::hardware::graphics::composer3::DisplayLuts; +using aidl::android::hardware::graphics::composer3::Lut; using aidl::android::hardware::graphics::composer3::OverlayProperties; namespace android { @@ -588,7 +590,7 @@ Error HidlComposer::getReleaseFences(Display display, std::vector<Layer>* outLay } Error HidlComposer::presentDisplay(Display display, int* outPresentFence) { - ATRACE_NAME("HwcPresentDisplay"); + SFTRACE_NAME("HwcPresentDisplay"); mWriter.selectDisplay(display); mWriter.presentDisplay(); @@ -676,7 +678,7 @@ Error HidlComposer::setClientTargetSlotCount(Display display) { Error HidlComposer::validateDisplay(Display display, nsecs_t /*expectedPresentTime*/, int32_t /*frameIntervalNs*/, uint32_t* outNumTypes, uint32_t* outNumRequests) { - ATRACE_NAME("HwcValidateDisplay"); + SFTRACE_NAME("HwcValidateDisplay"); mWriter.selectDisplay(display); mWriter.validateDisplay(); @@ -694,7 +696,7 @@ Error HidlComposer::presentOrValidateDisplay(Display display, nsecs_t /*expected int32_t /*frameIntervalNs*/, uint32_t* outNumTypes, uint32_t* outNumRequests, int* outPresentFence, uint32_t* state) { - ATRACE_NAME("HwcPresentOrValidateDisplay"); + SFTRACE_NAME("HwcPresentOrValidateDisplay"); mWriter.selectDisplay(display); mWriter.presentOrvalidateDisplay(); @@ -1408,6 +1410,14 @@ Error HidlComposer::getClientTargetProperty( return Error::NONE; } +Error HidlComposer::getRequestedLuts(Display, std::vector<DisplayLuts::LayerLut>*) { + return Error::NONE; +} + +Error HidlComposer::setLayerLuts(Display, Layer, std::vector<Lut>&) { + return Error::NONE; +} + Error HidlComposer::setLayerBrightness(Display, Layer, float) { return Error::NONE; } diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h index d78bfb7c6b..701a54bc66 100644 --- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h @@ -351,6 +351,12 @@ public: Hdr*) override; Error setRefreshRateChangedCallbackDebugEnabled(Display, bool) override; Error notifyExpectedPresent(Display, nsecs_t, int32_t) override; + Error getRequestedLuts( + Display, + std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*) + override; + Error setLayerLuts(Display, Layer, + std::vector<aidl::android::hardware::graphics::composer3::Lut>&) override; private: class CommandWriter : public CommandWriterBase { diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp index 6c1a81314d..334c104faf 100644 --- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp +++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp @@ -27,9 +27,9 @@ #include <optional> #include <android-base/properties.h> +#include <common/trace.h> #include <utils/Log.h> #include <utils/Mutex.h> -#include <utils/Trace.h> #include <binder/IServiceManager.h> @@ -74,9 +74,9 @@ std::chrono::milliseconds getUpdateTimeout() { void traceExpensiveRendering(bool enabled) { if (enabled) { - ATRACE_ASYNC_BEGIN("ExpensiveRendering", 0); + SFTRACE_ASYNC_BEGIN("ExpensiveRendering", 0); } else { - ATRACE_ASYNC_END("ExpensiveRendering", 0); + SFTRACE_ASYNC_END("ExpensiveRendering", 0); } } @@ -210,8 +210,8 @@ void PowerAdvisor::sendHintSessionHint(SessionHint hint) { ALOGV("Power hint session is not enabled, skip sending session hint"); return; } - ATRACE_CALL(); - if (sTraceHintSessionData) ATRACE_INT("Session hint", static_cast<int>(hint)); + SFTRACE_CALL(); + if (sTraceHintSessionData) SFTRACE_INT("Session hint", static_cast<int>(hint)); { std::scoped_lock lock(mHintSessionMutex); if (!ensurePowerHintSessionRunning()) { @@ -295,10 +295,10 @@ void PowerAdvisor::updateTargetWorkDuration(Duration targetDuration) { ALOGV("Power hint session is not enabled, skipping target update"); return; } - ATRACE_CALL(); + SFTRACE_CALL(); { mTargetDuration = targetDuration; - if (sTraceHintSessionData) ATRACE_INT64("Time target", targetDuration.ns()); + if (sTraceHintSessionData) SFTRACE_INT64("Time target", targetDuration.ns()); if (targetDuration == mLastTargetDurationSent) return; std::scoped_lock lock(mHintSessionMutex); if (!ensurePowerHintSessionRunning()) { @@ -324,7 +324,7 @@ void PowerAdvisor::reportActualWorkDuration() { ALOGV("Actual work duration power hint cannot be sent, skipping"); return; } - ATRACE_CALL(); + SFTRACE_CALL(); std::optional<WorkDuration> actualDuration = estimateWorkDuration(); if (!actualDuration.has_value() || actualDuration->durationNanos < 0) { ALOGV("Failed to send actual work duration, skipping"); @@ -332,16 +332,16 @@ void PowerAdvisor::reportActualWorkDuration() { } actualDuration->durationNanos += sTargetSafetyMargin.ns(); if (sTraceHintSessionData) { - ATRACE_INT64("Measured duration", actualDuration->durationNanos); - ATRACE_INT64("Target error term", actualDuration->durationNanos - mTargetDuration.ns()); - ATRACE_INT64("Reported duration", actualDuration->durationNanos); + SFTRACE_INT64("Measured duration", actualDuration->durationNanos); + SFTRACE_INT64("Target error term", actualDuration->durationNanos - mTargetDuration.ns()); + SFTRACE_INT64("Reported duration", actualDuration->durationNanos); if (supportsGpuReporting()) { - ATRACE_INT64("Reported cpu duration", actualDuration->cpuDurationNanos); - ATRACE_INT64("Reported gpu duration", actualDuration->gpuDurationNanos); + SFTRACE_INT64("Reported cpu duration", actualDuration->cpuDurationNanos); + SFTRACE_INT64("Reported gpu duration", actualDuration->gpuDurationNanos); } - ATRACE_INT64("Reported target", mLastTargetDurationSent.ns()); - ATRACE_INT64("Reported target error term", - actualDuration->durationNanos - mLastTargetDurationSent.ns()); + SFTRACE_INT64("Reported target", mLastTargetDurationSent.ns()); + SFTRACE_INT64("Reported target error term", + actualDuration->durationNanos - mLastTargetDurationSent.ns()); } ALOGV("Sending actual work duration of: %" PRId64 " with cpu: %" PRId64 " and gpu: %" PRId64 @@ -664,9 +664,9 @@ std::optional<WorkDuration> PowerAdvisor::estimateWorkDuration() { .gpuDurationNanos = supportsGpuReporting() ? estimatedGpuDuration.ns() : 0, }; if (sTraceHintSessionData) { - ATRACE_INT64("Idle duration", idleDuration.ns()); - ATRACE_INT64("Total duration", totalDuration.ns()); - ATRACE_INT64("Flinger duration", flingerDuration.ns()); + SFTRACE_INT64("Idle duration", idleDuration.ns()); + SFTRACE_INT64("Total duration", totalDuration.ns()); + SFTRACE_INT64("Flinger duration", flingerDuration.ns()); } return std::make_optional(duration); } diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h index bc4a41bf84..1076b2b79b 100644 --- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h +++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h @@ -300,7 +300,7 @@ private: bool mSessionConfigSupported = true; bool mFirstConfigSupportCheck = true; - // Whether we should emit ATRACE_INT data for hint sessions + // Whether we should emit SFTRACE_INT data for hint sessions static const bool sTraceHintSessionData; // Default target duration for the hint session diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp index 4b5a68cefa..384f7b22c7 100644 --- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp +++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp @@ -22,6 +22,7 @@ #include <cinttypes> +#include <com_android_graphics_libgui_flags.h> #include <ftl/enum.h> #include <ftl/flags.h> #include <gui/BufferItem.h> @@ -51,7 +52,11 @@ VirtualDisplaySurface::VirtualDisplaySurface(HWComposer& hwc, VirtualDisplayId d const sp<IGraphicBufferProducer>& bqProducer, const sp<IGraphicBufferConsumer>& bqConsumer, const std::string& name) +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) + : ConsumerBase(bqProducer, bqConsumer), +#else : ConsumerBase(bqConsumer), +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) mHwc(hwc), mDisplayId(displayId), mDisplayName(name), diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp index 2596a25d15..47b811b721 100644 --- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp +++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp @@ -22,20 +22,24 @@ #include <android-base/stringprintf.h> #include <common/FlagManager.h> +#include <common/trace.h> #include <utils/Log.h> -#include <utils/Trace.h> #include <chrono> #include <cinttypes> #include <numeric> #include <unordered_set> +#include "../Jank/JankTracker.h" + namespace android::frametimeline { using base::StringAppendF; using FrameTimelineEvent = perfetto::protos::pbzero::FrameTimelineEvent; using FrameTimelineDataSource = impl::FrameTimeline::FrameTimelineDataSource; +namespace { + void dumpTable(std::string& result, TimelineItem predictions, TimelineItem actuals, const std::string& indent, PredictionState predictionState, nsecs_t baseTime) { StringAppendF(&result, "%s", indent.c_str()); @@ -317,6 +321,16 @@ nsecs_t getMinTime(PredictionState predictionState, TimelineItem predictions, return minTime; } +bool shouldTraceForDataSource(const FrameTimelineDataSource::TraceContext& ctx, nsecs_t timestamp) { + if (auto ds = ctx.GetDataSourceLocked(); ds && ds->getStartTime() > timestamp) { + return false; + } + + return true; +} + +} // namespace + int64_t TraceCookieCounter::getCookieForTracing() { return ++mTraceCookie; } @@ -357,7 +371,11 @@ void SurfaceFrame::setActualQueueTime(nsecs_t actualQueueTime) { void SurfaceFrame::setAcquireFenceTime(nsecs_t acquireFenceTime) { std::scoped_lock lock(mMutex); - mActuals.endTime = std::max(acquireFenceTime, mActualQueueTime); + if (CC_UNLIKELY(acquireFenceTime == Fence::SIGNAL_TIME_PENDING)) { + mActuals.endTime = mActualQueueTime; + } else { + mActuals.endTime = std::max(acquireFenceTime, mActualQueueTime); + } } void SurfaceFrame::setDropTime(nsecs_t dropTime) { @@ -685,6 +703,30 @@ void SurfaceFrame::onPresent(nsecs_t presentTime, int32_t displayFrameJankType, mTimeStats->incrementJankyFrames({refreshRate, mRenderRate, mOwnerUid, mLayerName, mGameMode, mJankType, displayDeadlineDelta, displayPresentDelta, deadlineDelta}); + + gui::JankData jd; + jd.frameVsyncId = mToken; + jd.jankType = mJankType; + jd.frameIntervalNs = + (mRenderRate ? *mRenderRate : mDisplayFrameRenderRate).getPeriodNsecs(); + + if (mPredictionState == PredictionState::Valid) { + jd.scheduledAppFrameTimeNs = mPredictions.endTime - mPredictions.startTime; + + // Using expected start, rather than actual, to measure the entire frame time. That is + // if the application starts the frame later than scheduled, include that delay in the + // frame time, as it usually means main thread being busy with non-rendering work. + if (mPresentState == PresentState::Dropped) { + jd.actualAppFrameTimeNs = mDropTime - mPredictions.startTime; + } else { + jd.actualAppFrameTimeNs = mActuals.endTime - mPredictions.startTime; + } + } else { + jd.scheduledAppFrameTimeNs = 0; + jd.actualAppFrameTimeNs = 0; + } + + JankTracker::onJankData(mLayerId, jd); } } @@ -696,15 +738,24 @@ void SurfaceFrame::onCommitNotComposited(Fps refreshRate, Fps displayFrameRender classifyJankLocked(JankType::None, refreshRate, displayFrameRenderRate, nullptr); } -void SurfaceFrame::tracePredictions(int64_t displayFrameToken, nsecs_t monoBootOffset) const { +void SurfaceFrame::tracePredictions(int64_t displayFrameToken, nsecs_t monoBootOffset, + bool filterFramesBeforeTraceStarts) const { int64_t expectedTimelineCookie = mTraceCookieCounter.getCookieForTracing(); + bool traced = false; // Expected timeline start FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) { + const auto timestamp = mPredictions.startTime; + if (filterFramesBeforeTraceStarts && !shouldTraceForDataSource(ctx, timestamp)) { + // Do not trace packets started before tracing starts. + return; + } + traced = true; + std::scoped_lock lock(mMutex); auto packet = ctx.NewTracePacket(); packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME); - packet->set_timestamp(static_cast<uint64_t>(mPredictions.startTime + monoBootOffset)); + packet->set_timestamp(static_cast<uint64_t>(timestamp + monoBootOffset)); auto* event = packet->set_frame_timeline_event(); auto* expectedSurfaceFrameStartEvent = event->set_expected_surface_frame_start(); @@ -718,42 +769,54 @@ void SurfaceFrame::tracePredictions(int64_t displayFrameToken, nsecs_t monoBootO expectedSurfaceFrameStartEvent->set_layer_name(mDebugName); }); - // Expected timeline end - FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) { - std::scoped_lock lock(mMutex); - auto packet = ctx.NewTracePacket(); - packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME); - packet->set_timestamp(static_cast<uint64_t>(mPredictions.endTime + monoBootOffset)); + if (traced) { + // Expected timeline end + FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) { + std::scoped_lock lock(mMutex); + auto packet = ctx.NewTracePacket(); + packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME); + packet->set_timestamp(static_cast<uint64_t>(mPredictions.endTime + monoBootOffset)); - auto* event = packet->set_frame_timeline_event(); - auto* expectedSurfaceFrameEndEvent = event->set_frame_end(); + auto* event = packet->set_frame_timeline_event(); + auto* expectedSurfaceFrameEndEvent = event->set_frame_end(); - expectedSurfaceFrameEndEvent->set_cookie(expectedTimelineCookie); - }); + expectedSurfaceFrameEndEvent->set_cookie(expectedTimelineCookie); + }); + } } -void SurfaceFrame::traceActuals(int64_t displayFrameToken, nsecs_t monoBootOffset) const { +void SurfaceFrame::traceActuals(int64_t displayFrameToken, nsecs_t monoBootOffset, + bool filterFramesBeforeTraceStarts) const { int64_t actualTimelineCookie = mTraceCookieCounter.getCookieForTracing(); + bool traced = false; // Actual timeline start FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) { + const auto timestamp = [&]() { + std::scoped_lock lock(mMutex); + // Actual start time is not yet available, so use expected start instead + if (mPredictionState == PredictionState::Expired) { + // If prediction is expired, we can't use the predicted start time. Instead, just + // use a start time a little earlier than the end time so that we have some info + // about this frame in the trace. + nsecs_t endTime = + (mPresentState == PresentState::Dropped ? mDropTime : mActuals.endTime); + return endTime - kPredictionExpiredStartTimeDelta; + } + + return mActuals.startTime == 0 ? mPredictions.startTime : mActuals.startTime; + }(); + + if (filterFramesBeforeTraceStarts && !shouldTraceForDataSource(ctx, timestamp)) { + // Do not trace packets started before tracing starts. + return; + } + traced = true; + std::scoped_lock lock(mMutex); auto packet = ctx.NewTracePacket(); packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME); - // Actual start time is not yet available, so use expected start instead - if (mPredictionState == PredictionState::Expired) { - // If prediction is expired, we can't use the predicted start time. Instead, just use a - // start time a little earlier than the end time so that we have some info about this - // frame in the trace. - nsecs_t endTime = - (mPresentState == PresentState::Dropped ? mDropTime : mActuals.endTime); - const auto timestamp = endTime - kPredictionExpiredStartTimeDelta; - packet->set_timestamp(static_cast<uint64_t>(timestamp + monoBootOffset)); - } else { - const auto timestamp = - mActuals.startTime == 0 ? mPredictions.startTime : mActuals.startTime; - packet->set_timestamp(static_cast<uint64_t>(timestamp + monoBootOffset)); - } + packet->set_timestamp(static_cast<uint64_t>(timestamp + monoBootOffset)); auto* event = packet->set_frame_timeline_event(); auto* actualSurfaceFrameStartEvent = event->set_actual_surface_frame_start(); @@ -782,28 +845,31 @@ void SurfaceFrame::traceActuals(int64_t displayFrameToken, nsecs_t monoBootOffse actualSurfaceFrameStartEvent->set_jank_severity_type(toProto(mJankSeverityType)); }); - // Actual timeline end - FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) { - std::scoped_lock lock(mMutex); - auto packet = ctx.NewTracePacket(); - packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME); - if (mPresentState == PresentState::Dropped) { - packet->set_timestamp(static_cast<uint64_t>(mDropTime + monoBootOffset)); - } else { - packet->set_timestamp(static_cast<uint64_t>(mActuals.endTime + monoBootOffset)); - } + if (traced) { + // Actual timeline end + FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) { + std::scoped_lock lock(mMutex); + auto packet = ctx.NewTracePacket(); + packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME); + if (mPresentState == PresentState::Dropped) { + packet->set_timestamp(static_cast<uint64_t>(mDropTime + monoBootOffset)); + } else { + packet->set_timestamp(static_cast<uint64_t>(mActuals.endTime + monoBootOffset)); + } - auto* event = packet->set_frame_timeline_event(); - auto* actualSurfaceFrameEndEvent = event->set_frame_end(); + auto* event = packet->set_frame_timeline_event(); + auto* actualSurfaceFrameEndEvent = event->set_frame_end(); - actualSurfaceFrameEndEvent->set_cookie(actualTimelineCookie); - }); + actualSurfaceFrameEndEvent->set_cookie(actualTimelineCookie); + }); + } } /** * TODO(b/178637512): add inputEventId to the perfetto trace. */ -void SurfaceFrame::trace(int64_t displayFrameToken, nsecs_t monoBootOffset) const { +void SurfaceFrame::trace(int64_t displayFrameToken, nsecs_t monoBootOffset, + bool filterFramesBeforeTraceStarts) const { if (mToken == FrameTimelineInfo::INVALID_VSYNC_ID || displayFrameToken == FrameTimelineInfo::INVALID_VSYNC_ID) { // No packets can be traced with a missing token. @@ -812,15 +878,15 @@ void SurfaceFrame::trace(int64_t displayFrameToken, nsecs_t monoBootOffset) cons if (getPredictionState() != PredictionState::Expired) { // Expired predictions have zeroed timestamps. This cannot be used in any meaningful way in // a trace. - tracePredictions(displayFrameToken, monoBootOffset); + tracePredictions(displayFrameToken, monoBootOffset, filterFramesBeforeTraceStarts); } - traceActuals(displayFrameToken, monoBootOffset); + traceActuals(displayFrameToken, monoBootOffset, filterFramesBeforeTraceStarts); } namespace impl { int64_t TokenManager::generateTokenForPredictions(TimelineItem&& predictions) { - ATRACE_CALL(); + SFTRACE_CALL(); std::scoped_lock lock(mMutex); while (mPredictions.size() >= kMaxTokens) { mPredictions.erase(mPredictions.begin()); @@ -840,8 +906,12 @@ std::optional<TimelineItem> TokenManager::getPredictionsForToken(int64_t token) } FrameTimeline::FrameTimeline(std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid, - JankClassificationThresholds thresholds, bool useBootTimeClock) + JankClassificationThresholds thresholds, bool useBootTimeClock, + bool filterFramesBeforeTraceStarts) : mUseBootTimeClock(useBootTimeClock), + mFilterFramesBeforeTraceStarts( + FlagManager::getInstance().filter_frames_before_trace_starts() && + filterFramesBeforeTraceStarts), mMaxDisplayFrames(kDefaultMaxDisplayFrames), mTimeStats(std::move(timeStats)), mSurfaceFlingerPid(surfaceFlingerPid), @@ -866,7 +936,7 @@ void FrameTimeline::registerDataSource() { std::shared_ptr<SurfaceFrame> FrameTimeline::createSurfaceFrameForToken( const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid, int32_t layerId, std::string layerName, std::string debugName, bool isBuffer, GameMode gameMode) { - ATRACE_CALL(); + SFTRACE_CALL(); if (frameTimelineInfo.vsyncId == FrameTimelineInfo::INVALID_VSYNC_ID) { return std::make_shared<SurfaceFrame>(frameTimelineInfo, ownerPid, ownerUid, layerId, std::move(layerName), std::move(debugName), @@ -902,14 +972,14 @@ FrameTimeline::DisplayFrame::DisplayFrame(std::shared_ptr<TimeStats> timeStats, } void FrameTimeline::addSurfaceFrame(std::shared_ptr<SurfaceFrame> surfaceFrame) { - ATRACE_CALL(); + SFTRACE_CALL(); std::scoped_lock lock(mMutex); mCurrentDisplayFrame->addSurfaceFrame(surfaceFrame); } void FrameTimeline::setSfWakeUp(int64_t token, nsecs_t wakeUpTime, Fps refreshRate, Fps renderRate) { - ATRACE_CALL(); + SFTRACE_CALL(); std::scoped_lock lock(mMutex); mCurrentDisplayFrame->onSfWakeUp(token, refreshRate, renderRate, mTokenManager.getPredictionsForToken(token), wakeUpTime); @@ -918,7 +988,7 @@ void FrameTimeline::setSfWakeUp(int64_t token, nsecs_t wakeUpTime, Fps refreshRa void FrameTimeline::setSfPresent(nsecs_t sfPresentTime, const std::shared_ptr<FenceTime>& presentFence, const std::shared_ptr<FenceTime>& gpuFence) { - ATRACE_CALL(); + SFTRACE_CALL(); std::scoped_lock lock(mMutex); mCurrentDisplayFrame->setActualEndTime(sfPresentTime); mCurrentDisplayFrame->setGpuFence(gpuFence); @@ -928,7 +998,7 @@ void FrameTimeline::setSfPresent(nsecs_t sfPresentTime, } void FrameTimeline::onCommitNotComposited() { - ATRACE_CALL(); + SFTRACE_CALL(); std::scoped_lock lock(mMutex); mCurrentDisplayFrame->onCommitNotComposited(); mCurrentDisplayFrame.reset(); @@ -1124,16 +1194,23 @@ void FrameTimeline::DisplayFrame::onCommitNotComposited() { } } -void FrameTimeline::DisplayFrame::tracePredictions(pid_t surfaceFlingerPid, - nsecs_t monoBootOffset) const { +void FrameTimeline::DisplayFrame::tracePredictions(pid_t surfaceFlingerPid, nsecs_t monoBootOffset, + bool filterFramesBeforeTraceStarts) const { int64_t expectedTimelineCookie = mTraceCookieCounter.getCookieForTracing(); + bool traced = false; // Expected timeline start FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) { + const auto timestamp = mSurfaceFlingerPredictions.startTime; + if (filterFramesBeforeTraceStarts && !shouldTraceForDataSource(ctx, timestamp)) { + // Do not trace packets started before tracing starts. + return; + } + traced = true; + auto packet = ctx.NewTracePacket(); packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME); - packet->set_timestamp( - static_cast<uint64_t>(mSurfaceFlingerPredictions.startTime + monoBootOffset)); + packet->set_timestamp(static_cast<uint64_t>(timestamp + monoBootOffset)); auto* event = packet->set_frame_timeline_event(); auto* expectedDisplayFrameStartEvent = event->set_expected_display_frame_start(); @@ -1144,22 +1221,25 @@ void FrameTimeline::DisplayFrame::tracePredictions(pid_t surfaceFlingerPid, expectedDisplayFrameStartEvent->set_pid(surfaceFlingerPid); }); - // Expected timeline end - FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) { - auto packet = ctx.NewTracePacket(); - packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME); - packet->set_timestamp( - static_cast<uint64_t>(mSurfaceFlingerPredictions.endTime + monoBootOffset)); + if (traced) { + // Expected timeline end + FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) { + auto packet = ctx.NewTracePacket(); + packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME); + packet->set_timestamp( + static_cast<uint64_t>(mSurfaceFlingerPredictions.endTime + monoBootOffset)); - auto* event = packet->set_frame_timeline_event(); - auto* expectedDisplayFrameEndEvent = event->set_frame_end(); + auto* event = packet->set_frame_timeline_event(); + auto* expectedDisplayFrameEndEvent = event->set_frame_end(); - expectedDisplayFrameEndEvent->set_cookie(expectedTimelineCookie); - }); + expectedDisplayFrameEndEvent->set_cookie(expectedTimelineCookie); + }); + } } void FrameTimeline::DisplayFrame::addSkippedFrame(pid_t surfaceFlingerPid, nsecs_t monoBootOffset, - nsecs_t previousPredictionPresentTime) const { + nsecs_t previousPredictionPresentTime, + bool filterFramesBeforeTraceStarts) const { nsecs_t skippedFrameStartTime = 0, skippedFramePresentTime = 0; const constexpr float kThresh = 0.5f; const constexpr float kRange = 1.5f; @@ -1175,7 +1255,7 @@ void FrameTimeline::DisplayFrame::addSkippedFrame(pid_t surfaceFlingerPid, nsecs (static_cast<float>(previousPredictionPresentTime) + kThresh * static_cast<float>(mRenderRate.getPeriodNsecs())) && // sf skipped frame is not considered if app is self janked - !surfaceFrame->isSelfJanky()) { + surfaceFrame->getJankType() != JankType::None && !surfaceFrame->isSelfJanky()) { skippedFrameStartTime = surfaceFrame->getPredictions().endTime; skippedFramePresentTime = surfaceFrame->getPredictions().presentTime; break; @@ -1185,9 +1265,17 @@ void FrameTimeline::DisplayFrame::addSkippedFrame(pid_t surfaceFlingerPid, nsecs // add slice if (skippedFrameStartTime != 0 && skippedFramePresentTime != 0) { int64_t actualTimelineCookie = mTraceCookieCounter.getCookieForTracing(); + bool traced = false; // Actual timeline start FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) { + if (filterFramesBeforeTraceStarts && + !shouldTraceForDataSource(ctx, skippedFrameStartTime)) { + // Do not trace packets started before tracing starts. + return; + } + traced = true; + auto packet = ctx.NewTracePacket(); packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME); packet->set_timestamp(static_cast<uint64_t>(skippedFrameStartTime + monoBootOffset)); @@ -1208,30 +1296,40 @@ void FrameTimeline::DisplayFrame::addSkippedFrame(pid_t surfaceFlingerPid, nsecs actualDisplayFrameStartEvent->set_jank_severity_type(toProto(JankSeverityType::None)); }); - // Actual timeline end - FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) { - auto packet = ctx.NewTracePacket(); - packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME); - packet->set_timestamp(static_cast<uint64_t>(skippedFramePresentTime + monoBootOffset)); + if (traced) { + // Actual timeline end + FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) { + auto packet = ctx.NewTracePacket(); + packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME); + packet->set_timestamp( + static_cast<uint64_t>(skippedFramePresentTime + monoBootOffset)); - auto* event = packet->set_frame_timeline_event(); - auto* actualDisplayFrameEndEvent = event->set_frame_end(); + auto* event = packet->set_frame_timeline_event(); + auto* actualDisplayFrameEndEvent = event->set_frame_end(); - actualDisplayFrameEndEvent->set_cookie(actualTimelineCookie); - }); + actualDisplayFrameEndEvent->set_cookie(actualTimelineCookie); + }); + } } } -void FrameTimeline::DisplayFrame::traceActuals(pid_t surfaceFlingerPid, - nsecs_t monoBootOffset) const { +void FrameTimeline::DisplayFrame::traceActuals(pid_t surfaceFlingerPid, nsecs_t monoBootOffset, + bool filterFramesBeforeTraceStarts) const { int64_t actualTimelineCookie = mTraceCookieCounter.getCookieForTracing(); + bool traced = false; // Actual timeline start FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) { + const auto timestamp = mSurfaceFlingerActuals.startTime; + if (filterFramesBeforeTraceStarts && !shouldTraceForDataSource(ctx, timestamp)) { + // Do not trace packets started before tracing starts. + return; + } + traced = true; + auto packet = ctx.NewTracePacket(); packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME); - packet->set_timestamp( - static_cast<uint64_t>(mSurfaceFlingerActuals.startTime + monoBootOffset)); + packet->set_timestamp(static_cast<uint64_t>(timestamp + monoBootOffset)); auto* event = packet->set_frame_timeline_event(); auto* actualDisplayFrameStartEvent = event->set_actual_display_frame_start(); @@ -1250,22 +1348,25 @@ void FrameTimeline::DisplayFrame::traceActuals(pid_t surfaceFlingerPid, actualDisplayFrameStartEvent->set_jank_severity_type(toProto(mJankSeverityType)); }); - // Actual timeline end - FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) { - auto packet = ctx.NewTracePacket(); - packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME); - packet->set_timestamp( - static_cast<uint64_t>(mSurfaceFlingerActuals.presentTime + monoBootOffset)); + if (traced) { + // Actual timeline end + FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) { + auto packet = ctx.NewTracePacket(); + packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME); + packet->set_timestamp( + static_cast<uint64_t>(mSurfaceFlingerActuals.presentTime + monoBootOffset)); - auto* event = packet->set_frame_timeline_event(); - auto* actualDisplayFrameEndEvent = event->set_frame_end(); + auto* event = packet->set_frame_timeline_event(); + auto* actualDisplayFrameEndEvent = event->set_frame_end(); - actualDisplayFrameEndEvent->set_cookie(actualTimelineCookie); - }); + actualDisplayFrameEndEvent->set_cookie(actualTimelineCookie); + }); + } } nsecs_t FrameTimeline::DisplayFrame::trace(pid_t surfaceFlingerPid, nsecs_t monoBootOffset, - nsecs_t previousPredictionPresentTime) const { + nsecs_t previousPredictionPresentTime, + bool filterFramesBeforeTraceStarts) const { if (mSurfaceFrames.empty()) { // We don't want to trace display frames without any surface frames updates as this cannot // be janky @@ -1281,16 +1382,17 @@ nsecs_t FrameTimeline::DisplayFrame::trace(pid_t surfaceFlingerPid, nsecs_t mono if (mPredictionState == PredictionState::Valid) { // Expired and unknown predictions have zeroed timestamps. This cannot be used in any // meaningful way in a trace. - tracePredictions(surfaceFlingerPid, monoBootOffset); + tracePredictions(surfaceFlingerPid, monoBootOffset, filterFramesBeforeTraceStarts); } - traceActuals(surfaceFlingerPid, monoBootOffset); + traceActuals(surfaceFlingerPid, monoBootOffset, filterFramesBeforeTraceStarts); for (auto& surfaceFrame : mSurfaceFrames) { - surfaceFrame->trace(mToken, monoBootOffset); + surfaceFrame->trace(mToken, monoBootOffset, filterFramesBeforeTraceStarts); } if (FlagManager::getInstance().add_sf_skipped_frames_to_trace()) { - addSkippedFrame(surfaceFlingerPid, monoBootOffset, previousPredictionPresentTime); + addSkippedFrame(surfaceFlingerPid, monoBootOffset, previousPredictionPresentTime, + filterFramesBeforeTraceStarts); } return mSurfaceFlingerPredictions.presentTime; } @@ -1384,8 +1486,9 @@ void FrameTimeline::flushPendingPresentFences() { const nsecs_t signalTime = Fence::SIGNAL_TIME_INVALID; auto& displayFrame = pendingPresentFence.second; displayFrame->onPresent(signalTime, mPreviousActualPresentTime); - mPreviousPredictionPresentTime = displayFrame->trace(mSurfaceFlingerPid, monoBootOffset, - mPreviousPredictionPresentTime); + mPreviousPredictionPresentTime = + displayFrame->trace(mSurfaceFlingerPid, monoBootOffset, + mPreviousPredictionPresentTime, mFilterFramesBeforeTraceStarts); mPendingPresentFences.erase(mPendingPresentFences.begin()); } @@ -1401,8 +1504,9 @@ void FrameTimeline::flushPendingPresentFences() { auto& displayFrame = pendingPresentFence.second; displayFrame->onPresent(signalTime, mPreviousActualPresentTime); - mPreviousPredictionPresentTime = displayFrame->trace(mSurfaceFlingerPid, monoBootOffset, - mPreviousPredictionPresentTime); + mPreviousPredictionPresentTime = + displayFrame->trace(mSurfaceFlingerPid, monoBootOffset, + mPreviousPredictionPresentTime, mFilterFramesBeforeTraceStarts); mPreviousActualPresentTime = signalTime; mPendingPresentFences.erase(mPendingPresentFences.begin() + static_cast<int>(i)); @@ -1507,7 +1611,7 @@ void FrameTimeline::dumpJank(std::string& result) { } void FrameTimeline::parseArgs(const Vector<String16>& args, std::string& result) { - ATRACE_CALL(); + SFTRACE_CALL(); std::unordered_map<std::string, bool> argsMap; for (size_t i = 0; i < args.size(); i++) { argsMap[std::string(String8(args[i]).c_str())] = true; diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h index 94cfcb40b9..cffb61ee10 100644 --- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h +++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h @@ -214,7 +214,8 @@ public: // enabled. The displayFrameToken is needed to link the SurfaceFrame to the corresponding // DisplayFrame at the trace processor side. monoBootOffset is the difference // between SYSTEM_TIME_BOOTTIME and SYSTEM_TIME_MONOTONIC. - void trace(int64_t displayFrameToken, nsecs_t monoBootOffset) const; + void trace(int64_t displayFrameToken, nsecs_t monoBootOffset, + bool filterFramesBeforeTraceStarts) const; // Getter functions used only by FrameTimelineTests and SurfaceFrame internally TimelineItem getActuals() const; @@ -234,8 +235,10 @@ public: std::chrono::duration_cast<std::chrono::nanoseconds>(2ms).count(); private: - void tracePredictions(int64_t displayFrameToken, nsecs_t monoBootOffset) const; - void traceActuals(int64_t displayFrameToken, nsecs_t monoBootOffset) const; + void tracePredictions(int64_t displayFrameToken, nsecs_t monoBootOffset, + bool filterFramesBeforeTraceStarts) const; + void traceActuals(int64_t displayFrameToken, nsecs_t monoBootOffset, + bool filterFramesBeforeTraceStarts) const; void classifyJankLocked(int32_t displayFrameJankType, const Fps& refreshRate, Fps displayFrameRenderRate, nsecs_t* outDeadlineDelta) REQUIRES(mMutex); @@ -367,9 +370,15 @@ private: class FrameTimeline : public android::frametimeline::FrameTimeline { public: class FrameTimelineDataSource : public perfetto::DataSource<FrameTimelineDataSource> { - void OnSetup(const SetupArgs&) override{}; - void OnStart(const StartArgs&) override{}; - void OnStop(const StopArgs&) override{}; + public: + nsecs_t getStartTime() const { return mTraceStartTime; } + + private: + void OnSetup(const SetupArgs&) override {}; + void OnStart(const StartArgs&) override { mTraceStartTime = systemTime(); }; + void OnStop(const StopArgs&) override {}; + + nsecs_t mTraceStartTime = 0; }; /* @@ -390,7 +399,8 @@ public: // is enabled. monoBootOffset is the difference between SYSTEM_TIME_BOOTTIME // and SYSTEM_TIME_MONOTONIC. nsecs_t trace(pid_t surfaceFlingerPid, nsecs_t monoBootOffset, - nsecs_t previousPredictionPresentTime) const; + nsecs_t previousPredictionPresentTime, + bool filterFramesBeforeTraceStarts) const; // Sets the token, vsyncPeriod, predictions and SF start time. void onSfWakeUp(int64_t token, Fps refreshRate, Fps renderRate, std::optional<TimelineItem> predictions, nsecs_t wakeUpTime); @@ -424,10 +434,13 @@ public: private: void dump(std::string& result, nsecs_t baseTime) const; - void tracePredictions(pid_t surfaceFlingerPid, nsecs_t monoBootOffset) const; - void traceActuals(pid_t surfaceFlingerPid, nsecs_t monoBootOffset) const; + void tracePredictions(pid_t surfaceFlingerPid, nsecs_t monoBootOffset, + bool filterFramesBeforeTraceStarts) const; + void traceActuals(pid_t surfaceFlingerPid, nsecs_t monoBootOffset, + bool filterFramesBeforeTraceStarts) const; void addSkippedFrame(pid_t surfaceFlingerPid, nsecs_t monoBootOffset, - nsecs_t previousActualPresentTime) const; + nsecs_t previousActualPresentTime, + bool filterFramesBeforeTraceStarts) const; void classifyJank(nsecs_t& deadlineDelta, nsecs_t& deltaToVsync, nsecs_t previousPresentTime); @@ -471,7 +484,8 @@ public: }; FrameTimeline(std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid, - JankClassificationThresholds thresholds = {}, bool useBootTimeClock = true); + JankClassificationThresholds thresholds = {}, bool useBootTimeClock = true, + bool filterFramesBeforeTraceStarts = true); ~FrameTimeline() = default; frametimeline::TokenManager* getTokenManager() override { return &mTokenManager; } @@ -516,6 +530,7 @@ private: TraceCookieCounter mTraceCookieCounter; mutable std::mutex mMutex; const bool mUseBootTimeClock; + const bool mFilterFramesBeforeTraceStarts; uint32_t mMaxDisplayFrames; std::shared_ptr<TimeStats> mTimeStats; const pid_t mSurfaceFlingerPid; diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp index 39a6b777bb..d709530990 100644 --- a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp +++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp @@ -18,6 +18,8 @@ #undef LOG_TAG #define LOG_TAG "SurfaceFlinger" +#include <android-base/logging.h> + #include "LayerHierarchy.h" #include "LayerLog.h" #include "SwapErase.h" @@ -413,11 +415,11 @@ void LayerHierarchyBuilder::doUpdate( void LayerHierarchyBuilder::update(LayerLifecycleManager& layerLifecycleManager) { if (!mInitialized) { - ATRACE_NAME("LayerHierarchyBuilder:init"); + SFTRACE_NAME("LayerHierarchyBuilder:init"); init(layerLifecycleManager.getLayers()); } else if (layerLifecycleManager.getGlobalChanges().test( RequestedLayerState::Changes::Hierarchy)) { - ATRACE_NAME("LayerHierarchyBuilder:update"); + SFTRACE_NAME("LayerHierarchyBuilder:update"); doUpdate(layerLifecycleManager.getLayers(), layerLifecycleManager.getDestroyedLayers()); } else { return; // nothing to do @@ -426,7 +428,7 @@ void LayerHierarchyBuilder::update(LayerLifecycleManager& layerLifecycleManager) uint32_t invalidRelativeRoot; bool hasRelZLoop = mRoot.hasRelZLoop(invalidRelativeRoot); while (hasRelZLoop) { - ATRACE_NAME("FixRelZLoop"); + SFTRACE_NAME("FixRelZLoop"); TransactionTraceWriter::getInstance().invoke("relz_loop_detected", /*overwrite=*/false); layerLifecycleManager.fixRelativeZLoop(invalidRelativeRoot); @@ -485,6 +487,55 @@ LayerHierarchy* LayerHierarchyBuilder::getHierarchyFromId(uint32_t layerId, bool return it->second; } +void LayerHierarchyBuilder::logSampledChildren(const LayerHierarchy& hierarchy) const { + LOG(ERROR) << "Dumping random sampling of child layers."; + int sampleSize = static_cast<int>(hierarchy.mChildren.size() / 100 + 1); + for (const auto& [child, variant] : hierarchy.mChildren) { + if (rand() % sampleSize == 0) { + LOG(ERROR) << "Child Layer: " << *(child->mLayer); + } + } +} + +void LayerHierarchyBuilder::dumpLayerSample(const LayerHierarchy& root) const { + LOG(ERROR) << "Dumping layer keeping > 20 children alive:"; + // If mLayer is nullptr, it will be skipped while traversing. + if (!root.mLayer && root.mChildren.size() > 20) { + LOG(ERROR) << "ROOT has " << root.mChildren.size() << " children"; + logSampledChildren(root); + } + root.traverse([&](const LayerHierarchy& hierarchy, const auto&) -> bool { + if (hierarchy.mChildren.size() <= 20) { + return true; + } + // mLayer is ensured to be non-null. See LayerHierarchy::traverse. + const auto* layer = hierarchy.mLayer; + const auto childrenCount = hierarchy.mChildren.size(); + LOG(ERROR) << "Layer " << *layer << " has " << childrenCount << " children"; + + const auto* parent = hierarchy.mParent; + while (parent != nullptr) { + if (!parent->mLayer) break; + LOG(ERROR) << "Parent Layer: " << *(parent->mLayer); + parent = parent->mParent; + } + + logSampledChildren(hierarchy); + // Stop traversing. + return false; + }); + LOG(ERROR) << "Dumping random sampled layers."; + size_t numLayers = 0; + root.traverse([&](const LayerHierarchy& hierarchy, const auto&) -> bool { + if (hierarchy.mLayer) numLayers++; + if ((rand() % 20 == 13) && hierarchy.mLayer) { + LOG(ERROR) << "Layer: " << *(hierarchy.mLayer); + } + return true; + }); + LOG(ERROR) << "Total layer count: " << numLayers; +} + const LayerHierarchy::TraversalPath LayerHierarchy::TraversalPath::ROOT = {.id = UNASSIGNED_LAYER_ID, .variant = LayerHierarchy::Attached}; diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.h b/services/surfaceflinger/FrontEnd/LayerHierarchy.h index d023f9e9f5..47d0041a8b 100644 --- a/services/surfaceflinger/FrontEnd/LayerHierarchy.h +++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.h @@ -211,8 +211,11 @@ public: const LayerHierarchy& getHierarchy() const; const LayerHierarchy& getOffscreenHierarchy() const; std::string getDebugString(uint32_t layerId, uint32_t depth = 0) const; + void dumpLayerSample(const LayerHierarchy& layerHierarchy) const; private: + void logSampledChildren(const LayerHierarchy& hierarchy) const; + void onLayerAdded(RequestedLayerState* layer); void attachToParent(LayerHierarchy*); void detachFromParent(LayerHierarchy*); diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp index 70e3c64a0f..e5f6b7bcd1 100644 --- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp +++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp @@ -322,6 +322,10 @@ std::ostream& operator<<(std::ostream& out, const LayerSnapshot& obj) { << touchableRegion.bottom << "," << touchableRegion.right << "}" << "}"; } + + if (obj.edgeExtensionEffect.hasEffect()) { + out << obj.edgeExtensionEffect; + } return out; } @@ -492,8 +496,10 @@ void LayerSnapshot::merge(const RequestedLayerState& requested, bool forceUpdate requested.what & (layer_state_t::eBufferChanged | layer_state_t::eDataspaceChanged | layer_state_t::eApiChanged | layer_state_t::eShadowRadiusChanged | - layer_state_t::eBlurRegionsChanged | layer_state_t::eStretchChanged)) { - forceClientComposition = shadowSettings.length > 0 || stretchEffect.hasEffect(); + layer_state_t::eBlurRegionsChanged | layer_state_t::eStretchChanged | + layer_state_t::eEdgeExtensionChanged)) { + forceClientComposition = shadowSettings.length > 0 || stretchEffect.hasEffect() || + edgeExtensionEffect.hasEffect(); } if (forceUpdate || diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp index ca53a0dfa6..ee605b7a3e 100644 --- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp +++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp @@ -23,8 +23,8 @@ #include <optional> #include <common/FlagManager.h> +#include <common/trace.h> #include <ftl/small_map.h> -#include <gui/TraceUtils.h> #include <ui/DisplayMap.h> #include <ui/FloatRect.h> @@ -279,24 +279,6 @@ void updateVisibility(LayerSnapshot& snapshot, bool visible) { snapshot.getDebugString().c_str()); } -bool needsInputInfo(const LayerSnapshot& snapshot, const RequestedLayerState& requested) { - if (requested.potentialCursor) { - return false; - } - - if (snapshot.inputInfo.token != nullptr) { - return true; - } - - if (snapshot.hasBufferOrSidebandStream()) { - return true; - } - - return requested.windowInfoHandle && - requested.windowInfoHandle->getInfo()->inputConfig.test( - gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL); -} - void updateMetadata(LayerSnapshot& snapshot, const RequestedLayerState& requested, const LayerSnapshotBuilder::Args& args) { snapshot.metadata.clear(); @@ -317,6 +299,18 @@ void updateMetadata(LayerSnapshot& snapshot, const RequestedLayerState& requeste } } +void updateMetadataAndGameMode(LayerSnapshot& snapshot, const RequestedLayerState& requested, + const LayerSnapshotBuilder::Args& args, + const LayerSnapshot& parentSnapshot) { + snapshot.gameMode = requested.metadata.has(gui::METADATA_GAME_MODE) ? requested.gameMode + : parentSnapshot.gameMode; + updateMetadata(snapshot, requested, args); + if (args.includeMetadata) { + snapshot.layerMetadata = parentSnapshot.layerMetadata; + snapshot.layerMetadata.merge(requested.metadata); + } +} + void clearChanges(LayerSnapshot& snapshot) { snapshot.changes.clear(); snapshot.clientChanges = 0; @@ -352,6 +346,7 @@ LayerSnapshot LayerSnapshotBuilder::getRootSnapshot() { snapshot.geomLayerBounds = getMaxDisplayBounds({}); snapshot.roundedCorner = RoundedCornerState(); snapshot.stretchEffect = {}; + snapshot.edgeExtensionEffect = {}; snapshot.outputFilter.layerStack = ui::DEFAULT_LAYER_STACK; snapshot.outputFilter.toInternalDisplay = false; snapshot.isSecure = false; @@ -387,7 +382,7 @@ bool LayerSnapshotBuilder::tryFastUpdate(const Args& args) { // There are only content changes which do not require any child layer snapshots to be updated. ALOGV("%s", __func__); - ATRACE_NAME("FastPath"); + SFTRACE_NAME("FastPath"); uint32_t primaryDisplayRotationFlags = getPrimaryDisplayRotationFlags(args.displays); if (forceUpdate || args.displayChanges) { @@ -421,7 +416,7 @@ bool LayerSnapshotBuilder::tryFastUpdate(const Args& args) { } void LayerSnapshotBuilder::updateSnapshots(const Args& args) { - ATRACE_NAME("UpdateSnapshots"); + SFTRACE_NAME("UpdateSnapshots"); LayerSnapshot rootSnapshot = args.rootSnapshot; if (args.parentCrop) { rootSnapshot.geomLayerBounds = *args.parentCrop; @@ -548,6 +543,7 @@ const LayerSnapshot& LayerSnapshotBuilder::updateSnapshotsInHierarchy( updateSnapshot(*snapshot, args, *layer, parentSnapshot, traversalPath); } + bool childHasValidFrameRate = false; for (auto& [childHierarchy, variant] : hierarchy.mChildren) { LayerHierarchy::ScopedAddToTraversalPath addChildToPath(traversalPath, childHierarchy->getLayer()->id, @@ -555,7 +551,8 @@ const LayerSnapshot& LayerSnapshotBuilder::updateSnapshotsInHierarchy( const LayerSnapshot& childSnapshot = updateSnapshotsInHierarchy(args, *childHierarchy, traversalPath, *snapshot, depth + 1); - updateFrameRateFromChildSnapshot(*snapshot, childSnapshot, args); + updateFrameRateFromChildSnapshot(*snapshot, childSnapshot, *childHierarchy->getLayer(), + args, &childHasValidFrameRate); } return *snapshot; @@ -662,9 +659,10 @@ void LayerSnapshotBuilder::updateRelativeState(LayerSnapshot& snapshot, } } -void LayerSnapshotBuilder::updateFrameRateFromChildSnapshot(LayerSnapshot& snapshot, - const LayerSnapshot& childSnapshot, - const Args& args) { +void LayerSnapshotBuilder::updateFrameRateFromChildSnapshot( + LayerSnapshot& snapshot, const LayerSnapshot& childSnapshot, + const RequestedLayerState& /* requestedChildState */, const Args& args, + bool* outChildHasValidFrameRate) { if (args.forceUpdate == ForceUpdateFlags::NONE && !args.layerLifecycleManager.getGlobalChanges().any( RequestedLayerState::Changes::Hierarchy) && @@ -674,7 +672,7 @@ void LayerSnapshotBuilder::updateFrameRateFromChildSnapshot(LayerSnapshot& snaps } using FrameRateCompatibility = scheduler::FrameRateCompatibility; - if (snapshot.frameRate.isValid()) { + if (snapshot.inheritedFrameRate.isValid() || *outChildHasValidFrameRate) { // we already have a valid framerate. return; } @@ -691,13 +689,18 @@ void LayerSnapshotBuilder::updateFrameRateFromChildSnapshot(LayerSnapshot& snaps const auto layerVotedWithExactCompatibility = childSnapshot.frameRate.vote.rate.isValid() && childSnapshot.frameRate.vote.type == FrameRateCompatibility::Exact; - bool childHasValidFrameRate = layerVotedWithDefaultCompatibility || layerVotedWithNoVote || + *outChildHasValidFrameRate |= layerVotedWithDefaultCompatibility || layerVotedWithNoVote || layerVotedWithCategory || layerVotedWithExactCompatibility; // If we don't have a valid frame rate, but the children do, we set this // layer as NoVote to allow the children to control the refresh rate - if (childHasValidFrameRate) { - snapshot.frameRate = scheduler::LayerInfo::FrameRate(Fps(), FrameRateCompatibility::NoVote); + static const auto noVote = + scheduler::LayerInfo::FrameRate(Fps(), FrameRateCompatibility::NoVote); + if (*outChildHasValidFrameRate) { + snapshot.frameRate = noVote; + snapshot.changes |= RequestedLayerState::Changes::FrameRate; + } else if (snapshot.frameRate != snapshot.inheritedFrameRate) { + snapshot.frameRate = snapshot.inheritedFrameRate; snapshot.changes |= RequestedLayerState::Changes::FrameRate; } } @@ -762,6 +765,12 @@ void LayerSnapshotBuilder::updateSnapshot(LayerSnapshot& snapshot, const Args& a RequestedLayerState::Changes::Input)) { updateInput(snapshot, requested, parentSnapshot, path, args); } + if (forceUpdate || + (args.includeMetadata && + snapshot.changes.any(RequestedLayerState::Changes::Metadata | + RequestedLayerState::Changes::Geometry))) { + updateMetadataAndGameMode(snapshot, requested, args, parentSnapshot); + } return; } @@ -791,6 +800,32 @@ void LayerSnapshotBuilder::updateSnapshot(LayerSnapshot& snapshot, const Args& a : parentSnapshot.stretchEffect; } + if (forceUpdate || + (snapshot.clientChanges | parentSnapshot.clientChanges) & + layer_state_t::eEdgeExtensionChanged) { + if (requested.edgeExtensionParameters.extendLeft || + requested.edgeExtensionParameters.extendRight || + requested.edgeExtensionParameters.extendTop || + requested.edgeExtensionParameters.extendBottom) { + // This is the root layer to which the extension is applied + snapshot.edgeExtensionEffect = + EdgeExtensionEffect(requested.edgeExtensionParameters.extendLeft, + requested.edgeExtensionParameters.extendRight, + requested.edgeExtensionParameters.extendTop, + requested.edgeExtensionParameters.extendBottom); + } else if (parentSnapshot.clientChanges & layer_state_t::eEdgeExtensionChanged) { + // Extension is inherited + snapshot.edgeExtensionEffect = parentSnapshot.edgeExtensionEffect; + } else { + // There is no edge extension + snapshot.edgeExtensionEffect.reset(); + } + if (snapshot.edgeExtensionEffect.hasEffect()) { + snapshot.clientChanges |= layer_state_t::eEdgeExtensionChanged; + snapshot.changes |= RequestedLayerState::Changes::Geometry; + } + } + if (forceUpdate || snapshot.clientChanges & layer_state_t::eColorTransformChanged) { if (!parentSnapshot.colorTransformIsIdentity) { snapshot.colorTransform = parentSnapshot.colorTransform * requested.colorTransform; @@ -801,15 +836,10 @@ void LayerSnapshotBuilder::updateSnapshot(LayerSnapshot& snapshot, const Args& a } } - if (forceUpdate || snapshot.changes.test(RequestedLayerState::Changes::GameMode)) { - snapshot.gameMode = requested.metadata.has(gui::METADATA_GAME_MODE) - ? requested.gameMode - : parentSnapshot.gameMode; - updateMetadata(snapshot, requested, args); - if (args.includeMetadata) { - snapshot.layerMetadata = parentSnapshot.layerMetadata; - snapshot.layerMetadata.merge(requested.metadata); - } + if (forceUpdate || + snapshot.changes.any(RequestedLayerState::Changes::Metadata | + RequestedLayerState::Changes::Hierarchy)) { + updateMetadataAndGameMode(snapshot, requested, args, parentSnapshot); } if (forceUpdate || snapshot.clientChanges & layer_state_t::eFixedTransformHintChanged || @@ -886,6 +916,10 @@ void LayerSnapshotBuilder::updateSnapshot(LayerSnapshot& snapshot, const Args& a updateLayerBounds(snapshot, requested, parentSnapshot, primaryDisplayRotationFlags); } + if (snapshot.edgeExtensionEffect.hasEffect()) { + updateBoundsForEdgeExtension(snapshot); + } + if (forceUpdate || snapshot.clientChanges & layer_state_t::eCornerRadiusChanged || snapshot.changes.any(RequestedLayerState::Changes::Geometry | RequestedLayerState::Changes::BufferUsageFlags)) { @@ -904,8 +938,8 @@ void LayerSnapshotBuilder::updateSnapshot(LayerSnapshot& snapshot, const Args& a } // computed snapshot properties - snapshot.forceClientComposition = - snapshot.shadowSettings.length > 0 || snapshot.stretchEffect.hasEffect(); + snapshot.forceClientComposition = snapshot.shadowSettings.length > 0 || + snapshot.stretchEffect.hasEffect() || snapshot.edgeExtensionEffect.hasEffect(); snapshot.contentOpaque = snapshot.isContentOpaque(); snapshot.isOpaque = snapshot.contentOpaque && !snapshot.roundedCorner.hasRoundedCorners() && snapshot.color.a == 1.f; @@ -960,6 +994,31 @@ void LayerSnapshotBuilder::updateRoundedCorner(LayerSnapshot& snapshot, } } +/** + * According to the edges that we are requested to extend, we increase the bounds to the maximum + * extension allowed by the crop (parent crop + requested crop). The animation that called + * Transition#setEdgeExtensionEffect is in charge of setting the requested crop. + * @param snapshot + */ +void LayerSnapshotBuilder::updateBoundsForEdgeExtension(LayerSnapshot& snapshot) { + EdgeExtensionEffect& effect = snapshot.edgeExtensionEffect; + + if (effect.extendsEdge(LEFT)) { + snapshot.geomLayerBounds.left = snapshot.geomLayerCrop.left; + } + if (effect.extendsEdge(RIGHT)) { + snapshot.geomLayerBounds.right = snapshot.geomLayerCrop.right; + } + if (effect.extendsEdge(TOP)) { + snapshot.geomLayerBounds.top = snapshot.geomLayerCrop.top; + } + if (effect.extendsEdge(BOTTOM)) { + snapshot.geomLayerBounds.bottom = snapshot.geomLayerCrop.bottom; + } + + snapshot.transformedBounds = snapshot.geomLayerTransform.transform(snapshot.geomLayerBounds); +} + void LayerSnapshotBuilder::updateLayerBounds(LayerSnapshot& snapshot, const RequestedLayerState& requested, const LayerSnapshot& parentSnapshot, @@ -999,11 +1058,12 @@ void LayerSnapshotBuilder::updateLayerBounds(LayerSnapshot& snapshot, FloatRect parentBounds = parentSnapshot.geomLayerBounds; parentBounds = snapshot.localTransform.inverse().transform(parentBounds); snapshot.geomLayerBounds = - (requested.externalTexture) ? snapshot.bufferSize.toFloatRect() : parentBounds; + requested.externalTexture ? snapshot.bufferSize.toFloatRect() : parentBounds; + snapshot.geomLayerCrop = parentBounds; if (!requested.crop.isEmpty()) { - snapshot.geomLayerBounds = snapshot.geomLayerBounds.intersect(requested.crop.toFloatRect()); + snapshot.geomLayerCrop = snapshot.geomLayerCrop.intersect(requested.crop.toFloatRect()); } - snapshot.geomLayerBounds = snapshot.geomLayerBounds.intersect(parentBounds); + snapshot.geomLayerBounds = snapshot.geomLayerBounds.intersect(snapshot.geomLayerCrop); snapshot.transformedBounds = snapshot.geomLayerTransform.transform(snapshot.geomLayerBounds); const Rect geomLayerBoundsWithoutTransparentRegion = RequestedLayerState::reduce(Rect(snapshot.geomLayerBounds), @@ -1084,7 +1144,7 @@ void LayerSnapshotBuilder::updateInput(LayerSnapshot& snapshot, } updateVisibility(snapshot, snapshot.isVisible); - if (!needsInputInfo(snapshot, requested)) { + if (!requested.needsInputInfo()) { return; } @@ -1094,7 +1154,7 @@ void LayerSnapshotBuilder::updateInput(LayerSnapshot& snapshot, bool noValidDisplay = !displayInfoOpt.has_value(); auto displayInfo = displayInfoOpt.value_or(sDefaultInfo); - if (!requested.windowInfoHandle) { + if (!requested.hasInputInfo()) { snapshot.inputInfo.inputConfig = InputConfig::NO_INPUT_CHANNEL; } fillInputFrameInfo(snapshot.inputInfo, displayInfo.transform, snapshot); @@ -1178,6 +1238,21 @@ void LayerSnapshotBuilder::forEachVisibleSnapshot(const Visitor& visitor) { } } +void LayerSnapshotBuilder::forEachSnapshot(const Visitor& visitor, + const ConstPredicate& predicate) { + for (int i = 0; i < mNumInterestingSnapshots; i++) { + std::unique_ptr<LayerSnapshot>& snapshot = mSnapshots.at((size_t)i); + if (!predicate(*snapshot)) continue; + visitor(snapshot); + } +} + +void LayerSnapshotBuilder::forEachSnapshot(const ConstVisitor& visitor) const { + for (auto& snapshot : mSnapshots) { + visitor(*snapshot); + } +} + void LayerSnapshotBuilder::forEachInputSnapshot(const ConstVisitor& visitor) const { for (int i = mNumInterestingSnapshots - 1; i >= 0; i--) { LayerSnapshot& snapshot = *mSnapshots[(size_t)i]; diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h index 1cec0183b9..486cb33959 100644 --- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h +++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h @@ -86,6 +86,14 @@ public: // Visit each visible snapshot in z-order and move the snapshot if needed void forEachVisibleSnapshot(const Visitor& visitor); + typedef std::function<bool(const LayerSnapshot& snapshot)> ConstPredicate; + // Visit each snapshot that satisfies the predicate and move the snapshot if needed with visible + // snapshots in z-order + void forEachSnapshot(const Visitor& visitor, const ConstPredicate& predicate); + + // Visit each snapshot + void forEachSnapshot(const ConstVisitor& visitor) const; + // Visit each snapshot interesting to input reverse z-order void forEachInputSnapshot(const ConstVisitor& visitor) const; @@ -108,6 +116,10 @@ private: static void resetRelativeState(LayerSnapshot& snapshot); static void updateRoundedCorner(LayerSnapshot& snapshot, const RequestedLayerState& layerState, const LayerSnapshot& parentSnapshot, const Args& args); + static bool extensionEdgeSharedWithParent(LayerSnapshot& snapshot, + const RequestedLayerState& requested, + const LayerSnapshot& parentSnapshot); + static void updateBoundsForEdgeExtension(LayerSnapshot& snapshot); void updateLayerBounds(LayerSnapshot& snapshot, const RequestedLayerState& layerState, const LayerSnapshot& parentSnapshot, uint32_t displayRotationFlags); static void updateShadows(LayerSnapshot& snapshot, const RequestedLayerState& requested, @@ -121,7 +133,9 @@ private: const RequestedLayerState& layer, const LayerSnapshot& parentSnapshot); void updateFrameRateFromChildSnapshot(LayerSnapshot& snapshot, - const LayerSnapshot& childSnapshot, const Args& args); + const LayerSnapshot& childSnapshot, + const RequestedLayerState& requestedCHildState, + const Args& args, bool* outChildHasValidFrameRate); void updateTouchableRegionCrop(const Args& args); std::unordered_map<LayerHierarchy::TraversalPath, LayerSnapshot*, diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp index 3e8d74094d..5734ccf38f 100644 --- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp +++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp @@ -19,7 +19,7 @@ #undef LOG_TAG #define LOG_TAG "SurfaceFlinger" -#include <gui/TraceUtils.h> +#include <common/trace.h> #include <log/log.h> #include <private/android_filesystem_config.h> #include <sys/types.h> @@ -62,6 +62,8 @@ RequestedLayerState::RequestedLayerState(const LayerCreationArgs& args) metadata.merge(args.metadata); changes |= RequestedLayerState::Changes::Metadata; handleAlive = true; + // TODO: b/305254099 remove once we don't pass invisible windows to input + windowInfoHandle = nullptr; if (parentId != UNASSIGNED_LAYER_ID) { canBeRoot = false; } @@ -328,6 +330,7 @@ void RequestedLayerState::merge(const ResolvedComposerState& resolvedComposerSta changes |= RequestedLayerState::Changes::GameMode; } } + changes |= RequestedLayerState::Changes::Metadata; } if (clientState.what & layer_state_t::eFrameRateChanged) { const auto compatibility = @@ -552,6 +555,24 @@ bool RequestedLayerState::hasInputInfo() const { windowInfo->inputConfig.test(gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL); } +bool RequestedLayerState::needsInputInfo() const { + if (potentialCursor) { + return false; + } + + if ((sidebandStream != nullptr) || (externalTexture != nullptr)) { + return true; + } + + if (!windowInfoHandle) { + return false; + } + + const auto windowInfo = windowInfoHandle->getInfo(); + return windowInfo->token != nullptr || + windowInfo->inputConfig.test(gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL); +} + bool RequestedLayerState::hasBlur() const { return backgroundBlurRadius > 0 || blurRegions.size() > 0; } @@ -580,8 +601,8 @@ bool RequestedLayerState::backpressureEnabled() const { bool RequestedLayerState::isSimpleBufferUpdate(const layer_state_t& s) const { static constexpr uint64_t requiredFlags = layer_state_t::eBufferChanged; if ((s.what & requiredFlags) != requiredFlags) { - ATRACE_FORMAT_INSTANT("%s: false [missing required flags 0x%" PRIx64 "]", __func__, - (s.what | requiredFlags) & ~s.what); + SFTRACE_FORMAT_INSTANT("%s: false [missing required flags 0x%" PRIx64 "]", __func__, + (s.what | requiredFlags) & ~s.what); return false; } @@ -593,8 +614,8 @@ bool RequestedLayerState::isSimpleBufferUpdate(const layer_state_t& s) const { ? 0 : (layer_state_t::eAutoRefreshChanged | layer_state_t::eFlagsChanged)); if (s.what & deniedFlags) { - ATRACE_FORMAT_INSTANT("%s: false [has denied flags 0x%" PRIx64 "]", __func__, - s.what & deniedFlags); + SFTRACE_FORMAT_INSTANT("%s: false [has denied flags 0x%" PRIx64 "]", __func__, + s.what & deniedFlags); return false; } @@ -608,15 +629,16 @@ bool RequestedLayerState::isSimpleBufferUpdate(const layer_state_t& s) const { layer_state_t::eSidebandStreamChanged | layer_state_t::eColorSpaceAgnosticChanged | layer_state_t::eShadowRadiusChanged | layer_state_t::eFixedTransformHintChanged | layer_state_t::eTrustedOverlayChanged | layer_state_t::eStretchChanged | - layer_state_t::eBufferCropChanged | layer_state_t::eDestinationFrameChanged | - layer_state_t::eDimmingEnabledChanged | layer_state_t::eExtendedRangeBrightnessChanged | + layer_state_t::eEdgeExtensionChanged | layer_state_t::eBufferCropChanged | + layer_state_t::eDestinationFrameChanged | layer_state_t::eDimmingEnabledChanged | + layer_state_t::eExtendedRangeBrightnessChanged | layer_state_t::eDesiredHdrHeadroomChanged | (FlagManager::getInstance().latch_unsignaled_with_auto_refresh_changed() ? layer_state_t::eFlagsChanged : 0); if (changedFlags & deniedChanges) { - ATRACE_FORMAT_INSTANT("%s: false [has denied changes flags 0x%" PRIx64 "]", __func__, - changedFlags & deniedChanges); + SFTRACE_FORMAT_INSTANT("%s: false [has denied changes flags 0x%" PRIx64 "]", __func__, + changedFlags & deniedChanges); return false; } diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.h b/services/surfaceflinger/FrontEnd/RequestedLayerState.h index 48b9640486..1d96dff336 100644 --- a/services/surfaceflinger/FrontEnd/RequestedLayerState.h +++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.h @@ -87,6 +87,7 @@ struct RequestedLayerState : layer_state_t { aidl::android::hardware::graphics::composer3::Composition getCompositionType() const; bool hasValidRelativeParent() const; bool hasInputInfo() const; + bool needsInputInfo() const; bool hasBlur() const; bool hasFrameUpdate() const; bool hasReadyFrame() const; diff --git a/services/surfaceflinger/FrontEnd/TransactionHandler.cpp b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp index d3d950974f..a1e8213132 100644 --- a/services/surfaceflinger/FrontEnd/TransactionHandler.cpp +++ b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp @@ -19,9 +19,9 @@ #define LOG_TAG "SurfaceFlinger" #define ATRACE_TAG ATRACE_TAG_GRAPHICS +#include <common/trace.h> #include <cutils/trace.h> #include <utils/Log.h> -#include <utils/Trace.h> #include "FrontEnd/LayerLog.h" #include "TransactionHandler.h" @@ -31,7 +31,7 @@ namespace android::surfaceflinger::frontend { void TransactionHandler::queueTransaction(TransactionState&& state) { mLocklessTransactionQueue.push(std::move(state)); mPendingTransactionCount.fetch_add(1); - ATRACE_INT("TransactionQueue", static_cast<int>(mPendingTransactionCount.load())); + SFTRACE_INT("TransactionQueue", static_cast<int>(mPendingTransactionCount.load())); } void TransactionHandler::collectTransactions() { @@ -71,7 +71,7 @@ std::vector<TransactionState> TransactionHandler::flushTransactions() { applyUnsignaledBufferTransaction(transactions, flushState); mPendingTransactionCount.fetch_sub(transactions.size()); - ATRACE_INT("TransactionQueue", static_cast<int>(mPendingTransactionCount.load())); + SFTRACE_INT("TransactionQueue", static_cast<int>(mPendingTransactionCount.load())); return transactions; } @@ -83,7 +83,7 @@ void TransactionHandler::applyUnsignaledBufferTransaction( // only apply an unsignaled buffer transaction if it's the first one if (!transactions.empty()) { - ATRACE_NAME("fence unsignaled"); + SFTRACE_NAME("fence unsignaled"); return; } diff --git a/services/surfaceflinger/FrontEnd/Update.h b/services/surfaceflinger/FrontEnd/Update.h index e5cca8fa95..4af27ab84d 100644 --- a/services/surfaceflinger/FrontEnd/Update.h +++ b/services/surfaceflinger/FrontEnd/Update.h @@ -22,28 +22,13 @@ #include "RequestedLayerState.h" #include "TransactionState.h" -namespace android { -struct LayerCreatedState { - LayerCreatedState(const wp<Layer>& layer, const wp<Layer>& parent, bool addToRoot) - : layer(layer), initialParent(parent), addToRoot(addToRoot) {} - wp<Layer> layer; - // Indicates the initial parent of the created layer, only used for creating layer in - // SurfaceFlinger. If nullptr, it may add the created layer into the current root layers. - wp<Layer> initialParent; - // Indicates whether the layer getting created should be added at root if there's no parent - // and has permission ACCESS_SURFACE_FLINGER. If set to false and no parent, the layer will - // be added offscreen. - bool addToRoot; -}; -} // namespace android - namespace android::surfaceflinger::frontend { // Atomic set of changes affecting layer state. These changes are queued in binder threads and // applied every vsync. struct Update { std::vector<TransactionState> transactions; - std::vector<LayerCreatedState> layerCreatedStates; + std::vector<sp<Layer>> legacyLayers; std::vector<std::unique_ptr<frontend::RequestedLayerState>> newLayers; std::vector<LayerCreationArgs> layerCreationArgs; std::vector<std::pair<uint32_t, std::string /* debugName */>> destroyedHandles; diff --git a/services/surfaceflinger/HdrLayerInfoReporter.cpp b/services/surfaceflinger/HdrLayerInfoReporter.cpp index 278833244c..85921bb72d 100644 --- a/services/surfaceflinger/HdrLayerInfoReporter.cpp +++ b/services/surfaceflinger/HdrLayerInfoReporter.cpp @@ -19,8 +19,8 @@ #define ATRACE_TAG ATRACE_TAG_GRAPHICS #include <android-base/stringprintf.h> +#include <common/trace.h> #include <inttypes.h> -#include <utils/Trace.h> #include "HdrLayerInfoReporter.h" @@ -29,7 +29,7 @@ namespace android { using base::StringAppendF; void HdrLayerInfoReporter::dispatchHdrLayerInfo(const HdrLayerInfo& info) { - ATRACE_CALL(); + SFTRACE_CALL(); if (mHdrInfoHistory.size() == 0 || mHdrInfoHistory.back().info != info) { mHdrInfoHistory.next() = EventHistoryEntry{info}; } @@ -47,7 +47,7 @@ void HdrLayerInfoReporter::dispatchHdrLayerInfo(const HdrLayerInfo& info) { } for (const auto& listener : toInvoke) { - ATRACE_NAME("invoking onHdrLayerInfoChanged"); + SFTRACE_NAME("invoking onHdrLayerInfoChanged"); listener->onHdrLayerInfoChanged(info.numberOfHdrLayers, info.maxW, info.maxH, info.flags, info.maxDesiredHdrSdrRatio); } diff --git a/services/surfaceflinger/HdrSdrRatioOverlay.cpp b/services/surfaceflinger/HdrSdrRatioOverlay.cpp index dfb1c1e251..20886358b5 100644 --- a/services/surfaceflinger/HdrSdrRatioOverlay.cpp +++ b/services/surfaceflinger/HdrSdrRatioOverlay.cpp @@ -114,7 +114,7 @@ HdrSdrRatioOverlay::HdrSdrRatioOverlay(ConstructorTag) ALOGE("%s: Failed to create buffer state layer", __func__); return; } - SurfaceComposerClient::Transaction() + createTransaction() .setLayer(mSurfaceControl->get(), INT32_MAX - 2) .setTrustedOverlay(mSurfaceControl->get(), true) .apply(); @@ -130,7 +130,7 @@ void HdrSdrRatioOverlay::changeHdrSdrRatio(float currentHdrSdrRatio) { } void HdrSdrRatioOverlay::setLayerStack(ui::LayerStack stack) { - SurfaceComposerClient::Transaction().setLayerStack(mSurfaceControl->get(), stack).apply(); + createTransaction().setLayerStack(mSurfaceControl->get(), stack).apply(); } void HdrSdrRatioOverlay::setViewport(ui::Size viewport) { @@ -141,7 +141,7 @@ void HdrSdrRatioOverlay::setViewport(ui::Size viewport) { // set the ratio frame to the top right of the screen frame.offsetBy(viewport.width - frame.width(), height >> 4); - SurfaceComposerClient::Transaction() + createTransaction() .setMatrix(mSurfaceControl->get(), frame.getWidth() / static_cast<float>(kBufferWidth), 0, 0, frame.getHeight() / static_cast<float>(kBufferHeight)) .setPosition(mSurfaceControl->get(), frame.left, frame.top) @@ -167,7 +167,7 @@ auto HdrSdrRatioOverlay::getOrCreateBuffers(float currentHdrSdrRatio) -> const s } }(); - SurfaceComposerClient::Transaction().setTransform(mSurfaceControl->get(), transform).apply(); + createTransaction().setTransform(mSurfaceControl->get(), transform).apply(); constexpr SkColor kMinRatioColor = SK_ColorBLUE; constexpr SkColor kMaxRatioColor = SK_ColorGREEN; @@ -194,9 +194,21 @@ auto HdrSdrRatioOverlay::getOrCreateBuffers(float currentHdrSdrRatio) -> const s void HdrSdrRatioOverlay::animate() { if (!std::isfinite(mCurrentHdrSdrRatio) || mCurrentHdrSdrRatio < 1.0f) return; - SurfaceComposerClient::Transaction() + createTransaction() .setBuffer(mSurfaceControl->get(), getOrCreateBuffers(mCurrentHdrSdrRatio)) .apply(); } +SurfaceComposerClient::Transaction HdrSdrRatioOverlay::createTransaction() const { + constexpr float kFrameRate = 0.f; + constexpr int8_t kCompatibility = ANATIVEWINDOW_FRAME_RATE_NO_VOTE; + constexpr int8_t kSeamlessness = ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS; + + const sp<SurfaceControl>& surface = mSurfaceControl->get(); + + SurfaceComposerClient::Transaction transaction; + transaction.setFrameRate(surface, kFrameRate, kCompatibility, kSeamlessness); + return transaction; +} + } // namespace android
\ No newline at end of file diff --git a/services/surfaceflinger/HdrSdrRatioOverlay.h b/services/surfaceflinger/HdrSdrRatioOverlay.h index 72d401d444..ba88252011 100644 --- a/services/surfaceflinger/HdrSdrRatioOverlay.h +++ b/services/surfaceflinger/HdrSdrRatioOverlay.h @@ -53,5 +53,7 @@ private: size_t mIndex = 0; std::array<sp<GraphicBuffer>, 2> mRingBuffer; + + SurfaceComposerClient::Transaction createTransaction() const; }; } // namespace android diff --git a/services/surfaceflinger/Jank/JankTracker.cpp b/services/surfaceflinger/Jank/JankTracker.cpp new file mode 100644 index 0000000000..8e0e084cd2 --- /dev/null +++ b/services/surfaceflinger/Jank/JankTracker.cpp @@ -0,0 +1,206 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "JankTracker.h" + +#include <android/gui/IJankListener.h> +#include "BackgroundExecutor.h" + +namespace android { + +namespace { + +constexpr size_t kJankDataBatchSize = 50; + +} // anonymous namespace + +std::atomic<size_t> JankTracker::sListenerCount(0); +std::atomic<bool> JankTracker::sCollectAllJankDataForTesting(false); + +JankTracker::~JankTracker() {} + +void JankTracker::addJankListener(int32_t layerId, sp<IBinder> listener) { + // Increment right away, so that if an onJankData call comes in before the background thread has + // added this listener, it will not drop the data. + sListenerCount++; + + BackgroundExecutor::getLowPriorityInstance().sendCallbacks( + {[layerId, listener = std::move(listener)]() { + JankTracker& tracker = getInstance(); + const std::lock_guard<std::mutex> _l(tracker.mLock); + tracker.addJankListenerLocked(layerId, listener); + }}); +} + +void JankTracker::flushJankData(int32_t layerId) { + BackgroundExecutor::getLowPriorityInstance().sendCallbacks( + {[layerId]() { getInstance().doFlushJankData(layerId); }}); +} + +void JankTracker::removeJankListener(int32_t layerId, sp<IBinder> listener, int64_t afterVsync) { + BackgroundExecutor::getLowPriorityInstance().sendCallbacks( + {[layerId, listener = std::move(listener), afterVsync]() { + JankTracker& tracker = getInstance(); + const std::lock_guard<std::mutex> _l(tracker.mLock); + tracker.markJankListenerForRemovalLocked(layerId, listener, afterVsync); + }}); +} + +void JankTracker::onJankData(int32_t layerId, gui::JankData data) { + if (sListenerCount == 0) { + return; + } + + BackgroundExecutor::getLowPriorityInstance().sendCallbacks( + {[layerId, data = std::move(data)]() { + JankTracker& tracker = getInstance(); + + tracker.mLock.lock(); + bool hasListeners = tracker.mJankListeners.count(layerId) > 0; + tracker.mLock.unlock(); + + if (!hasListeners && !sCollectAllJankDataForTesting) { + return; + } + + tracker.mJankDataLock.lock(); + tracker.mJankData.emplace(layerId, data); + size_t count = tracker.mJankData.count(layerId); + tracker.mJankDataLock.unlock(); + + if (count >= kJankDataBatchSize && !sCollectAllJankDataForTesting) { + tracker.doFlushJankData(layerId); + } + }}); +} + +void JankTracker::addJankListenerLocked(int32_t layerId, sp<IBinder> listener) { + for (auto it = mJankListeners.find(layerId); it != mJankListeners.end(); it++) { + if (it->second.mListener == listener) { + // Undo the duplicate increment in addJankListener. + sListenerCount--; + return; + } + } + + mJankListeners.emplace(layerId, std::move(listener)); +} + +void JankTracker::doFlushJankData(int32_t layerId) { + std::vector<gui::JankData> jankData; + int64_t maxVsync = transferAvailableJankData(layerId, jankData); + + std::vector<sp<IBinder>> toSend; + + mLock.lock(); + for (auto it = mJankListeners.find(layerId); it != mJankListeners.end();) { + if (!jankData.empty()) { + toSend.emplace_back(it->second.mListener); + } + + int64_t removeAfter = it->second.mRemoveAfter; + if (removeAfter != -1 && removeAfter <= maxVsync) { + it = mJankListeners.erase(it); + sListenerCount--; + } else { + it++; + } + } + mLock.unlock(); + + for (const auto& listener : toSend) { + binder::Status status = interface_cast<gui::IJankListener>(listener)->onJankData(jankData); + if (status.exceptionCode() == binder::Status::EX_NULL_POINTER) { + // Remove any listeners, where the App side has gone away, without + // deregistering. + dropJankListener(layerId, listener); + } + } +} + +void JankTracker::markJankListenerForRemovalLocked(int32_t layerId, sp<IBinder> listener, + int64_t afterVysnc) { + for (auto it = mJankListeners.find(layerId); it != mJankListeners.end(); it++) { + if (it->second.mListener == listener) { + it->second.mRemoveAfter = std::max(static_cast<int64_t>(0), afterVysnc); + return; + } + } +} + +int64_t JankTracker::transferAvailableJankData(int32_t layerId, + std::vector<gui::JankData>& outJankData) { + const std::lock_guard<std::mutex> _l(mJankDataLock); + int64_t maxVsync = 0; + auto range = mJankData.equal_range(layerId); + for (auto it = range.first; it != range.second;) { + maxVsync = std::max(it->second.frameVsyncId, maxVsync); + outJankData.emplace_back(std::move(it->second)); + it = mJankData.erase(it); + } + return maxVsync; +} + +void JankTracker::dropJankListener(int32_t layerId, sp<IBinder> listener) { + const std::lock_guard<std::mutex> _l(mLock); + for (auto it = mJankListeners.find(layerId); it != mJankListeners.end(); it++) { + if (it->second.mListener == listener) { + mJankListeners.erase(it); + sListenerCount--; + return; + } + } +} + +void JankTracker::clearAndStartCollectingAllJankDataForTesting() { + BackgroundExecutor::getLowPriorityInstance().flushQueue(); + + // Clear all past tracked jank data. + JankTracker& tracker = getInstance(); + const std::lock_guard<std::mutex> _l(tracker.mJankDataLock); + tracker.mJankData.clear(); + + // Pretend there's at least one listener. + sListenerCount++; + sCollectAllJankDataForTesting = true; +} + +std::vector<gui::JankData> JankTracker::getCollectedJankDataForTesting(int32_t layerId) { + JankTracker& tracker = getInstance(); + const std::lock_guard<std::mutex> _l(tracker.mJankDataLock); + + auto range = tracker.mJankData.equal_range(layerId); + std::vector<gui::JankData> result; + std::transform(range.first, range.second, std::back_inserter(result), + [](std::pair<int32_t, gui::JankData> layerIdToJankData) { + return layerIdToJankData.second; + }); + + return result; +} + +void JankTracker::clearAndStopCollectingAllJankDataForTesting() { + // Undo startCollectingAllJankDataForTesting. + sListenerCount--; + sCollectAllJankDataForTesting = false; + + // Clear all tracked jank data. + JankTracker& tracker = getInstance(); + const std::lock_guard<std::mutex> _l(tracker.mJankDataLock); + tracker.mJankData.clear(); +} + +} // namespace android diff --git a/services/surfaceflinger/Jank/JankTracker.h b/services/surfaceflinger/Jank/JankTracker.h new file mode 100644 index 0000000000..5917358797 --- /dev/null +++ b/services/surfaceflinger/Jank/JankTracker.h @@ -0,0 +1,99 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <cstdint> +#include <mutex> +#include <unordered_map> + +#include <android/gui/JankData.h> +#include <binder/IBinder.h> +#include <utils/Mutex.h> + +namespace android { +namespace frametimeline { +class FrameTimelineTest; +} + +/** + * JankTracker maintains a backlog of frame jank classification and manages and notififies any + * registered jank data listeners. + */ +class JankTracker { +public: + ~JankTracker(); + + static void addJankListener(int32_t layerId, sp<IBinder> listener); + static void flushJankData(int32_t layerId); + static void removeJankListener(int32_t layerId, sp<IBinder> listener, int64_t afterVysnc); + + static void onJankData(int32_t layerId, gui::JankData data); + +protected: + // The following methods can be used to force the tracker to collect all jank data and not + // flush it for a short time period and should *only* be used for testing. Every call to + // clearAndStartCollectingAllJankDataForTesting needs to be followed by a call to + // clearAndStopCollectingAllJankDataForTesting. + static void clearAndStartCollectingAllJankDataForTesting(); + static std::vector<gui::JankData> getCollectedJankDataForTesting(int32_t layerId); + static void clearAndStopCollectingAllJankDataForTesting(); + + friend class frametimeline::FrameTimelineTest; + +private: + JankTracker() {} + JankTracker(const JankTracker&) = delete; + JankTracker(JankTracker&&) = delete; + + JankTracker& operator=(const JankTracker&) = delete; + JankTracker& operator=(JankTracker&&) = delete; + + static JankTracker& getInstance() { + static JankTracker instance; + return instance; + } + + void addJankListenerLocked(int32_t layerId, sp<IBinder> listener) REQUIRES(mLock); + void doFlushJankData(int32_t layerId); + void markJankListenerForRemovalLocked(int32_t layerId, sp<IBinder> listener, int64_t afterVysnc) + REQUIRES(mLock); + + int64_t transferAvailableJankData(int32_t layerId, std::vector<gui::JankData>& jankData); + void dropJankListener(int32_t layerId, sp<IBinder> listener); + + struct Listener { + sp<IBinder> mListener; + int64_t mRemoveAfter; + + Listener(sp<IBinder>&& listener) : mListener(listener), mRemoveAfter(-1) {} + }; + + // We keep track of the current listener count, so that the onJankData call, which is on the + // main thread, can short-curcuit the scheduling on the background thread (which involves + // locking) if there are no listeners registered, which is the most common case. + static std::atomic<size_t> sListenerCount; + static std::atomic<bool> sCollectAllJankDataForTesting; + + std::mutex mLock; + std::unordered_multimap<int32_t, Listener> mJankListeners GUARDED_BY(mLock); + std::mutex mJankDataLock; + std::unordered_multimap<int32_t, gui::JankData> mJankData GUARDED_BY(mJankDataLock); + + friend class JankTrackerTest; +}; + +} // namespace android diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index c39b7576df..dcb0812b67 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -27,6 +27,7 @@ #include <android-base/properties.h> #include <android-base/stringprintf.h> #include <binder/IPCThreadState.h> +#include <common/trace.h> #include <compositionengine/CompositionEngine.h> #include <compositionengine/Display.h> #include <compositionengine/LayerFECompositionState.h> @@ -39,7 +40,6 @@ #include <ftl/fake_guard.h> #include <gui/BufferItem.h> #include <gui/Surface.h> -#include <gui/TraceUtils.h> #include <math.h> #include <private/android_filesystem_config.h> #include <renderengine/RenderEngine.h> @@ -58,12 +58,9 @@ #include <utils/Log.h> #include <utils/NativeHandle.h> #include <utils/StopWatch.h> -#include <utils/Trace.h> #include <algorithm> -#include <mutex> #include <optional> -#include <sstream> #include "DisplayDevice.h" #include "DisplayHardware/HWComposer.h" @@ -73,7 +70,6 @@ #include "FrontEnd/LayerHandle.h" #include "Layer.h" #include "LayerProtoHelper.h" -#include "MutexUtils.h" #include "SurfaceFlinger.h" #include "TimeStats/TimeStats.h" #include "TransactionCallbackInvoker.h" @@ -90,18 +86,6 @@ constexpr int kDumpTableRowLength = 159; const ui::Transform kIdentityTransform; -ui::LogicalDisplayId toLogicalDisplayId(const ui::LayerStack& layerStack) { - return ui::LogicalDisplayId{static_cast<int32_t>(layerStack.id)}; -} - -bool assignTransform(ui::Transform* dst, ui::Transform& from) { - if (*dst == from) { - return false; - } - *dst = from; - return true; -} - TimeStats::SetFrameRateVote frameRateToSetFrameRateVotePayload(Layer::FrameRate frameRate) { using FrameRateCompatibility = TimeStats::SetFrameRateVote::FrameRateCompatibility; using Seamlessness = TimeStats::SetFrameRateVote::Seamlessness; @@ -150,24 +134,11 @@ Layer::Layer(const surfaceflinger::LayerCreationArgs& args) : sequence(args.sequence), mFlinger(sp<SurfaceFlinger>::fromExisting(args.flinger)), mName(base::StringPrintf("%s#%d", args.name.c_str(), sequence)), - mClientRef(args.client), mWindowType(static_cast<WindowInfo::Type>( - args.metadata.getInt32(gui::METADATA_WINDOW_TYPE, 0))), - mLayerCreationFlags(args.flags), - mLegacyLayerFE(args.flinger->getFactory().createLayerFE(mName, this)) { + args.metadata.getInt32(gui::METADATA_WINDOW_TYPE, 0))) { ALOGV("Creating Layer %s", getDebugName()); - uint32_t layerFlags = 0; - if (args.flags & ISurfaceComposerClient::eHidden) layerFlags |= layer_state_t::eLayerHidden; - if (args.flags & ISurfaceComposerClient::eOpaque) layerFlags |= layer_state_t::eLayerOpaque; - if (args.flags & ISurfaceComposerClient::eSecure) layerFlags |= layer_state_t::eLayerSecure; - if (args.flags & ISurfaceComposerClient::eSkipScreenshot) - layerFlags |= layer_state_t::eLayerSkipScreenshot; - mDrawingState.flags = layerFlags; mDrawingState.crop.makeInvalid(); - mDrawingState.z = 0; - mDrawingState.color.a = 1.0f; - mDrawingState.layerStack = ui::DEFAULT_LAYER_STACK; mDrawingState.sequence = 0; mDrawingState.transform.set(0, 0); mDrawingState.frameNumber = 0; @@ -180,33 +151,9 @@ Layer::Layer(const surfaceflinger::LayerCreationArgs& args) mDrawingState.acquireFence = sp<Fence>::make(-1); mDrawingState.acquireFenceTime = std::make_shared<FenceTime>(mDrawingState.acquireFence); mDrawingState.dataspace = ui::Dataspace::V0_SRGB; - mDrawingState.hdrMetadata.validTypes = 0; - mDrawingState.surfaceDamageRegion = Region::INVALID_REGION; - mDrawingState.cornerRadius = 0.0f; - mDrawingState.backgroundBlurRadius = 0; - mDrawingState.api = -1; - mDrawingState.hasColorTransform = false; - mDrawingState.colorSpaceAgnostic = false; - mDrawingState.frameRateSelectionPriority = PRIORITY_UNSET; mDrawingState.metadata = args.metadata; - mDrawingState.shadowRadius = 0.f; - mDrawingState.fixedTransformHint = ui::Transform::ROT_INVALID; mDrawingState.frameTimelineInfo = {}; mDrawingState.postTime = -1; - mDrawingState.destinationFrame.makeInvalid(); - mDrawingState.isTrustedOverlay = false; - mDrawingState.dropInputMode = gui::DropInputMode::NONE; - mDrawingState.dimmingEnabled = true; - mDrawingState.defaultFrameRateCompatibility = FrameRateCompatibility::Default; - mDrawingState.frameRateSelectionStrategy = FrameRateSelectionStrategy::Propagate; - - if (args.flags & ISurfaceComposerClient::eNoColorFill) { - // Set an invalid color so there is no color fill. - mDrawingState.color.r = -1.0_hf; - mDrawingState.color.g = -1.0_hf; - mDrawingState.color.b = -1.0_hf; - } - mFrameTracker.setDisplayRefreshPeriod( args.flinger->mScheduler->getPacesetterVsyncPeriod().ns()); @@ -214,14 +161,9 @@ Layer::Layer(const surfaceflinger::LayerCreationArgs& args) mOwnerPid = args.ownerPid; mOwnerAppId = mOwnerUid % PER_USER_RANGE; - mPremultipliedAlpha = !(args.flags & ISurfaceComposerClient::eNonPremultiplied); mPotentialCursor = args.flags & ISurfaceComposerClient::eCursorWindow; - mProtectedByApp = args.flags & ISurfaceComposerClient::eProtectedByApp; - - mSnapshot->sequence = sequence; - mSnapshot->name = getDebugName(); - mSnapshot->premultipliedAlpha = mPremultipliedAlpha; - mSnapshot->parentTransform = {}; + mLayerFEs.emplace_back(frontend::LayerHierarchy::TraversalPath{static_cast<uint32_t>(sequence)}, + args.flinger->getFactory().createLayerFE(mName, this)); } void Layer::onFirstRef() { @@ -232,10 +174,6 @@ Layer::~Layer() { LOG_ALWAYS_FATAL_IF(std::this_thread::get_id() != mFlinger->mMainThreadId, "Layer destructor called off the main thread."); - // The original layer and the clone layer share the same texture and buffer. Therefore, only - // one of the layers, in this case the original layer, needs to handle the deletion. The - // original layer and the clone should be removed at the same time so there shouldn't be any - // issue with the clone layer trying to use the texture. if (mBufferInfo.mBuffer != nullptr) { callReleaseBufferCallback(mDrawingState.releaseBufferListener, mBufferInfo.mBuffer->getBuffer(), mBufferInfo.mFrameNumber, @@ -251,10 +189,6 @@ Layer::~Layer() { if (mDrawingState.sidebandStream != nullptr) { mFlinger->mTunnelModeEnabledReporter->decrementTunnelModeCount(); } - if (mHadClonedChild) { - auto& roots = mFlinger->mLayerMirrorRoots; - roots.erase(std::remove(roots.begin(), roots.end(), this), roots.end()); - } if (hasTrustedPresentationListener()) { mFlinger->mNumTrustedPresentationListeners--; updateTrustedPresentationState(nullptr, nullptr, -1 /* time_in_ms */, true /* leaveState*/); @@ -262,78 +196,8 @@ Layer::~Layer() { } // --------------------------------------------------------------------------- -// callbacks -// --------------------------------------------------------------------------- - -void Layer::removeRelativeZ(const std::vector<Layer*>& layersInTree) { - if (mDrawingState.zOrderRelativeOf == nullptr) { - return; - } - - sp<Layer> strongRelative = mDrawingState.zOrderRelativeOf.promote(); - if (strongRelative == nullptr) { - setZOrderRelativeOf(nullptr); - return; - } - - if (!std::binary_search(layersInTree.begin(), layersInTree.end(), strongRelative.get())) { - strongRelative->removeZOrderRelative(wp<Layer>::fromExisting(this)); - mFlinger->setTransactionFlags(eTraversalNeeded); - setZOrderRelativeOf(nullptr); - } -} - -void Layer::removeFromCurrentState() { - if (!mRemovedFromDrawingState) { - mRemovedFromDrawingState = true; - mFlinger->mScheduler->deregisterLayer(this); - } - updateTrustedPresentationState(nullptr, nullptr, -1 /* time_in_ms */, true /* leaveState*/); - - mFlinger->markLayerPendingRemovalLocked(sp<Layer>::fromExisting(this)); -} - -sp<Layer> Layer::getRootLayer() { - sp<Layer> parent = getParent(); - if (parent == nullptr) { - return sp<Layer>::fromExisting(this); - } - return parent->getRootLayer(); -} - -void Layer::onRemovedFromCurrentState() { - // Use the root layer since we want to maintain the hierarchy for the entire subtree. - auto layersInTree = getRootLayer()->getLayersInTree(LayerVector::StateSet::Current); - std::sort(layersInTree.begin(), layersInTree.end()); - - REQUIRE_MUTEX(mFlinger->mStateLock); - traverse(LayerVector::StateSet::Current, - [&](Layer* layer) REQUIRES(layer->mFlinger->mStateLock) { - layer->removeFromCurrentState(); - layer->removeRelativeZ(layersInTree); - }); -} - -void Layer::addToCurrentState() { - if (mRemovedFromDrawingState) { - mRemovedFromDrawingState = false; - mFlinger->mScheduler->registerLayer(this); - mFlinger->removeFromOffscreenLayers(this); - } - - for (const auto& child : mCurrentChildren) { - child->addToCurrentState(); - } -} - -// --------------------------------------------------------------------------- // set-up // --------------------------------------------------------------------------- - -bool Layer::getPremultipledAlpha() const { - return mPremultipliedAlpha; -} - sp<IBinder> Layer::getHandle() { Mutex::Autolock _l(mLock); if (mGetHandleCalled) { @@ -349,46 +213,6 @@ sp<IBinder> Layer::getHandle() { // h/w composer set-up // --------------------------------------------------------------------------- -static Rect reduce(const Rect& win, const Region& exclude) { - if (CC_LIKELY(exclude.isEmpty())) { - return win; - } - if (exclude.isRect()) { - return win.reduce(exclude.getBounds()); - } - return Region(win).subtract(exclude).getBounds(); -} - -static FloatRect reduce(const FloatRect& win, const Region& exclude) { - if (CC_LIKELY(exclude.isEmpty())) { - return win; - } - // Convert through Rect (by rounding) for lack of FloatRegion - return Region(Rect{win}).subtract(exclude).getBounds().toFloatRect(); -} - -Rect Layer::getScreenBounds(bool reduceTransparentRegion) const { - if (!reduceTransparentRegion) { - return Rect{mScreenBounds}; - } - - FloatRect bounds = getBounds(); - ui::Transform t = getTransform(); - // Transform to screen space. - bounds = t.transform(bounds); - return Rect{bounds}; -} - -FloatRect Layer::getBounds() const { - const State& s(getDrawingState()); - return getBounds(getActiveTransparentRegion(s)); -} - -FloatRect Layer::getBounds(const Region& activeTransparentRegion) const { - // Subtract the transparent region and snap to the bounds. - return reduce(mBounds, activeTransparentRegion); -} - // No early returns. void Layer::updateTrustedPresentationState(const DisplayDevice* display, const frontend::LayerSnapshot* snapshot, @@ -490,57 +314,6 @@ bool Layer::computeTrustedPresentationState(const FloatRect& bounds, const Float return true; } -void Layer::computeBounds(FloatRect parentBounds, ui::Transform parentTransform, - float parentShadowRadius) { - const State& s(getDrawingState()); - - // Calculate effective layer transform - mEffectiveTransform = parentTransform * getActiveTransform(s); - - if (CC_UNLIKELY(!isTransformValid())) { - ALOGW("Stop computing bounds for %s because it has invalid transformation.", - getDebugName()); - return; - } - - // Transform parent bounds to layer space - parentBounds = getActiveTransform(s).inverse().transform(parentBounds); - - // Calculate source bounds - mSourceBounds = computeSourceBounds(parentBounds); - - // Calculate bounds by croping diplay frame with layer crop and parent bounds - FloatRect bounds = mSourceBounds; - const Rect layerCrop = getCrop(s); - if (!layerCrop.isEmpty()) { - bounds = mSourceBounds.intersect(layerCrop.toFloatRect()); - } - bounds = bounds.intersect(parentBounds); - - mBounds = bounds; - mScreenBounds = mEffectiveTransform.transform(mBounds); - - // Use the layer's own shadow radius if set. Otherwise get the radius from - // parent. - if (s.shadowRadius > 0.f) { - mEffectiveShadowRadius = s.shadowRadius; - } else { - mEffectiveShadowRadius = parentShadowRadius; - } - - // Shadow radius is passed down to only one layer so if the layer can draw shadows, - // don't pass it to its children. - const float childShadowRadius = canDrawShadows() ? 0.f : mEffectiveShadowRadius; - - for (const sp<Layer>& child : mDrawingChildren) { - child->computeBounds(mBounds, mEffectiveTransform, childShadowRadius); - } - - if (mPotentialCursor) { - prepareCursorCompositionState(); - } -} - Rect Layer::getCroppedBufferSize(const State& s) const { Rect size = getBufferSize(s); Rect crop = getCrop(s); @@ -552,181 +325,6 @@ Rect Layer::getCroppedBufferSize(const State& s) const { return size; } -void Layer::setupRoundedCornersCropCoordinates(Rect win, - const FloatRect& roundedCornersCrop) const { - // Translate win by the rounded corners rect coordinates, to have all values in - // layer coordinate space. - win.left -= roundedCornersCrop.left; - win.right -= roundedCornersCrop.left; - win.top -= roundedCornersCrop.top; - win.bottom -= roundedCornersCrop.top; -} - -void Layer::prepareBasicGeometryCompositionState() { - const auto& drawingState{getDrawingState()}; - const auto alpha = static_cast<float>(getAlpha()); - const bool opaque = isOpaque(drawingState); - const bool usesRoundedCorners = hasRoundedCorners(); - - auto blendMode = Hwc2::IComposerClient::BlendMode::NONE; - if (!opaque || alpha != 1.0f) { - blendMode = mPremultipliedAlpha ? Hwc2::IComposerClient::BlendMode::PREMULTIPLIED - : Hwc2::IComposerClient::BlendMode::COVERAGE; - } - - // Please keep in sync with LayerSnapshotBuilder - auto* snapshot = editLayerSnapshot(); - snapshot->outputFilter = getOutputFilter(); - snapshot->isVisible = isVisible(); - snapshot->isOpaque = opaque && !usesRoundedCorners && alpha == 1.f; - snapshot->shadowSettings.length = mEffectiveShadowRadius; - - snapshot->contentDirty = contentDirty; - contentDirty = false; - - snapshot->geomLayerBounds = mBounds; - snapshot->geomLayerTransform = getTransform(); - snapshot->geomInverseLayerTransform = snapshot->geomLayerTransform.inverse(); - snapshot->transparentRegionHint = getActiveTransparentRegion(drawingState); - snapshot->localTransform = getActiveTransform(drawingState); - snapshot->localTransformInverse = snapshot->localTransform.inverse(); - snapshot->blendMode = static_cast<Hwc2::IComposerClient::BlendMode>(blendMode); - snapshot->alpha = alpha; - snapshot->backgroundBlurRadius = getBackgroundBlurRadius(); - snapshot->blurRegions = getBlurRegions(); - snapshot->stretchEffect = getStretchEffect(); -} - -void Layer::prepareGeometryCompositionState() { - const auto& drawingState{getDrawingState()}; - auto* snapshot = editLayerSnapshot(); - - // Please keep in sync with LayerSnapshotBuilder - snapshot->geomBufferSize = getBufferSize(drawingState); - snapshot->geomContentCrop = getBufferCrop(); - snapshot->geomCrop = getCrop(drawingState); - snapshot->geomBufferTransform = getBufferTransform(); - snapshot->geomBufferUsesDisplayInverseTransform = getTransformToDisplayInverse(); - snapshot->geomUsesSourceCrop = usesSourceCrop(); - snapshot->isSecure = isSecure(); - - snapshot->metadata.clear(); - const auto& supportedMetadata = mFlinger->getHwComposer().getSupportedLayerGenericMetadata(); - for (const auto& [key, mandatory] : supportedMetadata) { - const auto& genericLayerMetadataCompatibilityMap = - mFlinger->getGenericLayerMetadataKeyMap(); - auto compatIter = genericLayerMetadataCompatibilityMap.find(key); - if (compatIter == std::end(genericLayerMetadataCompatibilityMap)) { - continue; - } - const uint32_t id = compatIter->second; - - auto it = drawingState.metadata.mMap.find(id); - if (it == std::end(drawingState.metadata.mMap)) { - continue; - } - - snapshot->metadata.emplace(key, - compositionengine::GenericLayerMetadataEntry{mandatory, - it->second}); - } -} - -void Layer::preparePerFrameCompositionState() { - const auto& drawingState{getDrawingState()}; - // Please keep in sync with LayerSnapshotBuilder - auto* snapshot = editLayerSnapshot(); - - snapshot->forceClientComposition = false; - - snapshot->isColorspaceAgnostic = isColorSpaceAgnostic(); - snapshot->dataspace = getDataSpace(); - snapshot->colorTransform = getColorTransform(); - snapshot->colorTransformIsIdentity = !hasColorTransform(); - snapshot->surfaceDamage = surfaceDamageRegion; - snapshot->hasProtectedContent = isProtected(); - snapshot->dimmingEnabled = isDimmingEnabled(); - snapshot->currentHdrSdrRatio = getCurrentHdrSdrRatio(); - snapshot->desiredHdrSdrRatio = getDesiredHdrSdrRatio(); - snapshot->cachingHint = getCachingHint(); - - const bool usesRoundedCorners = hasRoundedCorners(); - - snapshot->isOpaque = isOpaque(drawingState) && !usesRoundedCorners && getAlpha() == 1.0_hf; - - // Force client composition for special cases known only to the front-end. - // Rounded corners no longer force client composition, since we may use a - // hole punch so that the layer will appear to have rounded corners. - if (drawShadows() || snapshot->stretchEffect.hasEffect()) { - snapshot->forceClientComposition = true; - } - // If there are no visible region changes, we still need to update blur parameters. - snapshot->blurRegions = getBlurRegions(); - snapshot->backgroundBlurRadius = getBackgroundBlurRadius(); - - // Layer framerate is used in caching decisions. - // Retrieve it from the scheduler which maintains an instance of LayerHistory, and store it in - // LayerFECompositionState where it would be visible to Flattener. - snapshot->fps = mFlinger->getLayerFramerate(systemTime(), getSequence()); - - if (hasBufferOrSidebandStream()) { - preparePerFrameBufferCompositionState(); - } else { - preparePerFrameEffectsCompositionState(); - } -} - -void Layer::preparePerFrameBufferCompositionState() { - // Please keep in sync with LayerSnapshotBuilder - auto* snapshot = editLayerSnapshot(); - // Sideband layers - if (snapshot->sidebandStream.get() && !snapshot->sidebandStreamHasFrame) { - snapshot->compositionType = - aidl::android::hardware::graphics::composer3::Composition::SIDEBAND; - return; - } else if ((mDrawingState.flags & layer_state_t::eLayerIsDisplayDecoration) != 0) { - snapshot->compositionType = - aidl::android::hardware::graphics::composer3::Composition::DISPLAY_DECORATION; - } else if ((mDrawingState.flags & layer_state_t::eLayerIsRefreshRateIndicator) != 0) { - snapshot->compositionType = - aidl::android::hardware::graphics::composer3::Composition::REFRESH_RATE_INDICATOR; - } else { - // Normal buffer layers - snapshot->hdrMetadata = mBufferInfo.mHdrMetadata; - snapshot->compositionType = mPotentialCursor - ? aidl::android::hardware::graphics::composer3::Composition::CURSOR - : aidl::android::hardware::graphics::composer3::Composition::DEVICE; - } - - snapshot->buffer = getBuffer(); - snapshot->acquireFence = mBufferInfo.mFence; - snapshot->frameNumber = mBufferInfo.mFrameNumber; - snapshot->sidebandStreamHasFrame = false; -} - -void Layer::preparePerFrameEffectsCompositionState() { - // Please keep in sync with LayerSnapshotBuilder - auto* snapshot = editLayerSnapshot(); - snapshot->color = getColor(); - snapshot->compositionType = - aidl::android::hardware::graphics::composer3::Composition::SOLID_COLOR; -} - -void Layer::prepareCursorCompositionState() { - const State& drawingState{getDrawingState()}; - // Please keep in sync with LayerSnapshotBuilder - auto* snapshot = editLayerSnapshot(); - - // Apply the layer's transform, followed by the display's global transform - // Here we're guaranteed that the layer's transform preserves rects - Rect win = getCroppedBufferSize(drawingState); - // Subtract the transparent region and snap to the bounds - Rect bounds = reduce(win, getActiveTransparentRegion(drawingState)); - Rect frame(getTransform().transform(bounds)); - - snapshot->cursorFrame = frame; -} - const char* Layer::getDebugName() const { return mName.c_str(); } @@ -754,91 +352,9 @@ aidl::android::hardware::graphics::composer3::Composition Layer::getCompositionT } // ---------------------------------------------------------------------------- -// local state -// ---------------------------------------------------------------------------- - -bool Layer::isSecure() const { - const State& s(mDrawingState); - if (s.flags & layer_state_t::eLayerSecure) { - return true; - } - - const auto p = mDrawingParent.promote(); - return (p != nullptr) ? p->isSecure() : false; -} - -void Layer::transferAvailableJankData(const std::deque<sp<CallbackHandle>>& handles, - std::vector<JankData>& jankData) { - if (mPendingJankClassifications.empty() || - !mPendingJankClassifications.front()->getJankType()) { - return; - } - - bool includeJankData = false; - for (const auto& handle : handles) { - for (const auto& cb : handle->callbackIds) { - if (cb.includeJankData) { - includeJankData = true; - break; - } - } - - if (includeJankData) { - jankData.reserve(mPendingJankClassifications.size()); - break; - } - } - - while (!mPendingJankClassifications.empty() && - mPendingJankClassifications.front()->getJankType()) { - if (includeJankData) { - std::shared_ptr<frametimeline::SurfaceFrame> surfaceFrame = - mPendingJankClassifications.front(); - jankData.emplace_back(JankData(surfaceFrame->getToken(), - surfaceFrame->getJankType().value(), - surfaceFrame->getRenderRate().getPeriodNsecs())); - } - mPendingJankClassifications.pop_front(); - } -} - -// ---------------------------------------------------------------------------- // transaction // ---------------------------------------------------------------------------- -uint32_t Layer::doTransaction(uint32_t flags) { - ATRACE_CALL(); - - // TODO: This is unfortunate. - mDrawingStateModified = mDrawingState.modified; - mDrawingState.modified = false; - - const State& s(getDrawingState()); - - if (updateGeometry()) { - // invalidate and recompute the visible regions if needed - flags |= Layer::eVisibleRegion; - } - - if (s.sequence != mLastCommittedTxSequence) { - // invalidate and recompute the visible regions if needed - mLastCommittedTxSequence = s.sequence; - flags |= eVisibleRegion; - this->contentDirty = true; - - // we may use linear filtering, if the matrix scales us - mNeedsFiltering = getActiveTransform(s).needsBilinearFiltering(); - } - - if (!mPotentialCursor && (flags & Layer::eVisibleRegion)) { - mFlinger->mUpdateInputInfo = true; - } - - commitTransaction(); - - return flags; -} - void Layer::commitTransaction() { // Set the present state for all bufferlessSurfaceFramesTX to Presented. The // bufferSurfaceFrameTX will be presented in latchBuffer. @@ -853,504 +369,25 @@ void Layer::commitTransaction() { mDrawingState.bufferlessSurfaceFramesTX.clear(); } -uint32_t Layer::clearTransactionFlags(uint32_t mask) { - const auto flags = mTransactionFlags & mask; - mTransactionFlags &= ~mask; - return flags; -} - void Layer::setTransactionFlags(uint32_t mask) { mTransactionFlags |= mask; } -bool Layer::setChildLayer(const sp<Layer>& childLayer, int32_t z) { - ssize_t idx = mCurrentChildren.indexOf(childLayer); - if (idx < 0) { - return false; - } - if (childLayer->setLayer(z)) { - mCurrentChildren.removeAt(idx); - mCurrentChildren.add(childLayer); - return true; - } - return false; -} - -bool Layer::setChildRelativeLayer(const sp<Layer>& childLayer, - const sp<IBinder>& relativeToHandle, int32_t relativeZ) { - ssize_t idx = mCurrentChildren.indexOf(childLayer); - if (idx < 0) { - return false; - } - if (childLayer->setRelativeLayer(relativeToHandle, relativeZ)) { - mCurrentChildren.removeAt(idx); - mCurrentChildren.add(childLayer); - return true; - } - return false; -} - -bool Layer::setLayer(int32_t z) { - if (mDrawingState.z == z && !usingRelativeZ(LayerVector::StateSet::Current)) return false; - mDrawingState.sequence++; - mDrawingState.z = z; - mDrawingState.modified = true; - - mFlinger->mSomeChildrenChanged = true; - - // Discard all relative layering. - if (mDrawingState.zOrderRelativeOf != nullptr) { - sp<Layer> strongRelative = mDrawingState.zOrderRelativeOf.promote(); - if (strongRelative != nullptr) { - strongRelative->removeZOrderRelative(wp<Layer>::fromExisting(this)); - } - setZOrderRelativeOf(nullptr); - } - setTransactionFlags(eTransactionNeeded); - return true; -} - -void Layer::removeZOrderRelative(const wp<Layer>& relative) { - mDrawingState.zOrderRelatives.remove(relative); - mDrawingState.sequence++; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); -} - -void Layer::addZOrderRelative(const wp<Layer>& relative) { - mDrawingState.zOrderRelatives.add(relative); - mDrawingState.modified = true; - mDrawingState.sequence++; - setTransactionFlags(eTransactionNeeded); -} - -void Layer::setZOrderRelativeOf(const wp<Layer>& relativeOf) { - mDrawingState.zOrderRelativeOf = relativeOf; - mDrawingState.sequence++; - mDrawingState.modified = true; - mDrawingState.isRelativeOf = relativeOf != nullptr; - - setTransactionFlags(eTransactionNeeded); -} - -bool Layer::setRelativeLayer(const sp<IBinder>& relativeToHandle, int32_t relativeZ) { - sp<Layer> relative = LayerHandle::getLayer(relativeToHandle); - if (relative == nullptr) { - return false; - } - - if (mDrawingState.z == relativeZ && usingRelativeZ(LayerVector::StateSet::Current) && - mDrawingState.zOrderRelativeOf == relative) { - return false; - } - - if (CC_UNLIKELY(relative->usingRelativeZ(LayerVector::StateSet::Drawing)) && - (relative->mDrawingState.zOrderRelativeOf == this)) { - ALOGE("Detected relative layer loop between %s and %s", - mName.c_str(), relative->mName.c_str()); - ALOGE("Ignoring new call to set relative layer"); - return false; - } - - mFlinger->mSomeChildrenChanged = true; - - mDrawingState.sequence++; - mDrawingState.modified = true; - mDrawingState.z = relativeZ; - - auto oldZOrderRelativeOf = mDrawingState.zOrderRelativeOf.promote(); - if (oldZOrderRelativeOf != nullptr) { - oldZOrderRelativeOf->removeZOrderRelative(wp<Layer>::fromExisting(this)); - } - setZOrderRelativeOf(relative); - relative->addZOrderRelative(wp<Layer>::fromExisting(this)); - - setTransactionFlags(eTransactionNeeded); - - return true; -} - -bool Layer::setTrustedOverlay(bool isTrustedOverlay) { - if (mDrawingState.isTrustedOverlay == isTrustedOverlay) return false; - mDrawingState.isTrustedOverlay = isTrustedOverlay; - mDrawingState.modified = true; - mFlinger->mUpdateInputInfo = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -bool Layer::isTrustedOverlay() const { - if (getDrawingState().isTrustedOverlay) { - return true; - } - const auto& p = mDrawingParent.promote(); - return (p != nullptr) && p->isTrustedOverlay(); -} - -bool Layer::setAlpha(float alpha) { - if (mDrawingState.color.a == alpha) return false; - mDrawingState.sequence++; - mDrawingState.color.a = alpha; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -bool Layer::setBackgroundColor(const half3& color, float alpha, ui::Dataspace dataspace) { - if (!mDrawingState.bgColorLayer && alpha == 0) { - return false; - } - mDrawingState.sequence++; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - - if (!mDrawingState.bgColorLayer && alpha != 0) { - // create background color layer if one does not yet exist - uint32_t flags = ISurfaceComposerClient::eFXSurfaceEffect; - std::string name = mName + "BackgroundColorLayer"; - mDrawingState.bgColorLayer = mFlinger->getFactory().createEffectLayer( - surfaceflinger::LayerCreationArgs(mFlinger.get(), nullptr, std::move(name), flags, - LayerMetadata())); - - // add to child list - addChild(mDrawingState.bgColorLayer); - mFlinger->mLayersAdded = true; - // set up SF to handle added color layer - if (isRemovedFromCurrentState()) { - MUTEX_ALIAS(mFlinger->mStateLock, mDrawingState.bgColorLayer->mFlinger->mStateLock); - mDrawingState.bgColorLayer->onRemovedFromCurrentState(); - } - mFlinger->setTransactionFlags(eTransactionNeeded); - } else if (mDrawingState.bgColorLayer && alpha == 0) { - MUTEX_ALIAS(mFlinger->mStateLock, mDrawingState.bgColorLayer->mFlinger->mStateLock); - mDrawingState.bgColorLayer->reparent(nullptr); - mDrawingState.bgColorLayer = nullptr; - return true; - } - - mDrawingState.bgColorLayer->setColor(color); - mDrawingState.bgColorLayer->setLayer(std::numeric_limits<int32_t>::min()); - mDrawingState.bgColorLayer->setAlpha(alpha); - mDrawingState.bgColorLayer->setDataspace(dataspace); - - return true; -} - -bool Layer::setCornerRadius(float cornerRadius) { - if (mDrawingState.cornerRadius == cornerRadius) return false; - - mDrawingState.sequence++; - mDrawingState.cornerRadius = cornerRadius; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -bool Layer::setBackgroundBlurRadius(int backgroundBlurRadius) { - if (mDrawingState.backgroundBlurRadius == backgroundBlurRadius) return false; - // If we start or stop drawing blur then the layer's visibility state may change so increment - // the magic sequence number. - if (mDrawingState.backgroundBlurRadius == 0 || backgroundBlurRadius == 0) { - mDrawingState.sequence++; - } - mDrawingState.backgroundBlurRadius = backgroundBlurRadius; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -bool Layer::setTransparentRegionHint(const Region& transparent) { - mDrawingState.sequence++; - mDrawingState.transparentRegionHint = transparent; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -bool Layer::setBlurRegions(const std::vector<BlurRegion>& blurRegions) { - // If we start or stop drawing blur then the layer's visibility state may change so increment - // the magic sequence number. - if (mDrawingState.blurRegions.size() == 0 || blurRegions.size() == 0) { - mDrawingState.sequence++; - } - mDrawingState.blurRegions = blurRegions; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -bool Layer::setFlags(uint32_t flags, uint32_t mask) { - const uint32_t newFlags = (mDrawingState.flags & ~mask) | (flags & mask); - if (mDrawingState.flags == newFlags) return false; - mDrawingState.sequence++; - mDrawingState.flags = newFlags; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - bool Layer::setCrop(const Rect& crop) { if (mDrawingState.crop == crop) return false; mDrawingState.sequence++; mDrawingState.crop = crop; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -bool Layer::setMetadata(const LayerMetadata& data) { - if (!mDrawingState.metadata.merge(data, true /* eraseEmpty */)) return false; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -bool Layer::setLayerStack(ui::LayerStack layerStack) { - if (mDrawingState.layerStack == layerStack) return false; - mDrawingState.sequence++; - mDrawingState.layerStack = layerStack; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -bool Layer::setColorSpaceAgnostic(const bool agnostic) { - if (mDrawingState.colorSpaceAgnostic == agnostic) { - return false; - } - mDrawingState.sequence++; - mDrawingState.colorSpaceAgnostic = agnostic; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -bool Layer::setDimmingEnabled(const bool dimmingEnabled) { - if (mDrawingState.dimmingEnabled == dimmingEnabled) return false; - - mDrawingState.sequence++; - mDrawingState.dimmingEnabled = dimmingEnabled; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -bool Layer::setFrameRateSelectionPriority(int32_t priority) { - if (mDrawingState.frameRateSelectionPriority == priority) return false; - mDrawingState.frameRateSelectionPriority = priority; - mDrawingState.sequence++; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -int32_t Layer::getFrameRateSelectionPriority() const { - // Check if layer has priority set. - if (mDrawingState.frameRateSelectionPriority != PRIORITY_UNSET) { - return mDrawingState.frameRateSelectionPriority; - } - // If not, search whether its parents have it set. - sp<Layer> parent = getParent(); - if (parent != nullptr) { - return parent->getFrameRateSelectionPriority(); - } - - return Layer::PRIORITY_UNSET; -} - -bool Layer::setDefaultFrameRateCompatibility(FrameRateCompatibility compatibility) { - if (mDrawingState.defaultFrameRateCompatibility == compatibility) return false; - mDrawingState.defaultFrameRateCompatibility = compatibility; - mDrawingState.modified = true; - mFlinger->mScheduler->setDefaultFrameRateCompatibility(sequence, compatibility); setTransactionFlags(eTransactionNeeded); return true; } -scheduler::FrameRateCompatibility Layer::getDefaultFrameRateCompatibility() const { - return mDrawingState.defaultFrameRateCompatibility; -} - bool Layer::isLayerFocusedBasedOnPriority(int32_t priority) { return priority == PRIORITY_FOCUSED_WITH_MODE || priority == PRIORITY_FOCUSED_WITHOUT_MODE; }; -ui::LayerStack Layer::getLayerStack(LayerVector::StateSet state) const { - bool useDrawing = state == LayerVector::StateSet::Drawing; - const auto parent = useDrawing ? mDrawingParent.promote() : mCurrentParent.promote(); - if (parent) { - return parent->getLayerStack(); - } - return getDrawingState().layerStack; -} - -bool Layer::setShadowRadius(float shadowRadius) { - if (mDrawingState.shadowRadius == shadowRadius) { - return false; - } - - mDrawingState.sequence++; - mDrawingState.shadowRadius = shadowRadius; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -bool Layer::setFixedTransformHint(ui::Transform::RotationFlags fixedTransformHint) { - if (mDrawingState.fixedTransformHint == fixedTransformHint) { - return false; - } - - mDrawingState.sequence++; - mDrawingState.fixedTransformHint = fixedTransformHint; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -bool Layer::setStretchEffect(const StretchEffect& effect) { - StretchEffect temp = effect; - temp.sanitize(); - if (mDrawingState.stretchEffect == temp) { - return false; - } - mDrawingState.sequence++; - mDrawingState.stretchEffect = temp; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -StretchEffect Layer::getStretchEffect() const { - if (mDrawingState.stretchEffect.hasEffect()) { - return mDrawingState.stretchEffect; - } - - sp<Layer> parent = getParent(); - if (parent != nullptr) { - auto effect = parent->getStretchEffect(); - if (effect.hasEffect()) { - // TODO(b/179047472): Map it? Or do we make the effect be in global space? - return effect; - } - } - return StretchEffect{}; -} - -bool Layer::propagateFrameRateForLayerTree(FrameRate parentFrameRate, bool overrideChildren, - bool* transactionNeeded) { - // Gets the frame rate to propagate to children. - const auto frameRate = [&] { - if (overrideChildren && parentFrameRate.isValid()) { - return parentFrameRate; - } - - if (mDrawingState.frameRate.isValid()) { - return mDrawingState.frameRate; - } - - return parentFrameRate; - }(); - - auto now = systemTime(); - *transactionNeeded |= setFrameRateForLayerTreeLegacy(frameRate, now); - - // The frame rate is propagated to the children by default, but some properties may override it. - bool childrenHaveFrameRate = false; - const bool overrideChildrenFrameRate = overrideChildren || shouldOverrideChildrenFrameRate(); - const bool canPropagateFrameRate = shouldPropagateFrameRate() || overrideChildrenFrameRate; - for (const sp<Layer>& child : mCurrentChildren) { - childrenHaveFrameRate |= - child->propagateFrameRateForLayerTree(canPropagateFrameRate ? frameRate - : FrameRate(), - overrideChildrenFrameRate, transactionNeeded); - } - - // If we don't have a valid frame rate specification, but the children do, we set this - // layer as NoVote to allow the children to control the refresh rate - if (!frameRate.isValid() && childrenHaveFrameRate) { - *transactionNeeded |= - setFrameRateForLayerTreeLegacy(FrameRate(Fps(), FrameRateCompatibility::NoVote), - now); - } - - // We return whether this layer or its children has a vote. We ignore ExactOrMultiple votes for - // the same reason we are allowing touch boost for those layers. See - // RefreshRateSelector::rankFrameRates for details. - const auto layerVotedWithDefaultCompatibility = - frameRate.vote.rate.isValid() && frameRate.vote.type == FrameRateCompatibility::Default; - const auto layerVotedWithNoVote = frameRate.vote.type == FrameRateCompatibility::NoVote; - const auto layerVotedWithCategory = frameRate.category != FrameRateCategory::Default; - const auto layerVotedWithExactCompatibility = - frameRate.vote.rate.isValid() && frameRate.vote.type == FrameRateCompatibility::Exact; - return layerVotedWithDefaultCompatibility || layerVotedWithNoVote || layerVotedWithCategory || - layerVotedWithExactCompatibility || childrenHaveFrameRate; -} - -void Layer::updateTreeHasFrameRateVote() { - const auto root = [&]() -> sp<Layer> { - sp<Layer> layer = sp<Layer>::fromExisting(this); - while (auto parent = layer->getParent()) { - layer = parent; - } - return layer; - }(); - - bool transactionNeeded = false; - root->propagateFrameRateForLayerTree({}, false, &transactionNeeded); - - // TODO(b/195668952): we probably don't need eTraversalNeeded here - if (transactionNeeded) { - mFlinger->setTransactionFlags(eTraversalNeeded); - } -} - -bool Layer::setFrameRate(FrameRate::FrameRateVote frameRateVote) { - if (mDrawingState.frameRate.vote == frameRateVote) { - return false; - } - - mDrawingState.sequence++; - mDrawingState.frameRate.vote = frameRateVote; - mDrawingState.modified = true; - - updateTreeHasFrameRateVote(); - - setTransactionFlags(eTransactionNeeded); - return true; -} - -bool Layer::setFrameRateCategory(FrameRateCategory category, bool smoothSwitchOnly) { - if (mDrawingState.frameRate.category == category && - mDrawingState.frameRate.categorySmoothSwitchOnly == smoothSwitchOnly) { - return false; - } - - mDrawingState.sequence++; - mDrawingState.frameRate.category = category; - mDrawingState.frameRate.categorySmoothSwitchOnly = smoothSwitchOnly; - mDrawingState.modified = true; - - updateTreeHasFrameRateVote(); - - setTransactionFlags(eTransactionNeeded); - return true; -} - -bool Layer::setFrameRateSelectionStrategy(FrameRateSelectionStrategy strategy) { - if (mDrawingState.frameRateSelectionStrategy == strategy) return false; - mDrawingState.frameRateSelectionStrategy = strategy; - mDrawingState.sequence++; - mDrawingState.modified = true; - - updateTreeHasFrameRateVote(); - setTransactionFlags(eTransactionNeeded); - return true; -} - void Layer::setFrameTimelineVsyncForBufferTransaction(const FrameTimelineInfo& info, - nsecs_t postTime) { + nsecs_t postTime, gui::GameMode gameMode) { mDrawingState.postTime = postTime; // Check if one of the bufferlessSurfaceFramesTX contains the same vsyncId. This can happen if @@ -1366,17 +403,17 @@ void Layer::setFrameTimelineVsyncForBufferTransaction(const FrameTimelineInfo& i mDrawingState.bufferSurfaceFrameTX->setActualQueueTime(postTime); } else { mDrawingState.bufferSurfaceFrameTX = - createSurfaceFrameForBuffer(info, postTime, mTransactionName); + createSurfaceFrameForBuffer(info, postTime, mTransactionName, gameMode); } - setFrameTimelineVsyncForSkippedFrames(info, postTime, mTransactionName); + setFrameTimelineVsyncForSkippedFrames(info, postTime, mTransactionName, gameMode); } void Layer::setFrameTimelineVsyncForBufferlessTransaction(const FrameTimelineInfo& info, - nsecs_t postTime) { + nsecs_t postTime, + gui::GameMode gameMode) { mDrawingState.frameTimelineInfo = info; mDrawingState.postTime = postTime; - mDrawingState.modified = true; setTransactionFlags(eTransactionNeeded); if (const auto& bufferSurfaceFrameTX = mDrawingState.bufferSurfaceFrameTX; @@ -1392,17 +429,17 @@ void Layer::setFrameTimelineVsyncForBufferlessTransaction(const FrameTimelineInf // targeting different vsyncs). auto it = mDrawingState.bufferlessSurfaceFramesTX.find(info.vsyncId); if (it == mDrawingState.bufferlessSurfaceFramesTX.end()) { - auto surfaceFrame = createSurfaceFrameForTransaction(info, postTime); + auto surfaceFrame = createSurfaceFrameForTransaction(info, postTime, gameMode); mDrawingState.bufferlessSurfaceFramesTX[info.vsyncId] = surfaceFrame; } else { if (it->second->getPresentState() == PresentState::Presented) { // If the SurfaceFrame was already presented, its safe to overwrite it since it must // have been from previous vsync. - it->second = createSurfaceFrameForTransaction(info, postTime); + it->second = createSurfaceFrameForTransaction(info, postTime, gameMode); } } - setFrameTimelineVsyncForSkippedFrames(info, postTime, mTransactionName); + setFrameTimelineVsyncForSkippedFrames(info, postTime, mTransactionName, gameMode); } void Layer::addSurfaceFrameDroppedForBuffer( @@ -1422,12 +459,12 @@ void Layer::addSurfaceFramePresentedForBuffer( } std::shared_ptr<frametimeline::SurfaceFrame> Layer::createSurfaceFrameForTransaction( - const FrameTimelineInfo& info, nsecs_t postTime) { + const FrameTimelineInfo& info, nsecs_t postTime, gui::GameMode gameMode) { auto surfaceFrame = mFlinger->mFrameTimeline->createSurfaceFrameForToken(info, mOwnerPid, mOwnerUid, getSequence(), mName, mTransactionName, - /*isBuffer*/ false, getGameMode()); + /*isBuffer*/ false, gameMode); surfaceFrame->setActualStartTime(info.startTimeNanos); // For Transactions, the post time is considered to be both queue and acquire fence time. surfaceFrame->setActualQueueTime(postTime); @@ -1436,16 +473,16 @@ std::shared_ptr<frametimeline::SurfaceFrame> Layer::createSurfaceFrameForTransac if (fps) { surfaceFrame->setRenderRate(*fps); } - onSurfaceFrameCreated(surfaceFrame); return surfaceFrame; } std::shared_ptr<frametimeline::SurfaceFrame> Layer::createSurfaceFrameForBuffer( - const FrameTimelineInfo& info, nsecs_t queueTime, std::string debugName) { + const FrameTimelineInfo& info, nsecs_t queueTime, std::string debugName, + gui::GameMode gameMode) { auto surfaceFrame = mFlinger->mFrameTimeline->createSurfaceFrameForToken(info, mOwnerPid, mOwnerUid, getSequence(), mName, debugName, - /*isBuffer*/ true, getGameMode()); + /*isBuffer*/ true, gameMode); surfaceFrame->setActualStartTime(info.startTimeNanos); // For buffers, acquire fence time will set during latch. surfaceFrame->setActualQueueTime(queueTime); @@ -1453,12 +490,11 @@ std::shared_ptr<frametimeline::SurfaceFrame> Layer::createSurfaceFrameForBuffer( if (fps) { surfaceFrame->setRenderRate(*fps); } - onSurfaceFrameCreated(surfaceFrame); return surfaceFrame; } void Layer::setFrameTimelineVsyncForSkippedFrames(const FrameTimelineInfo& info, nsecs_t postTime, - std::string debugName) { + std::string debugName, gui::GameMode gameMode) { if (info.skippedFrameVsyncId == FrameTimelineInfo::INVALID_VSYNC_ID) { return; } @@ -1470,7 +506,7 @@ void Layer::setFrameTimelineVsyncForSkippedFrames(const FrameTimelineInfo& info, mFlinger->mFrameTimeline->createSurfaceFrameForToken(skippedFrameTimelineInfo, mOwnerPid, mOwnerUid, getSequence(), mName, debugName, - /*isBuffer*/ false, getGameMode()); + /*isBuffer*/ false, gameMode); surfaceFrame->setActualStartTime(skippedFrameTimelineInfo.skippedFrameStartTimeNanos); // For Transactions, the post time is considered to be both queue and acquire fence time. surfaceFrame->setActualQueueTime(postTime); @@ -1479,29 +515,9 @@ void Layer::setFrameTimelineVsyncForSkippedFrames(const FrameTimelineInfo& info, if (fps) { surfaceFrame->setRenderRate(*fps); } - onSurfaceFrameCreated(surfaceFrame); addSurfaceFrameDroppedForBuffer(surfaceFrame, postTime); } -bool Layer::setFrameRateForLayerTreeLegacy(FrameRate frameRate, nsecs_t now) { - if (mDrawingState.frameRateForLayerTree == frameRate) { - return false; - } - - mDrawingState.frameRateForLayerTree = frameRate; - - // TODO(b/195668952): we probably don't need to dirty visible regions here - // or even store frameRateForLayerTree in mDrawingState - mDrawingState.sequence++; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - - mFlinger->mScheduler - ->recordLayerHistory(sequence, getLayerProps(), now, now, - scheduler::LayerHistory::LayerUpdateType::SetFrameRate); - return true; -} - bool Layer::setFrameRateForLayerTree(FrameRate frameRate, const scheduler::LayerProps& layerProps, nsecs_t now) { if (mDrawingState.frameRateForLayerTree == frameRate) { @@ -1519,48 +535,6 @@ Layer::FrameRate Layer::getFrameRateForLayerTree() const { return getDrawingState().frameRateForLayerTree; } -bool Layer::isHiddenByPolicy() const { - const State& s(mDrawingState); - const auto& parent = mDrawingParent.promote(); - if (parent != nullptr && parent->isHiddenByPolicy()) { - return true; - } - if (usingRelativeZ(LayerVector::StateSet::Drawing)) { - auto zOrderRelativeOf = mDrawingState.zOrderRelativeOf.promote(); - if (zOrderRelativeOf != nullptr) { - if (zOrderRelativeOf->isHiddenByPolicy()) { - return true; - } - } - } - if (CC_UNLIKELY(!isTransformValid())) { - ALOGW("Hide layer %s because it has invalid transformation.", getDebugName()); - return true; - } - return s.flags & layer_state_t::eLayerHidden; -} - -uint32_t Layer::getEffectiveUsage(uint32_t usage) const { - // TODO: should we do something special if mSecure is set? - if (mProtectedByApp) { - // need a hardware-protected path to external video sink - usage |= GraphicBuffer::USAGE_PROTECTED; - } - if (mPotentialCursor) { - usage |= GraphicBuffer::USAGE_CURSOR; - } - usage |= GraphicBuffer::USAGE_HW_COMPOSER; - return usage; -} - -void Layer::updateTransformHint(ui::Transform::RotationFlags transformHint) { - if (mFlinger->mDebugDisableTransformHint || transformHint & ui::Transform::ROT_INVALID) { - transformHint = ui::Transform::ROT_0; - } - - setTransformHintLegacy(transformHint); -} - // ---------------------------------------------------------------------------- // debugging // ---------------------------------------------------------------------------- @@ -1580,57 +554,6 @@ void Layer::miniDumpHeader(std::string& result) { result.append("\n"); } -void Layer::miniDumpLegacy(std::string& result, const DisplayDevice& display) const { - const auto outputLayer = findOutputLayerForDisplay(&display); - if (!outputLayer) { - return; - } - - std::string name; - if (mName.length() > 77) { - std::string shortened; - shortened.append(mName, 0, 36); - shortened.append("[...]"); - shortened.append(mName, mName.length() - 36); - name = std::move(shortened); - } else { - name = mName; - } - - StringAppendF(&result, " %s\n", name.c_str()); - - const State& layerState(getDrawingState()); - const auto& outputLayerState = outputLayer->getState(); - - if (layerState.zOrderRelativeOf != nullptr || mDrawingParent != nullptr) { - StringAppendF(&result, " rel %6d | ", layerState.z); - } else { - StringAppendF(&result, " %10d | ", layerState.z); - } - StringAppendF(&result, " %10d | ", mWindowType); - StringAppendF(&result, "%10s | ", toString(getCompositionType(display)).c_str()); - StringAppendF(&result, "%10s | ", toString(outputLayerState.bufferTransform).c_str()); - const Rect& frame = outputLayerState.displayFrame; - StringAppendF(&result, "%4d %4d %4d %4d | ", frame.left, frame.top, frame.right, frame.bottom); - const FloatRect& crop = outputLayerState.sourceCrop; - StringAppendF(&result, "%6.1f %6.1f %6.1f %6.1f | ", crop.left, crop.top, crop.right, - crop.bottom); - const auto frameRate = getFrameRateForLayerTree(); - if (frameRate.vote.rate.isValid() || frameRate.vote.type != FrameRateCompatibility::Default) { - StringAppendF(&result, "%s %15s %17s", to_string(frameRate.vote.rate).c_str(), - ftl::enum_string(frameRate.vote.type).c_str(), - ftl::enum_string(frameRate.vote.seamlessness).c_str()); - } else { - result.append(41, ' '); - } - - const auto focused = isLayerFocusedBasedOnPriority(getFrameRateSelectionPriority()); - StringAppendF(&result, " [%s]\n", focused ? "*" : " "); - - result.append(kDumpTableRowLength, '-'); - result.append("\n"); -} - void Layer::miniDump(std::string& result, const frontend::LayerSnapshot& snapshot, const DisplayDevice& display) const { const auto outputLayer = findOutputLayerForDisplay(&display, snapshot.path); @@ -1690,507 +613,12 @@ void Layer::getFrameStats(FrameStats* outStats) const { mFrameTracker.getStats(outStats); } -void Layer::dumpOffscreenDebugInfo(std::string& result) const { - std::string hasBuffer = hasBufferOrSidebandStream() ? " (contains buffer)" : ""; - StringAppendF(&result, "Layer %s%s pid:%d uid:%d%s\n", getName().c_str(), hasBuffer.c_str(), - mOwnerPid, mOwnerUid, isHandleAlive() ? " handleAlive" : ""); -} - void Layer::onDisconnect() { const int32_t layerId = getSequence(); mFlinger->mTimeStats->onDestroy(layerId); mFlinger->mFrameTracer->onDestroy(layerId); } -size_t Layer::getDescendantCount() const { - size_t count = 0; - for (const sp<Layer>& child : mDrawingChildren) { - count += 1 + child->getChildrenCount(); - } - return count; -} - -void Layer::setGameModeForTree(GameMode gameMode) { - const auto& currentState = getDrawingState(); - if (currentState.metadata.has(gui::METADATA_GAME_MODE)) { - gameMode = - static_cast<GameMode>(currentState.metadata.getInt32(gui::METADATA_GAME_MODE, 0)); - } - setGameMode(gameMode); - for (const sp<Layer>& child : mCurrentChildren) { - child->setGameModeForTree(gameMode); - } -} - -void Layer::addChild(const sp<Layer>& layer) { - mFlinger->mSomeChildrenChanged = true; - setTransactionFlags(eTransactionNeeded); - - mCurrentChildren.add(layer); - layer->setParent(sp<Layer>::fromExisting(this)); - layer->setGameModeForTree(mGameMode); - updateTreeHasFrameRateVote(); -} - -ssize_t Layer::removeChild(const sp<Layer>& layer) { - mFlinger->mSomeChildrenChanged = true; - setTransactionFlags(eTransactionNeeded); - - layer->setParent(nullptr); - const auto removeResult = mCurrentChildren.remove(layer); - - updateTreeHasFrameRateVote(); - layer->setGameModeForTree(GameMode::Unsupported); - layer->updateTreeHasFrameRateVote(); - - return removeResult; -} - -void Layer::setChildrenDrawingParent(const sp<Layer>& newParent) { - for (const sp<Layer>& child : mDrawingChildren) { - child->mDrawingParent = newParent; - const float parentShadowRadius = - newParent->canDrawShadows() ? 0.f : newParent->mEffectiveShadowRadius; - child->computeBounds(newParent->mBounds, newParent->mEffectiveTransform, - parentShadowRadius); - } -} - -bool Layer::reparent(const sp<IBinder>& newParentHandle) { - sp<Layer> newParent; - if (newParentHandle != nullptr) { - newParent = LayerHandle::getLayer(newParentHandle); - if (newParent == nullptr) { - ALOGE("Unable to promote Layer handle"); - return false; - } - if (newParent == this) { - ALOGE("Invalid attempt to reparent Layer (%s) to itself", getName().c_str()); - return false; - } - } - - sp<Layer> parent = getParent(); - if (parent != nullptr) { - parent->removeChild(sp<Layer>::fromExisting(this)); - } - - if (newParentHandle != nullptr) { - newParent->addChild(sp<Layer>::fromExisting(this)); - if (!newParent->isRemovedFromCurrentState()) { - addToCurrentState(); - } else { - onRemovedFromCurrentState(); - } - } else { - onRemovedFromCurrentState(); - } - - return true; -} - -bool Layer::setColorTransform(const mat4& matrix) { - static const mat4 identityMatrix = mat4(); - - if (mDrawingState.colorTransform == matrix) { - return false; - } - ++mDrawingState.sequence; - mDrawingState.colorTransform = matrix; - mDrawingState.hasColorTransform = matrix != identityMatrix; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -mat4 Layer::getColorTransform() const { - mat4 colorTransform = mat4(getDrawingState().colorTransform); - if (sp<Layer> parent = mDrawingParent.promote(); parent != nullptr) { - colorTransform = parent->getColorTransform() * colorTransform; - } - return colorTransform; -} - -bool Layer::hasColorTransform() const { - bool hasColorTransform = getDrawingState().hasColorTransform; - if (sp<Layer> parent = mDrawingParent.promote(); parent != nullptr) { - hasColorTransform = hasColorTransform || parent->hasColorTransform(); - } - return hasColorTransform; -} - -bool Layer::isLegacyDataSpace() const { - // return true when no higher bits are set - return !(getDataSpace() & - (ui::Dataspace::STANDARD_MASK | ui::Dataspace::TRANSFER_MASK | - ui::Dataspace::RANGE_MASK)); -} - -void Layer::setParent(const sp<Layer>& layer) { - mCurrentParent = layer; -} - -int32_t Layer::getZ(LayerVector::StateSet) const { - return mDrawingState.z; -} - -bool Layer::usingRelativeZ(LayerVector::StateSet stateSet) const { - const bool useDrawing = stateSet == LayerVector::StateSet::Drawing; - const State& state = useDrawing ? mDrawingState : mDrawingState; - return state.isRelativeOf; -} - -__attribute__((no_sanitize("unsigned-integer-overflow"))) LayerVector Layer::makeTraversalList( - LayerVector::StateSet stateSet, bool* outSkipRelativeZUsers) { - LOG_ALWAYS_FATAL_IF(stateSet == LayerVector::StateSet::Invalid, - "makeTraversalList received invalid stateSet"); - const bool useDrawing = stateSet == LayerVector::StateSet::Drawing; - const LayerVector& children = useDrawing ? mDrawingChildren : mCurrentChildren; - const State& state = useDrawing ? mDrawingState : mDrawingState; - - if (state.zOrderRelatives.size() == 0) { - *outSkipRelativeZUsers = true; - return children; - } - - LayerVector traverse(stateSet); - for (const wp<Layer>& weakRelative : state.zOrderRelatives) { - sp<Layer> strongRelative = weakRelative.promote(); - if (strongRelative != nullptr) { - traverse.add(strongRelative); - } - } - - for (const sp<Layer>& child : children) { - if (child->usingRelativeZ(stateSet)) { - continue; - } - traverse.add(child); - } - - return traverse; -} - -/** - * Negatively signed relatives are before 'this' in Z-order. - */ -void Layer::traverseInZOrder(LayerVector::StateSet stateSet, const LayerVector::Visitor& visitor) { - // In the case we have other layers who are using a relative Z to us, makeTraversalList will - // produce a new list for traversing, including our relatives, and not including our children - // who are relatives of another surface. In the case that there are no relative Z, - // makeTraversalList returns our children directly to avoid significant overhead. - // However in this case we need to take the responsibility for filtering children which - // are relatives of another surface here. - bool skipRelativeZUsers = false; - const LayerVector list = makeTraversalList(stateSet, &skipRelativeZUsers); - - size_t i = 0; - for (; i < list.size(); i++) { - const auto& relative = list[i]; - if (skipRelativeZUsers && relative->usingRelativeZ(stateSet)) { - continue; - } - - if (relative->getZ(stateSet) >= 0) { - break; - } - relative->traverseInZOrder(stateSet, visitor); - } - - visitor(this); - for (; i < list.size(); i++) { - const auto& relative = list[i]; - - if (skipRelativeZUsers && relative->usingRelativeZ(stateSet)) { - continue; - } - relative->traverseInZOrder(stateSet, visitor); - } -} - -/** - * Positively signed relatives are before 'this' in reverse Z-order. - */ -void Layer::traverseInReverseZOrder(LayerVector::StateSet stateSet, - const LayerVector::Visitor& visitor) { - // See traverseInZOrder for documentation. - bool skipRelativeZUsers = false; - LayerVector list = makeTraversalList(stateSet, &skipRelativeZUsers); - - int32_t i = 0; - for (i = int32_t(list.size()) - 1; i >= 0; i--) { - const auto& relative = list[i]; - - if (skipRelativeZUsers && relative->usingRelativeZ(stateSet)) { - continue; - } - - if (relative->getZ(stateSet) < 0) { - break; - } - relative->traverseInReverseZOrder(stateSet, visitor); - } - visitor(this); - for (; i >= 0; i--) { - const auto& relative = list[i]; - - if (skipRelativeZUsers && relative->usingRelativeZ(stateSet)) { - continue; - } - - relative->traverseInReverseZOrder(stateSet, visitor); - } -} - -void Layer::traverse(LayerVector::StateSet state, const LayerVector::Visitor& visitor) { - visitor(this); - const LayerVector& children = - state == LayerVector::StateSet::Drawing ? mDrawingChildren : mCurrentChildren; - for (const sp<Layer>& child : children) { - child->traverse(state, visitor); - } -} - -void Layer::traverseChildren(const LayerVector::Visitor& visitor) { - for (const sp<Layer>& child : mDrawingChildren) { - visitor(child.get()); - } -} - -LayerVector Layer::makeChildrenTraversalList(LayerVector::StateSet stateSet, - const std::vector<Layer*>& layersInTree) { - LOG_ALWAYS_FATAL_IF(stateSet == LayerVector::StateSet::Invalid, - "makeTraversalList received invalid stateSet"); - const bool useDrawing = stateSet == LayerVector::StateSet::Drawing; - const LayerVector& children = useDrawing ? mDrawingChildren : mCurrentChildren; - const State& state = useDrawing ? mDrawingState : mDrawingState; - - LayerVector traverse(stateSet); - for (const wp<Layer>& weakRelative : state.zOrderRelatives) { - sp<Layer> strongRelative = weakRelative.promote(); - // Only add relative layers that are also descendents of the top most parent of the tree. - // If a relative layer is not a descendent, then it should be ignored. - if (std::binary_search(layersInTree.begin(), layersInTree.end(), strongRelative.get())) { - traverse.add(strongRelative); - } - } - - for (const sp<Layer>& child : children) { - const State& childState = useDrawing ? child->mDrawingState : child->mDrawingState; - // If a layer has a relativeOf layer, only ignore if the layer it's relative to is a - // descendent of the top most parent of the tree. If it's not a descendent, then just add - // the child here since it won't be added later as a relative. - if (std::binary_search(layersInTree.begin(), layersInTree.end(), - childState.zOrderRelativeOf.promote().get())) { - continue; - } - traverse.add(child); - } - - return traverse; -} - -void Layer::traverseChildrenInZOrderInner(const std::vector<Layer*>& layersInTree, - LayerVector::StateSet stateSet, - const LayerVector::Visitor& visitor) { - const LayerVector list = makeChildrenTraversalList(stateSet, layersInTree); - - size_t i = 0; - for (; i < list.size(); i++) { - const auto& relative = list[i]; - if (relative->getZ(stateSet) >= 0) { - break; - } - relative->traverseChildrenInZOrderInner(layersInTree, stateSet, visitor); - } - - visitor(this); - for (; i < list.size(); i++) { - const auto& relative = list[i]; - relative->traverseChildrenInZOrderInner(layersInTree, stateSet, visitor); - } -} - -std::vector<Layer*> Layer::getLayersInTree(LayerVector::StateSet stateSet) { - const bool useDrawing = stateSet == LayerVector::StateSet::Drawing; - const LayerVector& children = useDrawing ? mDrawingChildren : mCurrentChildren; - - std::vector<Layer*> layersInTree = {this}; - for (size_t i = 0; i < children.size(); i++) { - const auto& child = children[i]; - std::vector<Layer*> childLayers = child->getLayersInTree(stateSet); - layersInTree.insert(layersInTree.end(), childLayers.cbegin(), childLayers.cend()); - } - - return layersInTree; -} - -void Layer::traverseChildrenInZOrder(LayerVector::StateSet stateSet, - const LayerVector::Visitor& visitor) { - std::vector<Layer*> layersInTree = getLayersInTree(stateSet); - std::sort(layersInTree.begin(), layersInTree.end()); - traverseChildrenInZOrderInner(layersInTree, stateSet, visitor); -} - -ui::Transform Layer::getTransform() const { - return mEffectiveTransform; -} - -bool Layer::isTransformValid() const { - float transformDet = getTransform().det(); - return transformDet != 0 && !isinf(transformDet) && !isnan(transformDet); -} - -half Layer::getAlpha() const { - const auto& p = mDrawingParent.promote(); - - half parentAlpha = (p != nullptr) ? p->getAlpha() : 1.0_hf; - return parentAlpha * getDrawingState().color.a; -} - -ui::Transform::RotationFlags Layer::getFixedTransformHint() const { - ui::Transform::RotationFlags fixedTransformHint = mDrawingState.fixedTransformHint; - if (fixedTransformHint != ui::Transform::ROT_INVALID) { - return fixedTransformHint; - } - const auto& p = mCurrentParent.promote(); - if (!p) return fixedTransformHint; - return p->getFixedTransformHint(); -} - -half4 Layer::getColor() const { - const half4 color(getDrawingState().color); - return half4(color.r, color.g, color.b, getAlpha()); -} - -int32_t Layer::getBackgroundBlurRadius() const { - if (getDrawingState().backgroundBlurRadius == 0) { - return 0; - } - - const auto& p = mDrawingParent.promote(); - half parentAlpha = (p != nullptr) ? p->getAlpha() : 1.0_hf; - return parentAlpha * getDrawingState().backgroundBlurRadius; -} - -const std::vector<BlurRegion> Layer::getBlurRegions() const { - auto regionsCopy(getDrawingState().blurRegions); - float layerAlpha = getAlpha(); - for (auto& region : regionsCopy) { - region.alpha = region.alpha * layerAlpha; - } - return regionsCopy; -} - -RoundedCornerState Layer::getRoundedCornerState() const { - // Today's DPUs cannot do rounded corners. If RenderEngine cannot render - // protected content, remove rounded corners from protected content so it - // can be rendered by the DPU. - if (isProtected() && !mFlinger->getRenderEngine().supportsProtectedContent()) { - return {}; - } - - // Get parent settings - RoundedCornerState parentSettings; - const auto& parent = mDrawingParent.promote(); - if (parent != nullptr) { - parentSettings = parent->getRoundedCornerState(); - if (parentSettings.hasRoundedCorners()) { - ui::Transform t = getActiveTransform(getDrawingState()); - t = t.inverse(); - parentSettings.cropRect = t.transform(parentSettings.cropRect); - parentSettings.radius.x *= t.getScaleX(); - parentSettings.radius.y *= t.getScaleY(); - } - } - - // Get layer settings - Rect layerCropRect = getCroppedBufferSize(getDrawingState()); - const vec2 radius(getDrawingState().cornerRadius, getDrawingState().cornerRadius); - RoundedCornerState layerSettings(layerCropRect.toFloatRect(), radius); - const bool layerSettingsValid = layerSettings.hasRoundedCorners() && layerCropRect.isValid(); - - if (layerSettingsValid && parentSettings.hasRoundedCorners()) { - // If the parent and the layer have rounded corner settings, use the parent settings if the - // parent crop is entirely inside the layer crop. - // This has limitations and cause rendering artifacts. See b/200300845 for correct fix. - if (parentSettings.cropRect.left > layerCropRect.left && - parentSettings.cropRect.top > layerCropRect.top && - parentSettings.cropRect.right < layerCropRect.right && - parentSettings.cropRect.bottom < layerCropRect.bottom) { - return parentSettings; - } else { - return layerSettings; - } - } else if (layerSettingsValid) { - return layerSettings; - } else if (parentSettings.hasRoundedCorners()) { - return parentSettings; - } - return {}; -} - -bool Layer::findInHierarchy(const sp<Layer>& l) { - if (l == this) { - return true; - } - for (auto& child : mDrawingChildren) { - if (child->findInHierarchy(l)) { - return true; - } - } - return false; -} - -void Layer::commitChildList() { - for (size_t i = 0; i < mCurrentChildren.size(); i++) { - const auto& child = mCurrentChildren[i]; - child->commitChildList(); - } - mDrawingChildren = mCurrentChildren; - mDrawingParent = mCurrentParent; - if (CC_UNLIKELY(usingRelativeZ(LayerVector::StateSet::Drawing))) { - auto zOrderRelativeOf = mDrawingState.zOrderRelativeOf.promote(); - if (zOrderRelativeOf == nullptr) return; - if (findInHierarchy(zOrderRelativeOf)) { - ALOGE("Detected Z ordering loop between %s and %s", mName.c_str(), - zOrderRelativeOf->mName.c_str()); - ALOGE("Severing rel Z loop, potentially dangerous"); - mDrawingState.isRelativeOf = false; - zOrderRelativeOf->removeZOrderRelative(wp<Layer>::fromExisting(this)); - } - } -} - - -void Layer::setInputInfo(const WindowInfo& info) { - mDrawingState.inputInfo = info; - mDrawingState.touchableRegionCrop = - LayerHandle::getLayer(info.touchableRegionCropHandle.promote()); - mDrawingState.modified = true; - mFlinger->mUpdateInputInfo = true; - setTransactionFlags(eTransactionNeeded); -} - -perfetto::protos::LayerProto* Layer::writeToProto(perfetto::protos::LayersProto& layersProto, - uint32_t traceFlags) { - perfetto::protos::LayerProto* layerProto = layersProto.add_layers(); - writeToProtoDrawingState(layerProto); - writeToProtoCommonState(layerProto, LayerVector::StateSet::Drawing, traceFlags); - - if (traceFlags & LayerTracing::TRACE_COMPOSITION) { - ui::LayerStack layerStack = - (mSnapshot) ? mSnapshot->outputFilter.layerStack : ui::INVALID_LAYER_STACK; - writeCompositionStateToProto(layerProto, layerStack); - } - - for (const sp<Layer>& layer : mDrawingChildren) { - layer->writeToProto(layersProto, traceFlags); - } - - return layerProto; -} - void Layer::writeCompositionStateToProto(perfetto::protos::LayerProto* layerProto, ui::LayerStack layerStack) { ftl::FakeGuard guard(mFlinger->mStateLock); // Called from the main thread. @@ -2206,383 +634,9 @@ void Layer::writeCompositionStateToProto(perfetto::protos::LayerProto* layerProt } } -void Layer::writeToProtoDrawingState(perfetto::protos::LayerProto* layerInfo) { - const ui::Transform transform = getTransform(); - auto buffer = getExternalTexture(); - if (buffer != nullptr) { - LayerProtoHelper::writeToProto(*buffer, - [&]() { return layerInfo->mutable_active_buffer(); }); - LayerProtoHelper::writeToProtoDeprecated(ui::Transform(getBufferTransform()), - layerInfo->mutable_buffer_transform()); - } - layerInfo->set_invalidate(contentDirty); - layerInfo->set_is_protected(isProtected()); - layerInfo->set_dataspace(dataspaceDetails(static_cast<android_dataspace>(getDataSpace()))); - layerInfo->set_queued_frames(getQueuedFrameCount()); - layerInfo->set_curr_frame(mCurrentFrameNumber); - layerInfo->set_requested_corner_radius(getDrawingState().cornerRadius); - layerInfo->set_corner_radius( - (getRoundedCornerState().radius.x + getRoundedCornerState().radius.y) / 2.0); - layerInfo->set_background_blur_radius(getBackgroundBlurRadius()); - layerInfo->set_is_trusted_overlay(isTrustedOverlay()); - LayerProtoHelper::writeToProtoDeprecated(transform, layerInfo->mutable_transform()); - LayerProtoHelper::writePositionToProto(transform.tx(), transform.ty(), - [&]() { return layerInfo->mutable_position(); }); - LayerProtoHelper::writeToProto(mBounds, [&]() { return layerInfo->mutable_bounds(); }); - LayerProtoHelper::writeToProto(surfaceDamageRegion, - [&]() { return layerInfo->mutable_damage_region(); }); - - if (hasColorTransform()) { - LayerProtoHelper::writeToProto(getColorTransform(), layerInfo->mutable_color_transform()); - } - - LayerProtoHelper::writeToProto(mSourceBounds, - [&]() { return layerInfo->mutable_source_bounds(); }); - LayerProtoHelper::writeToProto(mScreenBounds, - [&]() { return layerInfo->mutable_screen_bounds(); }); - LayerProtoHelper::writeToProto(getRoundedCornerState().cropRect, - [&]() { return layerInfo->mutable_corner_radius_crop(); }); - layerInfo->set_shadow_radius(mEffectiveShadowRadius); -} - -void Layer::writeToProtoCommonState(perfetto::protos::LayerProto* layerInfo, - LayerVector::StateSet stateSet, uint32_t traceFlags) { - const bool useDrawing = stateSet == LayerVector::StateSet::Drawing; - const LayerVector& children = useDrawing ? mDrawingChildren : mCurrentChildren; - const State& state = useDrawing ? mDrawingState : mDrawingState; - - ui::Transform requestedTransform = state.transform; - - layerInfo->set_id(sequence); - layerInfo->set_name(getName().c_str()); - layerInfo->set_type(getType()); - - for (const auto& child : children) { - layerInfo->add_children(child->sequence); - } - - for (const wp<Layer>& weakRelative : state.zOrderRelatives) { - sp<Layer> strongRelative = weakRelative.promote(); - if (strongRelative != nullptr) { - layerInfo->add_relatives(strongRelative->sequence); - } - } - - LayerProtoHelper::writeToProto(state.transparentRegionHint, - [&]() { return layerInfo->mutable_transparent_region(); }); - - layerInfo->set_layer_stack(getLayerStack().id); - layerInfo->set_z(state.z); - - LayerProtoHelper::writePositionToProto(requestedTransform.tx(), requestedTransform.ty(), [&]() { - return layerInfo->mutable_requested_position(); - }); - - LayerProtoHelper::writeToProto(state.crop, [&]() { return layerInfo->mutable_crop(); }); - - layerInfo->set_is_opaque(isOpaque(state)); - - layerInfo->set_pixel_format(decodePixelFormat(getPixelFormat())); - LayerProtoHelper::writeToProto(getColor(), [&]() { return layerInfo->mutable_color(); }); - LayerProtoHelper::writeToProto(state.color, - [&]() { return layerInfo->mutable_requested_color(); }); - layerInfo->set_flags(state.flags); - - LayerProtoHelper::writeToProtoDeprecated(requestedTransform, - layerInfo->mutable_requested_transform()); - - auto parent = useDrawing ? mDrawingParent.promote() : mCurrentParent.promote(); - if (parent != nullptr) { - layerInfo->set_parent(parent->sequence); - } - - auto zOrderRelativeOf = state.zOrderRelativeOf.promote(); - if (zOrderRelativeOf != nullptr) { - layerInfo->set_z_order_relative_of(zOrderRelativeOf->sequence); - } - - layerInfo->set_is_relative_of(state.isRelativeOf); - - layerInfo->set_owner_uid(mOwnerUid); - - if ((traceFlags & LayerTracing::TRACE_INPUT) && needsInputInfo()) { - WindowInfo info; - if (useDrawing) { - info = fillInputInfo( - InputDisplayArgs{.transform = &kIdentityTransform, .isSecure = true}); - } else { - info = state.inputInfo; - } - - LayerProtoHelper::writeToProto(info, state.touchableRegionCrop, - [&]() { return layerInfo->mutable_input_window_info(); }); - } - - if (traceFlags & LayerTracing::TRACE_EXTRA) { - auto protoMap = layerInfo->mutable_metadata(); - for (const auto& entry : state.metadata.mMap) { - (*protoMap)[entry.first] = std::string(entry.second.cbegin(), entry.second.cend()); - } - } - - LayerProtoHelper::writeToProto(state.destinationFrame, - [&]() { return layerInfo->mutable_destination_frame(); }); -} - -bool Layer::isRemovedFromCurrentState() const { - return mRemovedFromDrawingState; -} - -// Applies the given transform to the region, while protecting against overflows caused by any -// offsets. If applying the offset in the transform to any of the Rects in the region would result -// in an overflow, they are not added to the output Region. -static Region transformTouchableRegionSafely(const ui::Transform& t, const Region& r, - const std::string& debugWindowName) { - // Round the translation using the same rounding strategy used by ui::Transform. - const auto tx = static_cast<int32_t>(t.tx() + 0.5); - const auto ty = static_cast<int32_t>(t.ty() + 0.5); - - ui::Transform transformWithoutOffset = t; - transformWithoutOffset.set(0.f, 0.f); - - const Region transformed = transformWithoutOffset.transform(r); - - // Apply the translation to each of the Rects in the region while discarding any that overflow. - Region ret; - for (const auto& rect : transformed) { - Rect newRect; - if (__builtin_add_overflow(rect.left, tx, &newRect.left) || - __builtin_add_overflow(rect.top, ty, &newRect.top) || - __builtin_add_overflow(rect.right, tx, &newRect.right) || - __builtin_add_overflow(rect.bottom, ty, &newRect.bottom)) { - ALOGE("Applying transform to touchable region of window '%s' resulted in an overflow.", - debugWindowName.c_str()); - continue; - } - ret.orSelf(newRect); - } - return ret; -} - -void Layer::fillInputFrameInfo(WindowInfo& info, const ui::Transform& screenToDisplay) { - auto [inputBounds, inputBoundsValid] = getInputBounds(/*fillParentBounds=*/false); - if (!inputBoundsValid) { - info.touchableRegion.clear(); - } - - info.frame = getInputBoundsInDisplaySpace(inputBounds, screenToDisplay); - - ui::Transform inputToLayer; - inputToLayer.set(inputBounds.left, inputBounds.top); - const ui::Transform layerToScreen = getInputTransform(); - const ui::Transform inputToDisplay = screenToDisplay * layerToScreen * inputToLayer; - - // InputDispatcher expects a display-to-input transform. - info.transform = inputToDisplay.inverse(); - - // The touchable region is specified in the input coordinate space. Change it to display space. - info.touchableRegion = - transformTouchableRegionSafely(inputToDisplay, info.touchableRegion, mName); -} - -void Layer::fillTouchOcclusionMode(WindowInfo& info) { - sp<Layer> p = sp<Layer>::fromExisting(this); - while (p != nullptr && !p->hasInputInfo()) { - p = p->mDrawingParent.promote(); - } - if (p != nullptr) { - info.touchOcclusionMode = p->mDrawingState.inputInfo.touchOcclusionMode; - } -} - -gui::DropInputMode Layer::getDropInputMode() const { - gui::DropInputMode mode = mDrawingState.dropInputMode; - if (mode == gui::DropInputMode::ALL) { - return mode; - } - sp<Layer> parent = mDrawingParent.promote(); - if (parent) { - gui::DropInputMode parentMode = parent->getDropInputMode(); - if (parentMode != gui::DropInputMode::NONE) { - return parentMode; - } - } - return mode; -} - -void Layer::handleDropInputMode(gui::WindowInfo& info) const { - if (mDrawingState.inputInfo.inputConfig.test(WindowInfo::InputConfig::NO_INPUT_CHANNEL)) { - return; - } - - // Check if we need to drop input unconditionally - gui::DropInputMode dropInputMode = getDropInputMode(); - if (dropInputMode == gui::DropInputMode::ALL) { - info.inputConfig |= WindowInfo::InputConfig::DROP_INPUT; - ALOGV("Dropping input for %s as requested by policy.", getDebugName()); - return; - } - - // Check if we need to check if the window is obscured by parent - if (dropInputMode != gui::DropInputMode::OBSCURED) { - return; - } - - // Check if the parent has set an alpha on the layer - sp<Layer> parent = mDrawingParent.promote(); - if (parent && parent->getAlpha() != 1.0_hf) { - info.inputConfig |= WindowInfo::InputConfig::DROP_INPUT; - ALOGV("Dropping input for %s as requested by policy because alpha=%f", getDebugName(), - static_cast<float>(getAlpha())); - } - - // Check if the parent has cropped the buffer - Rect bufferSize = getCroppedBufferSize(getDrawingState()); - if (!bufferSize.isValid()) { - info.inputConfig |= WindowInfo::InputConfig::DROP_INPUT_IF_OBSCURED; - return; - } - - // Screenbounds are the layer bounds cropped by parents, transformed to screenspace. - // To check if the layer has been cropped, we take the buffer bounds, apply the local - // layer crop and apply the same set of transforms to move to screenspace. If the bounds - // match then the layer has not been cropped by its parents. - Rect bufferInScreenSpace(getTransform().transform(bufferSize)); - bool croppedByParent = bufferInScreenSpace != Rect{mScreenBounds}; - - if (croppedByParent) { - info.inputConfig |= WindowInfo::InputConfig::DROP_INPUT; - ALOGV("Dropping input for %s as requested by policy because buffer is cropped by parent", - getDebugName()); - } else { - // If the layer is not obscured by its parents (by setting an alpha or crop), then only drop - // input if the window is obscured. This check should be done in surfaceflinger but the - // logic currently resides in inputflinger. So pass the if_obscured check to input to only - // drop input events if the window is obscured. - info.inputConfig |= WindowInfo::InputConfig::DROP_INPUT_IF_OBSCURED; - } -} - -WindowInfo Layer::fillInputInfo(const InputDisplayArgs& displayArgs) { - if (!hasInputInfo()) { - mDrawingState.inputInfo.name = getName(); - mDrawingState.inputInfo.ownerUid = gui::Uid{mOwnerUid}; - mDrawingState.inputInfo.ownerPid = gui::Pid{mOwnerPid}; - mDrawingState.inputInfo.inputConfig |= WindowInfo::InputConfig::NO_INPUT_CHANNEL; - mDrawingState.inputInfo.displayId = toLogicalDisplayId(getLayerStack()); - } - - const ui::Transform& displayTransform = - displayArgs.transform != nullptr ? *displayArgs.transform : kIdentityTransform; - - WindowInfo info = mDrawingState.inputInfo; - info.id = sequence; - info.displayId = toLogicalDisplayId(getLayerStack()); - - fillInputFrameInfo(info, displayTransform); - - if (displayArgs.transform == nullptr) { - // Do not let the window receive touches if it is not associated with a valid display - // transform. We still allow the window to receive keys and prevent ANRs. - info.inputConfig |= WindowInfo::InputConfig::NOT_TOUCHABLE; - } - - info.setInputConfig(WindowInfo::InputConfig::NOT_VISIBLE, !isVisibleForInput()); - - info.alpha = getAlpha(); - fillTouchOcclusionMode(info); - handleDropInputMode(info); - - // If the window will be blacked out on a display because the display does not have the secure - // flag and the layer has the secure flag set, then drop input. - if (!displayArgs.isSecure && isSecure()) { - info.inputConfig |= WindowInfo::InputConfig::DROP_INPUT; - } - - sp<Layer> cropLayer = mDrawingState.touchableRegionCrop.promote(); - if (info.replaceTouchableRegionWithCrop) { - Rect inputBoundsInDisplaySpace; - if (!cropLayer) { - FloatRect inputBounds = getInputBounds(/*fillParentBounds=*/true).first; - inputBoundsInDisplaySpace = getInputBoundsInDisplaySpace(inputBounds, displayTransform); - } else { - FloatRect inputBounds = cropLayer->getInputBounds(/*fillParentBounds=*/true).first; - inputBoundsInDisplaySpace = - cropLayer->getInputBoundsInDisplaySpace(inputBounds, displayTransform); - } - info.touchableRegion = Region(inputBoundsInDisplaySpace); - } else if (cropLayer != nullptr) { - FloatRect inputBounds = cropLayer->getInputBounds(/*fillParentBounds=*/true).first; - Rect inputBoundsInDisplaySpace = - cropLayer->getInputBoundsInDisplaySpace(inputBounds, displayTransform); - info.touchableRegion = info.touchableRegion.intersect(inputBoundsInDisplaySpace); - } - - // Inherit the trusted state from the parent hierarchy, but don't clobber the trusted state - // if it was set by WM for a known system overlay - if (isTrustedOverlay()) { - info.inputConfig |= WindowInfo::InputConfig::TRUSTED_OVERLAY; - } - - // If the layer is a clone, we need to crop the input region to cloned root to prevent - // touches from going outside the cloned area. - if (isClone()) { - info.inputConfig |= WindowInfo::InputConfig::CLONE; - if (const sp<Layer> clonedRoot = getClonedRoot()) { - const Rect rect = displayTransform.transform(Rect{clonedRoot->mScreenBounds}); - info.touchableRegion = info.touchableRegion.intersect(rect); - } - } - - Rect bufferSize = getBufferSize(getDrawingState()); - info.contentSize = Size(bufferSize.width(), bufferSize.height()); - - return info; -} - -Rect Layer::getInputBoundsInDisplaySpace(const FloatRect& inputBounds, - const ui::Transform& screenToDisplay) { - // InputDispatcher works in the display device's coordinate space. Here, we calculate the - // frame and transform used for the layer, which determines the bounds and the coordinate space - // within which the layer will receive input. - - // Coordinate space definitions: - // - display: The display device's coordinate space. Correlates to pixels on the display. - // - screen: The post-rotation coordinate space for the display, a.k.a. logical display space. - // - layer: The coordinate space of this layer. - // - input: The coordinate space in which this layer will receive input events. This could be - // different than layer space if a surfaceInset is used, which changes the origin - // of the input space. - - // Crop the input bounds to ensure it is within the parent's bounds. - const FloatRect croppedInputBounds = mBounds.intersect(inputBounds); - const ui::Transform layerToScreen = getInputTransform(); - const ui::Transform layerToDisplay = screenToDisplay * layerToScreen; - return Rect{layerToDisplay.transform(croppedInputBounds)}; -} - -sp<Layer> Layer::getClonedRoot() { - if (mClonedChild != nullptr) { - return sp<Layer>::fromExisting(this); - } - if (mDrawingParent == nullptr || mDrawingParent.promote() == nullptr) { - return nullptr; - } - return mDrawingParent.promote()->getClonedRoot(); -} - -bool Layer::hasInputInfo() const { - return mDrawingState.inputInfo.token != nullptr || - mDrawingState.inputInfo.inputConfig.test(WindowInfo::InputConfig::NO_INPUT_CHANNEL); -} - compositionengine::OutputLayer* Layer::findOutputLayerForDisplay( const DisplayDevice* display) const { if (!display) return nullptr; - if (!mFlinger->mLayerLifecycleManagerEnabled) { - return display->getCompositionDisplay()->getOutputLayerForLayer( - getCompositionEngineLayerFE()); - } sp<LayerFE> layerFE; frontend::LayerHierarchy::TraversalPath path{.id = static_cast<uint32_t>(sequence)}; for (auto& [p, layer] : mLayerFEs) { @@ -2598,10 +652,6 @@ compositionengine::OutputLayer* Layer::findOutputLayerForDisplay( compositionengine::OutputLayer* Layer::findOutputLayerForDisplay( const DisplayDevice* display, const frontend::LayerHierarchy::TraversalPath& path) const { if (!display) return nullptr; - if (!mFlinger->mLayerLifecycleManagerEnabled) { - return display->getCompositionDisplay()->getOutputLayerForLayer( - getCompositionEngineLayerFE()); - } sp<LayerFE> layerFE; for (auto& [p, layer] : mLayerFEs) { if (p == path) { @@ -2618,215 +668,27 @@ Region Layer::getVisibleRegion(const DisplayDevice* display) const { return outputLayer ? outputLayer->getState().visibleRegion : Region(); } -void Layer::updateCloneBufferInfo() { - if (!isClone() || !isClonedFromAlive()) { - return; - } - - sp<Layer> clonedFrom = getClonedFrom(); - mBufferInfo = clonedFrom->mBufferInfo; - mSidebandStream = clonedFrom->mSidebandStream; - surfaceDamageRegion = clonedFrom->surfaceDamageRegion; - mCurrentFrameNumber = clonedFrom->mCurrentFrameNumber.load(); - mPreviousFrameNumber = clonedFrom->mPreviousFrameNumber; - - // After buffer info is updated, the drawingState from the real layer needs to be copied into - // the cloned. This is because some properties of drawingState can change when latchBuffer is - // called. However, copying the drawingState would also overwrite the cloned layer's relatives - // and touchableRegionCrop. Therefore, temporarily store the relatives so they can be set in - // the cloned drawingState again. - wp<Layer> tmpZOrderRelativeOf = mDrawingState.zOrderRelativeOf; - SortedVector<wp<Layer>> tmpZOrderRelatives = mDrawingState.zOrderRelatives; - wp<Layer> tmpTouchableRegionCrop = mDrawingState.touchableRegionCrop; - WindowInfo tmpInputInfo = mDrawingState.inputInfo; - - cloneDrawingState(clonedFrom.get()); - - mDrawingState.touchableRegionCrop = tmpTouchableRegionCrop; - mDrawingState.zOrderRelativeOf = tmpZOrderRelativeOf; - mDrawingState.zOrderRelatives = tmpZOrderRelatives; - mDrawingState.inputInfo = tmpInputInfo; -} - -bool Layer::updateMirrorInfo(const std::deque<Layer*>& cloneRootsPendingUpdates) { - if (mClonedChild == nullptr || !mClonedChild->isClonedFromAlive()) { - // If mClonedChild is null, there is nothing to mirror. If isClonedFromAlive returns false, - // it means that there is a clone, but the layer it was cloned from has been destroyed. In - // that case, we want to delete the reference to the clone since we want it to get - // destroyed. The root, this layer, will still be around since the client can continue - // to hold a reference, but no cloned layers will be displayed. - mClonedChild = nullptr; - return true; - } - - std::map<sp<Layer>, sp<Layer>> clonedLayersMap; - // If the real layer exists and is in current state, add the clone as a child of the root. - // There's no need to remove from drawingState when the layer is offscreen since currentState is - // copied to drawingState for the root layer. So the clonedChild is always removed from - // drawingState and then needs to be added back each traversal. - if (!mClonedChild->getClonedFrom()->isRemovedFromCurrentState()) { - addChildToDrawing(mClonedChild); - } - - mClonedChild->updateClonedDrawingState(clonedLayersMap); - mClonedChild->updateClonedChildren(sp<Layer>::fromExisting(this), clonedLayersMap); - mClonedChild->updateClonedRelatives(clonedLayersMap); - - for (Layer* root : cloneRootsPendingUpdates) { - if (clonedLayersMap.find(sp<Layer>::fromExisting(root)) != clonedLayersMap.end()) { - return false; - } - } - return true; -} - -void Layer::updateClonedDrawingState(std::map<sp<Layer>, sp<Layer>>& clonedLayersMap) { - // If the layer the clone was cloned from is alive, copy the content of the drawingState - // to the clone. If the real layer is no longer alive, continue traversing the children - // since we may be able to pull out other children that are still alive. - if (isClonedFromAlive()) { - sp<Layer> clonedFrom = getClonedFrom(); - cloneDrawingState(clonedFrom.get()); - clonedLayersMap.emplace(clonedFrom, sp<Layer>::fromExisting(this)); - } - - // The clone layer may have children in drawingState since they may have been created and - // added from a previous request to updateMirorInfo. This is to ensure we don't recreate clones - // that already exist, since we can just re-use them. - // The drawingChildren will not get overwritten by the currentChildren since the clones are - // not updated in the regular traversal. They are skipped since the root will lose the - // reference to them when it copies its currentChildren to drawing. - for (sp<Layer>& child : mDrawingChildren) { - child->updateClonedDrawingState(clonedLayersMap); - } -} - -void Layer::updateClonedChildren(const sp<Layer>& mirrorRoot, - std::map<sp<Layer>, sp<Layer>>& clonedLayersMap) { - mDrawingChildren.clear(); - - if (!isClonedFromAlive()) { - return; - } - - sp<Layer> clonedFrom = getClonedFrom(); - for (sp<Layer>& child : clonedFrom->mDrawingChildren) { - if (child == mirrorRoot) { - // This is to avoid cyclical mirroring. - continue; - } - sp<Layer> clonedChild = clonedLayersMap[child]; - if (clonedChild == nullptr) { - clonedChild = child->createClone(); - clonedLayersMap[child] = clonedChild; - } - addChildToDrawing(clonedChild); - clonedChild->updateClonedChildren(mirrorRoot, clonedLayersMap); - } -} - -void Layer::updateClonedInputInfo(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap) { - auto cropLayer = mDrawingState.touchableRegionCrop.promote(); - if (cropLayer != nullptr) { - if (clonedLayersMap.count(cropLayer) == 0) { - // Real layer had a crop layer but it's not in the cloned hierarchy. Just set to - // self as crop layer to avoid going outside bounds. - mDrawingState.touchableRegionCrop = wp<Layer>::fromExisting(this); - } else { - const sp<Layer>& clonedCropLayer = clonedLayersMap.at(cropLayer); - mDrawingState.touchableRegionCrop = clonedCropLayer; - } - } - // Cloned layers shouldn't handle watch outside since their z order is not determined by - // WM or the client. - mDrawingState.inputInfo.setInputConfig(WindowInfo::InputConfig::WATCH_OUTSIDE_TOUCH, false); -} - -void Layer::updateClonedRelatives(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap) { - mDrawingState.zOrderRelativeOf = wp<Layer>(); - mDrawingState.zOrderRelatives.clear(); - - if (!isClonedFromAlive()) { +void Layer::callReleaseBufferCallback(const sp<ITransactionCompletedListener>& listener, + const sp<GraphicBuffer>& buffer, uint64_t framenumber, + const sp<Fence>& releaseFence) { + if (!listener && !mBufferReleaseChannel) { return; } - const sp<Layer>& clonedFrom = getClonedFrom(); - for (wp<Layer>& relativeWeak : clonedFrom->mDrawingState.zOrderRelatives) { - const sp<Layer>& relative = relativeWeak.promote(); - if (clonedLayersMap.count(relative) > 0) { - auto& clonedRelative = clonedLayersMap.at(relative); - mDrawingState.zOrderRelatives.add(clonedRelative); - } - } - - // Check if the relativeLayer for the real layer is part of the cloned hierarchy. - // It's possible that the layer it's relative to is outside the requested cloned hierarchy. - // In that case, we treat the layer as if the relativeOf has been removed. This way, it will - // still traverse the children, but the layer with the missing relativeOf will not be shown - // on screen. - const sp<Layer>& relativeOf = clonedFrom->mDrawingState.zOrderRelativeOf.promote(); - if (clonedLayersMap.count(relativeOf) > 0) { - const sp<Layer>& clonedRelativeOf = clonedLayersMap.at(relativeOf); - mDrawingState.zOrderRelativeOf = clonedRelativeOf; - } - - updateClonedInputInfo(clonedLayersMap); - - for (sp<Layer>& child : mDrawingChildren) { - child->updateClonedRelatives(clonedLayersMap); - } -} - -void Layer::addChildToDrawing(const sp<Layer>& layer) { - mDrawingChildren.add(layer); - layer->mDrawingParent = sp<Layer>::fromExisting(this); -} - -bool Layer::isInternalDisplayOverlay() const { - const State& s(mDrawingState); - if (s.flags & layer_state_t::eLayerSkipScreenshot) { - return true; - } - - sp<Layer> parent = mDrawingParent.promote(); - return parent && parent->isInternalDisplayOverlay(); -} + SFTRACE_FORMAT_INSTANT("callReleaseBufferCallback %s - %" PRIu64, getDebugName(), framenumber); -void Layer::setClonedChild(const sp<Layer>& clonedChild) { - mClonedChild = clonedChild; - mHadClonedChild = true; - mFlinger->mLayerMirrorRoots.push_back(this); -} + ReleaseCallbackId callbackId{buffer->getId(), framenumber}; + const sp<Fence>& fence = releaseFence ? releaseFence : Fence::NO_FENCE; + uint32_t currentMaxAcquiredBufferCount = + mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(mOwnerUid); -bool Layer::setDropInputMode(gui::DropInputMode mode) { - if (mDrawingState.dropInputMode == mode) { - return false; + if (listener) { + listener->onReleaseBuffer(callbackId, fence, currentMaxAcquiredBufferCount); } - mDrawingState.dropInputMode = mode; - return true; -} -void Layer::cloneDrawingState(const Layer* from) { - mDrawingState = from->mDrawingState; - // Skip callback info since they are not applicable for cloned layers. - mDrawingState.releaseBufferListener = nullptr; - // TODO (b/238781169) currently broken for mirror layers because we do not - // track release fences for mirror layers composed on other displays - mDrawingState.callbackHandles = {}; -} - -void Layer::callReleaseBufferCallback(const sp<ITransactionCompletedListener>& listener, - const sp<GraphicBuffer>& buffer, uint64_t framenumber, - const sp<Fence>& releaseFence) { - if (!listener) { - return; + if (mBufferReleaseChannel) { + mBufferReleaseChannel->writeReleaseFence(callbackId, fence, currentMaxAcquiredBufferCount); } - ATRACE_FORMAT_INSTANT("callReleaseBufferCallback %s - %" PRIu64, getDebugName(), framenumber); - uint32_t currentMaxAcquiredBufferCount = - mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(mOwnerUid); - listener->onReleaseBuffer({buffer->getId(), framenumber}, - releaseFence ? releaseFence : Fence::NO_FENCE, - currentMaxAcquiredBufferCount); } sp<CallbackHandle> Layer::findCallbackHandle() { @@ -2942,33 +804,15 @@ void Layer::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult, } } -void Layer::onSurfaceFrameCreated( - const std::shared_ptr<frametimeline::SurfaceFrame>& surfaceFrame) { - while (mPendingJankClassifications.size() >= kPendingClassificationMaxSurfaceFrames) { - // Too many SurfaceFrames pending classification. The front of the deque is probably not - // tracked by FrameTimeline and will never be presented. This will only result in a memory - // leak. - if (hasBufferOrSidebandStreamInDrawing()) { - // Only log for layers with a buffer, since we expect the jank data to be drained for - // these, while there may be no jank listeners for bufferless layers. - ALOGW("Removing the front of pending jank deque from layer - %s to prevent memory leak", - mName.c_str()); - std::string miniDump = mPendingJankClassifications.front()->miniDump(); - ALOGD("Head SurfaceFrame mini dump\n%s", miniDump.c_str()); - } - mPendingJankClassifications.pop_front(); - } - mPendingJankClassifications.emplace_back(surfaceFrame); -} - void Layer::releasePendingBuffer(nsecs_t dequeueReadyTime) { for (const auto& handle : mDrawingState.callbackHandles) { + handle->bufferReleaseChannel = mBufferReleaseChannel; handle->transformHint = mTransformHint; handle->dequeueReadyTime = dequeueReadyTime; handle->currentMaxAcquiredBufferCount = mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(mOwnerUid); - ATRACE_FORMAT_INSTANT("releasePendingBuffer %s - %" PRIu64, getDebugName(), - handle->previousReleaseCallbackId.framenumber); + SFTRACE_FORMAT_INSTANT("releasePendingBuffer %s - %" PRIu64, getDebugName(), + handle->previousReleaseCallbackId.framenumber); } for (auto& handle : mDrawingState.callbackHandles) { @@ -2978,24 +822,13 @@ void Layer::releasePendingBuffer(nsecs_t dequeueReadyTime) { } } - std::vector<JankData> jankData; - transferAvailableJankData(mDrawingState.callbackHandles, jankData); - mFlinger->getTransactionCallbackInvoker().addCallbackHandles(mDrawingState.callbackHandles, - jankData); + mFlinger->getTransactionCallbackInvoker().addCallbackHandles(mDrawingState.callbackHandles); mDrawingState.callbackHandles = {}; } -bool Layer::willPresentCurrentTransaction() const { - // Returns true if the most recent Transaction applied to CurrentState will be presented. - return (getSidebandStreamChanged() || getAutoRefresh() || - (mDrawingState.modified && - (mDrawingState.buffer != nullptr || mDrawingState.bgColorLayer != nullptr))); -} - bool Layer::setTransform(uint32_t transform) { if (mDrawingState.bufferTransform == transform) return false; mDrawingState.bufferTransform = transform; - mDrawingState.modified = true; setTransactionFlags(eTransactionNeeded); return true; } @@ -3004,111 +837,10 @@ bool Layer::setTransformToDisplayInverse(bool transformToDisplayInverse) { if (mDrawingState.transformToDisplayInverse == transformToDisplayInverse) return false; mDrawingState.sequence++; mDrawingState.transformToDisplayInverse = transformToDisplayInverse; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -bool Layer::setBufferCrop(const Rect& bufferCrop) { - if (mDrawingState.bufferCrop == bufferCrop) return false; - - mDrawingState.sequence++; - mDrawingState.bufferCrop = bufferCrop; - - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -bool Layer::setDestinationFrame(const Rect& destinationFrame) { - if (mDrawingState.destinationFrame == destinationFrame) return false; - - mDrawingState.sequence++; - mDrawingState.destinationFrame = destinationFrame; - - mDrawingState.modified = true; setTransactionFlags(eTransactionNeeded); return true; } -// Translate destination frame into scale and position. If a destination frame is not set, use the -// provided scale and position -bool Layer::updateGeometry() { - if ((mDrawingState.flags & layer_state_t::eIgnoreDestinationFrame) || - mDrawingState.destinationFrame.isEmpty()) { - // If destination frame is not set, use the requested transform set via - // Layer::setPosition and Layer::setMatrix. - return assignTransform(&mDrawingState.transform, mRequestedTransform); - } - - Rect destRect = mDrawingState.destinationFrame; - int32_t destW = destRect.width(); - int32_t destH = destRect.height(); - if (destRect.left < 0) { - destRect.left = 0; - destRect.right = destW; - } - if (destRect.top < 0) { - destRect.top = 0; - destRect.bottom = destH; - } - - if (!mDrawingState.buffer) { - ui::Transform t; - t.set(destRect.left, destRect.top); - return assignTransform(&mDrawingState.transform, t); - } - - uint32_t bufferWidth = mDrawingState.buffer->getWidth(); - uint32_t bufferHeight = mDrawingState.buffer->getHeight(); - // Undo any transformations on the buffer. - if (mDrawingState.bufferTransform & ui::Transform::ROT_90) { - std::swap(bufferWidth, bufferHeight); - } - uint32_t invTransform = SurfaceFlinger::getActiveDisplayRotationFlags(); - if (mDrawingState.transformToDisplayInverse) { - if (invTransform & ui::Transform::ROT_90) { - std::swap(bufferWidth, bufferHeight); - } - } - - float sx = destW / static_cast<float>(bufferWidth); - float sy = destH / static_cast<float>(bufferHeight); - ui::Transform t; - t.set(sx, 0, 0, sy); - t.set(destRect.left, destRect.top); - return assignTransform(&mDrawingState.transform, t); -} - -bool Layer::setMatrix(const layer_state_t::matrix22_t& matrix) { - if (mRequestedTransform.dsdx() == matrix.dsdx && mRequestedTransform.dtdy() == matrix.dtdy && - mRequestedTransform.dtdx() == matrix.dtdx && mRequestedTransform.dsdy() == matrix.dsdy) { - return false; - } - - mRequestedTransform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy); - - mDrawingState.sequence++; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - - return true; -} - -bool Layer::setPosition(float x, float y) { - if (mRequestedTransform.tx() == x && mRequestedTransform.ty() == y) { - return false; - } - - mRequestedTransform.set(x, y); - - mDrawingState.sequence++; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - - return true; -} - void Layer::releasePreviousBuffer() { mReleasePreviousBuffer = true; if (!mBufferInfo.mBuffer || @@ -3151,14 +883,14 @@ void Layer::resetDrawingStateBufferInfo() { bool Layer::setBuffer(std::shared_ptr<renderengine::ExternalTexture>& buffer, const BufferData& bufferData, nsecs_t postTime, nsecs_t desiredPresentTime, - bool isAutoTimestamp, const FrameTimelineInfo& info) { - ATRACE_FORMAT("setBuffer %s - hasBuffer=%s", getDebugName(), (buffer ? "true" : "false")); + bool isAutoTimestamp, const FrameTimelineInfo& info, gui::GameMode gameMode) { + SFTRACE_FORMAT("setBuffer %s - hasBuffer=%s", getDebugName(), (buffer ? "true" : "false")); const bool frameNumberChanged = bufferData.flags.test(BufferData::BufferDataChange::frameNumberChanged); const uint64_t frameNumber = frameNumberChanged ? bufferData.frameNumber : mDrawingState.frameNumber + 1; - ATRACE_FORMAT_INSTANT("setBuffer %s - %" PRIu64, getDebugName(), frameNumber); + SFTRACE_FORMAT_INSTANT("setBuffer %s - %" PRIu64, getDebugName(), frameNumber); if (mDrawingState.buffer) { releasePreviousBuffer(); @@ -3172,17 +904,16 @@ bool Layer::setBuffer(std::shared_ptr<renderengine::ExternalTexture>& buffer, mDrawingState.isAutoTimestamp = isAutoTimestamp; mDrawingState.latchedVsyncId = info.vsyncId; mDrawingState.useVsyncIdForRefreshRateSelection = info.useForRefreshRateSelection; - mDrawingState.modified = true; if (!buffer) { resetDrawingStateBufferInfo(); setTransactionFlags(eTransactionNeeded); mDrawingState.bufferSurfaceFrameTX = nullptr; - setFrameTimelineVsyncForBufferlessTransaction(info, postTime); + setFrameTimelineVsyncForBufferlessTransaction(info, postTime, gameMode); return true; } else { // release sideband stream if it exists and a non null buffer is being set if (mDrawingState.sidebandStream != nullptr) { - setSidebandStream(nullptr, info, postTime); + setSidebandStream(nullptr, info, postTime, gameMode); } } @@ -3221,9 +952,9 @@ bool Layer::setBuffer(std::shared_ptr<renderengine::ExternalTexture>& buffer, const int32_t layerId = getSequence(); mFlinger->mTimeStats->setPostTime(layerId, mDrawingState.frameNumber, getName().c_str(), - mOwnerUid, postTime, getGameMode()); + mOwnerUid, postTime, gameMode); - setFrameTimelineVsyncForBufferTransaction(info, postTime); + setFrameTimelineVsyncForBufferTransaction(info, postTime, gameMode); if (bufferData.dequeueTime > 0) { const uint64_t bufferId = mDrawingState.buffer->getId(); @@ -3253,10 +984,10 @@ void Layer::setDesiredPresentTime(nsecs_t desiredPresentTime, bool isAutoTimesta } void Layer::recordLayerHistoryBufferUpdate(const scheduler::LayerProps& layerProps, nsecs_t now) { - ATRACE_CALL(); + SFTRACE_CALL(); const nsecs_t presentTime = [&] { if (!mDrawingState.isAutoTimestamp) { - ATRACE_FORMAT_INSTANT("desiredPresentTime"); + SFTRACE_FORMAT_INSTANT("desiredPresentTime"); return mDrawingState.desiredPresentTime; } @@ -3265,7 +996,7 @@ void Layer::recordLayerHistoryBufferUpdate(const scheduler::LayerProps& layerPro mFlinger->mFrameTimeline->getTokenManager()->getPredictionsForToken( mDrawingState.latchedVsyncId); if (prediction.has_value()) { - ATRACE_FORMAT_INSTANT("predictedPresentTime"); + SFTRACE_FORMAT_INSTANT("predictedPresentTime"); mMaxTimeForUseVsyncId = prediction->presentTime + scheduler::LayerHistory::kMaxPeriodForHistory.count(); return prediction->presentTime; @@ -3301,9 +1032,9 @@ void Layer::recordLayerHistoryBufferUpdate(const scheduler::LayerProps& layerPro return static_cast<nsecs_t>(0); }(); - if (ATRACE_ENABLED() && presentTime > 0) { + if (SFTRACE_ENABLED() && presentTime > 0) { const auto presentIn = TimePoint::fromNs(presentTime) - TimePoint::now(); - ATRACE_FORMAT_INSTANT("presentIn %s", to_string(presentIn).c_str()); + SFTRACE_FORMAT_INSTANT("presentIn %s", to_string(presentIn).c_str()); } mFlinger->mScheduler->recordLayerHistory(sequence, layerProps, presentTime, now, @@ -3320,7 +1051,6 @@ void Layer::recordLayerHistoryAnimationTx(const scheduler::LayerProps& layerProp bool Layer::setDataspace(ui::Dataspace dataspace) { if (mDrawingState.dataspace == dataspace) return false; mDrawingState.dataspace = dataspace; - mDrawingState.modified = true; setTransactionFlags(eTransactionNeeded); return true; } @@ -3331,7 +1061,6 @@ bool Layer::setExtendedRangeBrightness(float currentBufferRatio, float desiredRa return false; mDrawingState.currentHdrSdrRatio = currentBufferRatio; mDrawingState.desiredHdrSdrRatio = desiredRatio; - mDrawingState.modified = true; setTransactionFlags(eTransactionNeeded); return true; } @@ -3339,46 +1068,12 @@ bool Layer::setExtendedRangeBrightness(float currentBufferRatio, float desiredRa bool Layer::setDesiredHdrHeadroom(float desiredRatio) { if (mDrawingState.desiredHdrSdrRatio == desiredRatio) return false; mDrawingState.desiredHdrSdrRatio = desiredRatio; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -bool Layer::setCachingHint(gui::CachingHint cachingHint) { - if (mDrawingState.cachingHint == cachingHint) return false; - mDrawingState.cachingHint = cachingHint; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -bool Layer::setHdrMetadata(const HdrMetadata& hdrMetadata) { - if (mDrawingState.hdrMetadata == hdrMetadata) return false; - mDrawingState.hdrMetadata = hdrMetadata; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -bool Layer::setSurfaceDamageRegion(const Region& surfaceDamage) { - if (mDrawingState.surfaceDamageRegion.hasSameRects(surfaceDamage)) return false; - mDrawingState.surfaceDamageRegion = surfaceDamage; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - setIsSmallDirty(surfaceDamage, getTransform()); - return true; -} - -bool Layer::setApi(int32_t api) { - if (mDrawingState.api == api) return false; - mDrawingState.api = api; - mDrawingState.modified = true; setTransactionFlags(eTransactionNeeded); return true; } bool Layer::setSidebandStream(const sp<NativeHandle>& sidebandStream, const FrameTimelineInfo& info, - nsecs_t postTime) { + nsecs_t postTime, gui::GameMode gameMode) { if (mDrawingState.sidebandStream == sidebandStream) return false; if (mDrawingState.sidebandStream != nullptr && sidebandStream == nullptr) { @@ -3388,12 +1083,11 @@ bool Layer::setSidebandStream(const sp<NativeHandle>& sidebandStream, const Fram } mDrawingState.sidebandStream = sidebandStream; - mDrawingState.modified = true; if (sidebandStream != nullptr && mDrawingState.buffer != nullptr) { releasePreviousBuffer(); resetDrawingStateBufferInfo(); mDrawingState.bufferSurfaceFrameTX = nullptr; - setFrameTimelineVsyncForBufferlessTransaction(info, postTime); + setFrameTimelineVsyncForBufferlessTransaction(info, postTime, gameMode); } setTransactionFlags(eTransactionNeeded); if (!mSidebandStreamChanged.exchange(true)) { @@ -3451,9 +1145,7 @@ bool Layer::setTransactionCompletedListeners(const std::vector<sp<CallbackHandle if (!remainingHandles.empty()) { // Notify the transaction completed threads these handles are done. These are only the // handles that were not added to the mDrawingState, which will be notified later. - std::vector<JankData> jankData; - transferAvailableJankData(remainingHandles, jankData); - mFlinger->getTransactionCallbackInvoker().addCallbackHandles(remainingHandles, jankData); + mFlinger->getTransactionCallbackInvoker().addCallbackHandles(remainingHandles); } mReleasePreviousBuffer = false; @@ -3487,14 +1179,6 @@ Rect Layer::getBufferSize(const State& /*s*/) const { return Rect(0, 0, static_cast<int32_t>(bufWidth), static_cast<int32_t>(bufHeight)); } -FloatRect Layer::computeSourceBounds(const FloatRect& parentBounds) const { - if (mBufferInfo.mBuffer == nullptr) { - return parentBounds; - } - - return getBufferSize(getDrawingState()).toFloatRect(); -} - bool Layer::fenceHasSignaled() const { if (SurfaceFlinger::enableLatchUnsignaledConfig != LatchUnsignaledConfig::Disabled) { return true; @@ -3516,37 +1200,21 @@ void Layer::onPreComposition(nsecs_t refreshStartTime) { } } -void Layer::setAutoRefresh(bool autoRefresh) { - mDrawingState.autoRefresh = autoRefresh; -} - bool Layer::latchSidebandStream(bool& recomputeVisibleRegions) { - // We need to update the sideband stream if the layer has both a buffer and a sideband stream. - auto* snapshot = editLayerSnapshot(); - snapshot->sidebandStreamHasFrame = hasFrameUpdate() && mSidebandStream.get(); - if (mSidebandStreamChanged.exchange(false)) { const State& s(getDrawingState()); // mSidebandStreamChanged was true mSidebandStream = s.sidebandStream; - snapshot->sidebandStream = mSidebandStream; if (mSidebandStream != nullptr) { setTransactionFlags(eTransactionNeeded); mFlinger->setTransactionFlags(eTraversalNeeded); } recomputeVisibleRegions = true; - return true; } return false; } -bool Layer::hasFrameUpdate() const { - const State& c(getDrawingState()); - return (mDrawingStateModified || mDrawingState.modified) && - (c.buffer != nullptr || c.bgColorLayer != nullptr); -} - void Layer::updateTexImage(nsecs_t latchTime, bool bgColorOnly) { const State& s(getDrawingState()); @@ -3593,8 +1261,6 @@ void Layer::updateTexImage(nsecs_t latchTime, bool bgColorOnly) { mFlinger->getTransactionCallbackInvoker() .addOnCommitCallbackHandles(mDrawingState.callbackHandles, remainingHandles); mDrawingState.callbackHandles = remainingHandles; - - mDrawingStateModified = false; } void Layer::gatherBufferInfo() { @@ -3618,7 +1284,6 @@ void Layer::gatherBufferInfo() { mBufferInfo.mFrameLatencyNeeded = true; mBufferInfo.mDesiredPresentTime = mDrawingState.desiredPresentTime; mBufferInfo.mFenceTime = std::make_shared<FenceTime>(mDrawingState.acquireFence); - mBufferInfo.mFence = mDrawingState.acquireFence; mBufferInfo.mTransform = mDrawingState.bufferTransform; auto lastDataspace = mBufferInfo.mDataspace; mBufferInfo.mDataspace = translateDataspace(mDrawingState.dataspace); @@ -3629,12 +1294,12 @@ void Layer::gatherBufferInfo() { ui::Dataspace dataspace = ui::Dataspace::UNKNOWN; status_t err = OK; { - ATRACE_NAME("getDataspace"); + SFTRACE_NAME("getDataspace"); err = mapper.getDataspace(mBufferInfo.mBuffer->getBuffer()->handle, &dataspace); } if (err != OK || dataspace != mBufferInfo.mDataspace) { { - ATRACE_NAME("setDataspace"); + SFTRACE_NAME("setDataspace"); err = mapper.setDataspace(mBufferInfo.mBuffer->getBuffer()->handle, static_cast<ui::Dataspace>(mBufferInfo.mDataspace)); } @@ -3666,10 +1331,6 @@ void Layer::gatherBufferInfo() { mFlinger->mHdrLayerInfoChanged = true; } mBufferInfo.mCrop = computeBufferCrop(mDrawingState); - mBufferInfo.mScaleMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW; - mBufferInfo.mSurfaceDamage = mDrawingState.surfaceDamageRegion; - mBufferInfo.mHdrMetadata = mDrawingState.hdrMetadata; - mBufferInfo.mApi = mDrawingState.api; mBufferInfo.mTransformToDisplayInverse = mDrawingState.transformToDisplayInverse; } @@ -3685,308 +1346,13 @@ Rect Layer::computeBufferCrop(const State& s) { } } -sp<Layer> Layer::createClone() { - surfaceflinger::LayerCreationArgs args(mFlinger.get(), nullptr, mName + " (Mirror)", 0, - LayerMetadata()); - sp<Layer> layer = mFlinger->getFactory().createBufferStateLayer(args); - return layer; -} - void Layer::decrementPendingBufferCount() { int32_t pendingBuffers = --mPendingBufferTransactions; tracePendingBufferCount(pendingBuffers); } void Layer::tracePendingBufferCount(int32_t pendingBuffers) { - ATRACE_INT(mBlastTransactionName.c_str(), pendingBuffers); -} - -/* - * We don't want to send the layer's transform to input, but rather the - * parent's transform. This is because Layer's transform is - * information about how the buffer is placed on screen. The parent's - * transform makes more sense to send since it's information about how the - * layer is placed on screen. This transform is used by input to determine - * how to go from screen space back to window space. - */ -ui::Transform Layer::getInputTransform() const { - if (!hasBufferOrSidebandStream()) { - return getTransform(); - } - sp<Layer> parent = mDrawingParent.promote(); - if (parent == nullptr) { - return ui::Transform(); - } - - return parent->getTransform(); -} - -/** - * Returns the bounds used to fill the input frame and the touchable region. - * - * Similar to getInputTransform, we need to update the bounds to include the transform. - * This is because bounds don't include the buffer transform, where the input assumes - * that's already included. - */ -std::pair<FloatRect, bool> Layer::getInputBounds(bool fillParentBounds) const { - Rect croppedBufferSize = getCroppedBufferSize(getDrawingState()); - FloatRect inputBounds = croppedBufferSize.toFloatRect(); - if (hasBufferOrSidebandStream() && croppedBufferSize.isValid() && - mDrawingState.transform.getType() != ui::Transform::IDENTITY) { - inputBounds = mDrawingState.transform.transform(inputBounds); - } - - bool inputBoundsValid = croppedBufferSize.isValid(); - if (!inputBoundsValid) { - /** - * Input bounds are based on the layer crop or buffer size. But if we are using - * the layer bounds as the input bounds (replaceTouchableRegionWithCrop flag) then - * we can use the parent bounds as the input bounds if the layer does not have buffer - * or a crop. We want to unify this logic but because of compat reasons we cannot always - * use the parent bounds. A layer without a buffer can get input. So when a window is - * initially added, its touchable region can fill its parent layer bounds and that can - * have negative consequences. - */ - inputBounds = fillParentBounds ? mBounds : FloatRect{}; - } - - // Clamp surface inset to the input bounds. - const float inset = static_cast<float>(mDrawingState.inputInfo.surfaceInset); - const float xSurfaceInset = std::clamp(inset, 0.f, inputBounds.getWidth() / 2.f); - const float ySurfaceInset = std::clamp(inset, 0.f, inputBounds.getHeight() / 2.f); - - // Apply the insets to the input bounds. - inputBounds.left += xSurfaceInset; - inputBounds.top += ySurfaceInset; - inputBounds.right -= xSurfaceInset; - inputBounds.bottom -= ySurfaceInset; - - return {inputBounds, inputBoundsValid}; -} - -bool Layer::isSimpleBufferUpdate(const layer_state_t& s) const { - const uint64_t requiredFlags = layer_state_t::eBufferChanged; - - const uint64_t deniedFlags = layer_state_t::eProducerDisconnect | layer_state_t::eLayerChanged | - layer_state_t::eRelativeLayerChanged | layer_state_t::eTransparentRegionChanged | - layer_state_t::eFlagsChanged | layer_state_t::eBlurRegionsChanged | - layer_state_t::eLayerStackChanged | layer_state_t::eReparent | - (FlagManager::getInstance().latch_unsignaled_with_auto_refresh_changed() - ? 0 - : layer_state_t::eAutoRefreshChanged); - - if ((s.what & requiredFlags) != requiredFlags) { - ATRACE_FORMAT_INSTANT("%s: false [missing required flags 0x%" PRIx64 "]", __func__, - (s.what | requiredFlags) & ~s.what); - return false; - } - - if (s.what & deniedFlags) { - ATRACE_FORMAT_INSTANT("%s: false [has denied flags 0x%" PRIx64 "]", __func__, - s.what & deniedFlags); - return false; - } - - if (s.what & layer_state_t::ePositionChanged) { - if (mRequestedTransform.tx() != s.x || mRequestedTransform.ty() != s.y) { - ATRACE_FORMAT_INSTANT("%s: false [ePositionChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eAlphaChanged) { - if (mDrawingState.color.a != s.color.a) { - ATRACE_FORMAT_INSTANT("%s: false [eAlphaChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eColorTransformChanged) { - if (mDrawingState.colorTransform != s.colorTransform) { - ATRACE_FORMAT_INSTANT("%s: false [eColorTransformChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eBackgroundColorChanged) { - if (mDrawingState.bgColorLayer || s.bgColor.a != 0) { - ATRACE_FORMAT_INSTANT("%s: false [eBackgroundColorChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eMatrixChanged) { - if (mRequestedTransform.dsdx() != s.matrix.dsdx || - mRequestedTransform.dtdy() != s.matrix.dtdy || - mRequestedTransform.dtdx() != s.matrix.dtdx || - mRequestedTransform.dsdy() != s.matrix.dsdy) { - ATRACE_FORMAT_INSTANT("%s: false [eMatrixChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eCornerRadiusChanged) { - if (mDrawingState.cornerRadius != s.cornerRadius) { - ATRACE_FORMAT_INSTANT("%s: false [eCornerRadiusChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eBackgroundBlurRadiusChanged) { - if (mDrawingState.backgroundBlurRadius != static_cast<int>(s.backgroundBlurRadius)) { - ATRACE_FORMAT_INSTANT("%s: false [eBackgroundBlurRadiusChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eBufferTransformChanged) { - if (mDrawingState.bufferTransform != s.bufferTransform) { - ATRACE_FORMAT_INSTANT("%s: false [eBufferTransformChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eTransformToDisplayInverseChanged) { - if (mDrawingState.transformToDisplayInverse != s.transformToDisplayInverse) { - ATRACE_FORMAT_INSTANT("%s: false [eTransformToDisplayInverseChanged changed]", - __func__); - return false; - } - } - - if (s.what & layer_state_t::eCropChanged) { - if (mDrawingState.crop != s.crop) { - ATRACE_FORMAT_INSTANT("%s: false [eCropChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eDataspaceChanged) { - if (mDrawingState.dataspace != s.dataspace) { - ATRACE_FORMAT_INSTANT("%s: false [eDataspaceChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eHdrMetadataChanged) { - if (mDrawingState.hdrMetadata != s.hdrMetadata) { - ATRACE_FORMAT_INSTANT("%s: false [eHdrMetadataChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eSidebandStreamChanged) { - if (mDrawingState.sidebandStream != s.sidebandStream) { - ATRACE_FORMAT_INSTANT("%s: false [eSidebandStreamChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eColorSpaceAgnosticChanged) { - if (mDrawingState.colorSpaceAgnostic != s.colorSpaceAgnostic) { - ATRACE_FORMAT_INSTANT("%s: false [eColorSpaceAgnosticChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eShadowRadiusChanged) { - if (mDrawingState.shadowRadius != s.shadowRadius) { - ATRACE_FORMAT_INSTANT("%s: false [eShadowRadiusChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eFixedTransformHintChanged) { - if (mDrawingState.fixedTransformHint != s.fixedTransformHint) { - ATRACE_FORMAT_INSTANT("%s: false [eFixedTransformHintChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eTrustedOverlayChanged) { - if (mDrawingState.isTrustedOverlay != (s.trustedOverlay == gui::TrustedOverlay::ENABLED)) { - ATRACE_FORMAT_INSTANT("%s: false [eTrustedOverlayChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eStretchChanged) { - StretchEffect temp = s.stretchEffect; - temp.sanitize(); - if (mDrawingState.stretchEffect != temp) { - ATRACE_FORMAT_INSTANT("%s: false [eStretchChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eBufferCropChanged) { - if (mDrawingState.bufferCrop != s.bufferCrop) { - ATRACE_FORMAT_INSTANT("%s: false [eBufferCropChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eDestinationFrameChanged) { - if (mDrawingState.destinationFrame != s.destinationFrame) { - ATRACE_FORMAT_INSTANT("%s: false [eDestinationFrameChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eDimmingEnabledChanged) { - if (mDrawingState.dimmingEnabled != s.dimmingEnabled) { - ATRACE_FORMAT_INSTANT("%s: false [eDimmingEnabledChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eExtendedRangeBrightnessChanged) { - if (mDrawingState.currentHdrSdrRatio != s.currentHdrSdrRatio || - mDrawingState.desiredHdrSdrRatio != s.desiredHdrSdrRatio) { - ATRACE_FORMAT_INSTANT("%s: false [eExtendedRangeBrightnessChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eDesiredHdrHeadroomChanged) { - if (mDrawingState.desiredHdrSdrRatio != s.desiredHdrSdrRatio) { - ATRACE_FORMAT_INSTANT("%s: false [eDesiredHdrHeadroomChanged changed]", __func__); - return false; - } - } - - return true; -} - -sp<LayerFE> Layer::getCompositionEngineLayerFE() const { - // There's no need to get a CE Layer if the layer isn't going to draw anything. - return hasSomethingToDraw() ? mLegacyLayerFE : nullptr; -} - -const LayerSnapshot* Layer::getLayerSnapshot() const { - return mSnapshot.get(); -} - -LayerSnapshot* Layer::editLayerSnapshot() { - return mSnapshot.get(); -} - -std::unique_ptr<frontend::LayerSnapshot> Layer::stealLayerSnapshot() { - return std::move(mSnapshot); -} - -void Layer::updateLayerSnapshot(std::unique_ptr<frontend::LayerSnapshot> snapshot) { - mSnapshot = std::move(snapshot); -} - -const compositionengine::LayerFECompositionState* Layer::getCompositionState() const { - return mSnapshot.get(); -} - -sp<LayerFE> Layer::copyCompositionEngineLayerFE() const { - auto result = mFlinger->getFactory().createLayerFE(mName, this); - result->mSnapshot = std::make_unique<LayerSnapshot>(*mSnapshot); - return result; + SFTRACE_INT(mBlastTransactionName.c_str(), pendingBuffers); } sp<LayerFE> Layer::getCompositionEngineLayerFE( @@ -4001,59 +1367,11 @@ sp<LayerFE> Layer::getCompositionEngineLayerFE( return layerFE; } -void Layer::useSurfaceDamage() { - if (mFlinger->mForceFullDamage) { - surfaceDamageRegion = Region::INVALID_REGION; - } else { - surfaceDamageRegion = mBufferInfo.mSurfaceDamage; - } -} - -void Layer::useEmptyDamage() { - surfaceDamageRegion.clear(); -} - -bool Layer::isOpaque(const Layer::State& s) const { - // if we don't have a buffer or sidebandStream yet, we're translucent regardless of the - // layer's opaque flag. - if (!hasSomethingToDraw()) { - return false; - } - - // if the layer has the opaque flag, then we're always opaque - if ((s.flags & layer_state_t::eLayerOpaque) == layer_state_t::eLayerOpaque) { - return true; - } - - // If the buffer has no alpha channel, then we are opaque - if (hasBufferOrSidebandStream() && LayerSnapshot::isOpaqueFormat(getPixelFormat())) { - return true; - } - - // Lastly consider the layer opaque if drawing a color with alpha == 1.0 - return fillsColor() && getAlpha() == 1.0_hf; -} - -bool Layer::canReceiveInput() const { - return !isHiddenByPolicy() && (mBufferInfo.mBuffer == nullptr || getAlpha() > 0.0f); -} - -bool Layer::isVisible() const { - if (!hasSomethingToDraw()) { - return false; - } - - if (isHiddenByPolicy()) { - return false; - } - - return getAlpha() > 0.0f || hasBlur(); -} - void Layer::onCompositionPresented(const DisplayDevice* display, const std::shared_ptr<FenceTime>& glDoneFence, const std::shared_ptr<FenceTime>& presentFence, - const CompositorTiming& compositorTiming) { + const CompositorTiming& compositorTiming, + gui::GameMode gameMode) { // mFrameLatencyNeeded is true when a new frame was latched for the // composition. if (!mBufferInfo.mFrameLatencyNeeded) return; @@ -4095,12 +1413,12 @@ void Layer::onCompositionPresented(const DisplayDevice* display, } if (display) { - const Fps refreshRate = display->refreshRateSelector().getActiveMode().fps; + const auto activeMode = display->refreshRateSelector().getActiveMode(); + const Fps refreshRate = activeMode.fps; const std::optional<Fps> renderRate = mFlinger->mScheduler->getFrameRateOverride(getOwnerUid()); const auto vote = frameRateToSetFrameRateVotePayload(getFrameRateForLayerTree()); - const auto gameMode = getGameMode(); if (presentFence->isValid()) { mFlinger->mTimeStats->setPresentFence(layerId, mCurrentFrameNumber, presentFence, @@ -4116,7 +1434,12 @@ void Layer::onCompositionPresented(const DisplayDevice* display, mFlinger->getHwComposer().getPresentTimestamp(*displayId); const nsecs_t now = systemTime(CLOCK_MONOTONIC); - const nsecs_t vsyncPeriod = display->getVsyncPeriodFromHWC(); + const nsecs_t vsyncPeriod = + mFlinger->getHwComposer() + .getDisplayVsyncPeriod(*displayId) + .value_opt() + .value_or(activeMode.modePtr->getVsyncRate().getPeriodNsecs()); + const nsecs_t actualPresentTime = now - ((now - presentTimestamp) % vsyncPeriod); mFlinger->mTimeStats->setPresentTime(layerId, mCurrentFrameNumber, actualPresentTime, @@ -4132,18 +1455,9 @@ void Layer::onCompositionPresented(const DisplayDevice* display, mBufferInfo.mFrameLatencyNeeded = false; } -bool Layer::willReleaseBufferOnLatch() const { - return !mDrawingState.buffer && mBufferInfo.mBuffer; -} - -bool Layer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime) { - const bool bgColorOnly = mDrawingState.bgColorLayer != nullptr; - return latchBufferImpl(recomputeVisibleRegions, latchTime, bgColorOnly); -} - bool Layer::latchBufferImpl(bool& recomputeVisibleRegions, nsecs_t latchTime, bool bgColorOnly) { - ATRACE_FORMAT_INSTANT("latchBuffer %s - %" PRIu64, getDebugName(), - getDrawingState().frameNumber); + SFTRACE_FORMAT_INSTANT("latchBuffer %s - %" PRIu64, getDebugName(), + getDrawingState().frameNumber); bool refreshRequired = latchSidebandStream(recomputeVisibleRegions); @@ -4154,7 +1468,7 @@ bool Layer::latchBufferImpl(bool& recomputeVisibleRegions, nsecs_t latchTime, bo // If the head buffer's acquire fence hasn't signaled yet, return and // try again later if (!fenceHasSignaled()) { - ATRACE_NAME("!fenceHasSignaled()"); + SFTRACE_NAME("!fenceHasSignaled()"); mFlinger->onLayerUpdate(); return false; } @@ -4162,7 +1476,6 @@ bool Layer::latchBufferImpl(bool& recomputeVisibleRegions, nsecs_t latchTime, bo // Capture the old state of the layer for comparisons later BufferInfo oldBufferInfo = mBufferInfo; - const bool oldOpacity = isOpaque(mDrawingState); mPreviousFrameNumber = mCurrentFrameNumber; mCurrentFrameNumber = mDrawingState.frameNumber; gatherBufferInfo(); @@ -4187,7 +1500,6 @@ bool Layer::latchBufferImpl(bool& recomputeVisibleRegions, nsecs_t latchTime, bo if ((mBufferInfo.mCrop != oldBufferInfo.mCrop) || (mBufferInfo.mTransform != oldBufferInfo.mTransform) || - (mBufferInfo.mScaleMode != oldBufferInfo.mScaleMode) || (mBufferInfo.mTransformToDisplayInverse != oldBufferInfo.mTransformToDisplayInverse)) { recomputeVisibleRegions = true; } @@ -4200,70 +1512,13 @@ bool Layer::latchBufferImpl(bool& recomputeVisibleRegions, nsecs_t latchTime, bo recomputeVisibleRegions = true; } } - - if (oldOpacity != isOpaque(mDrawingState)) { - recomputeVisibleRegions = true; - } - return true; } -bool Layer::hasReadyFrame() const { - return hasFrameUpdate() || getSidebandStreamChanged() || getAutoRefresh(); -} - -bool Layer::isProtected() const { - return (mBufferInfo.mBuffer != nullptr) && - (mBufferInfo.mBuffer->getUsage() & GRALLOC_USAGE_PROTECTED); -} - -void Layer::latchAndReleaseBuffer() { - if (hasReadyFrame()) { - bool ignored = false; - latchBuffer(ignored, systemTime()); - } - releasePendingBuffer(systemTime()); -} - -PixelFormat Layer::getPixelFormat() const { - return mBufferInfo.mPixelFormat; -} - bool Layer::getTransformToDisplayInverse() const { return mBufferInfo.mTransformToDisplayInverse; } -Rect Layer::getBufferCrop() const { - // this is the crop rectangle that applies to the buffer - // itself (as opposed to the window) - if (!mBufferInfo.mCrop.isEmpty()) { - // if the buffer crop is defined, we use that - return mBufferInfo.mCrop; - } else if (mBufferInfo.mBuffer != nullptr) { - // otherwise we use the whole buffer - return mBufferInfo.mBuffer->getBounds(); - } else { - // if we don't have a buffer yet, we use an empty/invalid crop - return Rect(); - } -} - -uint32_t Layer::getBufferTransform() const { - return mBufferInfo.mTransform; -} - -ui::Dataspace Layer::getDataSpace() const { - return hasBufferOrSidebandStream() ? mBufferInfo.mDataspace : mDrawingState.dataspace; -} - -bool Layer::isFrontBuffered() const { - if (mBufferInfo.mBuffer == nullptr) { - return false; - } - - return mBufferInfo.mBuffer->getUsage() & AHARDWAREBUFFER_USAGE_FRONT_BUFFER; -} - ui::Dataspace Layer::translateDataspace(ui::Dataspace dataspace) { ui::Dataspace updatedDataspace = dataspace; // translate legacy dataspaces to modern dataspaces @@ -4299,123 +1554,6 @@ sp<GraphicBuffer> Layer::getBuffer() const { return mBufferInfo.mBuffer ? mBufferInfo.mBuffer->getBuffer() : nullptr; } -void Layer::setTransformHintLegacy(ui::Transform::RotationFlags displayTransformHint) { - mTransformHintLegacy = getFixedTransformHint(); - if (mTransformHintLegacy == ui::Transform::ROT_INVALID) { - mTransformHintLegacy = displayTransformHint; - } -} - -const std::shared_ptr<renderengine::ExternalTexture>& Layer::getExternalTexture() const { - return mBufferInfo.mBuffer; -} - -bool Layer::setColor(const half3& color) { - if (mDrawingState.color.rgb == color) { - return false; - } - - mDrawingState.sequence++; - mDrawingState.color.rgb = color; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -bool Layer::fillsColor() const { - return !hasBufferOrSidebandStream() && mDrawingState.color.r >= 0.0_hf && - mDrawingState.color.g >= 0.0_hf && mDrawingState.color.b >= 0.0_hf; -} - -bool Layer::hasBlur() const { - return getBackgroundBlurRadius() > 0 || getDrawingState().blurRegions.size() > 0; -} - -void Layer::updateSnapshot(bool updateGeometry) { - if (!getCompositionEngineLayerFE()) { - return; - } - - auto* snapshot = editLayerSnapshot(); - if (updateGeometry) { - prepareBasicGeometryCompositionState(); - prepareGeometryCompositionState(); - snapshot->roundedCorner = getRoundedCornerState(); - snapshot->transformedBounds = mScreenBounds; - if (mEffectiveShadowRadius > 0.f) { - snapshot->shadowSettings = mFlinger->mDrawingState.globalShadowSettings; - - // Note: this preserves existing behavior of shadowing the entire layer and not cropping - // it if transparent regions are present. This may not be necessary since shadows are - // typically cast by layers without transparent regions. - snapshot->shadowSettings.boundaries = mBounds; - - const float casterAlpha = snapshot->alpha; - const bool casterIsOpaque = - ((mBufferInfo.mBuffer != nullptr) && isOpaque(mDrawingState)); - - // If the casting layer is translucent, we need to fill in the shadow underneath the - // layer. Otherwise the generated shadow will only be shown around the casting layer. - snapshot->shadowSettings.casterIsTranslucent = !casterIsOpaque || (casterAlpha < 1.0f); - snapshot->shadowSettings.ambientColor *= casterAlpha; - snapshot->shadowSettings.spotColor *= casterAlpha; - } - snapshot->shadowSettings.length = mEffectiveShadowRadius; - } - snapshot->contentOpaque = isOpaque(mDrawingState); - snapshot->layerOpaqueFlagSet = - (mDrawingState.flags & layer_state_t::eLayerOpaque) == layer_state_t::eLayerOpaque; - sp<Layer> p = mDrawingParent.promote(); - if (p != nullptr) { - snapshot->parentTransform = p->getTransform(); - } else { - snapshot->parentTransform.reset(); - } - snapshot->bufferSize = getBufferSize(mDrawingState); - snapshot->externalTexture = mBufferInfo.mBuffer; - snapshot->hasReadyFrame = hasReadyFrame(); - preparePerFrameCompositionState(); -} - -void Layer::updateChildrenSnapshots(bool updateGeometry) { - for (const sp<Layer>& child : mDrawingChildren) { - child->updateSnapshot(updateGeometry); - child->updateChildrenSnapshots(updateGeometry); - } -} - -void Layer::updateMetadataSnapshot(const LayerMetadata& parentMetadata) { - mSnapshot->layerMetadata = parentMetadata; - mSnapshot->layerMetadata.merge(mDrawingState.metadata); - for (const sp<Layer>& child : mDrawingChildren) { - child->updateMetadataSnapshot(mSnapshot->layerMetadata); - } -} - -void Layer::updateRelativeMetadataSnapshot(const LayerMetadata& relativeLayerMetadata, - std::unordered_set<Layer*>& visited) { - if (visited.find(this) != visited.end()) { - ALOGW("Cycle containing layer %s detected in z-order relatives", getDebugName()); - return; - } - visited.insert(this); - - mSnapshot->relativeLayerMetadata = relativeLayerMetadata; - - if (mDrawingState.zOrderRelatives.empty()) { - return; - } - LayerMetadata childRelativeLayerMetadata = mSnapshot->relativeLayerMetadata; - childRelativeLayerMetadata.merge(mSnapshot->layerMetadata); - for (wp<Layer> weakRelative : mDrawingState.zOrderRelatives) { - sp<Layer> relative = weakRelative.promote(); - if (!relative) { - continue; - } - relative->updateRelativeMetadataSnapshot(childRelativeLayerMetadata, visited); - } -} - bool Layer::setTrustedPresentationInfo(TrustedPresentationThresholds const& thresholds, TrustedPresentationListener const& listener) { bool hadTrustedPresentationListener = hasTrustedPresentationListener(); @@ -4439,39 +1577,41 @@ bool Layer::setTrustedPresentationInfo(TrustedPresentationThresholds const& thre return haveTrustedPresentationListener; } +void Layer::setBufferReleaseChannel( + const std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint>& channel) { + mBufferReleaseChannel = channel; +} + void Layer::updateLastLatchTime(nsecs_t latchTime) { mLastLatchTime = latchTime; } -void Layer::setIsSmallDirty(const Region& damageRegion, - const ui::Transform& layerToDisplayTransform) { - mSmallDirty = false; +void Layer::setIsSmallDirty(frontend::LayerSnapshot* snapshot) { if (!mFlinger->mScheduler->supportSmallDirtyDetection(mOwnerAppId)) { + snapshot->isSmallDirty = false; return; } if (mWindowType != WindowInfo::Type::APPLICATION && mWindowType != WindowInfo::Type::BASE_APPLICATION) { + snapshot->isSmallDirty = false; return; } - Rect bounds = damageRegion.getBounds(); + Rect bounds = snapshot->surfaceDamage.getBounds(); if (!bounds.isValid()) { + snapshot->isSmallDirty = false; return; } // Transform to screen space. - bounds = layerToDisplayTransform.transform(bounds); + bounds = snapshot->localTransform.transform(bounds); // If the damage region is a small dirty, this could give the hint for the layer history that // it could suppress the heuristic rate when calculating. - mSmallDirty = mFlinger->mScheduler->isSmallDirtyArea(mOwnerAppId, - bounds.getWidth() * bounds.getHeight()); -} - -void Layer::setIsSmallDirty(frontend::LayerSnapshot* snapshot) { - setIsSmallDirty(snapshot->surfaceDamage, snapshot->localTransform); - snapshot->isSmallDirty = mSmallDirty; + snapshot->isSmallDirty = + mFlinger->mScheduler->isSmallDirtyArea(mOwnerAppId, + bounds.getWidth() * bounds.getHeight()); } } // namespace android diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index b9fcd5c333..9bc557e917 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -43,9 +43,7 @@ #include <scheduler/Fps.h> #include <scheduler/Seamlessness.h> -#include <chrono> #include <cstdint> -#include <list> #include <optional> #include <vector> @@ -56,7 +54,6 @@ #include "LayerVector.h" #include "Scheduler/LayerInfo.h" #include "SurfaceFlinger.h" -#include "Tracing/LayerTracing.h" #include "TransactionCallbackInvoker.h" using namespace android::surfaceflinger; @@ -91,48 +88,15 @@ public: // Windows that are not in focus, but voted for a specific mode ID. static constexpr int32_t PRIORITY_NOT_FOCUSED_WITH_MODE = 2; - enum { // flags for doTransaction() - eDontUpdateGeometryState = 0x00000001, - eVisibleRegion = 0x00000002, - eInputInfoChanged = 0x00000004 - }; - - struct Geometry { - uint32_t w; - uint32_t h; - ui::Transform transform; - - inline bool operator==(const Geometry& rhs) const { - return (w == rhs.w && h == rhs.h) && (transform.tx() == rhs.transform.tx()) && - (transform.ty() == rhs.transform.ty()); - } - inline bool operator!=(const Geometry& rhs) const { return !operator==(rhs); } - }; - using FrameRate = scheduler::LayerInfo::FrameRate; using FrameRateCompatibility = scheduler::FrameRateCompatibility; using FrameRateSelectionStrategy = scheduler::LayerInfo::FrameRateSelectionStrategy; struct State { - int32_t z; - ui::LayerStack layerStack; - uint32_t flags; int32_t sequence; // changes when visible regions can change - bool modified; // Crop is expressed in layer space coordinate. Rect crop; LayerMetadata metadata; - // If non-null, a Surface this Surface's Z-order is interpreted relative to. - wp<Layer> zOrderRelativeOf; - bool isRelativeOf{false}; - - // A list of surfaces whose Z-order is interpreted relative to ours. - SortedVector<wp<Layer>> zOrderRelatives; - half4 color; - float cornerRadius; - int backgroundBlurRadius; - gui::WindowInfo inputInfo; - wp<Layer> touchableRegionCrop; ui::Dataspace dataspace; @@ -154,52 +118,18 @@ public: std::shared_ptr<renderengine::ExternalTexture> buffer; sp<Fence> acquireFence; std::shared_ptr<FenceTime> acquireFenceTime; - HdrMetadata hdrMetadata; - Region surfaceDamageRegion; - int32_t api; sp<NativeHandle> sidebandStream; mat4 colorTransform; - bool hasColorTransform; - // pointer to background color layer that, if set, appears below the buffer state layer - // and the buffer state layer's children. Z order will be set to - // INT_MIN - sp<Layer> bgColorLayer; // The deque of callback handles for this frame. The back of the deque contains the most // recent callback handle. std::deque<sp<CallbackHandle>> callbackHandles; - bool colorSpaceAgnostic; nsecs_t desiredPresentTime = 0; bool isAutoTimestamp = true; - // Length of the cast shadow. If the radius is > 0, a shadow of length shadowRadius will - // be rendered around the layer. - float shadowRadius; - - // Layer regions that are made of custom materials, like frosted glass - std::vector<BlurRegion> blurRegions; - - // Priority of the layer assigned by Window Manager. - int32_t frameRateSelectionPriority; - - // Default frame rate compatibility used to set the layer refresh rate votetype. - FrameRateCompatibility defaultFrameRateCompatibility; - FrameRate frameRate; - // The combined frame rate of parents / children of this layer FrameRate frameRateForLayerTree; - FrameRateSelectionStrategy frameRateSelectionStrategy; - - // Set by window manager indicating the layer and all its children are - // in a different orientation than the display. The hint suggests that - // the graphic producers should receive a transform hint as if the - // display was in this orientation. When the display changes to match - // the layer orientation, the graphic producer may not need to allocate - // a buffer of a different size. ui::Transform::ROT_INVALID means the - // a fixed transform hint is not set. - ui::Transform::RotationFlags fixedTransformHint; - // The vsync info that was used to start the transaction FrameTimelineInfo frameTimelineInfo; @@ -219,21 +149,12 @@ public: // An arbitrary threshold for the number of BufferlessSurfaceFrames in the state. Used to // trigger a warning if the number of SurfaceFrames crosses the threshold. static constexpr uint32_t kStateSurfaceFramesThreshold = 25; - - // Stretch effect to apply to this layer - StretchEffect stretchEffect; - - // Whether or not this layer is a trusted overlay for input - bool isTrustedOverlay; Rect bufferCrop; Rect destinationFrame; sp<IBinder> releaseBufferEndpoint; - gui::DropInputMode dropInputMode; bool autoRefresh = false; - bool dimmingEnabled = true; float currentHdrSdrRatio = 1.f; float desiredHdrSdrRatio = -1.f; - gui::CachingHint cachingHint = gui::CachingHint::Enabled; int64_t latchedVsyncId = 0; bool useVsyncIdForRefreshRateSelection = false; }; @@ -244,202 +165,49 @@ public: static bool isLayerFocusedBasedOnPriority(int32_t priority); static void miniDumpHeader(std::string& result); - // Provide unique string for each class type in the Layer hierarchy - virtual const char* getType() const { return "Layer"; } - - // true if this layer is visible, false otherwise - virtual bool isVisible() const; - - virtual sp<Layer> createClone(); - - // Set a 2x2 transformation matrix on the layer. This transform - // will be applied after parent transforms, but before any final - // producer specified transform. - bool setMatrix(const layer_state_t::matrix22_t& matrix); - // This second set of geometry attributes are controlled by // setGeometryAppliesWithResize, and their default mode is to be // immediate. If setGeometryAppliesWithResize is specified // while a resize is pending, then update of these attributes will // be delayed until the resize completes. - // setPosition operates in parent buffer space (pre parent-transform) or display - // space for top-level layers. - bool setPosition(float x, float y); // Buffer space bool setCrop(const Rect& crop); - // TODO(b/38182121): Could we eliminate the various latching modes by - // using the layer hierarchy? - // ----------------------------------------------------------------------- - virtual bool setLayer(int32_t z); - virtual bool setRelativeLayer(const sp<IBinder>& relativeToHandle, int32_t relativeZ); - - virtual bool setAlpha(float alpha); - bool setColor(const half3& /*color*/); - - // Set rounded corner radius for this layer and its children. - // - // We only support 1 radius per layer in the hierarchy, where parent layers have precedence. - // The shape of the rounded corner rectangle is specified by the crop rectangle of the layer - // from which we inferred the rounded corner radius. - virtual bool setCornerRadius(float cornerRadius); - // When non-zero, everything below this layer will be blurred by backgroundBlurRadius, which - // is specified in pixels. - virtual bool setBackgroundBlurRadius(int backgroundBlurRadius); - virtual bool setBlurRegions(const std::vector<BlurRegion>& effectRegions); - bool setTransparentRegionHint(const Region& transparent); - virtual bool setTrustedOverlay(bool); - virtual bool setFlags(uint32_t flags, uint32_t mask); - virtual bool setLayerStack(ui::LayerStack); - virtual ui::LayerStack getLayerStack( - LayerVector::StateSet state = LayerVector::StateSet::Drawing) const; - - virtual bool setMetadata(const LayerMetadata& data); - virtual void setChildrenDrawingParent(const sp<Layer>&); - virtual bool reparent(const sp<IBinder>& newParentHandle) REQUIRES(mFlinger->mStateLock); - virtual bool setColorTransform(const mat4& matrix); - virtual mat4 getColorTransform() const; - virtual bool hasColorTransform() const; - virtual bool isColorSpaceAgnostic() const { return mDrawingState.colorSpaceAgnostic; } - virtual bool isDimmingEnabled() const { return getDrawingState().dimmingEnabled; } - float getDesiredHdrSdrRatio() const { return getDrawingState().desiredHdrSdrRatio; } - float getCurrentHdrSdrRatio() const { return getDrawingState().currentHdrSdrRatio; } - gui::CachingHint getCachingHint() const { return getDrawingState().cachingHint; } - bool setTransform(uint32_t /*transform*/); bool setTransformToDisplayInverse(bool /*transformToDisplayInverse*/); bool setBuffer(std::shared_ptr<renderengine::ExternalTexture>& /* buffer */, const BufferData& /* bufferData */, nsecs_t /* postTime */, nsecs_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/, - const FrameTimelineInfo& /*info*/); + const FrameTimelineInfo& /*info*/, gui::GameMode gameMode); void setDesiredPresentTime(nsecs_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/); bool setDataspace(ui::Dataspace /*dataspace*/); bool setExtendedRangeBrightness(float currentBufferRatio, float desiredRatio); bool setDesiredHdrHeadroom(float desiredRatio); - bool setCachingHint(gui::CachingHint cachingHint); - bool setHdrMetadata(const HdrMetadata& /*hdrMetadata*/); - bool setSurfaceDamageRegion(const Region& /*surfaceDamage*/); - bool setApi(int32_t /*api*/); bool setSidebandStream(const sp<NativeHandle>& /*sidebandStream*/, - const FrameTimelineInfo& /* info*/, nsecs_t /* postTime */); + const FrameTimelineInfo& /* info*/, nsecs_t /* postTime */, + gui::GameMode gameMode); bool setTransactionCompletedListeners(const std::vector<sp<CallbackHandle>>& /*handles*/, bool willPresent); - virtual bool setBackgroundColor(const half3& color, float alpha, ui::Dataspace dataspace) - REQUIRES(mFlinger->mStateLock); - virtual bool setColorSpaceAgnostic(const bool agnostic); - virtual bool setDimmingEnabled(const bool dimmingEnabled); - virtual bool setDefaultFrameRateCompatibility(FrameRateCompatibility compatibility); - virtual bool setFrameRateSelectionPriority(int32_t priority); - virtual bool setFixedTransformHint(ui::Transform::RotationFlags fixedTransformHint); - void setAutoRefresh(bool /* autoRefresh */); - bool setDropInputMode(gui::DropInputMode); - - // If the variable is not set on the layer, it traverses up the tree to inherit the frame - // rate priority from its parent. - virtual int32_t getFrameRateSelectionPriority() const; - // - virtual FrameRateCompatibility getDefaultFrameRateCompatibility() const; - // - ui::Dataspace getDataSpace() const; - - virtual bool isFrontBuffered() const; - virtual sp<LayerFE> getCompositionEngineLayerFE() const; - virtual sp<LayerFE> copyCompositionEngineLayerFE() const; sp<LayerFE> getCompositionEngineLayerFE(const frontend::LayerHierarchy::TraversalPath&); - sp<LayerFE> getOrCreateCompositionEngineLayerFE(const frontend::LayerHierarchy::TraversalPath&); - - const frontend::LayerSnapshot* getLayerSnapshot() const; - frontend::LayerSnapshot* editLayerSnapshot(); - std::unique_ptr<frontend::LayerSnapshot> stealLayerSnapshot(); - void updateLayerSnapshot(std::unique_ptr<frontend::LayerSnapshot> snapshot); // If we have received a new buffer this frame, we will pass its surface // damage down to hardware composer. Otherwise, we must send a region with // one empty rect. - void useSurfaceDamage(); - void useEmptyDamage(); Region getVisibleRegion(const DisplayDevice*) const; void updateLastLatchTime(nsecs_t latchtime); - /* - * isOpaque - true if this surface is opaque - * - * This takes into account the buffer format (i.e. whether or not the - * pixel format includes an alpha channel) and the "opaque" flag set - * on the layer. It does not examine the current plane alpha value. - */ - bool isOpaque(const Layer::State&) const; - - /* - * Returns whether this layer can receive input. - */ - bool canReceiveInput() const; - - /* - * Whether or not the layer should be considered visible for input calculations. - */ - virtual bool isVisibleForInput() const { - // For compatibility reasons we let layers which can receive input - // receive input before they have actually submitted a buffer. Because - // of this we use canReceiveInput instead of isVisible to check the - // policy-visibility, ignoring the buffer state. However for layers with - // hasInputInfo()==false we can use the real visibility state. - // We are just using these layers for occlusion detection in - // InputDispatcher, and obviously if they aren't visible they can't occlude - // anything. - return hasInputInfo() ? canReceiveInput() : isVisible(); - } - - /* - * isProtected - true if the layer may contain protected contents in the - * GRALLOC_USAGE_PROTECTED sense. - */ - bool isProtected() const; - - /* - * isFixedSize - true if content has a fixed size - */ - virtual bool isFixedSize() const { return true; } - - /* - * usesSourceCrop - true if content should use a source crop - */ - bool usesSourceCrop() const { return hasBufferOrSidebandStream(); } - - // Most layers aren't created from the main thread, and therefore need to - // grab the SF state lock to access HWC, but ContainerLayer does, so we need - // to avoid grabbing the lock again to avoid deadlock - virtual bool isCreatedFromMainThread() const { return false; } - - ui::Transform getActiveTransform(const Layer::State& s) const { return s.transform; } - Region getActiveTransparentRegion(const Layer::State& s) const { - return s.transparentRegionHint; - } Rect getCrop(const Layer::State& s) const { return s.crop; } - bool needsFiltering(const DisplayDevice*) const; - - // True if this layer requires filtering - // This method is distinct from needsFiltering() in how the filter - // requirement is computed. needsFiltering() compares displayFrame and crop, - // where as this method transforms the displayFrame to layer-stack space - // first. This method should be used if there is no physical display to - // project onto when taking screenshots, as the filtering requirements are - // different. - // If the parent transform needs to be undone when capturing the layer, then - // the inverse parent transform is also required. - bool needsFilteringForScreenshots(const DisplayDevice*, const ui::Transform&) const; // from graphics API static ui::Dataspace translateDataspace(ui::Dataspace dataspace); - void updateCloneBufferInfo(); uint64_t mPreviousFrameNumber = 0; void onCompositionPresented(const DisplayDevice*, const std::shared_ptr<FenceTime>& /*glDoneFence*/, const std::shared_ptr<FenceTime>& /*presentFence*/, - const CompositorTiming&); + const CompositorTiming&, gui::GameMode gameMode); // If a buffer was replaced this frame, release the former buffer void releasePendingBuffer(nsecs_t /*dequeueReadyTime*/); @@ -450,46 +218,10 @@ public: * operation, so this should be set only if needed). Typically this is used * to figure out if the content or size of a surface has changed. */ - bool latchBuffer(bool& /*recomputeVisibleRegions*/, nsecs_t /*latchTime*/); - bool latchBufferImpl(bool& /*recomputeVisibleRegions*/, nsecs_t /*latchTime*/, bool bgColorOnly); - /* - * Returns true if the currently presented buffer will be released when this layer state - * is latched. This will return false if there is no buffer currently presented. - */ - bool willReleaseBufferOnLatch() const; - - /* - * Calls latchBuffer if the buffer has a frame queued and then releases the buffer. - * This is used if the buffer is just latched and releases to free up the buffer - * and will not be shown on screen. - * Should only be called on the main thread. - */ - void latchAndReleaseBuffer(); - - /* - * returns the rectangle that crops the content of the layer and scales it - * to the layer's size. - */ - Rect getBufferCrop() const; - - /* - * Returns the transform applied to the buffer. - */ - uint32_t getBufferTransform() const; - sp<GraphicBuffer> getBuffer() const; - const std::shared_ptr<renderengine::ExternalTexture>& getExternalTexture() const; - - /* - * Returns if a frame is ready - */ - bool hasReadyFrame() const; - - virtual int32_t getQueuedFrameCount() const { return 0; } - /** * Returns active buffer size in the correct orientation. Buffer size is determined by undoing * any buffer transformations. Returns Rect::INVALID_RECT if the layer has no buffer or the @@ -497,33 +229,10 @@ public: */ Rect getBufferSize(const Layer::State&) const; - /** - * Returns the source bounds. If the bounds are not defined, it is inferred from the - * buffer size. Failing that, the bounds are determined from the passed in parent bounds. - * For the root layer, this is the display viewport size. - */ - FloatRect computeSourceBounds(const FloatRect& parentBounds) const; - virtual FrameRate getFrameRateForLayerTree() const; + FrameRate getFrameRateForLayerTree() const; bool getTransformToDisplayInverse() const; - // Returns how rounded corners should be drawn for this layer. - // A layer can override its parent's rounded corner settings if the parent's rounded - // corner crop does not intersect with its own rounded corner crop. - virtual frontend::RoundedCornerState getRoundedCornerState() const; - - bool hasRoundedCorners() const { return getRoundedCornerState().hasRoundedCorners(); } - - PixelFormat getPixelFormat() const; - /** - * Return whether this layer needs an input info. We generate InputWindowHandles for all - * non-cursor buffered layers regardless of whether they have an InputChannel. This is to enable - * the InputDispatcher to do PID based occlusion detection. - */ - bool needsInputInfo() const { - return (hasInputInfo() || hasBufferOrSidebandStream()) && !mPotentialCursor; - } - // Implements RefBase. void onFirstRef() override; @@ -534,25 +243,18 @@ public: uint32_t mTransform{0}; ui::Dataspace mDataspace{ui::Dataspace::UNKNOWN}; Rect mCrop; - uint32_t mScaleMode{NATIVE_WINDOW_SCALING_MODE_FREEZE}; - Region mSurfaceDamage; - HdrMetadata mHdrMetadata; - int mApi; PixelFormat mPixelFormat{PIXEL_FORMAT_NONE}; bool mTransformToDisplayInverse{false}; - std::shared_ptr<renderengine::ExternalTexture> mBuffer; uint64_t mFrameNumber; sp<IBinder> mReleaseBufferEndpoint; - bool mFrameLatencyNeeded{false}; float mDesiredHdrSdrRatio = -1.f; }; BufferInfo mBufferInfo; + std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint> mBufferReleaseChannel; - // implements compositionengine::LayerFE - const compositionengine::LayerFECompositionState* getCompositionState() const; bool fenceHasSignaled() const; void onPreComposition(nsecs_t refreshStartTime); void onLayerDisplayed(ftl::SharedFuture<FenceResult>, ui::LayerStack layerStack, @@ -573,17 +275,6 @@ public: const char* getDebugName() const; - bool setShadowRadius(float shadowRadius); - - // Before color management is introduced, contents on Android have to be - // desaturated in order to match what they appears like visually. - // With color management, these contents will appear desaturated, thus - // needed to be saturated so that they match what they are designed for - // visually. - bool isLegacyDataSpace() const; - - uint32_t getTransactionFlags() const { return mTransactionFlags; } - static bool computeTrustedPresentationState(const FloatRect& bounds, const FloatRect& sourceBounds, const Region& coveredRegion, @@ -601,17 +292,6 @@ public: // Sets the masked bits. void setTransactionFlags(uint32_t mask); - // Clears and returns the masked bits. - uint32_t clearTransactionFlags(uint32_t mask); - - FloatRect getBounds(const Region& activeTransparentRegion) const; - FloatRect getBounds() const; - Rect getInputBoundsInDisplaySpace(const FloatRect& insetBounds, - const ui::Transform& displayTransform); - - // Compute bounds for the layer and cache the results. - void computeBounds(FloatRect parentBounds, ui::Transform parentTransform, float shadowRadius); - int32_t getSequence() const { return sequence; } // For tracing. @@ -622,164 +302,21 @@ public: // only used within a single layer. uint64_t getCurrentBufferId() const { return getBuffer() ? getBuffer()->getId() : 0; } - /* - * isSecure - true if this surface is secure, that is if it prevents - * screenshots or VNC servers. A surface can be set to be secure by the - * application, being secure doesn't mean the surface has DRM contents. - */ - bool isSecure() const; - - /* - * isHiddenByPolicy - true if this layer has been forced invisible. - * just because this is false, doesn't mean isVisible() is true. - * For example if this layer has no active buffer, it may not be hidden by - * policy, but it still can not be visible. - */ - bool isHiddenByPolicy() const; - - // True if the layer should be skipped in screenshots, screen recordings, - // and mirroring to external or virtual displays. - bool isInternalDisplayOverlay() const; - - ui::LayerFilter getOutputFilter() const { - return {getLayerStack(), isInternalDisplayOverlay()}; - } - - bool isRemovedFromCurrentState() const; - - perfetto::protos::LayerProto* writeToProto(perfetto::protos::LayersProto& layersProto, - uint32_t traceFlags); void writeCompositionStateToProto(perfetto::protos::LayerProto* layerProto, ui::LayerStack layerStack); - // Write states that are modified by the main thread. This includes drawing - // state as well as buffer data. This should be called in the main or tracing - // thread. - void writeToProtoDrawingState(perfetto::protos::LayerProto* layerInfo); - // Write drawing or current state. If writing current state, the caller should hold the - // external mStateLock. If writing drawing state, this function should be called on the - // main or tracing thread. - void writeToProtoCommonState(perfetto::protos::LayerProto* layerInfo, LayerVector::StateSet, - uint32_t traceFlags = LayerTracing::TRACE_ALL); - - gui::WindowInfo::Type getWindowType() const { return mWindowType; } - - bool updateMirrorInfo(const std::deque<Layer*>& cloneRootsPendingUpdates); - - /* - * doTransaction - process the transaction. This is a good place to figure - * out which attributes of the surface have changed. - */ - virtual uint32_t doTransaction(uint32_t transactionFlags); - - /* - * Remove relative z for the layer if its relative parent is not part of the - * provided layer tree. - */ - void removeRelativeZ(const std::vector<Layer*>& layersInTree); - - /* - * Remove from current state and mark for removal. - */ - void removeFromCurrentState() REQUIRES(mFlinger->mStateLock); - - /* - * called with the state lock from a binder thread when the layer is - * removed from the current list to the pending removal list - */ - void onRemovedFromCurrentState() REQUIRES(mFlinger->mStateLock); - - /* - * Called when the layer is added back to the current state list. - */ - void addToCurrentState(); - - /* - * Sets display transform hint on BufferLayerConsumer. - */ - void updateTransformHint(ui::Transform::RotationFlags); inline const State& getDrawingState() const { return mDrawingState; } inline State& getDrawingState() { return mDrawingState; } - void miniDumpLegacy(std::string& result, const DisplayDevice&) const; void miniDump(std::string& result, const frontend::LayerSnapshot&, const DisplayDevice&) const; void dumpFrameStats(std::string& result) const; - void dumpOffscreenDebugInfo(std::string& result) const; void clearFrameStats(); void logFrameStats(); void getFrameStats(FrameStats* outStats) const; void onDisconnect(); - ui::Transform getTransform() const; - bool isTransformValid() const; - - // Returns the Alpha of the Surface, accounting for the Alpha - // of parent Surfaces in the hierarchy (alpha's will be multiplied - // down the hierarchy). - half getAlpha() const; - half4 getColor() const; - int32_t getBackgroundBlurRadius() const; - bool drawShadows() const { return mEffectiveShadowRadius > 0.f; }; - - // Returns the transform hint set by Window Manager on the layer or one of its parents. - // This traverses the current state because the data is needed when creating - // the layer(off drawing thread) and the hint should be available before the producer - // is ready to acquire a buffer. - ui::Transform::RotationFlags getFixedTransformHint() const; - - /** - * Traverse this layer and it's hierarchy of children directly. Unlike traverseInZOrder - * which will not emit children who have relativeZOrder to another layer, this method - * just directly emits all children. It also emits them in no particular order. - * So this method is not suitable for graphical operations, as it doesn't represent - * the scene state, but it's also more efficient than traverseInZOrder and so useful for - * book-keeping. - */ - void traverse(LayerVector::StateSet, const LayerVector::Visitor&); - void traverseInReverseZOrder(LayerVector::StateSet, const LayerVector::Visitor&); - void traverseInZOrder(LayerVector::StateSet, const LayerVector::Visitor&); - void traverseChildren(const LayerVector::Visitor&); - - /** - * Traverse only children in z order, ignoring relative layers that are not children of the - * parent. - */ - void traverseChildrenInZOrder(LayerVector::StateSet, const LayerVector::Visitor&); - - size_t getDescendantCount() const; - size_t getChildrenCount() const { return mDrawingChildren.size(); } - bool isHandleAlive() const { return mHandleAlive; } bool onHandleDestroyed() { return mHandleAlive = false; } - // ONLY CALL THIS FROM THE LAYER DTOR! - // See b/141111965. We need to add current children to offscreen layers in - // the layer dtor so as not to dangle layers. Since the layer has not - // committed its transaction when the layer is destroyed, we must add - // current children. This is safe in the dtor as we will no longer update - // the current state, but should not be called anywhere else! - LayerVector& getCurrentChildren() { return mCurrentChildren; } - - void addChild(const sp<Layer>&); - // Returns index if removed, or negative value otherwise - // for symmetry with Vector::remove - ssize_t removeChild(const sp<Layer>& layer); - sp<Layer> getParent() const { return mCurrentParent.promote(); } - - // Should be called with the surfaceflinger statelock held - bool isAtRoot() const { return mIsAtRoot; } - void setIsAtRoot(bool isAtRoot) { mIsAtRoot = isAtRoot; } - - bool hasParent() const { return getParent() != nullptr; } - Rect getScreenBounds(bool reduceTransparentRegion = true) const; - bool setChildLayer(const sp<Layer>& childLayer, int32_t z); - bool setChildRelativeLayer(const sp<Layer>& childLayer, - const sp<IBinder>& relativeToHandle, int32_t relativeZ); - - // Copy the current list of children to the drawing state. Called by - // SurfaceFlinger to complete a transaction. - void commitChildList(); - int32_t getZ(LayerVector::StateSet) const; - /** * Returns the cropped buffer size or the layer crop if the layer has no buffer. Return * INVALID_RECT if the layer has no buffer and no crop. @@ -788,15 +325,10 @@ public: */ Rect getCroppedBufferSize(const Layer::State& s) const; - bool setFrameRate(FrameRate::FrameRateVote); - bool setFrameRateCategory(FrameRateCategory, bool smoothSwitchOnly); - - bool setFrameRateSelectionStrategy(FrameRateSelectionStrategy); - - virtual void setFrameTimelineInfoForBuffer(const FrameTimelineInfo& /*info*/) {} - void setFrameTimelineVsyncForBufferTransaction(const FrameTimelineInfo& info, nsecs_t postTime); + void setFrameTimelineVsyncForBufferTransaction(const FrameTimelineInfo& info, nsecs_t postTime, + gui::GameMode gameMode); void setFrameTimelineVsyncForBufferlessTransaction(const FrameTimelineInfo& info, - nsecs_t postTime); + nsecs_t postTime, gui::GameMode gameMode); void addSurfaceFrameDroppedForBuffer(std::shared_ptr<frametimeline::SurfaceFrame>& surfaceFrame, nsecs_t dropTime); @@ -805,60 +337,25 @@ public: nsecs_t currentLatchTime); std::shared_ptr<frametimeline::SurfaceFrame> createSurfaceFrameForTransaction( - const FrameTimelineInfo& info, nsecs_t postTime); + const FrameTimelineInfo& info, nsecs_t postTime, gui::GameMode gameMode); std::shared_ptr<frametimeline::SurfaceFrame> createSurfaceFrameForBuffer( - const FrameTimelineInfo& info, nsecs_t queueTime, std::string debugName); + const FrameTimelineInfo& info, nsecs_t queueTime, std::string debugName, + gui::GameMode gameMode); void setFrameTimelineVsyncForSkippedFrames(const FrameTimelineInfo& info, nsecs_t postTime, - std::string debugName); + std::string debugName, gui::GameMode gameMode); bool setTrustedPresentationInfo(TrustedPresentationThresholds const& thresholds, TrustedPresentationListener const& listener); + void setBufferReleaseChannel( + const std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint>& channel); // Creates a new handle each time, so we only expect // this to be called once. sp<IBinder> getHandle(); const std::string& getName() const { return mName; } - bool getPremultipledAlpha() const; - void setInputInfo(const gui::WindowInfo& info); - - struct InputDisplayArgs { - const ui::Transform* transform = nullptr; - bool isSecure = false; - }; - gui::WindowInfo fillInputInfo(const InputDisplayArgs& displayArgs); - - /** - * Returns whether this layer has an explicitly set input-info. - */ - bool hasInputInfo() const; - - // Sets the gui::GameMode for the tree rooted at this layer. A layer in the tree inherits this - // gui::GameMode unless it (or an ancestor) has GAME_MODE_METADATA. - void setGameModeForTree(gui::GameMode); - - void setGameMode(gui::GameMode gameMode) { mGameMode = gameMode; } - gui::GameMode getGameMode() const { return mGameMode; } virtual uid_t getOwnerUid() const { return mOwnerUid; } - pid_t getOwnerPid() { return mOwnerPid; } - - int32_t getOwnerAppId() { return mOwnerAppId; } - - // This layer is not a clone, but it's the parent to the cloned hierarchy. The - // variable mClonedChild represents the top layer that will be cloned so this - // layer will be the parent of mClonedChild. - // The layers in the cloned hierarchy will match the lifetime of the real layers. That is - // if the real layer is destroyed, then the clone layer will also be destroyed. - sp<Layer> mClonedChild; - bool mHadClonedChild = false; - void setClonedChild(const sp<Layer>& mClonedChild); - - mutable bool contentDirty{false}; - Region surfaceDamageRegion; - - // True when the surfaceDamageRegion is recognized as a small area update. - bool mSmallDirty{false}; // Used to check if mUsedVsyncIdForRefreshRateSelection should be expired when it stop updating. nsecs_t mMaxTimeForUseVsyncId = 0; // True when DrawState.useVsyncIdForRefreshRateSelection previously set to true during updating @@ -870,61 +367,16 @@ public: // the same. const int32_t sequence; - bool mPendingHWCDestroy{false}; - - bool backpressureEnabled() const { - return mDrawingState.flags & layer_state_t::eEnableBackpressure; - } - - bool setStretchEffect(const StretchEffect& effect); - StretchEffect getStretchEffect() const; - - bool setBufferCrop(const Rect& /* bufferCrop */); - bool setDestinationFrame(const Rect& /* destinationFrame */); // See mPendingBufferTransactions void decrementPendingBufferCount(); std::atomic<int32_t>* getPendingBufferCounter() { return &mPendingBufferTransactions; } std::string getPendingBufferCounterName() { return mBlastTransactionName; } - bool updateGeometry(); - - bool isSimpleBufferUpdate(const layer_state_t& s) const; - - static bool isOpaqueFormat(PixelFormat format); - - // Updates the LayerSnapshot. This must be called prior to sending layer data to - // CompositionEngine or RenderEngine (i.e. before calling CompositionEngine::present or - // LayerFE::prepareClientComposition). - // - // TODO(b/238781169) Remove direct calls to RenderEngine::drawLayers that don't go through - // CompositionEngine to create a single path for composing layers. - void updateSnapshot(bool updateGeometry); - void updateChildrenSnapshots(bool updateGeometry); - void updateMetadataSnapshot(const LayerMetadata& parentMetadata); - void updateRelativeMetadataSnapshot(const LayerMetadata& relativeLayerMetadata, - std::unordered_set<Layer*>& visited); - sp<Layer> getClonedFrom() const { - return mClonedFrom != nullptr ? mClonedFrom.promote() : nullptr; - } - bool isClone() { return mClonedFrom != nullptr; } - - bool willPresentCurrentTransaction() const; - void callReleaseBufferCallback(const sp<ITransactionCompletedListener>& listener, const sp<GraphicBuffer>& buffer, uint64_t framenumber, const sp<Fence>& releaseFence); - bool setFrameRateForLayerTreeLegacy(FrameRate, nsecs_t now); bool setFrameRateForLayerTree(FrameRate, const scheduler::LayerProps&, nsecs_t now); void recordLayerHistoryBufferUpdate(const scheduler::LayerProps&, nsecs_t now); void recordLayerHistoryAnimationTx(const scheduler::LayerProps&, nsecs_t now); - auto getLayerProps() const { - return scheduler::LayerProps{.visible = isVisible(), - .bounds = getBounds(), - .transform = getTransform(), - .setFrameRateVote = getFrameRateForLayerTree(), - .frameRateSelectionPriority = getFrameRateSelectionPriority(), - .isSmallDirty = mSmallDirty, - .isFrontBuffered = isFrontBuffered()}; - }; bool hasBuffer() const { return mBufferInfo.mBuffer != nullptr; } void setTransformHint(std::optional<ui::Transform::RotationFlags> transformHint) { mTransformHint = transformHint; @@ -964,7 +416,6 @@ public: const sp<SurfaceFlinger> mFlinger; // Check if the damage region is a small dirty. - void setIsSmallDirty(const Region& damageRegion, const ui::Transform& layerToDisplayTransform); void setIsSmallDirty(frontend::LayerSnapshot* snapshot); protected: @@ -976,63 +427,16 @@ protected: friend class TransactionFrameTracerTest; friend class TransactionSurfaceFrameTest; - void preparePerFrameCompositionState(); - void preparePerFrameBufferCompositionState(); - void preparePerFrameEffectsCompositionState(); void gatherBufferInfo(); - void onSurfaceFrameCreated(const std::shared_ptr<frametimeline::SurfaceFrame>&); - - bool isClonedFromAlive() { return getClonedFrom() != nullptr; } - - void cloneDrawingState(const Layer* from); - void updateClonedDrawingState(std::map<sp<Layer>, sp<Layer>>& clonedLayersMap); - void updateClonedChildren(const sp<Layer>& mirrorRoot, - std::map<sp<Layer>, sp<Layer>>& clonedLayersMap); - void updateClonedRelatives(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap); - void addChildToDrawing(const sp<Layer>&); - void updateClonedInputInfo(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap); - - void prepareBasicGeometryCompositionState(); - void prepareGeometryCompositionState(); - void prepareCursorCompositionState(); - uint32_t getEffectiveUsage(uint32_t usage) const; - - /** - * Setup rounded corners coordinates of this layer, taking into account the layer bounds and - * crop coordinates, transforming them into layer space. - */ - void setupRoundedCornersCropCoordinates(Rect win, const FloatRect& roundedCornersCrop) const; - void setParent(const sp<Layer>&); - LayerVector makeTraversalList(LayerVector::StateSet, bool* outSkipRelativeZUsers); - void addZOrderRelative(const wp<Layer>& relative); - void removeZOrderRelative(const wp<Layer>& relative); compositionengine::OutputLayer* findOutputLayerForDisplay(const DisplayDevice*) const; compositionengine::OutputLayer* findOutputLayerForDisplay( const DisplayDevice*, const frontend::LayerHierarchy::TraversalPath& path) const; - bool usingRelativeZ(LayerVector::StateSet) const; - - virtual ui::Transform getInputTransform() const; - /** - * Get the bounds in layer space within which this layer can receive input. - * - * These bounds are used to: - * - Determine the input frame for the layer to be used for occlusion detection; and - * - Determine the coordinate space within which the layer will receive input. The top-left of - * this rect will be the origin of the coordinate space that the input events sent to the - * layer will be in (prior to accounting for surface insets). - * - * The layer can still receive touch input if these bounds are invalid if - * "replaceTouchableRegionWithCrop" is specified. In this case, the layer will receive input - * in this layer's space, regardless of the specified crop layer. - */ - std::pair<FloatRect, bool> getInputBounds(bool fillParentBounds) const; - bool mPremultipliedAlpha{true}; const std::string mName; const std::string mTransactionName{"TX - " + mName}; - // These are only accessed by the main thread or the tracing thread. + // These are only accessed by the main thread. State mDrawingState; TrustedPresentationThresholds mTrustedPresentationThresholds; @@ -1042,44 +446,22 @@ protected: int64_t mEnteredTrustedPresentationStateTime = -1; uint32_t mTransactionFlags{0}; - // Updated in doTransaction, used to track the last sequence number we - // committed. Currently this is really only used for updating visible - // regions. - int32_t mLastCommittedTxSequence = -1; // Timestamp history for UIAutomation. Thread safe. FrameTracker mFrameTracker; // main thread sp<NativeHandle> mSidebandStream; - // False if the buffer and its contents have been previously used for GPU - // composition, true otherwise. - bool mIsActiveBufferUpdatedForGpu = true; // We encode unset as -1. std::atomic<uint64_t> mCurrentFrameNumber{0}; - // Whether filtering is needed b/c of the drawingstate - bool mNeedsFiltering{false}; - - std::atomic<bool> mRemovedFromDrawingState{false}; - - // page-flip thread (currently main thread) - bool mProtectedByApp{false}; // application requires protected path to external sink // protected by mLock mutable Mutex mLock; - const wp<Client> mClientRef; - // This layer can be a cursor on some displays. bool mPotentialCursor{false}; - LayerVector mCurrentChildren{LayerVector::StateSet::Current}; - LayerVector mDrawingChildren{LayerVector::StateSet::Drawing}; - - wp<Layer> mCurrentParent; - wp<Layer> mDrawingParent; - // Window types from WindowManager.LayoutParams const gui::WindowInfo::Type mWindowType; @@ -1097,8 +479,6 @@ protected: // Used in buffer stuffing analysis in FrameTimeline. nsecs_t mLastLatchTime = 0; - mutable bool mDrawingStateModified = false; - sp<Fence> mLastClientCompositionFence; bool mClearClientCompositionFenceOnLayerDisplayed = false; private: @@ -1110,62 +490,20 @@ private: friend class TransactionFrameTracerTest; friend class TransactionSurfaceFrameTest; - bool getAutoRefresh() const { return mDrawingState.autoRefresh; } bool getSidebandStreamChanged() const { return mSidebandStreamChanged; } std::atomic<bool> mSidebandStreamChanged{false}; - // Returns true if the layer can draw shadows on its border. - virtual bool canDrawShadows() const { return true; } - aidl::android::hardware::graphics::composer3::Composition getCompositionType( const DisplayDevice&) const; aidl::android::hardware::graphics::composer3::Composition getCompositionType( const compositionengine::OutputLayer*) const; - /** - * Returns an unsorted vector of all layers that are part of this tree. - * That includes the current layer and all its descendants. - */ - std::vector<Layer*> getLayersInTree(LayerVector::StateSet); - /** - * Traverses layers that are part of this tree in the correct z order. - * layersInTree must be sorted before calling this method. - */ - void traverseChildrenInZOrderInner(const std::vector<Layer*>& layersInTree, - LayerVector::StateSet, const LayerVector::Visitor&); - LayerVector makeChildrenTraversalList(LayerVector::StateSet, - const std::vector<Layer*>& layersInTree); - - void updateTreeHasFrameRateVote(); - bool propagateFrameRateForLayerTree(FrameRate parentFrameRate, bool overrideChildren, - bool* transactionNeeded); - void setZOrderRelativeOf(const wp<Layer>& relativeOf); - bool isTrustedOverlay() const; - gui::DropInputMode getDropInputMode() const; - void handleDropInputMode(gui::WindowInfo& info) const; - - // Find the root of the cloned hierarchy, this means the first non cloned parent. - // This will return null if first non cloned parent is not found. - sp<Layer> getClonedRoot(); - - // Finds the top most layer in the hierarchy. This will find the root Layer where the parent is - // null. - sp<Layer> getRootLayer(); - - // Fills in the touch occlusion mode of the first parent (including this layer) that - // hasInputInfo() or no-op if no such parent is found. - void fillTouchOcclusionMode(gui::WindowInfo& info); - - // Fills in the frame and transform info for the gui::WindowInfo. - void fillInputFrameInfo(gui::WindowInfo&, const ui::Transform& screenToDisplay); inline void tracePendingBufferCount(int32_t pendingBuffers); // Latch sideband stream and returns true if the dirty region should be updated. bool latchSidebandStream(bool& recomputeVisibleRegions); - bool hasFrameUpdate() const; - void updateTexImage(nsecs_t latchTime, bool bgColorOnly = false); // Crop that applies to the buffer @@ -1176,15 +514,6 @@ private: const sp<Fence>& releaseFence, uint32_t currentMaxAcquiredBufferCount); - // Returns true if the transformed buffer size does not match the layer size and we need - // to apply filtering. - bool bufferNeedsFiltering() const; - - // Returns true if there is a valid color to fill. - bool fillsColor() const; - // Returns true if this layer has a blur value. - bool hasBlur() const; - bool hasEffect() const { return fillsColor() || drawShadows() || hasBlur(); } bool hasBufferOrSidebandStream() const { return ((mSidebandStream != nullptr) || (mBufferInfo.mBuffer != nullptr)); } @@ -1193,46 +522,8 @@ private: return ((mDrawingState.sidebandStream != nullptr) || (mDrawingState.buffer != nullptr)); } - bool hasSomethingToDraw() const { return hasEffect() || hasBufferOrSidebandStream(); } - - // Fills the provided vector with the currently available JankData and removes the processed - // JankData from the pending list. - void transferAvailableJankData(const std::deque<sp<CallbackHandle>>& handles, - std::vector<JankData>& jankData); - - bool shouldOverrideChildrenFrameRate() const { - return getDrawingState().frameRateSelectionStrategy == - FrameRateSelectionStrategy::OverrideChildren; - } - - bool shouldPropagateFrameRate() const { - return getDrawingState().frameRateSelectionStrategy != FrameRateSelectionStrategy::Self; - } - - // Cached properties computed from drawing state - // Effective transform taking into account parent transforms and any parent scaling, which is - // a transform from the current layer coordinate space to display(screen) coordinate space. - ui::Transform mEffectiveTransform; - - // Bounds of the layer before any transformation is applied and before it has been cropped - // by its parents. - FloatRect mSourceBounds; - - // Bounds of the layer in layer space. This is the mSourceBounds cropped by its layer crop and - // its parent bounds. - FloatRect mBounds; - - // Layer bounds in screen space. - FloatRect mScreenBounds; - bool mGetHandleCalled = false; - // The current layer is a clone of mClonedFrom. This means that this layer will update it's - // properties based on mClonedFrom. When mClonedFrom latches a new buffer for BufferLayers, - // this layer will update it's buffer. When mClonedFrom updates it's drawing state, children, - // and relatives, this layer will update as well. - wp<Layer> mClonedFrom; - // The inherited shadow radius after taking into account the layer hierarchy. This is the // final shadow radius for this layer. If a shadow is specified for a layer, then effective // shadow radius is the set shadow radius, otherwise its the parent's shadow radius. @@ -1241,22 +532,15 @@ private: // Game mode for the layer. Set by WindowManagerShell and recorded by SurfaceFlingerStats. gui::GameMode mGameMode = gui::GameMode::Unsupported; - // A list of regions on this layer that should have blurs. - const std::vector<BlurRegion> getBlurRegions() const; - bool mIsAtRoot = false; uint32_t mLayerCreationFlags; - bool findInHierarchy(const sp<Layer>&); - - void setTransformHintLegacy(ui::Transform::RotationFlags); void releasePreviousBuffer(); void resetDrawingStateBufferInfo(); // Transform hint provided to the producer. This must be accessed holding // the mStateLock. - ui::Transform::RotationFlags mTransformHintLegacy = ui::Transform::ROT_0; std::optional<ui::Transform::RotationFlags> mTransformHint = std::nullopt; ReleaseCallbackId mPreviousReleaseCallbackId = ReleaseCallbackId::INVALID_ID; @@ -1268,30 +552,23 @@ private: // time. std::variant<nsecs_t, sp<Fence>> mCallbackHandleAcquireTimeOrFence = -1; - std::deque<std::shared_ptr<android::frametimeline::SurfaceFrame>> mPendingJankClassifications; - // An upper bound on the number of SurfaceFrames in the pending classifications deque. - static constexpr int kPendingClassificationMaxSurfaceFrames = 50; - const std::string mBlastTransactionName{"BufferTX - " + mName}; // This integer is incremented everytime a buffer arrives at the server for this layer, // and decremented when a buffer is dropped or latched. When changed the integer is exported - // to systrace with ATRACE_INT and mBlastTransactionName. This way when debugging perf it is + // to systrace with SFTRACE_INT and mBlastTransactionName. This way when debugging perf it is // possible to see when a buffer arrived at the server, and in which frame it latched. // // You can understand the trace this way: // - If the integer increases, a buffer arrived at the server. // - If the integer decreases in latchBuffer, that buffer was latched - // - If the integer decreases in setBuffer or doTransaction, a buffer was dropped + // - If the integer decreases in setBuffer, a buffer was dropped std::atomic<int32_t> mPendingBufferTransactions{0}; // Contains requested position and matrix updates. This will be applied if the client does // not specify a destination frame. ui::Transform mRequestedTransform; - sp<LayerFE> mLegacyLayerFE; std::vector<std::pair<frontend::LayerHierarchy::TraversalPath, sp<LayerFE>>> mLayerFEs; - std::unique_ptr<frontend::LayerSnapshot> mSnapshot = - std::make_unique<frontend::LayerSnapshot>(); bool mHandleAlive = false; }; diff --git a/services/surfaceflinger/LayerFE.cpp b/services/surfaceflinger/LayerFE.cpp index c2251a858b..b05f0eecc4 100644 --- a/services/surfaceflinger/LayerFE.cpp +++ b/services/surfaceflinger/LayerFE.cpp @@ -19,11 +19,10 @@ #define LOG_TAG "SurfaceFlinger" #define ATRACE_TAG ATRACE_TAG_GRAPHICS +#include <common/trace.h> #include <gui/GLConsumer.h> -#include <gui/TraceUtils.h> #include <math/vec3.h> #include <system/window.h> -#include <utils/Log.h> #include "LayerFE.h" #include "SurfaceFlinger.h" @@ -122,7 +121,7 @@ std::optional<compositionengine::LayerFE::LayerSettings> LayerFE::prepareClientC std::optional<compositionengine::LayerFE::LayerSettings> LayerFE::prepareClientCompositionInternal( compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) const { - ATRACE_CALL(); + SFTRACE_CALL(); compositionengine::LayerFE::LayerSettings layerSettings; layerSettings.geometry.boundaries = reduce(mSnapshot->geomLayerBounds, mSnapshot->transparentRegionHint); @@ -174,6 +173,7 @@ std::optional<compositionengine::LayerFE::LayerSettings> LayerFE::prepareClientC break; } layerSettings.stretchEffect = mSnapshot->stretchEffect; + layerSettings.edgeExtensionEffect = mSnapshot->edgeExtensionEffect; // Record the name of the layer for debugging further down the stack. layerSettings.name = mSnapshot->name; @@ -214,7 +214,7 @@ void LayerFE::prepareEffectsClientComposition( void LayerFE::prepareBufferStateClientComposition( compositionengine::LayerFE::LayerSettings& layerSettings, compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) const { - ATRACE_CALL(); + SFTRACE_CALL(); if (CC_UNLIKELY(!mSnapshot->externalTexture)) { // If there is no buffer for the layer or we have sidebandstream where there is no // activeBuffer, then we need to return LayerSettings. diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp index 496033b749..5eea45b436 100644 --- a/services/surfaceflinger/LayerProtoHelper.cpp +++ b/services/surfaceflinger/LayerProtoHelper.cpp @@ -178,7 +178,7 @@ void LayerProtoHelper::writeToProto( } void LayerProtoHelper::writeToProto( - const WindowInfo& inputInfo, const wp<Layer>& touchableRegionBounds, + const WindowInfo& inputInfo, std::function<perfetto::protos::InputWindowInfoProto*()> getInputWindowInfoProto) { if (inputInfo.token == nullptr) { return; @@ -208,13 +208,6 @@ void LayerProtoHelper::writeToProto( proto->set_global_scale_factor(inputInfo.globalScaleFactor); LayerProtoHelper::writeToProtoDeprecated(inputInfo.transform, proto->mutable_transform()); proto->set_replace_touchable_region_with_crop(inputInfo.replaceTouchableRegionWithCrop); - auto cropLayer = touchableRegionBounds.promote(); - if (cropLayer != nullptr) { - proto->set_crop_layer_id(cropLayer->sequence); - LayerProtoHelper::writeToProto(cropLayer->getScreenBounds( - false /* reduceTransparentRegion */), - [&]() { return proto->mutable_touchable_region_crop(); }); - } } void LayerProtoHelper::writeToProto(const mat4 matrix, @@ -263,9 +256,10 @@ void LayerProtoHelper::readFromProto(const perfetto::protos::BlurRegion& proto, outRegion.bottom = proto.bottom(); } -perfetto::protos::LayersProto LayerProtoFromSnapshotGenerator::generate( +LayerProtoFromSnapshotGenerator& LayerProtoFromSnapshotGenerator::with( const frontend::LayerHierarchy& root) { mLayersProto.clear_layers(); + mVisitedLayers.clear(); std::unordered_set<uint64_t> stackIdsToSkip; if ((mTraceFlags & LayerTracing::TRACE_VIRTUAL_DISPLAYS) == 0) { for (const auto& [layerStack, displayInfo] : mDisplayInfos) { @@ -304,9 +298,40 @@ perfetto::protos::LayersProto LayerProtoFromSnapshotGenerator::generate( } } - mDefaultSnapshots.clear(); - mChildToRelativeParent.clear(); - return std::move(mLayersProto); + return *this; +} + +LayerProtoFromSnapshotGenerator& LayerProtoFromSnapshotGenerator::withOffscreenLayers( + const frontend::LayerHierarchy& offscreenRoot) { + // Add a fake invisible root layer to the proto output and parent all the offscreen layers to + // it. + perfetto::protos::LayerProto* rootProto = mLayersProto.add_layers(); + const int32_t offscreenRootLayerId = INT32_MAX - 2; + rootProto->set_id(offscreenRootLayerId); + rootProto->set_name("Offscreen Root"); + rootProto->set_parent(-1); + + perfetto::protos::LayersProto offscreenLayers = + LayerProtoFromSnapshotGenerator(mSnapshotBuilder, mDisplayInfos, mLegacyLayers, + mTraceFlags) + .with(offscreenRoot) + .generate(); + + for (int i = 0; i < offscreenLayers.layers_size(); i++) { + perfetto::protos::LayerProto* layerProto = offscreenLayers.mutable_layers()->Mutable(i); + if (layerProto->parent() == -1) { + layerProto->set_parent(offscreenRootLayerId); + // Add layer as child of the fake root + rootProto->add_children(layerProto->id()); + } + } + + mLayersProto.mutable_layers()->Reserve(mLayersProto.layers_size() + + offscreenLayers.layers_size()); + std::copy(offscreenLayers.layers().begin(), offscreenLayers.layers().end(), + RepeatedFieldBackInserter(mLayersProto.mutable_layers())); + + return *this; } frontend::LayerSnapshot* LayerProtoFromSnapshotGenerator::getSnapshot( @@ -326,6 +351,11 @@ void LayerProtoFromSnapshotGenerator::writeHierarchyToProto( perfetto::protos::LayerProto* layerProto = mLayersProto.add_layers(); const frontend::RequestedLayerState& layer = *root.getLayer(); frontend::LayerSnapshot* snapshot = getSnapshot(path, layer); + if (mVisitedLayers.find(snapshot->uniqueSequence) != mVisitedLayers.end()) { + TransactionTraceWriter::getInstance().invoke("DuplicateLayer", /* overwrite= */ false); + return; + } + mVisitedLayers.insert(snapshot->uniqueSequence); LayerProtoHelper::writeSnapshotToProto(layerProto, layer, *snapshot, mTraceFlags); for (const auto& [child, variant] : root.mChildren) { @@ -445,7 +475,7 @@ void LayerProtoHelper::writeSnapshotToProto(perfetto::protos::LayerProto* layerI layerInfo->set_owner_uid(requestedState.ownerUid.val()); if ((traceFlags & LayerTracing::TRACE_INPUT) && snapshot.hasInputInfo()) { - LayerProtoHelper::writeToProto(snapshot.inputInfo, {}, + LayerProtoHelper::writeToProto(snapshot.inputInfo, [&]() { return layerInfo->mutable_input_window_info(); }); } diff --git a/services/surfaceflinger/LayerProtoHelper.h b/services/surfaceflinger/LayerProtoHelper.h index 20c226006c..41ea68420f 100644 --- a/services/surfaceflinger/LayerProtoHelper.h +++ b/services/surfaceflinger/LayerProtoHelper.h @@ -62,7 +62,7 @@ public: const renderengine::ExternalTexture& buffer, std::function<perfetto::protos::ActiveBufferProto*()> getActiveBufferProto); static void writeToProto( - const gui::WindowInfo& inputInfo, const wp<Layer>& touchableRegionBounds, + const gui::WindowInfo& inputInfo, std::function<perfetto::protos::InputWindowInfoProto*()> getInputWindowInfoProto); static void writeToProto(const mat4 matrix, perfetto::protos::ColorTransformProto* colorTransformProto); @@ -88,7 +88,12 @@ public: mLegacyLayers(legacyLayers), mDisplayInfos(displayInfos), mTraceFlags(traceFlags) {} - perfetto::protos::LayersProto generate(const frontend::LayerHierarchy& root); + LayerProtoFromSnapshotGenerator& with(const frontend::LayerHierarchy& root); + // Creates a fake root and adds all offscreen layers from the passed in hierarchy to the fake + // root + LayerProtoFromSnapshotGenerator& withOffscreenLayers( + const frontend::LayerHierarchy& offscreenRoot); + perfetto::protos::LayersProto generate() { return mLayersProto; }; private: void writeHierarchyToProto(const frontend::LayerHierarchy& root, @@ -101,6 +106,8 @@ private: const frontend::DisplayInfos& mDisplayInfos; uint32_t mTraceFlags; perfetto::protos::LayersProto mLayersProto; + std::unordered_set<uint32_t> mVisitedLayers; + // winscope expects all the layers, so provide a snapshot even if it not currently drawing std::unordered_map<frontend::LayerHierarchy::TraversalPath, frontend::LayerSnapshot, frontend::LayerHierarchy::TraversalPathHash> diff --git a/services/surfaceflinger/LayerVector.cpp b/services/surfaceflinger/LayerVector.cpp index f52e60deda..13e054e249 100644 --- a/services/surfaceflinger/LayerVector.cpp +++ b/services/surfaceflinger/LayerVector.cpp @@ -45,51 +45,12 @@ int LayerVector::do_compare(const void* lhs, const void* rhs) const const auto& lState = l->getDrawingState(); const auto& rState = r->getDrawingState(); - const auto ls = lState.layerStack; - const auto rs = rState.layerStack; - if (ls != rs) - return (ls > rs) ? 1 : -1; - - int32_t lz = lState.z; - int32_t rz = rState.z; - if (lz != rz) - return (lz > rz) ? 1 : -1; - if (l->sequence == r->sequence) return 0; return (l->sequence > r->sequence) ? 1 : -1; } -void LayerVector::traverseInZOrder(StateSet stateSet, const Visitor& visitor) const { - for (size_t i = 0; i < size(); i++) { - const auto& layer = (*this)[i]; - auto& state = layer->getDrawingState(); - if (state.isRelativeOf) { - continue; - } - layer->traverseInZOrder(stateSet, visitor); - } -} - -void LayerVector::traverseInReverseZOrder(StateSet stateSet, const Visitor& visitor) const { - for (auto i = static_cast<int64_t>(size()) - 1; i >= 0; i--) { - const auto& layer = (*this)[i]; - auto& state = layer->getDrawingState(); - if (state.isRelativeOf) { - continue; - } - layer->traverseInReverseZOrder(stateSet, visitor); - } -} - -void LayerVector::traverse(const Visitor& visitor) const { - for (auto i = static_cast<int64_t>(size()) - 1; i >= 0; i--) { - const auto& layer = (*this)[i]; - layer->traverse(mStateSet, visitor); - } -} - } // namespace android // TODO(b/129481165): remove the #pragma below and fix conversion issues diff --git a/services/surfaceflinger/LayerVector.h b/services/surfaceflinger/LayerVector.h index a531f4fd95..38dc11d3bc 100644 --- a/services/surfaceflinger/LayerVector.h +++ b/services/surfaceflinger/LayerVector.h @@ -46,11 +46,8 @@ public: // Sorts layer by layer-stack, Z order, and finally creation order (sequence). int do_compare(const void* lhs, const void* rhs) const override; - using Visitor = std::function<void(Layer*)>; - void traverseInReverseZOrder(StateSet stateSet, const Visitor& visitor) const; - void traverseInZOrder(StateSet stateSet, const Visitor& visitor) const; - void traverse(const Visitor& visitor) const; + private: const StateSet mStateSet; }; diff --git a/services/surfaceflinger/LocklessQueue.h b/services/surfaceflinger/LocklessQueue.h index 6b633607ae..4d0b26147c 100644 --- a/services/surfaceflinger/LocklessQueue.h +++ b/services/surfaceflinger/LocklessQueue.h @@ -15,11 +15,11 @@ */ #pragma once + #include <atomic> #include <optional> -template <typename T> -// Single consumer multi producer stack. We can understand the two operations independently to see +// Single consumer multi producer queue. We can understand the two operations independently to see // why they are without race condition. // // push is responsible for maintaining a linked list stored in mPush, and called from multiple @@ -36,33 +36,27 @@ template <typename T> // then store the list and pop one element. // // If we already had something in the pop list we just pop directly. +template <typename T> class LocklessQueue { public: - class Entry { - public: - T mValue; - std::atomic<Entry*> mNext; - Entry(T value) : mValue(value) {} - }; - std::atomic<Entry*> mPush = nullptr; - std::atomic<Entry*> mPop = nullptr; bool isEmpty() { return (mPush.load() == nullptr) && (mPop.load() == nullptr); } void push(T value) { - Entry* entry = new Entry(value); + Entry* entry = new Entry(std::move(value)); Entry* previousHead = mPush.load(/*std::memory_order_relaxed*/); do { entry->mNext = previousHead; } while (!mPush.compare_exchange_weak(previousHead, entry)); /*std::memory_order_release*/ } + std::optional<T> pop() { Entry* popped = mPop.load(/*std::memory_order_acquire*/); if (popped) { // Single consumer so this is fine mPop.store(popped->mNext /* , std::memory_order_release */); - auto value = popped->mValue; + auto value = std::move(popped->mValue); delete popped; - return std::move(value); + return value; } else { Entry* grabbedList = mPush.exchange(nullptr /* , std::memory_order_acquire */); if (!grabbedList) return std::nullopt; @@ -74,9 +68,19 @@ public: grabbedList = next; } mPop.store(popped /* , std::memory_order_release */); - auto value = grabbedList->mValue; + auto value = std::move(grabbedList->mValue); delete grabbedList; - return std::move(value); + return value; } } + +private: + class Entry { + public: + T mValue; + std::atomic<Entry*> mNext; + Entry(T value) : mValue(value) {} + }; + std::atomic<Entry*> mPush = nullptr; + std::atomic<Entry*> mPop = nullptr; }; diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp index c77bcfa6ed..06c2f26a6d 100644 --- a/services/surfaceflinger/RegionSamplingThread.cpp +++ b/services/surfaceflinger/RegionSamplingThread.cpp @@ -26,6 +26,7 @@ #include "RegionSamplingThread.h" +#include <common/trace.h> #include <compositionengine/Display.h> #include <compositionengine/impl/OutputCompositionState.h> #include <cutils/properties.h> @@ -34,7 +35,6 @@ #include <gui/SyncScreenCaptureListener.h> #include <renderengine/impl/ExternalTexture.h> #include <ui/DisplayStatInfo.h> -#include <utils/Trace.h> #include <string> @@ -148,7 +148,7 @@ void RegionSamplingThread::checkForStaleLuma() { std::lock_guard lock(mThreadControlMutex); if (mSampleRequestTime.has_value()) { - ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForSamplePhase)); + SFTRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForSamplePhase)); mSampleRequestTime.reset(); mFlinger.scheduleSample(); } @@ -166,7 +166,7 @@ void RegionSamplingThread::doSample( if (mLastSampleTime + mTunables.mSamplingPeriod > now) { // content changed, but we sampled not too long ago, so we need to sample some time in the // future. - ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::idleTimerWaiting)); + SFTRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::idleTimerWaiting)); mSampleRequestTime = now; return; } @@ -175,13 +175,13 @@ void RegionSamplingThread::doSample( // until the next vsync deadline, defer this sampling work // to a later frame, when hopefully there will be more time. if (samplingDeadline.has_value() && now + mTunables.mSamplingDuration > *samplingDeadline) { - ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForQuietFrame)); + SFTRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForQuietFrame)); mSampleRequestTime = mSampleRequestTime.value_or(now); return; } } - ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::sample)); + SFTRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::sample)); mSampleRequestTime.reset(); mLastSampleTime = now; @@ -247,7 +247,7 @@ std::vector<float> RegionSamplingThread::sampleBuffer( } void RegionSamplingThread::captureSample() { - ATRACE_CALL(); + SFTRACE_CALL(); std::lock_guard lock(mSamplingMutex); if (mDescriptors.empty()) { @@ -346,6 +346,7 @@ void RegionSamplingThread::captureSample() { constexpr bool kRegionSampling = true; constexpr bool kGrayscale = false; constexpr bool kIsProtected = false; + constexpr bool kAttachGainmap = false; SurfaceFlinger::RenderAreaBuilderVariant renderAreaBuilder(std::in_place_type<DisplayRenderAreaBuilder>, sampledBounds, @@ -356,18 +357,17 @@ void RegionSamplingThread::captureSample() { if (FlagManager::getInstance().single_hop_screenshot() && FlagManager::getInstance().ce_fence_promise() && mFlinger.mRenderEngine->isThreaded()) { std::vector<sp<LayerFE>> layerFEs; - auto displayState = - mFlinger.getDisplayAndLayerSnapshotsFromMainThread(renderAreaBuilder, - getLayerSnapshotsFn, layerFEs); - fenceResult = - mFlinger.captureScreenshot(renderAreaBuilder, buffer, kRegionSampling, kGrayscale, - kIsProtected, nullptr, displayState, layerFEs) - .get(); + auto displayState = mFlinger.getSnapshotsFromMainThread(renderAreaBuilder, + getLayerSnapshotsFn, layerFEs); + fenceResult = mFlinger.captureScreenshot(renderAreaBuilder, buffer, kRegionSampling, + kGrayscale, kIsProtected, kAttachGainmap, nullptr, + displayState, layerFEs) + .get(); } else { - fenceResult = - mFlinger.captureScreenshotLegacy(renderAreaBuilder, getLayerSnapshotsFn, buffer, - kRegionSampling, kGrayscale, kIsProtected, nullptr) - .get(); + fenceResult = mFlinger.captureScreenshotLegacy(renderAreaBuilder, getLayerSnapshotsFn, + buffer, kRegionSampling, kGrayscale, + kIsProtected, kAttachGainmap, nullptr) + .get(); } if (fenceResult.ok()) { fenceResult.value()->waitForever(LOG_TAG); @@ -394,7 +394,7 @@ void RegionSamplingThread::captureSample() { } mCachedBuffer = buffer; - ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::noWorkNeeded)); + SFTRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::noWorkNeeded)); } // NO_THREAD_SAFETY_ANALYSIS is because std::unique_lock presently lacks thread safety annotations. diff --git a/services/surfaceflinger/RenderArea.h b/services/surfaceflinger/RenderArea.h index 034e467be9..aa66ccf172 100644 --- a/services/surfaceflinger/RenderArea.h +++ b/services/surfaceflinger/RenderArea.h @@ -39,21 +39,6 @@ public: mReqDataSpace(reqDataSpace), mCaptureFill(captureFill) {} - static std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()> fromTraverseLayersLambda( - std::function<void(const LayerVector::Visitor&)> traverseLayers) { - return [traverseLayers = std::move(traverseLayers)]() { - std::vector<std::pair<Layer*, sp<LayerFE>>> layers; - traverseLayers([&](Layer* layer) { - // Layer::prepareClientComposition uses the layer's snapshot to populate the - // resulting LayerSettings. Calling Layer::updateSnapshot ensures that LayerSettings - // are generated with the layer's current buffer and geometry. - layer->updateSnapshot(true /* updateGeometry */); - layers.emplace_back(layer, layer->copyCompositionEngineLayerFE()); - }); - return layers; - }; - } - virtual ~RenderArea() = default; // Returns true if the render area is secure. A secure layer should be diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp index 6b654499a2..218c56ef3d 100644 --- a/services/surfaceflinger/Scheduler/EventThread.cpp +++ b/services/surfaceflinger/Scheduler/EventThread.cpp @@ -33,7 +33,7 @@ #include <android-base/stringprintf.h> #include <binder/IPCThreadState.h> - +#include <common/trace.h> #include <cutils/compiler.h> #include <cutils/sched_policy.h> @@ -41,7 +41,6 @@ #include <gui/SchedulingPolicy.h> #include <utils/Errors.h> -#include <utils/Trace.h> #include <common/FlagManager.h> #include <scheduler/VsyncConfig.h> @@ -226,14 +225,14 @@ binder::Status EventThreadConnection::setVsyncRate(int rate) { } binder::Status EventThreadConnection::requestNextVsync() { - ATRACE_CALL(); + SFTRACE_CALL(); mEventThread->requestNextVsync(sp<EventThreadConnection>::fromExisting(this)); return binder::Status::ok(); } binder::Status EventThreadConnection::getLatestVsyncEventData( ParcelableVsyncEventData* outVsyncEventData) { - ATRACE_CALL(); + SFTRACE_CALL(); outVsyncEventData->vsync = mEventThread->getLatestVsyncEventData(sp<EventThreadConnection>::fromExisting(this), systemTime()); @@ -321,7 +320,8 @@ void EventThread::setDuration(std::chrono::nanoseconds workDuration, mVsyncRegistration.update({.workDuration = mWorkDuration.get().count(), .readyDuration = mReadyDuration.count(), - .lastVsync = mLastVsyncCallbackTime.ns()}); + .lastVsync = mLastVsyncCallbackTime.ns(), + .committedVsyncOpt = mLastCommittedVsyncTime.ns()}); } sp<EventThreadConnection> EventThread::createEventConnection( @@ -528,10 +528,11 @@ void EventThread::threadMain(std::unique_lock<std::mutex>& lock) { } if (mState == State::VSync) { - const auto scheduleResult = - mVsyncRegistration.schedule({.workDuration = mWorkDuration.get().count(), - .readyDuration = mReadyDuration.count(), - .lastVsync = mLastVsyncCallbackTime.ns()}); + const auto scheduleResult = mVsyncRegistration.schedule( + {.workDuration = mWorkDuration.get().count(), + .readyDuration = mReadyDuration.count(), + .lastVsync = mLastVsyncCallbackTime.ns(), + .committedVsyncOpt = mLastCommittedVsyncTime.ns()}); LOG_ALWAYS_FATAL_IF(!scheduleResult, "Error scheduling callback"); } else { mVsyncRegistration.cancel(); @@ -726,8 +727,9 @@ void EventThread::dispatchEvent(const DisplayEventReceiver::Event& event, } if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC && FlagManager::getInstance().vrr_config()) { - mCallback.onExpectedPresentTimePosted( - TimePoint::fromNs(event.vsync.vsyncData.preferredExpectedPresentationTime())); + mLastCommittedVsyncTime = + TimePoint::fromNs(event.vsync.vsyncData.preferredExpectedPresentationTime()); + mCallback.onExpectedPresentTimePosted(mLastCommittedVsyncTime); } } @@ -745,9 +747,12 @@ void EventThread::dump(std::string& result) const { const auto relativeLastCallTime = ticks<std::milli, float>(mLastVsyncCallbackTime - TimePoint::now()); + const auto relativeLastCommittedTime = + ticks<std::milli, float>(mLastCommittedVsyncTime - TimePoint::now()); StringAppendF(&result, "mWorkDuration=%.2f mReadyDuration=%.2f last vsync time ", mWorkDuration.get().count() / 1e6f, mReadyDuration.count() / 1e6f); StringAppendF(&result, "%.2fms relative to now\n", relativeLastCallTime); + StringAppendF(&result, " with vsync committed at %.2fms", relativeLastCommittedTime); StringAppendF(&result, " pending events (count=%zu):\n", mPendingEvents.size()); for (const auto& event : mPendingEvents) { @@ -795,7 +800,8 @@ scheduler::VSyncCallbackRegistration EventThread::onNewVsyncScheduleInternal( if (reschedule) { mVsyncRegistration.schedule({.workDuration = mWorkDuration.get().count(), .readyDuration = mReadyDuration.count(), - .lastVsync = mLastVsyncCallbackTime.ns()}); + .lastVsync = mLastVsyncCallbackTime.ns(), + .committedVsyncOpt = mLastCommittedVsyncTime.ns()}); } return oldRegistration; } diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h index f772126349..bbe4f9d899 100644 --- a/services/surfaceflinger/Scheduler/EventThread.h +++ b/services/surfaceflinger/Scheduler/EventThread.h @@ -220,6 +220,7 @@ private: std::chrono::nanoseconds mReadyDuration GUARDED_BY(mMutex); std::shared_ptr<scheduler::VsyncSchedule> mVsyncSchedule GUARDED_BY(mMutex); TimePoint mLastVsyncCallbackTime GUARDED_BY(mMutex) = TimePoint::now(); + TimePoint mLastCommittedVsyncTime GUARDED_BY(mMutex) = TimePoint::now(); scheduler::VSyncCallbackRegistration mVsyncRegistration GUARDED_BY(mMutex); frametimeline::TokenManager* const mTokenManager; diff --git a/services/surfaceflinger/Scheduler/ISchedulerCallback.h b/services/surfaceflinger/Scheduler/ISchedulerCallback.h index f430526b76..2b9e88c22a 100644 --- a/services/surfaceflinger/Scheduler/ISchedulerCallback.h +++ b/services/surfaceflinger/Scheduler/ISchedulerCallback.h @@ -28,7 +28,6 @@ struct ISchedulerCallback { virtual void requestHardwareVsync(PhysicalDisplayId, bool enabled) = 0; virtual void requestDisplayModes(std::vector<display::DisplayModeRequest>) = 0; virtual void kernelTimerChanged(bool expired) = 0; - virtual void triggerOnFrameRateOverridesChanged() = 0; virtual void onChoreographerAttached() = 0; virtual void onExpectedPresentTimePosted(TimePoint, ftl::NonNull<DisplayModePtr>, Fps renderRate) = 0; diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp index a819b7979f..64b85c080e 100644 --- a/services/surfaceflinger/Scheduler/LayerHistory.cpp +++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp @@ -21,8 +21,8 @@ #include "LayerHistory.h" #include <android-base/stringprintf.h> +#include <common/trace.h> #include <cutils/properties.h> -#include <gui/TraceUtils.h> #include <utils/Log.h> #include <utils/Timers.h> @@ -72,7 +72,7 @@ bool useFrameRatePriority() { void trace(const LayerInfo& info, LayerHistory::LayerVoteType type, int fps) { const auto traceType = [&](LayerHistory::LayerVoteType checkedType, int value) { - ATRACE_INT(info.getTraceTag(checkedType), type == checkedType ? value : 0); + SFTRACE_INT(info.getTraceTag(checkedType), type == checkedType ? value : 0); }; traceType(LayerHistory::LayerVoteType::NoVote, 1); @@ -109,12 +109,12 @@ LayerHistory::LayerHistory() LayerHistory::~LayerHistory() = default; -void LayerHistory::registerLayer(Layer* layer, bool contentDetectionEnabled) { +void LayerHistory::registerLayer(Layer* layer, bool contentDetectionEnabled, + FrameRateCompatibility frameRateCompatibility) { std::lock_guard lock(mLock); LOG_ALWAYS_FATAL_IF(findLayer(layer->getSequence()).first != LayerStatus::NotFound, "%s already registered", layer->getName().c_str()); - LayerVoteType type = - getVoteType(layer->getDefaultFrameRateCompatibility(), contentDetectionEnabled); + LayerVoteType type = getVoteType(frameRateCompatibility, contentDetectionEnabled); auto info = std::make_unique<LayerInfo>(layer->getName(), layer->getOwnerUid(), type); // The layer can be placed on either map, it is assumed that partitionLayers() will be called @@ -190,7 +190,7 @@ void LayerHistory::setLayerProperties(int32_t id, const LayerProps& properties) } auto LayerHistory::summarize(const RefreshRateSelector& selector, nsecs_t now) -> Summary { - ATRACE_CALL(); + SFTRACE_CALL(); Summary summary; std::lock_guard lock(mLock); @@ -204,7 +204,7 @@ auto LayerHistory::summarize(const RefreshRateSelector& selector, nsecs_t now) - ALOGV("%s has priority: %d %s focused", info->getName().c_str(), frameRateSelectionPriority, layerFocused ? "" : "not"); - ATRACE_FORMAT("%s", info->getName().c_str()); + SFTRACE_FORMAT("%s", info->getName().c_str()); const auto votes = info->getRefreshRateVote(selector, now); for (LayerInfo::LayerVote vote : votes) { if (vote.isNoVote()) { @@ -222,8 +222,8 @@ auto LayerHistory::summarize(const RefreshRateSelector& selector, nsecs_t now) - const std::string categoryString = vote.category == FrameRateCategory::Default ? "" : base::StringPrintf("category=%s", ftl::enum_string(vote.category).c_str()); - ATRACE_FORMAT_INSTANT("%s %s %s (%.2f)", ftl::enum_string(vote.type).c_str(), - to_string(vote.fps).c_str(), categoryString.c_str(), weight); + SFTRACE_FORMAT_INSTANT("%s %s %s (%.2f)", ftl::enum_string(vote.type).c_str(), + to_string(vote.fps).c_str(), categoryString.c_str(), weight); summary.push_back({info->getName(), info->getOwnerUid(), vote.type, vote.fps, vote.seamlessness, vote.category, vote.categorySmoothSwitchOnly, weight, layerFocused}); @@ -238,7 +238,7 @@ auto LayerHistory::summarize(const RefreshRateSelector& selector, nsecs_t now) - } void LayerHistory::partitionLayers(nsecs_t now, bool isVrrDevice) { - ATRACE_CALL(); + SFTRACE_CALL(); const nsecs_t threshold = getActiveLayerThreshold(now); // iterate over inactive map @@ -310,7 +310,7 @@ void LayerHistory::partitionLayers(nsecs_t now, bool isVrrDevice) { if (gameModeFrameRateOverride.isValid()) { info->setLayerVote({gameFrameRateOverrideVoteType, gameModeFrameRateOverride}); - ATRACE_FORMAT_INSTANT("GameModeFrameRateOverride"); + SFTRACE_FORMAT_INSTANT("GameModeFrameRateOverride"); if (CC_UNLIKELY(mTraceEnabled)) { trace(*info, gameFrameRateOverrideVoteType, gameModeFrameRateOverride.getIntValue()); @@ -326,19 +326,19 @@ void LayerHistory::partitionLayers(nsecs_t now, bool isVrrDevice) { } else if (gameDefaultFrameRateOverride.isValid()) { info->setLayerVote( {gameFrameRateOverrideVoteType, gameDefaultFrameRateOverride}); - ATRACE_FORMAT_INSTANT("GameDefaultFrameRateOverride"); + SFTRACE_FORMAT_INSTANT("GameDefaultFrameRateOverride"); if (CC_UNLIKELY(mTraceEnabled)) { trace(*info, gameFrameRateOverrideVoteType, gameDefaultFrameRateOverride.getIntValue()); } } else { if (frameRate.isValid() && !frameRate.isVoteValidForMrr(isVrrDevice)) { - ATRACE_FORMAT_INSTANT("Reset layer to ignore explicit vote on MRR %s: %s " - "%s %s", - info->getName().c_str(), - ftl::enum_string(frameRate.vote.type).c_str(), - to_string(frameRate.vote.rate).c_str(), - ftl::enum_string(frameRate.category).c_str()); + SFTRACE_FORMAT_INSTANT("Reset layer to ignore explicit vote on MRR %s: %s " + "%s %s", + info->getName().c_str(), + ftl::enum_string(frameRate.vote.type).c_str(), + to_string(frameRate.vote.rate).c_str(), + ftl::enum_string(frameRate.category).c_str()); } info->resetLayerVote(); } @@ -349,12 +349,12 @@ void LayerHistory::partitionLayers(nsecs_t now, bool isVrrDevice) { frameRate.vote.seamlessness, frameRate.category}); } else { if (!frameRate.isVoteValidForMrr(isVrrDevice)) { - ATRACE_FORMAT_INSTANT("Reset layer to ignore explicit vote on MRR %s: %s " - "%s %s", - info->getName().c_str(), - ftl::enum_string(frameRate.vote.type).c_str(), - to_string(frameRate.vote.rate).c_str(), - ftl::enum_string(frameRate.category).c_str()); + SFTRACE_FORMAT_INSTANT("Reset layer to ignore explicit vote on MRR %s: %s " + "%s %s", + info->getName().c_str(), + ftl::enum_string(frameRate.vote.type).c_str(), + to_string(frameRate.vote.rate).c_str(), + ftl::enum_string(frameRate.category).c_str()); } info->resetLayerVote(); } @@ -421,7 +421,7 @@ auto LayerHistory::findLayer(int32_t id) -> std::pair<LayerStatus, LayerPair*> { bool LayerHistory::isSmallDirtyArea(uint32_t dirtyArea, float threshold) const { const float ratio = (float)dirtyArea / mDisplayArea; const bool isSmallDirty = ratio <= threshold; - ATRACE_FORMAT_INSTANT("small dirty=%s, ratio=%.3f", isSmallDirty ? "true" : "false", ratio); + SFTRACE_FORMAT_INSTANT("small dirty=%s, ratio=%.3f", isSmallDirty ? "true" : "false", ratio); return isSmallDirty; } diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h index c09f148a9b..e3babbabef 100644 --- a/services/surfaceflinger/Scheduler/LayerHistory.h +++ b/services/surfaceflinger/Scheduler/LayerHistory.h @@ -51,7 +51,8 @@ public: ~LayerHistory(); // Layers are unregistered when the weak reference expires. - void registerLayer(Layer*, bool contentDetectionEnabled); + void registerLayer(Layer*, bool contentDetectionEnabled, + FrameRateCompatibility frameRateCompatibility); // Sets the display size. Client is responsible for synchronization. void setDisplayArea(uint32_t displayArea) { mDisplayArea = displayArea; } diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp index 632f42ab36..ff1926e03f 100644 --- a/services/surfaceflinger/Scheduler/LayerInfo.cpp +++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp @@ -27,10 +27,10 @@ #include <utility> #include <android/native_window.h> +#include <common/trace.h> #include <cutils/compiler.h> #include <cutils/trace.h> #include <ftl/enum.h> -#include <gui/TraceUtils.h> #include <system/window.h> #undef LOG_TAG @@ -259,7 +259,7 @@ std::optional<nsecs_t> LayerInfo::calculateAverageFrameTime() const { } if (smallDirtyCount > 0) { - ATRACE_FORMAT_INSTANT("small dirty = %" PRIu32, smallDirtyCount); + SFTRACE_FORMAT_INSTANT("small dirty = %" PRIu32, smallDirtyCount); } if (numDeltas == 0) { @@ -272,7 +272,7 @@ std::optional<nsecs_t> LayerInfo::calculateAverageFrameTime() const { std::optional<Fps> LayerInfo::calculateRefreshRateIfPossible(const RefreshRateSelector& selector, nsecs_t now) { - ATRACE_CALL(); + SFTRACE_CALL(); static constexpr float MARGIN = 1.0f; // 1Hz if (!hasEnoughDataForHeuristic()) { ALOGV("Not enough data"); @@ -307,7 +307,7 @@ std::optional<Fps> LayerInfo::calculateRefreshRateIfPossible(const RefreshRateSe LayerInfo::RefreshRateVotes LayerInfo::getRefreshRateVote(const RefreshRateSelector& selector, nsecs_t now) { - ATRACE_CALL(); + SFTRACE_CALL(); LayerInfo::RefreshRateVotes votes; if (mLayerVote.type != LayerHistory::LayerVoteType::Heuristic) { @@ -315,8 +315,8 @@ LayerInfo::RefreshRateVotes LayerInfo::getRefreshRateVote(const RefreshRateSelec const auto voteType = mLayerVote.type == LayerHistory::LayerVoteType::NoVote ? LayerHistory::LayerVoteType::NoVote : LayerHistory::LayerVoteType::ExplicitCategory; - ATRACE_FORMAT_INSTANT("Vote %s (category=%s)", ftl::enum_string(voteType).c_str(), - ftl::enum_string(mLayerVote.category).c_str()); + SFTRACE_FORMAT_INSTANT("Vote %s (category=%s)", ftl::enum_string(voteType).c_str(), + ftl::enum_string(mLayerVote.category).c_str()); ALOGV("%s voted %s with category: %s", mName.c_str(), ftl::enum_string(voteType).c_str(), ftl::enum_string(mLayerVote.category).c_str()); @@ -326,7 +326,7 @@ LayerInfo::RefreshRateVotes LayerInfo::getRefreshRateVote(const RefreshRateSelec if (mLayerVote.fps.isValid() || mLayerVote.type != LayerHistory::LayerVoteType::ExplicitDefault) { - ATRACE_FORMAT_INSTANT("Vote %s", ftl::enum_string(mLayerVote.type).c_str()); + SFTRACE_FORMAT_INSTANT("Vote %s", ftl::enum_string(mLayerVote.type).c_str()); ALOGV("%s voted %d", mName.c_str(), static_cast<int>(mLayerVote.type)); votes.push_back({mLayerVote.type, mLayerVote.fps, mLayerVote.seamlessness, FrameRateCategory::Default, mLayerVote.categorySmoothSwitchOnly}); @@ -336,7 +336,7 @@ LayerInfo::RefreshRateVotes LayerInfo::getRefreshRateVote(const RefreshRateSelec } if (isAnimating(now)) { - ATRACE_FORMAT_INSTANT("animating"); + SFTRACE_FORMAT_INSTANT("animating"); ALOGV("%s is animating", mName.c_str()); mLastRefreshRate.animating = true; votes.push_back({LayerHistory::LayerVoteType::Max, Fps()}); @@ -345,7 +345,7 @@ LayerInfo::RefreshRateVotes LayerInfo::getRefreshRateVote(const RefreshRateSelec // Vote for max refresh rate whenever we're front-buffered. if (FlagManager::getInstance().vrr_config() && isFrontBuffered()) { - ATRACE_FORMAT_INSTANT("front buffered"); + SFTRACE_FORMAT_INSTANT("front buffered"); ALOGV("%s is front-buffered", mName.c_str()); votes.push_back({LayerHistory::LayerVoteType::Max, Fps()}); return votes; @@ -354,7 +354,7 @@ LayerInfo::RefreshRateVotes LayerInfo::getRefreshRateVote(const RefreshRateSelec const LayerInfo::Frequent frequent = isFrequent(now); mIsFrequencyConclusive = frequent.isConclusive; if (!frequent.isFrequent) { - ATRACE_FORMAT_INSTANT("infrequent"); + SFTRACE_FORMAT_INSTANT("infrequent"); ALOGV("%s is infrequent", mName.c_str()); mLastRefreshRate.infrequent = true; mLastSmallDirtyCount = 0; @@ -365,14 +365,14 @@ LayerInfo::RefreshRateVotes LayerInfo::getRefreshRateVote(const RefreshRateSelec } if (frequent.clearHistory) { - ATRACE_FORMAT_INSTANT("frequent.clearHistory"); + SFTRACE_FORMAT_INSTANT("frequent.clearHistory"); ALOGV("%s frequent.clearHistory", mName.c_str()); clearHistory(now); } // Return no vote if the recent frames are small dirty. if (frequent.isSmallDirty && !mLastRefreshRate.reported.isValid()) { - ATRACE_FORMAT_INSTANT("NoVote (small dirty)"); + SFTRACE_FORMAT_INSTANT("NoVote (small dirty)"); ALOGV("%s is small dirty", mName.c_str()); votes.push_back({LayerHistory::LayerVoteType::NoVote, Fps()}); return votes; @@ -380,13 +380,13 @@ LayerInfo::RefreshRateVotes LayerInfo::getRefreshRateVote(const RefreshRateSelec auto refreshRate = calculateRefreshRateIfPossible(selector, now); if (refreshRate.has_value()) { - ATRACE_FORMAT_INSTANT("calculated (%s)", to_string(*refreshRate).c_str()); + SFTRACE_FORMAT_INSTANT("calculated (%s)", to_string(*refreshRate).c_str()); ALOGV("%s calculated refresh rate: %s", mName.c_str(), to_string(*refreshRate).c_str()); votes.push_back({LayerHistory::LayerVoteType::Heuristic, refreshRate.value()}); return votes; } - ATRACE_FORMAT_INSTANT("Max (can't resolve refresh rate)"); + SFTRACE_FORMAT_INSTANT("Max (can't resolve refresh rate)"); ALOGV("%s Max (can't resolve refresh rate)", mName.c_str()); votes.push_back({LayerHistory::LayerVoteType::Max, Fps()}); return votes; @@ -452,7 +452,7 @@ Fps LayerInfo::RefreshRateHistory::add(Fps refreshRate, nsecs_t now, mHeuristicTraceTagData = makeHeuristicTraceTagData(); } - ATRACE_INT(mHeuristicTraceTagData->average.c_str(), refreshRate.getIntValue()); + SFTRACE_INT(mHeuristicTraceTagData->average.c_str(), refreshRate.getIntValue()); } return selectRefreshRate(selector); @@ -486,9 +486,9 @@ Fps LayerInfo::RefreshRateHistory::selectRefreshRate(const RefreshRateSelector& mHeuristicTraceTagData = makeHeuristicTraceTagData(); } - ATRACE_INT(mHeuristicTraceTagData->max.c_str(), max->refreshRate.getIntValue()); - ATRACE_INT(mHeuristicTraceTagData->min.c_str(), min->refreshRate.getIntValue()); - ATRACE_INT(mHeuristicTraceTagData->consistent.c_str(), consistent); + SFTRACE_INT(mHeuristicTraceTagData->max.c_str(), max->refreshRate.getIntValue()); + SFTRACE_INT(mHeuristicTraceTagData->min.c_str(), min->refreshRate.getIntValue()); + SFTRACE_INT(mHeuristicTraceTagData->consistent.c_str(), consistent); } return consistent ? maxClosestRate : Fps(); @@ -595,6 +595,11 @@ bool LayerInfo::FrameRate::isVoteValidForMrr(bool isVrrDevice) const { return true; } + if (category == FrameRateCategory::NoPreference && vote.rate.isValid() && + vote.type == FrameRateCompatibility::ExactOrMultiple) { + return true; + } + return false; } diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp index ff88d71259..2e1f938126 100644 --- a/services/surfaceflinger/Scheduler/MessageQueue.cpp +++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp @@ -57,7 +57,7 @@ MessageQueue::MessageQueue(ICompositor& compositor, sp<Handler> handler) mHandler(std::move(handler)) {} void MessageQueue::vsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime) { - ATRACE_CALL(); + SFTRACE_CALL(); // Trace VSYNC-sf mVsync.value = (mVsync.value + 1) % 2; @@ -136,7 +136,7 @@ void MessageQueue::destroyVsync() { } void MessageQueue::setDuration(std::chrono::nanoseconds workDuration) { - ATRACE_CALL(); + SFTRACE_CALL(); std::lock_guard lock(mVsync.mutex); mVsync.workDuration = workDuration; mVsync.scheduledFrameTimeOpt = @@ -188,12 +188,13 @@ void MessageQueue::scheduleConfigure() { postMessage(sp<ConfigureHandler>::make(mCompositor)); } -void MessageQueue::scheduleFrame() { - ATRACE_CALL(); +void MessageQueue::scheduleFrame(Duration workDurationSlack) { + SFTRACE_CALL(); std::lock_guard lock(mVsync.mutex); + const auto workDuration = Duration(mVsync.workDuration.get() - workDurationSlack); mVsync.scheduledFrameTimeOpt = - mVsync.registration->schedule({.workDuration = mVsync.workDuration.get().count(), + mVsync.registration->schedule({.workDuration = workDuration.ns(), .readyDuration = 0, .lastVsync = mVsync.lastCallbackTime.ns()}); } diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h index c5fc371d3a..ba1efbe58f 100644 --- a/services/surfaceflinger/Scheduler/MessageQueue.h +++ b/services/surfaceflinger/Scheduler/MessageQueue.h @@ -74,7 +74,7 @@ public: virtual void postMessage(sp<MessageHandler>&&) = 0; virtual void postMessageDelayed(sp<MessageHandler>&&, nsecs_t uptimeDelay) = 0; virtual void scheduleConfigure() = 0; - virtual void scheduleFrame() = 0; + virtual void scheduleFrame(Duration workDurationSlack = Duration::fromNs(0)) = 0; virtual std::optional<scheduler::ScheduleResult> getScheduledFrameResult() const = 0; }; @@ -149,7 +149,7 @@ public: void postMessageDelayed(sp<MessageHandler>&&, nsecs_t uptimeDelay) override; void scheduleConfigure() override; - void scheduleFrame() override; + void scheduleFrame(Duration workDurationSlack = Duration::fromNs(0)) override; std::optional<scheduler::ScheduleResult> getScheduledFrameResult() const override; }; diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp index bbb3c52c9f..ab9014e418 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp +++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp @@ -28,13 +28,12 @@ #include <android-base/properties.h> #include <android-base/stringprintf.h> +#include <common/trace.h> #include <ftl/enum.h> #include <ftl/fake_guard.h> #include <ftl/match.h> #include <ftl/unit.h> -#include <gui/TraceUtils.h> #include <scheduler/FrameRateMode.h> -#include <utils/Trace.h> #include "RefreshRateSelector.h" @@ -494,7 +493,7 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi GlobalSignals signals, Fps pacesetterFps) const -> RankedFrameRates { using namespace fps_approx_ops; - ATRACE_CALL(); + SFTRACE_CALL(); ALOGV("%s: %zu layers", __func__, layers.size()); const auto& activeMode = *getActiveModeLocked().modePtr; @@ -508,8 +507,8 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi }); if (!ranking.empty()) { - ATRACE_FORMAT_INSTANT("%s (Follower display)", - to_string(ranking.front().frameRateMode.fps).c_str()); + SFTRACE_FORMAT_INSTANT("%s (Follower display)", + to_string(ranking.front().frameRateMode.fps).c_str()); return {ranking, kNoSignals, pacesetterFps}; } @@ -521,8 +520,8 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi if (signals.powerOnImminent) { ALOGV("Power On Imminent"); const auto ranking = rankFrameRates(activeMode.getGroup(), RefreshRateOrder::Descending); - ATRACE_FORMAT_INSTANT("%s (Power On Imminent)", - to_string(ranking.front().frameRateMode.fps).c_str()); + SFTRACE_FORMAT_INSTANT("%s (Power On Imminent)", + to_string(ranking.front().frameRateMode.fps).c_str()); return {ranking, GlobalSignals{.powerOnImminent = true}}; } @@ -608,8 +607,8 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi if (signals.touch && !hasExplicitVoteLayers) { ALOGV("Touch Boost"); const auto ranking = rankFrameRates(anchorGroup, RefreshRateOrder::Descending); - ATRACE_FORMAT_INSTANT("%s (Touch Boost)", - to_string(ranking.front().frameRateMode.fps).c_str()); + SFTRACE_FORMAT_INSTANT("%s (Touch Boost)", + to_string(ranking.front().frameRateMode.fps).c_str()); return {ranking, GlobalSignals{.touch = true}}; } @@ -620,26 +619,27 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi !(policy->primaryRangeIsSingleRate() && hasExplicitVoteLayers)) { ALOGV("Idle"); const auto ranking = rankFrameRates(activeMode.getGroup(), RefreshRateOrder::Ascending); - ATRACE_FORMAT_INSTANT("%s (Idle)", to_string(ranking.front().frameRateMode.fps).c_str()); + SFTRACE_FORMAT_INSTANT("%s (Idle)", to_string(ranking.front().frameRateMode.fps).c_str()); return {ranking, GlobalSignals{.idle = true}}; } if (layers.empty() || noVoteLayers == layers.size()) { ALOGV("No layers with votes"); const auto ranking = rankFrameRates(anchorGroup, RefreshRateOrder::Descending); - ATRACE_FORMAT_INSTANT("%s (No layers with votes)", - to_string(ranking.front().frameRateMode.fps).c_str()); + SFTRACE_FORMAT_INSTANT("%s (No layers with votes)", + to_string(ranking.front().frameRateMode.fps).c_str()); return {ranking, kNoSignals}; } // If all layers are category NoPreference, use the current config. if (noPreferenceLayers + noVoteLayers == layers.size()) { ALOGV("All layers NoPreference"); - const auto ascendingWithPreferred = - rankFrameRates(anchorGroup, RefreshRateOrder::Ascending, activeMode.getId()); - ATRACE_FORMAT_INSTANT("%s (All layers NoPreference)", - to_string(ascendingWithPreferred.front().frameRateMode.fps).c_str()); - return {ascendingWithPreferred, kNoSignals}; + constexpr float kScore = std::numeric_limits<float>::max(); + FrameRateRanking currentMode; + currentMode.emplace_back(ScoredFrameRate{getActiveModeLocked(), kScore}); + SFTRACE_FORMAT_INSTANT("%s (All layers NoPreference)", + to_string(currentMode.front().frameRateMode.fps).c_str()); + return {currentMode, kNoSignals}; } const bool smoothSwitchOnly = categorySmoothSwitchOnlyLayers > 0; @@ -653,8 +653,8 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi return !smoothSwitchOnly || mode.modePtr->getId() == activeModeId; }); - ATRACE_FORMAT_INSTANT("%s (All layers Min)", - to_string(ranking.front().frameRateMode.fps).c_str()); + SFTRACE_FORMAT_INSTANT("%s (All layers Min)", + to_string(ranking.front().frameRateMode.fps).c_str()); return {ranking, kNoSignals}; } @@ -848,13 +848,13 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi if (noLayerScore) { ALOGV("Layers not scored"); const auto descending = rankFrameRates(anchorGroup, RefreshRateOrder::Descending); - ATRACE_FORMAT_INSTANT("%s (Layers not scored)", - to_string(descending.front().frameRateMode.fps).c_str()); + SFTRACE_FORMAT_INSTANT("%s (Layers not scored)", + to_string(descending.front().frameRateMode.fps).c_str()); return {descending, kNoSignals}; } else { ALOGV("primaryRangeIsSingleRate"); - ATRACE_FORMAT_INSTANT("%s (primaryRangeIsSingleRate)", - to_string(ranking.front().frameRateMode.fps).c_str()); + SFTRACE_FORMAT_INSTANT("%s (primaryRangeIsSingleRate)", + to_string(ranking.front().frameRateMode.fps).c_str()); return {ranking, kNoSignals}; } } @@ -890,8 +890,8 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi if (scores.front().frameRateMode.fps <= touchRefreshRates.front().frameRateMode.fps) { ALOGV("Touch Boost [late]"); - ATRACE_FORMAT_INSTANT("%s (Touch Boost [late])", - to_string(touchRefreshRates.front().frameRateMode.fps).c_str()); + SFTRACE_FORMAT_INSTANT("%s (Touch Boost [late])", + to_string(touchRefreshRates.front().frameRateMode.fps).c_str()); return {touchRefreshRates, GlobalSignals{.touch = true}}; } } @@ -902,13 +902,13 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi ALOGV("preferredDisplayMode"); const auto ascendingWithPreferred = rankFrameRates(anchorGroup, RefreshRateOrder::Ascending, activeMode.getId()); - ATRACE_FORMAT_INSTANT("%s (preferredDisplayMode)", - to_string(ascendingWithPreferred.front().frameRateMode.fps).c_str()); + SFTRACE_FORMAT_INSTANT("%s (preferredDisplayMode)", + to_string(ascendingWithPreferred.front().frameRateMode.fps).c_str()); return {ascendingWithPreferred, kNoSignals}; } ALOGV("%s (scored)", to_string(ranking.front().frameRateMode.fps).c_str()); - ATRACE_FORMAT_INSTANT("%s (scored)", to_string(ranking.front().frameRateMode.fps).c_str()); + SFTRACE_FORMAT_INSTANT("%s (scored)", to_string(ranking.front().frameRateMode.fps).c_str()); return {ranking, kNoSignals}; } @@ -950,7 +950,7 @@ auto RefreshRateSelector::getFrameRateOverrides(const std::vector<LayerRequireme Fps displayRefreshRate, GlobalSignals globalSignals) const -> UidToFrameRateOverride { - ATRACE_CALL(); + SFTRACE_CALL(); if (mConfig.enableFrameRateOverride == Config::FrameRateOverride::Disabled) { return {}; } @@ -1065,12 +1065,12 @@ auto RefreshRateSelector::getFrameRateOverrides(const std::vector<LayerRequireme return lhs < rhs && !ScoredFrameRate::scoresEqual(lhs, rhs); }); ALOGV("%s: overriding to %s for uid=%d", __func__, to_string(overrideFps).c_str(), uid); - ATRACE_FORMAT_INSTANT("%s: overriding to %s for uid=%d", __func__, - to_string(overrideFps).c_str(), uid); - if (ATRACE_ENABLED() && FlagManager::getInstance().trace_frame_rate_override()) { + SFTRACE_FORMAT_INSTANT("%s: overriding to %s for uid=%d", __func__, + to_string(overrideFps).c_str(), uid); + if (SFTRACE_ENABLED() && FlagManager::getInstance().trace_frame_rate_override()) { std::stringstream ss; ss << "FrameRateOverride " << uid; - ATRACE_INT(ss.str().c_str(), overrideFps.getIntValue()); + SFTRACE_INT(ss.str().c_str(), overrideFps.getIntValue()); } frameRateOverrides.emplace(uid, overrideFps); } @@ -1395,13 +1395,14 @@ auto RefreshRateSelector::setPolicy(const PolicyVariant& policy) -> SetPolicyRes const auto& idleScreenConfigOpt = getCurrentPolicyLocked()->idleScreenConfigOpt; if (idleScreenConfigOpt != oldPolicy.idleScreenConfigOpt) { if (!idleScreenConfigOpt.has_value()) { - // fallback to legacy timer if existed, otherwise pause the old timer - LOG_ALWAYS_FATAL_IF(!mIdleTimer); - if (mConfig.legacyIdleTimerTimeout > 0ms) { - mIdleTimer->setInterval(mConfig.legacyIdleTimerTimeout); - mIdleTimer->resume(); - } else { - mIdleTimer->pause(); + if (mIdleTimer) { + // fallback to legacy timer if existed, otherwise pause the old timer + if (mConfig.legacyIdleTimerTimeout > 0ms) { + mIdleTimer->setInterval(mConfig.legacyIdleTimerTimeout); + mIdleTimer->resume(); + } else { + mIdleTimer->pause(); + } } } else if (idleScreenConfigOpt->timeoutMillis > 0) { // create a new timer or reconfigure @@ -1641,7 +1642,7 @@ FpsRange RefreshRateSelector::getFrameRateCategoryRange(FrameRateCategory catego case FrameRateCategory::Normal: return FpsRange{60_Hz, 120_Hz}; case FrameRateCategory::Low: - return FpsRange{30_Hz, 120_Hz}; + return FpsRange{48_Hz, 120_Hz}; case FrameRateCategory::HighHint: case FrameRateCategory::NoPreference: case FrameRateCategory::Default: diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h index 998b1b81b1..a398c01a8f 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateSelector.h +++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h @@ -210,6 +210,8 @@ public: // within the timeout of DisplayPowerTimer. bool powerOnImminent = false; + bool shouldEmitEvent() const { return !idle; } + bool operator==(GlobalSignals other) const { return touch == other.touch && idle == other.idle && powerOnImminent == other.powerOnImminent; diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index 5ec7e48332..5e131548aa 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -24,12 +24,12 @@ #include <android-base/stringprintf.h> #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h> #include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h> +#include <common/trace.h> #include <configstore/Utils.h> #include <ftl/concat.h> #include <ftl/enum.h> #include <ftl/fake_guard.h> #include <ftl/small_map.h> -#include <gui/TraceUtils.h> #include <gui/WindowInfo.h> #include <system/window.h> #include <ui/DisplayMap.h> @@ -38,7 +38,6 @@ #include <FrameTimeline/FrameTimeline.h> #include <scheduler/interface/ICompositor.h> -#include <algorithm> #include <cinttypes> #include <cstdint> #include <functional> @@ -46,16 +45,15 @@ #include <numeric> #include <common/FlagManager.h> -#include "../Layer.h" #include "EventThread.h" #include "FrameRateOverrideMappings.h" #include "FrontEnd/LayerHandle.h" +#include "Layer.h" #include "OneShotTimer.h" #include "RefreshRateStats.h" #include "SurfaceFlingerFactory.h" #include "SurfaceFlingerProperties.h" #include "TimeStats/TimeStats.h" -#include "VSyncTracker.h" #include "VsyncConfiguration.h" #include "VsyncController.h" #include "VsyncSchedule.h" @@ -122,6 +120,12 @@ void Scheduler::setPacesetterDisplay(PhysicalDisplayId pacesetterId) { demotePacesetterDisplay(kPromotionParams); promotePacesetterDisplay(pacesetterId, kPromotionParams); + + // Cancel the pending refresh rate change, if any, before updating the phase configuration. + mVsyncModulator->cancelRefreshRateChange(); + + mVsyncConfiguration->reset(); + updatePhaseConfiguration(pacesetterId, pacesetterSelectorPtr()->getActiveMode().fps); } void Scheduler::registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr, @@ -258,8 +262,8 @@ void Scheduler::onFrameSignal(ICompositor& compositor, VsyncId vsyncId, const auto period = pacesetterPtr->targeterPtr->target().expectedFrameDuration(); const auto skipDuration = Duration::fromNs( static_cast<nsecs_t>(period.ns() * mPacesetterFrameDurationFractionToSkip)); - ATRACE_FORMAT("Injecting jank for %f%% of the frame (%" PRId64 " ns)", - mPacesetterFrameDurationFractionToSkip * 100, skipDuration.ns()); + SFTRACE_FORMAT("Injecting jank for %f%% of the frame (%" PRId64 " ns)", + mPacesetterFrameDurationFractionToSkip * 100, skipDuration.ns()); std::this_thread::sleep_for(skipDuration); mPacesetterFrameDurationFractionToSkip = 0.f; } @@ -290,7 +294,7 @@ bool Scheduler::isVsyncValid(TimePoint expectedVsyncTime, uid_t uid) const { return true; } - ATRACE_FORMAT("%s uid: %d frameRate: %s", __func__, uid, to_string(*frameRate).c_str()); + SFTRACE_FORMAT("%s uid: %d frameRate: %s", __func__, uid, to_string(*frameRate).c_str()); return getVsyncSchedule()->getTracker().isVSyncInPhase(expectedVsyncTime.ns(), *frameRate); } @@ -355,10 +359,8 @@ void Scheduler::createEventThread(Cycle cycle, frametimeline::TokenManager* toke if (cycle == Cycle::Render) { mRenderEventThread = std::move(eventThread); - mRenderEventConnection = mRenderEventThread->createEventConnection(); } else { mLastCompositeEventThread = std::move(eventThread); - mLastCompositeEventConnection = mLastCompositeEventThread->createEventConnection(); } } @@ -403,14 +405,20 @@ void Scheduler::enableSyntheticVsync(bool enable) { eventThreadFor(Cycle::Render).enableSyntheticVsync(enable); } -void Scheduler::onFrameRateOverridesChanged(Cycle cycle, PhysicalDisplayId displayId) { - const bool supportsFrameRateOverrideByContent = - pacesetterSelectorPtr()->supportsAppFrameRateOverrideByContent(); +void Scheduler::onFrameRateOverridesChanged() { + const auto [pacesetterId, supportsFrameRateOverrideByContent] = [this] { + std::scoped_lock lock(mDisplayLock); + const auto pacesetterOpt = pacesetterDisplayLocked(); + LOG_ALWAYS_FATAL_IF(!pacesetterOpt); + const Display& pacesetter = *pacesetterOpt; + return std::make_pair(FTL_FAKE_GUARD(kMainThreadContext, *mPacesetterDisplayId), + pacesetter.selectorPtr->supportsAppFrameRateOverrideByContent()); + }(); std::vector<FrameRateOverride> overrides = mFrameRateOverrideMappings.getAllFrameRateOverrides(supportsFrameRateOverrideByContent); - eventThreadFor(cycle).onFrameRateOverridesChanged(displayId, std::move(overrides)); + eventThreadFor(Cycle::Render).onFrameRateOverridesChanged(pacesetterId, std::move(overrides)); } void Scheduler::onHdcpLevelsChanged(Cycle cycle, PhysicalDisplayId displayId, @@ -418,50 +426,49 @@ void Scheduler::onHdcpLevelsChanged(Cycle cycle, PhysicalDisplayId displayId, eventThreadFor(cycle).onHdcpLevelsChanged(displayId, connectedLevel, maxLevel); } -void Scheduler::onPrimaryDisplayModeChanged(Cycle cycle, const FrameRateMode& mode) { - { +bool Scheduler::onDisplayModeChanged(PhysicalDisplayId displayId, const FrameRateMode& mode) { + const bool isPacesetter = + FTL_FAKE_GUARD(kMainThreadContext, + (std::scoped_lock(mDisplayLock), displayId == mPacesetterDisplayId)); + + if (isPacesetter) { std::lock_guard<std::mutex> lock(mPolicyLock); - // Cache the last reported modes for primary display. - mPolicy.cachedModeChangedParams = {cycle, mode}; + mPolicy.emittedModeOpt = mode; // Invalidate content based refresh rate selection so it could be calculated // again for the new refresh rate. mPolicy.contentRequirements.clear(); } - onNonPrimaryDisplayModeChanged(cycle, mode); -} -void Scheduler::dispatchCachedReportedMode() { - // Check optional fields first. - if (!mPolicy.modeOpt) { - ALOGW("No mode ID found, not dispatching cached mode."); - return; + if (hasEventThreads()) { + eventThreadFor(Cycle::Render).onModeChanged(mode); } - if (!mPolicy.cachedModeChangedParams) { - ALOGW("No mode changed params found, not dispatching cached mode."); + + return isPacesetter; +} + +void Scheduler::emitModeChangeIfNeeded() { + if (!mPolicy.modeOpt || !mPolicy.emittedModeOpt) { + ALOGW("No mode change to emit"); return; } - // If the mode is not the current mode, this means that a - // mode change is in progress. In that case we shouldn't dispatch an event - // as it will be dispatched when the current mode changes. - if (pacesetterSelectorPtr()->getActiveMode() != mPolicy.modeOpt) { + const auto& mode = *mPolicy.modeOpt; + + if (mode != pacesetterSelectorPtr()->getActiveMode()) { + // A mode change is pending. The event will be emitted when the mode becomes active. return; } - // If there is no change from cached mode, there is no need to dispatch an event - if (*mPolicy.modeOpt == mPolicy.cachedModeChangedParams->mode) { + if (mode == *mPolicy.emittedModeOpt) { + // The event was already emitted. return; } - mPolicy.cachedModeChangedParams->mode = *mPolicy.modeOpt; - onNonPrimaryDisplayModeChanged(mPolicy.cachedModeChangedParams->cycle, - mPolicy.cachedModeChangedParams->mode); -} + mPolicy.emittedModeOpt = mode; -void Scheduler::onNonPrimaryDisplayModeChanged(Cycle cycle, const FrameRateMode& mode) { if (hasEventThreads()) { - eventThreadFor(cycle).onModeChanged(mode); + eventThreadFor(Cycle::Render).onModeChanged(mode); } } @@ -476,21 +483,18 @@ void Scheduler::setDuration(Cycle cycle, std::chrono::nanoseconds workDuration, } } -void Scheduler::updatePhaseConfiguration(Fps refreshRate) { +void Scheduler::updatePhaseConfiguration(PhysicalDisplayId displayId, Fps refreshRate) { + const bool isPacesetter = + FTL_FAKE_GUARD(kMainThreadContext, + (std::scoped_lock(mDisplayLock), displayId == mPacesetterDisplayId)); + if (!isPacesetter) return; + mRefreshRateStats->setRefreshRate(refreshRate); mVsyncConfiguration->setRefreshRateFps(refreshRate); setVsyncConfig(mVsyncModulator->setVsyncConfigSet(mVsyncConfiguration->getCurrentConfigs()), refreshRate.getPeriod()); } -void Scheduler::resetPhaseConfiguration(Fps refreshRate) { - // Cancel the pending refresh rate change, if any, before updating the phase configuration. - mVsyncModulator->cancelRefreshRateChange(); - - mVsyncConfiguration->reset(); - updatePhaseConfiguration(refreshRate); -} - void Scheduler::setActiveDisplayPowerModeForRefreshRateStats(hal::PowerMode powerMode) { mRefreshRateStats->setPowerMode(powerMode); } @@ -518,7 +522,7 @@ void Scheduler::disableHardwareVsync(PhysicalDisplayId id, bool disallow) { } void Scheduler::resyncAllToHardwareVsync(bool allowToEnable) { - ATRACE_CALL(); + SFTRACE_CALL(); std::scoped_lock lock(mDisplayLock); ftl::FakeGuard guard(kMainThreadContext); @@ -552,12 +556,12 @@ void Scheduler::resyncToHardwareVsyncLocked(PhysicalDisplayId id, bool allowToEn void Scheduler::onHardwareVsyncRequest(PhysicalDisplayId id, bool enabled) { static const auto& whence = __func__; - ATRACE_NAME(ftl::Concat(whence, ' ', id.value, ' ', enabled).c_str()); + SFTRACE_NAME(ftl::Concat(whence, ' ', id.value, ' ', enabled).c_str()); // On main thread to serialize reads/writes of pending hardware VSYNC state. static_cast<void>( schedule([=, this]() FTL_FAKE_GUARD(mDisplayLock) FTL_FAKE_GUARD(kMainThreadContext) { - ATRACE_NAME(ftl::Concat(whence, ' ', id.value, ' ', enabled).c_str()); + SFTRACE_NAME(ftl::Concat(whence, ' ', id.value, ' ', enabled).c_str()); if (const auto displayOpt = mDisplays.get(id)) { auto& display = displayOpt->get(); @@ -639,7 +643,7 @@ bool Scheduler::addResyncSample(PhysicalDisplayId id, nsecs_t timestamp, } void Scheduler::addPresentFence(PhysicalDisplayId id, std::shared_ptr<FenceTime> fence) { - ATRACE_NAME(ftl::Concat(__func__, ' ', id.value).c_str()); + SFTRACE_NAME(ftl::Concat(__func__, ' ', id.value).c_str()); const auto scheduleOpt = (ftl::FakeGuard(mDisplayLock), mDisplays.get(id)).and_then([](const Display& display) { return display.powerMode == hal::PowerMode::OFF @@ -659,11 +663,12 @@ void Scheduler::addPresentFence(PhysicalDisplayId id, std::shared_ptr<FenceTime> } } -void Scheduler::registerLayer(Layer* layer) { +void Scheduler::registerLayer(Layer* layer, FrameRateCompatibility frameRateCompatibility) { // If the content detection feature is off, we still keep the layer history, // since we use it for other features (like Frame Rate API), so layers // still need to be registered. - mLayerHistory.registerLayer(layer, mFeatures.test(Feature::kContentDetection)); + mLayerHistory.registerLayer(layer, mFeatures.test(Feature::kContentDetection), + frameRateCompatibility); } void Scheduler::deregisterLayer(Layer* layer) { @@ -702,7 +707,7 @@ void Scheduler::chooseRefreshRateForContent( const auto selectorPtr = pacesetterSelectorPtr(); if (!selectorPtr->canSwitch()) return; - ATRACE_CALL(); + SFTRACE_CALL(); LayerHistory::Summary summary = mLayerHistory.summarize(*selectorPtr, systemTime()); applyPolicy(&Policy::contentRequirements, std::move(summary)); @@ -787,7 +792,7 @@ auto Scheduler::getVsyncScheduleLocked(std::optional<PhysicalDisplayId> idOpt) c } void Scheduler::kernelIdleTimerCallback(TimerState state) { - ATRACE_INT("ExpiredKernelIdleTimer", static_cast<int>(state)); + SFTRACE_INT("ExpiredKernelIdleTimer", static_cast<int>(state)); // TODO(145561154): cleanup the kernel idle timer implementation and the refresh rate // magic number @@ -818,7 +823,7 @@ void Scheduler::kernelIdleTimerCallback(TimerState state) { void Scheduler::idleTimerCallback(TimerState state) { applyPolicy(&Policy::idleTimer, state); - ATRACE_INT("ExpiredIdleTimer", static_cast<int>(state)); + SFTRACE_INT("ExpiredIdleTimer", static_cast<int>(state)); } void Scheduler::touchTimerCallback(TimerState state) { @@ -830,12 +835,12 @@ void Scheduler::touchTimerCallback(TimerState state) { if (applyPolicy(&Policy::touch, touch).touch) { mLayerHistory.clear(); } - ATRACE_INT("TouchState", static_cast<int>(touch)); + SFTRACE_INT("TouchState", static_cast<int>(touch)); } void Scheduler::displayPowerTimerCallback(TimerState state) { applyPolicy(&Policy::displayPowerTimer, state); - ATRACE_INT("ExpiredDisplayPowerTimer", static_cast<int>(state)); + SFTRACE_INT("ExpiredDisplayPowerTimer", static_cast<int>(state)); } void Scheduler::dump(utils::Dumper& dumper) const { @@ -871,22 +876,19 @@ void Scheduler::dump(utils::Dumper& dumper) const { mRefreshRateStats->dump(dumper.out()); dumper.eol(); - { - utils::Dumper::Section section(dumper, "Frame Targeting"sv); + std::scoped_lock lock(mDisplayLock); + ftl::FakeGuard guard(kMainThreadContext); - std::scoped_lock lock(mDisplayLock); - ftl::FakeGuard guard(kMainThreadContext); + for (const auto& [id, display] : mDisplays) { + utils::Dumper::Section + section(dumper, + id == mPacesetterDisplayId + ? ftl::Concat("Pacesetter Display ", id.value).c_str() + : ftl::Concat("Follower Display ", id.value).c_str()); - for (const auto& [id, display] : mDisplays) { - utils::Dumper::Section - section(dumper, - id == mPacesetterDisplayId - ? ftl::Concat("Pacesetter Display ", id.value).c_str() - : ftl::Concat("Follower Display ", id.value).c_str()); - - display.targeterPtr->dump(dumper); - dumper.eol(); - } + display.selectorPtr->dump(dumper); + display.targeterPtr->dump(dumper); + dumper.eol(); } } @@ -907,9 +909,13 @@ void Scheduler::dumpVsync(std::string& out) const { } } -bool Scheduler::updateFrameRateOverrides(GlobalSignals consideredSignals, Fps displayRefreshRate) { - std::scoped_lock lock(mPolicyLock); - return updateFrameRateOverridesLocked(consideredSignals, displayRefreshRate); +void Scheduler::updateFrameRateOverrides(GlobalSignals consideredSignals, Fps displayRefreshRate) { + const bool changed = (std::scoped_lock(mPolicyLock), + updateFrameRateOverridesLocked(consideredSignals, displayRefreshRate)); + + if (changed) { + onFrameRateOverridesChanged(); + } } bool Scheduler::updateFrameRateOverridesLocked(GlobalSignals consideredSignals, @@ -1004,7 +1010,7 @@ void Scheduler::updateAttachedChoreographersFrameRate( auto& layerChoreographers = choreographers->second; layerChoreographers.frameRate = fps; - ATRACE_FORMAT_INSTANT("%s: %s for %s", __func__, to_string(fps).c_str(), layer.name.c_str()); + SFTRACE_FORMAT_INSTANT("%s: %s for %s", __func__, to_string(fps).c_str(), layer.name.c_str()); ALOGV("%s: %s for %s", __func__, to_string(fps).c_str(), layer.name.c_str()); auto it = layerChoreographers.connections.begin(); @@ -1086,13 +1092,13 @@ int Scheduler::updateAttachedChoreographersInternal( void Scheduler::updateAttachedChoreographers( const surfaceflinger::frontend::LayerHierarchy& layerHierarchy, Fps displayRefreshRate) { - ATRACE_CALL(); + SFTRACE_CALL(); updateAttachedChoreographersInternal(layerHierarchy, displayRefreshRate, 0); } template <typename S, typename T> auto Scheduler::applyPolicy(S Policy::*statePtr, T&& newState) -> GlobalSignals { - ATRACE_CALL(); + SFTRACE_CALL(); std::vector<display::DisplayModeRequest> modeRequests; GlobalSignals consideredSignals; @@ -1129,33 +1135,40 @@ auto Scheduler::applyPolicy(S Policy::*statePtr, T&& newState) -> GlobalSignals for (auto& [id, choice] : modeChoices) { modeRequests.emplace_back( display::DisplayModeRequest{.mode = std::move(choice.mode), - .emitEvent = !choice.consideredSignals.idle}); + .emitEvent = choice.consideredSignals + .shouldEmitEvent()}); } - frameRateOverridesChanged = updateFrameRateOverridesLocked(consideredSignals, modeOpt->fps); - + if (!FlagManager::getInstance().vrr_bugfix_dropped_frame()) { + frameRateOverridesChanged = + updateFrameRateOverridesLocked(consideredSignals, modeOpt->fps); + } if (mPolicy.modeOpt != modeOpt) { mPolicy.modeOpt = modeOpt; refreshRateChanged = true; - } else { - // We don't need to change the display mode, but we might need to send an event - // about a mode change, since it was suppressed if previously considered idle. - if (!consideredSignals.idle) { - dispatchCachedReportedMode(); - } + } else if (consideredSignals.shouldEmitEvent()) { + // The mode did not change, but we may need to emit if DisplayModeRequest::emitEvent was + // previously false. + emitModeChangeIfNeeded(); } } if (refreshRateChanged) { mSchedulerCallback.requestDisplayModes(std::move(modeRequests)); } + + if (FlagManager::getInstance().vrr_bugfix_dropped_frame()) { + std::scoped_lock lock(mPolicyLock); + frameRateOverridesChanged = + updateFrameRateOverridesLocked(consideredSignals, mPolicy.modeOpt->fps); + } if (frameRateOverridesChanged) { - mSchedulerCallback.triggerOnFrameRateOverridesChanged(); + onFrameRateOverridesChanged(); } return consideredSignals; } auto Scheduler::chooseDisplayModes() const -> DisplayModeChoiceMap { - ATRACE_CALL(); + SFTRACE_CALL(); DisplayModeChoiceMap modeChoices; const auto globalSignals = makeGlobalSignals(); @@ -1249,6 +1262,8 @@ void Scheduler::setGameModeFrameRateForUid(FrameRateOverride frameRateOverride) } else { mFrameRateOverrideMappings.setGameModeRefreshRateForUid(frameRateOverride); } + + onFrameRateOverridesChanged(); } void Scheduler::setGameDefaultFrameRateForUid(FrameRateOverride frameRateOverride) { @@ -1267,6 +1282,7 @@ void Scheduler::setPreferredRefreshRateForUid(FrameRateOverride frameRateOverrid } mFrameRateOverrideMappings.setPreferredRefreshRateForUid(frameRateOverride); + onFrameRateOverridesChanged(); } void Scheduler::updateSmallAreaDetection( diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h index 94583db5a8..c88b563805 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -145,22 +145,16 @@ public: Cycle, EventRegistrationFlags eventRegistration = {}, const sp<IBinder>& layerHandle = nullptr) EXCLUDES(mChoreographerLock); - const sp<EventThreadConnection>& getEventConnection(Cycle cycle) const { - return cycle == Cycle::Render ? mRenderEventConnection : mLastCompositeEventConnection; - } - enum class Hotplug { Connected, Disconnected }; void dispatchHotplug(PhysicalDisplayId, Hotplug); void dispatchHotplugError(int32_t errorCode); - void onPrimaryDisplayModeChanged(Cycle, const FrameRateMode&) EXCLUDES(mPolicyLock); - void onNonPrimaryDisplayModeChanged(Cycle, const FrameRateMode&); + // Returns true if the PhysicalDisplayId is the pacesetter. + bool onDisplayModeChanged(PhysicalDisplayId, const FrameRateMode&) EXCLUDES(mPolicyLock); void enableSyntheticVsync(bool = true) REQUIRES(kMainThreadContext); - void onFrameRateOverridesChanged(Cycle, PhysicalDisplayId); - void onHdcpLevelsChanged(Cycle, PhysicalDisplayId, int32_t, int32_t); // Modifies work duration in the event thread. @@ -189,8 +183,7 @@ public: } } - void updatePhaseConfiguration(Fps); - void resetPhaseConfiguration(Fps) REQUIRES(kMainThreadContext); + void updatePhaseConfiguration(PhysicalDisplayId, Fps); const VsyncConfiguration& getVsyncConfiguration() const { return *mVsyncConfiguration; } @@ -222,7 +215,7 @@ public: REQUIRES(kMainThreadContext); // Layers are registered on creation, and unregistered when the weak reference expires. - void registerLayer(Layer*); + void registerLayer(Layer*, FrameRateCompatibility); void recordLayerHistory(int32_t id, const LayerProps& layerProps, nsecs_t presentTime, nsecs_t now, LayerHistory::LayerUpdateType) EXCLUDES(mDisplayLock); void setModeChangePending(bool pending); @@ -326,7 +319,7 @@ public: return mLayerHistory.getLayerFramerate(now, id); } - bool updateFrameRateOverrides(GlobalSignals, Fps displayRefreshRate) EXCLUDES(mPolicyLock); + void updateFrameRateOverrides(GlobalSignals, Fps displayRefreshRate) EXCLUDES(mPolicyLock); // Returns true if the small dirty detection is enabled for the appId. bool supportSmallDirtyDetection(int32_t appId) { @@ -450,6 +443,9 @@ private: bool updateFrameRateOverridesLocked(GlobalSignals, Fps displayRefreshRate) REQUIRES(mPolicyLock); + + void onFrameRateOverridesChanged(); + void updateAttachedChoreographers(const surfaceflinger::frontend::LayerHierarchy&, Fps displayRefreshRate); int updateAttachedChoreographersInternal(const surfaceflinger::frontend::LayerHierarchy&, @@ -457,7 +453,7 @@ private: void updateAttachedChoreographersFrameRate(const surfaceflinger::frontend::RequestedLayerState&, Fps fps) EXCLUDES(mChoreographerLock); - void dispatchCachedReportedMode() REQUIRES(mPolicyLock) EXCLUDES(mDisplayLock); + void emitModeChangeIfNeeded() REQUIRES(mPolicyLock) EXCLUDES(mDisplayLock); // IEventThreadCallback overrides bool throttleVsync(TimePoint, uid_t) override; @@ -467,10 +463,7 @@ private: void onExpectedPresentTimePosted(TimePoint expectedPresentTime) override EXCLUDES(mDisplayLock); std::unique_ptr<EventThread> mRenderEventThread; - sp<EventThreadConnection> mRenderEventConnection; - std::unique_ptr<EventThread> mLastCompositeEventThread; - sp<EventThreadConnection> mLastCompositeEventConnection; std::atomic<nsecs_t> mLastResyncTime = 0; @@ -583,13 +576,8 @@ private: // Chosen display mode. ftl::Optional<FrameRateMode> modeOpt; - struct ModeChangedParams { - Cycle cycle; - FrameRateMode mode; - }; - - // Parameters for latest dispatch of mode change event. - std::optional<ModeChangedParams> cachedModeChangedParams; + // Display mode of latest emitted event. + std::optional<FrameRateMode> emittedModeOpt; } mPolicy GUARDED_BY(mPolicyLock); std::mutex mChoreographerLock; diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h index 0c43ffbc04..8993c38d37 100644 --- a/services/surfaceflinger/Scheduler/VSyncDispatch.h +++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h @@ -93,6 +93,8 @@ public: * readyDuration will typically be 0. * @lastVsync: The targeted display time. This will be snapped to the closest * predicted vsync time after lastVsync. + * @committedVsyncOpt: The display time that is committed to the callback as the + * target vsync time. * * callback will be dispatched at 'workDuration + readyDuration' nanoseconds before a vsync * event. @@ -101,10 +103,11 @@ public: nsecs_t workDuration = 0; nsecs_t readyDuration = 0; nsecs_t lastVsync = 0; + std::optional<nsecs_t> committedVsyncOpt; bool operator==(const ScheduleTiming& other) const { return workDuration == other.workDuration && readyDuration == other.readyDuration && - lastVsync == other.lastVsync; + lastVsync == other.lastVsync && committedVsyncOpt == other.committedVsyncOpt; } bool operator!=(const ScheduleTiming& other) const { return !(*this == other); } diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp index 6d6b70d198..1925f1165c 100644 --- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp +++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp @@ -19,8 +19,8 @@ #include <vector> #include <android-base/stringprintf.h> +#include <common/trace.h> #include <ftl/concat.h> -#include <gui/TraceUtils.h> #include <log/log_main.h> #include <scheduler/TimeKeeper.h> @@ -45,14 +45,14 @@ ScheduleResult getExpectedCallbackTime(nsecs_t nextVsyncTime, } void traceEntry(const VSyncDispatchTimerQueueEntry& entry, nsecs_t now) { - if (!ATRACE_ENABLED() || !entry.wakeupTime().has_value() || !entry.targetVsync().has_value()) { + if (!SFTRACE_ENABLED() || !entry.wakeupTime().has_value() || !entry.targetVsync().has_value()) { return; } ftl::Concat trace(ftl::truncated<5>(entry.name()), " alarm in ", ns2us(*entry.wakeupTime() - now), "us; VSYNC in ", ns2us(*entry.targetVsync() - now), "us"); - ATRACE_FORMAT_INSTANT(trace.c_str()); + SFTRACE_FORMAT_INSTANT(trace.c_str()); } } // namespace @@ -98,20 +98,21 @@ std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::targetVsync() const { ScheduleResult VSyncDispatchTimerQueueEntry::schedule(VSyncDispatch::ScheduleTiming timing, VSyncTracker& tracker, nsecs_t now) { - ATRACE_NAME("VSyncDispatchTimerQueueEntry::schedule"); + SFTRACE_NAME("VSyncDispatchTimerQueueEntry::schedule"); auto nextVsyncTime = tracker.nextAnticipatedVSyncTimeFrom(std::max(timing.lastVsync, now + timing.workDuration + timing.readyDuration), - timing.lastVsync); + timing.committedVsyncOpt.value_or( + timing.lastVsync)); auto nextWakeupTime = nextVsyncTime - timing.workDuration - timing.readyDuration; bool const wouldSkipAVsyncTarget = mArmedInfo && (nextVsyncTime > (mArmedInfo->mActualVsyncTime + mMinVsyncDistance)); bool const wouldSkipAWakeup = mArmedInfo && ((nextWakeupTime > (mArmedInfo->mActualWakeupTime + mMinVsyncDistance))); - ATRACE_FORMAT_INSTANT("%s: wouldSkipAVsyncTarget=%d wouldSkipAWakeup=%d", mName.c_str(), - wouldSkipAVsyncTarget, wouldSkipAWakeup); + SFTRACE_FORMAT_INSTANT("%s: wouldSkipAVsyncTarget=%d wouldSkipAWakeup=%d", mName.c_str(), + wouldSkipAVsyncTarget, wouldSkipAWakeup); if (FlagManager::getInstance().dont_skip_on_early_ro()) { if (wouldSkipAVsyncTarget || wouldSkipAWakeup) { nextVsyncTime = mArmedInfo->mActualVsyncTime; @@ -154,13 +155,13 @@ nsecs_t VSyncDispatchTimerQueueEntry::adjustVsyncIfNeeded(VSyncTracker& tracker, bool const nextVsyncTooClose = mLastDispatchTime && (nextVsyncTime - *mLastDispatchTime + mMinVsyncDistance) <= currentPeriod; if (alreadyDispatchedForVsync) { - ATRACE_FORMAT_INSTANT("alreadyDispatchedForVsync"); + SFTRACE_FORMAT_INSTANT("alreadyDispatchedForVsync"); return tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + mMinVsyncDistance, *mLastDispatchTime); } if (nextVsyncTooClose) { - ATRACE_FORMAT_INSTANT("nextVsyncTooClose"); + SFTRACE_FORMAT_INSTANT("nextVsyncTooClose"); return tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + currentPeriod, *mLastDispatchTime + currentPeriod); } @@ -172,7 +173,7 @@ auto VSyncDispatchTimerQueueEntry::getArmedInfo(VSyncTracker& tracker, nsecs_t n VSyncDispatch::ScheduleTiming timing, std::optional<ArmingInfo> armedInfo) const -> ArmingInfo { - ATRACE_NAME("VSyncDispatchTimerQueueEntry::getArmedInfo"); + SFTRACE_NAME("VSyncDispatchTimerQueueEntry::getArmedInfo"); const auto earliestReadyBy = now + timing.workDuration + timing.readyDuration; const auto earliestVsync = std::max(earliestReadyBy, timing.lastVsync); @@ -188,8 +189,8 @@ auto VSyncDispatchTimerQueueEntry::getArmedInfo(VSyncTracker& tracker, nsecs_t n armedInfo && (nextVsyncTime > (armedInfo->mActualVsyncTime + mMinVsyncDistance)); bool const wouldSkipAWakeup = armedInfo && (nextWakeupTime > (armedInfo->mActualWakeupTime + mMinVsyncDistance)); - ATRACE_FORMAT_INSTANT("%s: wouldSkipAVsyncTarget=%d wouldSkipAWakeup=%d", mName.c_str(), - wouldSkipAVsyncTarget, wouldSkipAWakeup); + SFTRACE_FORMAT_INSTANT("%s: wouldSkipAVsyncTarget=%d wouldSkipAWakeup=%d", mName.c_str(), + wouldSkipAVsyncTarget, wouldSkipAWakeup); if (wouldSkipAVsyncTarget || wouldSkipAWakeup) { return *armedInfo; } @@ -199,7 +200,7 @@ auto VSyncDispatchTimerQueueEntry::getArmedInfo(VSyncTracker& tracker, nsecs_t n } void VSyncDispatchTimerQueueEntry::update(VSyncTracker& tracker, nsecs_t now) { - ATRACE_NAME("VSyncDispatchTimerQueueEntry::update"); + SFTRACE_NAME("VSyncDispatchTimerQueueEntry::update"); if (!mArmedInfo && !mWorkloadUpdateInfo) { return; } @@ -208,9 +209,12 @@ void VSyncDispatchTimerQueueEntry::update(VSyncTracker& tracker, nsecs_t now) { const auto workDelta = mWorkloadUpdateInfo->workDuration - mScheduleTiming.workDuration; const auto readyDelta = mWorkloadUpdateInfo->readyDuration - mScheduleTiming.readyDuration; const auto lastVsyncDelta = mWorkloadUpdateInfo->lastVsync - mScheduleTiming.lastVsync; - ATRACE_FORMAT_INSTANT("Workload updated workDelta=%" PRId64 " readyDelta=%" PRId64 - " lastVsyncDelta=%" PRId64, - workDelta, readyDelta, lastVsyncDelta); + const auto lastCommittedVsyncDelta = + mWorkloadUpdateInfo->committedVsyncOpt.value_or(mWorkloadUpdateInfo->lastVsync) - + mScheduleTiming.committedVsyncOpt.value_or(mScheduleTiming.lastVsync); + SFTRACE_FORMAT_INSTANT("Workload updated workDelta=%" PRId64 " readyDelta=%" PRId64 + " lastVsyncDelta=%" PRId64 " committedVsyncDelta=%" PRId64, + workDelta, readyDelta, lastVsyncDelta, lastCommittedVsyncDelta); mScheduleTiming = *mWorkloadUpdateInfo; mWorkloadUpdateInfo.reset(); } @@ -261,10 +265,14 @@ void VSyncDispatchTimerQueueEntry::dump(std::string& result) const { StringAppendF(&result, "\t\t%s: %s %s\n", mName.c_str(), mRunning ? "(in callback function)" : "", armedInfo.c_str()); StringAppendF(&result, - "\t\t\tworkDuration: %.2fms readyDuration: %.2fms lastVsync: %.2fms relative " - "to now\n", + "\t\t\tworkDuration: %.2fms readyDuration: %.2fms " + "lastVsync: %.2fms relative to now " + "committedVsync: %.2fms relative to now\n", mScheduleTiming.workDuration / 1e6f, mScheduleTiming.readyDuration / 1e6f, - (mScheduleTiming.lastVsync - systemTime()) / 1e6f); + (mScheduleTiming.lastVsync - systemTime()) / 1e6f, + (mScheduleTiming.committedVsyncOpt.value_or(mScheduleTiming.lastVsync) - + systemTime()) / + 1e6f); if (mLastDispatchTime) { StringAppendF(&result, "\t\t\tmLastDispatchTime: %.2fms ago\n", @@ -310,7 +318,7 @@ void VSyncDispatchTimerQueue::rearmTimer(nsecs_t now) { void VSyncDispatchTimerQueue::rearmTimerSkippingUpdateFor( nsecs_t now, CallbackMap::const_iterator skipUpdateIt) { - ATRACE_CALL(); + SFTRACE_CALL(); std::optional<nsecs_t> min; std::optional<nsecs_t> targetVsync; std::optional<std::string_view> nextWakeupName; @@ -337,13 +345,13 @@ void VSyncDispatchTimerQueue::rearmTimerSkippingUpdateFor( if (min && min < mIntendedWakeupTime) { setTimer(*min, now); } else { - ATRACE_NAME("cancel timer"); + SFTRACE_NAME("cancel timer"); cancelTimer(); } } void VSyncDispatchTimerQueue::timerCallback() { - ATRACE_CALL(); + SFTRACE_CALL(); struct Invocation { std::shared_ptr<VSyncDispatchTimerQueueEntry> callback; nsecs_t vsyncTimestamp; @@ -383,7 +391,7 @@ void VSyncDispatchTimerQueue::timerCallback() { for (auto const& invocation : invocations) { ftl::Concat trace(ftl::truncated<5>(invocation.callback->name())); - ATRACE_FORMAT("%s: %s", __func__, trace.c_str()); + SFTRACE_FORMAT("%s: %s", __func__, trace.c_str()); invocation.callback->callback(invocation.vsyncTimestamp, invocation.wakeupTimestamp, invocation.deadlineTimestamp); } diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp index 1422cfa1d5..6e36f02463 100644 --- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp +++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp @@ -30,10 +30,10 @@ #include <android-base/logging.h> #include <android-base/stringprintf.h> #include <common/FlagManager.h> +#include <common/trace.h> #include <cutils/compiler.h> #include <cutils/properties.h> #include <ftl/concat.h> -#include <gui/TraceUtils.h> #include <utils/Log.h> #include "RefreshRateSelector.h" @@ -77,7 +77,7 @@ inline void VSyncPredictor::traceInt64If(const char* name, int64_t value) const } inline void VSyncPredictor::traceInt64(const char* name, int64_t value) const { - ATRACE_INT64(ftl::Concat(ftl::truncated<14>(name), " ", mId.value).c_str(), value); + SFTRACE_INT64(ftl::Concat(ftl::truncated<14>(name), " ", mId.value).c_str(), value); } inline size_t VSyncPredictor::next(size_t i) const { @@ -89,7 +89,9 @@ nsecs_t VSyncPredictor::idealPeriod() const { } bool VSyncPredictor::validate(nsecs_t timestamp) const { + SFTRACE_CALL(); if (mLastTimestampIndex < 0 || mTimestamps.empty()) { + SFTRACE_INSTANT("timestamp valid (first)"); return true; } @@ -98,7 +100,11 @@ bool VSyncPredictor::validate(nsecs_t timestamp) const { (timestamp - aValidTimestamp) % idealPeriod() * kMaxPercent / idealPeriod(); if (percent >= kOutlierTolerancePercent && percent <= (kMaxPercent - kOutlierTolerancePercent)) { - ATRACE_FORMAT_INSTANT("timestamp is not aligned with model"); + SFTRACE_FORMAT_INSTANT("timestamp not aligned with model. aValidTimestamp %.2fms ago" + ", timestamp %.2fms ago, idealPeriod=%.2 percent=%d", + (mClock->now() - aValidTimestamp) / 1e6f, + (mClock->now() - timestamp) / 1e6f, + idealPeriod() / 1e6f, percent); return false; } @@ -109,7 +115,7 @@ bool VSyncPredictor::validate(nsecs_t timestamp) const { const auto distancePercent = std::abs(*iter - timestamp) * kMaxPercent / idealPeriod(); if (distancePercent < kOutlierTolerancePercent) { // duplicate timestamp - ATRACE_FORMAT_INSTANT("duplicate timestamp"); + SFTRACE_FORMAT_INSTANT("duplicate timestamp"); return false; } return true; @@ -135,7 +141,7 @@ Period VSyncPredictor::minFramePeriodLocked() const { } bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) { - ATRACE_CALL(); + SFTRACE_CALL(); std::lock_guard lock(mMutex); @@ -148,15 +154,18 @@ bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) { // Add the timestamp to mTimestamps before clearing it so we could // update mKnownTimestamp based on the new timestamp. mTimestamps.push_back(timestamp); - clearTimestamps(); + + // Do not clear timelines as we don't want to break the phase while + // we are still learning. + clearTimestamps(/* clearTimelines */ false); } else if (!mTimestamps.empty()) { mKnownTimestamp = std::max(timestamp, *std::max_element(mTimestamps.begin(), mTimestamps.end())); } else { mKnownTimestamp = timestamp; } - ATRACE_FORMAT_INSTANT("timestamp rejected. mKnownTimestamp was %.2fms ago", - (mClock->now() - *mKnownTimestamp) / 1e6f); + SFTRACE_FORMAT_INSTANT("timestamp rejected. mKnownTimestamp was %.2fms ago", + (mClock->now() - *mKnownTimestamp) / 1e6f); return false; } @@ -235,7 +244,7 @@ bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) { if (CC_UNLIKELY(bottom == 0)) { it->second = {idealPeriod(), 0}; - clearTimestamps(); + clearTimestamps(/* clearTimelines */ true); return false; } @@ -245,7 +254,7 @@ bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) { auto const percent = std::abs(anticipatedPeriod - idealPeriod()) * kMaxPercent / idealPeriod(); if (percent >= kOutlierTolerancePercent) { it->second = {idealPeriod(), 0}; - clearTimestamps(); + clearTimestamps(/* clearTimelines */ true); return false; } @@ -297,7 +306,7 @@ nsecs_t VSyncPredictor::snapToVsync(nsecs_t timePoint) const { nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint, std::optional<nsecs_t> lastVsyncOpt) { - ATRACE_CALL(); + SFTRACE_CALL(); std::lock_guard lock(mMutex); const auto now = TimePoint::fromNs(mClock->now()); @@ -330,8 +339,8 @@ nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint, if (*vsyncOpt > mLastCommittedVsync) { mLastCommittedVsync = *vsyncOpt; - ATRACE_FORMAT_INSTANT("mLastCommittedVsync in %.2fms", - float(mLastCommittedVsync.ns() - mClock->now()) / 1e6f); + SFTRACE_FORMAT_INSTANT("mLastCommittedVsync in %.2fms", + float(mLastCommittedVsync.ns() - mClock->now()) / 1e6f); } return vsyncOpt->ns(); @@ -360,7 +369,11 @@ bool VSyncPredictor::isVSyncInPhase(nsecs_t timePoint, Fps frameRate) { purgeTimelines(now); for (auto& timeline : mTimelines) { - if (timeline.validUntil() && timeline.validUntil()->ns() > vsync) { + const bool isVsyncValid = FlagManager::getInstance().vrr_bugfix_24q4() + ? timeline.isWithin(TimePoint::fromNs(vsync)) == + VsyncTimeline::VsyncOnTimeline::Unique + : timeline.validUntil() && timeline.validUntil()->ns() > vsync; + if (isVsyncValid) { return timeline.isVSyncInPhase(model, vsync, frameRate); } } @@ -370,7 +383,7 @@ bool VSyncPredictor::isVSyncInPhase(nsecs_t timePoint, Fps frameRate) { } void VSyncPredictor::setRenderRate(Fps renderRate, bool applyImmediately) { - ATRACE_FORMAT("%s %s", __func__, to_string(renderRate).c_str()); + SFTRACE_FORMAT("%s %s", __func__, to_string(renderRate).c_str()); ALOGV("%s %s: RenderRate %s ", __func__, to_string(mId).c_str(), to_string(renderRate).c_str()); std::lock_guard lock(mMutex); const auto prevRenderRate = mRenderRateOpt; @@ -378,7 +391,7 @@ void VSyncPredictor::setRenderRate(Fps renderRate, bool applyImmediately) { const auto renderPeriodDelta = prevRenderRate ? prevRenderRate->getPeriodNsecs() - renderRate.getPeriodNsecs() : 0; if (applyImmediately) { - ATRACE_FORMAT_INSTANT("applyImmediately"); + SFTRACE_FORMAT_INSTANT("applyImmediately"); while (mTimelines.size() > 1) { mTimelines.pop_front(); } @@ -390,13 +403,20 @@ void VSyncPredictor::setRenderRate(Fps renderRate, bool applyImmediately) { const bool newRenderRateIsHigher = renderPeriodDelta > renderRate.getPeriodNsecs() && mLastCommittedVsync.ns() - mClock->now() > 2 * renderRate.getPeriodNsecs(); if (newRenderRateIsHigher) { - ATRACE_FORMAT_INSTANT("newRenderRateIsHigher"); + SFTRACE_FORMAT_INSTANT("newRenderRateIsHigher"); mTimelines.clear(); mLastCommittedVsync = TimePoint::fromNs(0); } else { - mTimelines.back().freeze( - TimePoint::fromNs(mLastCommittedVsync.ns() + mIdealPeriod.ns() / 2)); + if (FlagManager::getInstance().vrr_bugfix_24q4()) { + // We need to freeze the timeline at the committed vsync, and + // then use with threshold adjustments when required to avoid + // marginal errors when checking the vsync on the timeline. + mTimelines.back().freeze(mLastCommittedVsync); + } else { + mTimelines.back().freeze( + TimePoint::fromNs(mLastCommittedVsync.ns() + mIdealPeriod.ns() / 2)); + } } mTimelines.emplace_back(mLastCommittedVsync, mIdealPeriod, renderRate); purgeTimelines(TimePoint::fromNs(mClock->now())); @@ -405,7 +425,7 @@ void VSyncPredictor::setRenderRate(Fps renderRate, bool applyImmediately) { void VSyncPredictor::setDisplayModePtr(ftl::NonNull<DisplayModePtr> modePtr) { LOG_ALWAYS_FATAL_IF(mId != modePtr->getPhysicalDisplayId(), "mode does not belong to the display"); - ATRACE_FORMAT("%s %s", __func__, to_string(*modePtr).c_str()); + SFTRACE_FORMAT("%s %s", __func__, to_string(*modePtr).c_str()); const auto timeout = modePtr->getVrrConfig() ? modePtr->getVrrConfig()->notifyExpectedPresentConfig : std::nullopt; @@ -414,6 +434,9 @@ void VSyncPredictor::setDisplayModePtr(ftl::NonNull<DisplayModePtr> modePtr) { timeout ? std::to_string(timeout->timeoutNs).c_str() : "N/A"); std::lock_guard lock(mMutex); + // do not clear the timelines on VRR displays if we didn't change the mode + const bool isVrr = modePtr->getVrrConfig().has_value(); + const bool clearTimelines = !isVrr || mDisplayModePtr->getId() != modePtr->getId(); mDisplayModePtr = modePtr; mNumVsyncsForFrame = numVsyncsPerFrame(mDisplayModePtr); traceInt64("VSP-setPeriod", modePtr->getVsyncRate().getPeriodNsecs()); @@ -427,13 +450,15 @@ void VSyncPredictor::setDisplayModePtr(ftl::NonNull<DisplayModePtr> modePtr) { mRateMap[idealPeriod()] = {idealPeriod(), 0}; } - mTimelines.clear(); - clearTimestamps(); + if (clearTimelines) { + mTimelines.clear(); + } + clearTimestamps(clearTimelines); } Duration VSyncPredictor::ensureMinFrameDurationIsKept(TimePoint expectedPresentTime, TimePoint lastConfirmedPresentTime) { - ATRACE_CALL(); + SFTRACE_CALL(); if (mNumVsyncsForFrame <= 1) { return 0ns; @@ -441,20 +466,21 @@ Duration VSyncPredictor::ensureMinFrameDurationIsKept(TimePoint expectedPresentT const auto currentPeriod = mRateMap.find(idealPeriod())->second.slope; const auto threshold = currentPeriod / 2; - const auto minFramePeriod = minFramePeriodLocked().ns(); + const auto minFramePeriod = minFramePeriodLocked(); auto prev = lastConfirmedPresentTime.ns(); for (auto& current : mPastExpectedPresentTimes) { if (CC_UNLIKELY(mTraceOn)) { - ATRACE_FORMAT_INSTANT("current %.2f past last signaled fence", - static_cast<float>(current.ns() - lastConfirmedPresentTime.ns()) / - 1e6f); + SFTRACE_FORMAT_INSTANT("current %.2f past last signaled fence", + static_cast<float>(current.ns() - + lastConfirmedPresentTime.ns()) / + 1e6f); } - const auto minPeriodViolation = current.ns() - prev + threshold < minFramePeriod; + const auto minPeriodViolation = current.ns() - prev + threshold < minFramePeriod.ns(); if (minPeriodViolation) { - ATRACE_NAME("minPeriodViolation"); - current = TimePoint::fromNs(prev + minFramePeriod); + SFTRACE_NAME("minPeriodViolation"); + current = TimePoint::fromNs(prev + minFramePeriod.ns()); prev = current.ns(); } else { break; @@ -465,7 +491,7 @@ Duration VSyncPredictor::ensureMinFrameDurationIsKept(TimePoint expectedPresentT const auto phase = Duration(mPastExpectedPresentTimes.back() - expectedPresentTime); if (phase > 0ns) { for (auto& timeline : mTimelines) { - timeline.shiftVsyncSequence(phase); + timeline.shiftVsyncSequence(phase, minFramePeriod); } mPastExpectedPresentTimes.clear(); return phase; @@ -475,18 +501,18 @@ Duration VSyncPredictor::ensureMinFrameDurationIsKept(TimePoint expectedPresentT return 0ns; } -void VSyncPredictor::onFrameBegin(TimePoint expectedPresentTime, - TimePoint lastConfirmedPresentTime) { - ATRACE_NAME("VSyncPredictor::onFrameBegin"); +void VSyncPredictor::onFrameBegin(TimePoint expectedPresentTime, FrameTime lastSignaledFrameTime) { + SFTRACE_NAME("VSyncPredictor::onFrameBegin"); std::lock_guard lock(mMutex); if (!mDisplayModePtr->getVrrConfig()) return; + const auto [lastConfirmedPresentTime, lastConfirmedExpectedPresentTime] = lastSignaledFrameTime; if (CC_UNLIKELY(mTraceOn)) { - ATRACE_FORMAT_INSTANT("vsync is %.2f past last signaled fence", - static_cast<float>(expectedPresentTime.ns() - - lastConfirmedPresentTime.ns()) / - 1e6f); + SFTRACE_FORMAT_INSTANT("vsync is %.2f past last signaled fence", + static_cast<float>(expectedPresentTime.ns() - + lastConfirmedPresentTime.ns()) / + 1e6f); } const auto currentPeriod = mRateMap.find(idealPeriod())->second.slope; const auto threshold = currentPeriod / 2; @@ -497,9 +523,9 @@ void VSyncPredictor::onFrameBegin(TimePoint expectedPresentTime, const bool frontIsBeforeConfirmed = front < lastConfirmedPresentTime.ns() + threshold; if (frontIsBeforeConfirmed) { if (CC_UNLIKELY(mTraceOn)) { - ATRACE_FORMAT_INSTANT("Discarding old vsync - %.2f before last signaled fence", - static_cast<float>(lastConfirmedPresentTime.ns() - front) / - 1e6f); + SFTRACE_FORMAT_INSTANT("Discarding old vsync - %.2f before last signaled fence", + static_cast<float>(lastConfirmedPresentTime.ns() - front) / + 1e6f); } mPastExpectedPresentTimes.pop_front(); } else { @@ -507,6 +533,11 @@ void VSyncPredictor::onFrameBegin(TimePoint expectedPresentTime, } } + if (lastConfirmedExpectedPresentTime.ns() - lastConfirmedPresentTime.ns() > threshold) { + SFTRACE_FORMAT_INSTANT("lastFramePresentedEarly"); + return; + } + const auto phase = ensureMinFrameDurationIsKept(expectedPresentTime, lastConfirmedPresentTime); if (phase > 0ns) { mMissedVsync = {expectedPresentTime, minFramePeriodLocked()}; @@ -514,7 +545,7 @@ void VSyncPredictor::onFrameBegin(TimePoint expectedPresentTime, } void VSyncPredictor::onFrameMissed(TimePoint expectedPresentTime) { - ATRACE_NAME("VSyncPredictor::onFrameMissed"); + SFTRACE_NAME("VSyncPredictor::onFrameMissed"); std::lock_guard lock(mMutex); if (!mDisplayModePtr->getVrrConfig()) return; @@ -539,15 +570,19 @@ VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModelLocked() const { return mRateMap.find(idealPeriod())->second; } -void VSyncPredictor::clearTimestamps() { - ATRACE_CALL(); +void VSyncPredictor::clearTimestamps(bool clearTimelines) { + SFTRACE_FORMAT("%s: clearTimelines=%d", __func__, clearTimelines); if (!mTimestamps.empty()) { auto const maxRb = *std::max_element(mTimestamps.begin(), mTimestamps.end()); if (mKnownTimestamp) { mKnownTimestamp = std::max(*mKnownTimestamp, maxRb); + SFTRACE_FORMAT_INSTANT("mKnownTimestamp was %.2fms ago", + (mClock->now() - *mKnownTimestamp) / 1e6f); } else { mKnownTimestamp = maxRb; + SFTRACE_FORMAT_INSTANT("mKnownTimestamp (maxRb) was %.2fms ago", + (mClock->now() - *mKnownTimestamp) / 1e6f); } mTimestamps.clear(); @@ -558,7 +593,7 @@ void VSyncPredictor::clearTimestamps() { if (mTimelines.empty()) { mLastCommittedVsync = TimePoint::fromNs(0); mTimelines.emplace_back(mLastCommittedVsync, mIdealPeriod, mRenderRateOpt); - } else { + } else if (clearTimelines) { while (mTimelines.size() > 1) { mTimelines.pop_front(); } @@ -579,9 +614,10 @@ bool VSyncPredictor::needsMoreSamples() const { } void VSyncPredictor::resetModel() { + SFTRACE_CALL(); std::lock_guard lock(mMutex); mRateMap[idealPeriod()] = {idealPeriod(), 0}; - clearTimestamps(); + clearTimestamps(/* clearTimelines */ true); } void VSyncPredictor::dump(std::string& result) const { @@ -602,7 +638,7 @@ void VSyncPredictor::purgeTimelines(android::TimePoint now) { if (mRenderRateOpt && mLastCommittedVsync.ns() + mRenderRateOpt->getPeriodNsecs() * kEnoughFramesToBreakPhase < mClock->now()) { - ATRACE_FORMAT_INSTANT("kEnoughFramesToBreakPhase"); + SFTRACE_FORMAT_INSTANT("kEnoughFramesToBreakPhase"); mTimelines.clear(); mLastCommittedVsync = TimePoint::fromNs(0); mTimelines.emplace_back(mLastCommittedVsync, mIdealPeriod, mRenderRateOpt); @@ -611,7 +647,10 @@ void VSyncPredictor::purgeTimelines(android::TimePoint now) { while (mTimelines.size() > 1) { const auto validUntilOpt = mTimelines.front().validUntil(); - if (validUntilOpt && *validUntilOpt < now) { + const bool isTimelineOutDated = FlagManager::getInstance().vrr_bugfix_24q4() + ? mTimelines.front().isWithin(now) == VsyncTimeline::VsyncOnTimeline::Outside + : validUntilOpt && *validUntilOpt < now; + if (isTimelineOutDated) { mTimelines.pop_front(); } else { break; @@ -635,16 +674,16 @@ VSyncPredictor::VsyncTimeline::VsyncTimeline(TimePoint knownVsync, Period idealP void VSyncPredictor::VsyncTimeline::freeze(TimePoint lastVsync) { LOG_ALWAYS_FATAL_IF(mValidUntil.has_value()); - ATRACE_FORMAT_INSTANT("renderRate %s valid for %.2f", - mRenderRateOpt ? to_string(*mRenderRateOpt).c_str() : "NA", - float(lastVsync.ns() - TimePoint::now().ns()) / 1e6f); + SFTRACE_FORMAT_INSTANT("renderRate %s valid for %.2f", + mRenderRateOpt ? to_string(*mRenderRateOpt).c_str() : "NA", + float(lastVsync.ns() - TimePoint::now().ns()) / 1e6f); mValidUntil = lastVsync; } std::optional<TimePoint> VSyncPredictor::VsyncTimeline::nextAnticipatedVSyncTimeFrom( Model model, std::optional<Period> minFramePeriodOpt, nsecs_t vsync, MissedVsync missedVsync, std::optional<nsecs_t> lastVsyncOpt) { - ATRACE_FORMAT("renderRate %s", mRenderRateOpt ? to_string(*mRenderRateOpt).c_str() : "NA"); + SFTRACE_FORMAT("renderRate %s", mRenderRateOpt ? to_string(*mRenderRateOpt).c_str() : "NA"); nsecs_t vsyncTime = snapToVsyncAlignedWithRenderRate(model, vsync); const auto threshold = model.slope / 2; @@ -656,34 +695,43 @@ std::optional<TimePoint> VSyncPredictor::VsyncTimeline::nextAnticipatedVSyncTime if (lastFrameMissed) { // If the last frame missed is the last vsync, we already shifted the timeline. Depends // on whether we skipped the frame (onFrameMissed) or not (onFrameBegin) we apply a - // different fixup. There is no need to to shift the vsync timeline again. - vsyncTime += missedVsync.fixup.ns(); - ATRACE_FORMAT_INSTANT("lastFrameMissed"); + // different fixup if we are violating the minFramePeriod. + // There is no need to shift the vsync timeline again. + if (vsyncTime - missedVsync.vsync.ns() < minFramePeriodOpt->ns()) { + vsyncTime += missedVsync.fixup.ns(); + SFTRACE_FORMAT_INSTANT("lastFrameMissed"); + } } else if (mightBackpressure && lastVsyncOpt) { - // lastVsyncOpt is based on the old timeline before we shifted it. we should correct it - // first before trying to use it. - lastVsyncOpt = snapToVsyncAlignedWithRenderRate(model, *lastVsyncOpt); + if (!FlagManager::getInstance().vrr_bugfix_24q4()) { + // lastVsyncOpt does not need to be corrected with the new rate, and + // it should be used as is to avoid skipping a frame when changing rates are + // aligned at vsync time. + lastVsyncOpt = snapToVsyncAlignedWithRenderRate(model, *lastVsyncOpt); + } const auto vsyncDiff = vsyncTime - *lastVsyncOpt; if (vsyncDiff <= minFramePeriodOpt->ns() - threshold) { // avoid a duplicate vsync - ATRACE_FORMAT_INSTANT("skipping a vsync to avoid duplicate frame. next in %.2f " - "which " - "is %.2f " - "from " - "prev. " - "adjust by %.2f", - static_cast<float>(vsyncTime - TimePoint::now().ns()) / 1e6f, - static_cast<float>(vsyncDiff) / 1e6f, - static_cast<float>(mRenderRateOpt->getPeriodNsecs()) / 1e6f); + SFTRACE_FORMAT_INSTANT("skipping a vsync to avoid duplicate frame. next in %.2f " + "which " + "is %.2f " + "from " + "prev. " + "adjust by %.2f", + static_cast<float>(vsyncTime - TimePoint::now().ns()) / 1e6f, + static_cast<float>(vsyncDiff) / 1e6f, + static_cast<float>(mRenderRateOpt->getPeriodNsecs()) / 1e6f); vsyncTime += mRenderRateOpt->getPeriodNsecs(); } } } - ATRACE_FORMAT_INSTANT("vsync in %.2fms", float(vsyncTime - TimePoint::now().ns()) / 1e6f); - if (mValidUntil && vsyncTime > mValidUntil->ns()) { - ATRACE_FORMAT_INSTANT("no longer valid for vsync in %.2f", - static_cast<float>(vsyncTime - TimePoint::now().ns()) / 1e6f); + SFTRACE_FORMAT_INSTANT("vsync in %.2fms", float(vsyncTime - TimePoint::now().ns()) / 1e6f); + const bool isVsyncInvalid = FlagManager::getInstance().vrr_bugfix_24q4() + ? isWithin(TimePoint::fromNs(vsyncTime)) == VsyncOnTimeline::Outside + : mValidUntil && vsyncTime > mValidUntil->ns(); + if (isVsyncInvalid) { + SFTRACE_FORMAT_INSTANT("no longer valid for vsync in %.2f", + static_cast<float>(vsyncTime - TimePoint::now().ns()) / 1e6f); return std::nullopt; } @@ -737,7 +785,9 @@ bool VSyncPredictor::VsyncTimeline::isVSyncInPhase(Model model, nsecs_t vsync, F return ticks<std::milli, float>(TimePoint::fromNs(timePoint) - now); }; - Fps displayFps = Fps::fromPeriodNsecs(mIdealPeriod.ns()); + Fps displayFps = !FlagManager::getInstance().vrr_bugfix_24q4() && mRenderRateOpt + ? *mRenderRateOpt + : Fps::fromPeriodNsecs(mIdealPeriod.ns()); const auto divisor = RefreshRateSelector::getFrameRateDivisor(displayFps, frameRate); const auto now = TimePoint::now(); @@ -745,18 +795,39 @@ bool VSyncPredictor::VsyncTimeline::isVSyncInPhase(Model model, nsecs_t vsync, F return true; } const auto vsyncSequence = getVsyncSequenceLocked(model, vsync); - ATRACE_FORMAT_INSTANT("vsync in: %.2f sequence: %" PRId64 " divisor: %zu", - getVsyncIn(now, vsyncSequence.vsyncTime), vsyncSequence.seq, divisor); + SFTRACE_FORMAT_INSTANT("vsync in: %.2f sequence: %" PRId64 " divisor: %zu", + getVsyncIn(now, vsyncSequence.vsyncTime), vsyncSequence.seq, divisor); return vsyncSequence.seq % divisor == 0; } -void VSyncPredictor::VsyncTimeline::shiftVsyncSequence(Duration phase) { +void VSyncPredictor::VsyncTimeline::shiftVsyncSequence(Duration phase, Period minFramePeriod) { if (mLastVsyncSequence) { - ATRACE_FORMAT_INSTANT("adjusting vsync by %.2f", static_cast<float>(phase.ns()) / 1e6f); + const auto renderRate = mRenderRateOpt.value_or(Fps::fromPeriodNsecs(mIdealPeriod.ns())); + const auto threshold = mIdealPeriod.ns() / 2; + if (renderRate.getPeriodNsecs() - phase.ns() + threshold >= minFramePeriod.ns()) { + SFTRACE_FORMAT_INSTANT("Not-Adjusting vsync by %.2f", + static_cast<float>(phase.ns()) / 1e6f); + return; + } + SFTRACE_FORMAT_INSTANT("adjusting vsync by %.2f", static_cast<float>(phase.ns()) / 1e6f); mLastVsyncSequence->vsyncTime += phase.ns(); } } +VSyncPredictor::VsyncTimeline::VsyncOnTimeline VSyncPredictor::VsyncTimeline::isWithin( + TimePoint vsync) { + const auto threshold = mIdealPeriod.ns() / 2; + if (!mValidUntil || vsync.ns() < mValidUntil->ns() - threshold) { + // if mValidUntil is absent then timeline is not frozen and + // vsync should be unique to that timeline. + return VsyncOnTimeline::Unique; + } + if (vsync.ns() > mValidUntil->ns() + threshold) { + return VsyncOnTimeline::Outside; + } + return VsyncOnTimeline::Shared; +} + } // namespace android::scheduler // TODO(b/129481165): remove the #pragma below and fix conversion issues diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h index 8ce61d86c6..2df3d0465f 100644 --- a/services/surfaceflinger/Scheduler/VSyncPredictor.h +++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h @@ -22,6 +22,7 @@ #include <vector> #include <android-base/thread_annotations.h> +#include <scheduler/FrameTime.h> #include <scheduler/TimeKeeper.h> #include <ui/DisplayId.h> @@ -77,7 +78,7 @@ public: void setRenderRate(Fps, bool applyImmediately) final EXCLUDES(mMutex); - void onFrameBegin(TimePoint expectedPresentTime, TimePoint lastConfirmedPresentTime) final + void onFrameBegin(TimePoint expectedPresentTime, FrameTime lastSignaledFrameTime) final EXCLUDES(mMutex); void onFrameMissed(TimePoint expectedPresentTime) final EXCLUDES(mMutex); @@ -103,9 +104,16 @@ private: void freeze(TimePoint lastVsync); std::optional<TimePoint> validUntil() const { return mValidUntil; } bool isVSyncInPhase(Model, nsecs_t vsync, Fps frameRate); - void shiftVsyncSequence(Duration phase); + void shiftVsyncSequence(Duration phase, Period minFramePeriod); void setRenderRate(std::optional<Fps> renderRateOpt) { mRenderRateOpt = renderRateOpt; } + enum class VsyncOnTimeline { + Unique, // Within timeline, not shared with next timeline. + Shared, // Within timeline, shared with next timeline. + Outside, // Outside of the timeline. + }; + VsyncOnTimeline isWithin(TimePoint vsync); + private: nsecs_t snapToVsyncAlignedWithRenderRate(Model model, nsecs_t vsync); VsyncSequence getVsyncSequenceLocked(Model, nsecs_t vsync); @@ -119,7 +127,7 @@ private: VSyncPredictor(VSyncPredictor const&) = delete; VSyncPredictor& operator=(VSyncPredictor const&) = delete; - void clearTimestamps() REQUIRES(mMutex); + void clearTimestamps(bool clearTimelines) REQUIRES(mMutex); const std::unique_ptr<Clock> mClock; const PhysicalDisplayId mId; diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp index 8038364453..b974cd2b04 100644 --- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp +++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp @@ -20,11 +20,10 @@ //#define LOG_NDEBUG 0 #include <assert.h> +#include <common/trace.h> #include <cutils/properties.h> #include <ftl/concat.h> -#include <gui/TraceUtils.h> #include <log/log.h> -#include <utils/Trace.h> #include "../TracedOrdinal.h" #include "VSyncDispatch.h" @@ -53,7 +52,7 @@ VSyncReactor::VSyncReactor(PhysicalDisplayId id, std::unique_ptr<Clock> clock, VSyncReactor::~VSyncReactor() = default; bool VSyncReactor::addPresentFence(std::shared_ptr<FenceTime> fence) { - ATRACE_CALL(); + SFTRACE_CALL(); if (!fence) { return false; @@ -66,8 +65,8 @@ bool VSyncReactor::addPresentFence(std::shared_ptr<FenceTime> fence) { std::lock_guard lock(mMutex); if (mExternalIgnoreFences || mInternalIgnoreFences) { - ATRACE_FORMAT_INSTANT("mExternalIgnoreFences=%d mInternalIgnoreFences=%d", - mExternalIgnoreFences, mInternalIgnoreFences); + SFTRACE_FORMAT_INSTANT("mExternalIgnoreFences=%d mInternalIgnoreFences=%d", + mExternalIgnoreFences, mInternalIgnoreFences); return true; } @@ -121,7 +120,7 @@ void VSyncReactor::updateIgnorePresentFencesInternal() { } void VSyncReactor::startPeriodTransitionInternal(ftl::NonNull<DisplayModePtr> modePtr) { - ATRACE_FORMAT("%s %" PRIu64, __func__, mId.value); + SFTRACE_FORMAT("%s %" PRIu64, __func__, mId.value); mPeriodConfirmationInProgress = true; mModePtrTransitioningTo = modePtr.get(); mMoreSamplesNeeded = true; @@ -129,19 +128,21 @@ void VSyncReactor::startPeriodTransitionInternal(ftl::NonNull<DisplayModePtr> mo } void VSyncReactor::endPeriodTransition() { - ATRACE_FORMAT("%s %" PRIu64, __func__, mId.value); + SFTRACE_FORMAT("%s %" PRIu64, __func__, mId.value); mModePtrTransitioningTo.reset(); mPeriodConfirmationInProgress = false; mLastHwVsync.reset(); } void VSyncReactor::onDisplayModeChanged(ftl::NonNull<DisplayModePtr> modePtr, bool force) { - ATRACE_INT64(ftl::Concat("VSR-", __func__, " ", mId.value).c_str(), - modePtr->getVsyncRate().getPeriodNsecs()); + SFTRACE_INT64(ftl::Concat("VSR-", __func__, " ", mId.value).c_str(), + modePtr->getVsyncRate().getPeriodNsecs()); std::lock_guard lock(mMutex); mLastHwVsync.reset(); - if (!mSupportKernelIdleTimer && mTracker.isCurrentMode(modePtr) && !force) { + // kernel idle timer is not applicable for VRR + const bool supportKernelIdleTimer = mSupportKernelIdleTimer && !modePtr->getVrrConfig(); + if (!supportKernelIdleTimer && mTracker.isCurrentMode(modePtr) && !force) { endPeriodTransition(); setIgnorePresentFencesInternal(false); mMoreSamplesNeeded = false; @@ -191,7 +192,7 @@ bool VSyncReactor::addHwVsyncTimestamp(nsecs_t timestamp, std::optional<nsecs_t> std::lock_guard lock(mMutex); if (periodConfirmed(timestamp, hwcVsyncPeriod)) { - ATRACE_FORMAT("VSR %" PRIu64 ": period confirmed", mId.value); + SFTRACE_FORMAT("VSR %" PRIu64 ": period confirmed", mId.value); if (mModePtrTransitioningTo) { mTracker.setDisplayModePtr(ftl::as_non_null(mModePtrTransitioningTo)); *periodFlushed = true; @@ -205,12 +206,12 @@ bool VSyncReactor::addHwVsyncTimestamp(nsecs_t timestamp, std::optional<nsecs_t> endPeriodTransition(); mMoreSamplesNeeded = mTracker.needsMoreSamples(); } else if (mPeriodConfirmationInProgress) { - ATRACE_FORMAT("VSR %" PRIu64 ": still confirming period", mId.value); + SFTRACE_FORMAT("VSR %" PRIu64 ": still confirming period", mId.value); mLastHwVsync = timestamp; mMoreSamplesNeeded = true; *periodFlushed = false; } else { - ATRACE_FORMAT("VSR %" PRIu64 ": adding sample", mId.value); + SFTRACE_FORMAT("VSR %" PRIu64 ": adding sample", mId.value); *periodFlushed = false; mTracker.addVsyncTimestamp(timestamp); mMoreSamplesNeeded = mTracker.needsMoreSamples(); diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h index 134d28e1e5..3376fadad0 100644 --- a/services/surfaceflinger/Scheduler/VSyncTracker.h +++ b/services/surfaceflinger/Scheduler/VSyncTracker.h @@ -21,6 +21,7 @@ #include <scheduler/Fps.h> #include <scheduler/FrameRateMode.h> +#include <scheduler/FrameTime.h> #include "VSyncDispatch.h" @@ -112,8 +113,7 @@ public: */ virtual void setRenderRate(Fps, bool applyImmediately) = 0; - virtual void onFrameBegin(TimePoint expectedPresentTime, - TimePoint lastConfirmedPresentTime) = 0; + virtual void onFrameBegin(TimePoint expectedPresentTime, FrameTime lastSignaledFrameTime) = 0; virtual void onFrameMissed(TimePoint expectedPresentTime) = 0; diff --git a/services/surfaceflinger/Scheduler/VsyncModulator.cpp b/services/surfaceflinger/Scheduler/VsyncModulator.cpp index 586357f50a..3c5f68c4c2 100644 --- a/services/surfaceflinger/Scheduler/VsyncModulator.cpp +++ b/services/surfaceflinger/Scheduler/VsyncModulator.cpp @@ -21,9 +21,8 @@ #include "VsyncModulator.h" -#include <android-base/properties.h> +#include <common/trace.h> #include <log/log.h> -#include <utils/Trace.h> #include <chrono> #include <cinttypes> @@ -37,8 +36,7 @@ const std::chrono::nanoseconds VsyncModulator::MIN_EARLY_TRANSACTION_TIME = 1ms; VsyncModulator::VsyncModulator(const VsyncConfigSet& config, Now now) : mVsyncConfigSet(config), - mNow(now), - mTraceDetailedInfo(base::GetBoolProperty("debug.sf.vsync_trace_detailed_info", false)) {} + mNow(now) {} VsyncConfig VsyncModulator::setVsyncConfigSet(const VsyncConfigSet& config) { std::lock_guard<std::mutex> lock(mMutex); @@ -71,10 +69,6 @@ VsyncModulator::VsyncConfigOpt VsyncModulator::setTransactionSchedule(Transactio break; } - if (mTraceDetailedInfo) { - ATRACE_INT("mEarlyWakeup", static_cast<int>(mEarlyWakeupRequests.size())); - } - if (mEarlyWakeupRequests.empty() && schedule == Schedule::EarlyEnd) { mEarlyTransactionFrames = MIN_EARLY_TRANSACTION_FRAMES; mEarlyTransactionStartTime = mNow(); @@ -167,15 +161,19 @@ VsyncConfig VsyncModulator::updateVsyncConfigLocked() { const VsyncConfig& offsets = getNextVsyncConfig(); mVsyncConfig = offsets; - if (mTraceDetailedInfo) { - const bool isEarly = &offsets == &mVsyncConfigSet.early; - const bool isEarlyGpu = &offsets == &mVsyncConfigSet.earlyGpu; - const bool isLate = &offsets == &mVsyncConfigSet.late; + // Trace config type + SFTRACE_INT("Vsync-Early", &mVsyncConfig == &mVsyncConfigSet.early); + SFTRACE_INT("Vsync-EarlyGpu", &mVsyncConfig == &mVsyncConfigSet.earlyGpu); + SFTRACE_INT("Vsync-Late", &mVsyncConfig == &mVsyncConfigSet.late); - ATRACE_INT("Vsync-EarlyOffsetsOn", isEarly); - ATRACE_INT("Vsync-EarlyGpuOffsetsOn", isEarlyGpu); - ATRACE_INT("Vsync-LateOffsetsOn", isLate); - } + // Trace early vsync conditions + SFTRACE_INT("EarlyWakeupRequests", + static_cast<int>(mEarlyWakeupRequests.size())); + SFTRACE_INT("EarlyTransactionFrames", mEarlyTransactionFrames); + SFTRACE_INT("RefreshRateChangePending", mRefreshRateChangePending); + + // Trace early gpu conditions + SFTRACE_INT("EarlyGpuFrames", mEarlyGpuFrames); return offsets; } @@ -183,7 +181,6 @@ VsyncConfig VsyncModulator::updateVsyncConfigLocked() { void VsyncModulator::binderDied(const wp<IBinder>& who) { std::lock_guard<std::mutex> lock(mMutex); mEarlyWakeupRequests.erase(who); - static_cast<void>(updateVsyncConfigLocked()); } diff --git a/services/surfaceflinger/Scheduler/VsyncModulator.h b/services/surfaceflinger/Scheduler/VsyncModulator.h index be0d3348b5..d0a793535c 100644 --- a/services/surfaceflinger/Scheduler/VsyncModulator.h +++ b/services/surfaceflinger/Scheduler/VsyncModulator.h @@ -105,7 +105,6 @@ private: std::atomic<TimePoint> mLastTransactionCommitTime = TimePoint(); const Now mNow; - const bool mTraceDetailedInfo; }; } // namespace android::scheduler diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp index 2fa3318560..d3e312ae00 100644 --- a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp +++ b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp @@ -18,8 +18,8 @@ #include <common/FlagManager.h> +#include <common/trace.h> #include <ftl/fake_guard.h> -#include <gui/TraceUtils.h> #include <scheduler/Fps.h> #include <scheduler/Timer.h> @@ -182,7 +182,7 @@ void VsyncSchedule::enableHardwareVsync() { } void VsyncSchedule::enableHardwareVsyncLocked() { - ATRACE_CALL(); + SFTRACE_CALL(); if (mHwVsyncState == HwVsyncState::Disabled) { getTracker().resetModel(); mRequestHardwareVsync(mId, true); @@ -191,7 +191,7 @@ void VsyncSchedule::enableHardwareVsyncLocked() { } void VsyncSchedule::disableHardwareVsync(bool disallow) { - ATRACE_CALL(); + SFTRACE_CALL(); std::lock_guard<std::mutex> lock(mHwVsyncLock); switch (mHwVsyncState) { case HwVsyncState::Enabled: diff --git a/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h b/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h index a54d43582e..2185bb07ec 100644 --- a/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h +++ b/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h @@ -26,6 +26,7 @@ #include <ui/FenceTime.h> #include <scheduler/Features.h> +#include <scheduler/FrameTime.h> #include <scheduler/Time.h> #include <scheduler/VsyncId.h> #include <scheduler/interface/CompositeResult.h> @@ -54,31 +55,20 @@ public: std::optional<TimePoint> earliestPresentTime() const { return mEarliestPresentTime; } - // The time of the VSYNC that preceded this frame. See `presentFenceForPastVsync` for details. - TimePoint pastVsyncTime(Period minFramePeriod) const; - - // The present fence for the frame that had targeted the most recent VSYNC before this frame. - // If the target VSYNC for any given frame is more than `vsyncPeriod` in the future, then the - // VSYNC of at least one previous frame has not yet passed. In other words, this is NOT the - // `presentFenceForPreviousFrame` if running N VSYNCs ahead, but the one that should have been - // signaled by now (unless that frame missed). - FenceTimePtr presentFenceForPastVsync(Period minFramePeriod) const; - - // Equivalent to `presentFenceForPastVsync` unless running N VSYNCs ahead. - const FenceTimePtr& presentFenceForPreviousFrame() const { - return mPresentFences.front().fenceTime; - } + // Equivalent to `expectedSignaledPresentFence` unless running N VSYNCs ahead. + const FenceTimePtr& presentFenceForPreviousFrame() const; bool isFramePending() const { return mFramePending; } + bool wouldBackpressureHwc() const { return mWouldBackpressureHwc; } bool didMissFrame() const { return mFrameMissed; } bool didMissHwcFrame() const { return mHwcFrameMissed && !mGpuFrameMissed; } - TimePoint lastSignaledFrameTime() const { return mLastSignaledFrameTime; }; + FrameTime lastSignaledFrameTime() const { return mLastSignaledFrameTime; } protected: explicit FrameTarget(const std::string& displayLabel); ~FrameTarget() = default; - bool wouldPresentEarly(Period minFramePeriod) const; + bool wouldPresentEarly(Period vsyncPeriod, Period minFramePeriod) const; // Equivalent to `pastVsyncTime` unless running N VSYNCs ahead. TimePoint previousFrameVsyncTime(Period minFramePeriod) const { @@ -87,8 +77,7 @@ protected: void addFence(sp<Fence> presentFence, FenceTimePtr presentFenceTime, TimePoint expectedPresentTime) { - mFenceWithFenceTimes.next() = {std::move(presentFence), presentFenceTime, - expectedPresentTime}; + mPresentFences.next() = {std::move(presentFence), presentFenceTime, expectedPresentTime}; } VsyncId mVsyncId; @@ -100,17 +89,25 @@ protected: TracedOrdinal<bool> mFrameMissed; TracedOrdinal<bool> mHwcFrameMissed; TracedOrdinal<bool> mGpuFrameMissed; + bool mWouldBackpressureHwc = false; - struct FenceWithFenceTime { + struct PresentFence { sp<Fence> fence = Fence::NO_FENCE; FenceTimePtr fenceTime = FenceTime::NO_FENCE; TimePoint expectedPresentTime = TimePoint(); }; - // size should be longest sf-duration / shortest vsync period and round up - std::array<FenceWithFenceTime, 5> mPresentFences; // currently consider 166hz. - utils::RingBuffer<FenceWithFenceTime, 5> mFenceWithFenceTimes; - TimePoint mLastSignaledFrameTime; + // The present fence for the frame that had targeted the most recent VSYNC before this frame. + // If the target VSYNC for any given frame is more than `vsyncPeriod` in the future, then the + // VSYNC of at least one previous frame has not yet passed. In other words, this is NOT the + // `presentFenceForPreviousFrame` if running N VSYNCs ahead, but the one that should have been + // signaled by now (unless that frame missed). + std::pair<bool /* wouldBackpressure */, PresentFence> expectedSignaledPresentFence( + Period vsyncPeriod, Period minFramePeriod) const; + std::array<PresentFence, 2> mPresentFencesLegacy; + utils::RingBuffer<PresentFence, 5> mPresentFences; + + FrameTime mLastSignaledFrameTime; private: friend class FrameTargeterTestBase; @@ -120,33 +117,6 @@ private: static_assert(N > 1); return expectedFrameDuration() > (N - 1) * minFramePeriod; } - - const FenceTimePtr pastVsyncTimePtr() const { - auto pastFenceTimePtr = FenceTime::NO_FENCE; - for (size_t i = 0; i < mFenceWithFenceTimes.size(); i++) { - const auto& [_, fenceTimePtr, expectedPresentTime] = mFenceWithFenceTimes[i]; - if (expectedPresentTime > mFrameBeginTime) { - return pastFenceTimePtr; - } - pastFenceTimePtr = fenceTimePtr; - } - return pastFenceTimePtr; - } - - size_t getPresentFenceShift(Period minFramePeriod) const { - size_t shift = 0; - if (minFramePeriod.ns() == 0) { - return shift; - } - const bool isTwoVsyncsAhead = targetsVsyncsAhead<2>(minFramePeriod); - if (isTwoVsyncsAhead) { - shift = static_cast<size_t>(expectedFrameDuration().ns() / minFramePeriod.ns()); - if (shift >= mPresentFences.size()) { - shift = mPresentFences.size() - 1; - } - } - return shift; - } }; // Computes a display's per-frame metrics about past/upcoming targeting of present deadlines. @@ -169,7 +139,7 @@ public: void beginFrame(const BeginFrameArgs&, const IVsyncSource&); - std::optional<TimePoint> computeEarliestPresentTime(Period minFramePeriod, + std::optional<TimePoint> computeEarliestPresentTime(Period vsyncPeriod, Period minFramePeriod, Duration hwcMinWorkDuration); // TODO(b/241285191): Merge with FrameTargeter::endFrame. diff --git a/libs/tracing_perfetto/include/trace_result.h b/services/surfaceflinger/Scheduler/include/scheduler/FrameTime.h index f7581fc0fb..ed5c8991f3 100644 --- a/libs/tracing_perfetto/include/trace_result.h +++ b/services/surfaceflinger/Scheduler/include/scheduler/FrameTime.h @@ -14,17 +14,13 @@ * limitations under the License. */ -#ifndef TRACE_RESULT_H -#define TRACE_RESULT_H +#pragma once -namespace tracing_perfetto { +#include <scheduler/Time.h> -enum class Result { - SUCCESS, - NOT_SUPPORTED, - INVALID_INPUT, +namespace android::scheduler { +struct FrameTime { + TimePoint signalTime; + TimePoint expectedPresentTime; }; - -} - -#endif // TRACE_RESULT_H +} // namespace android::scheduler
\ No newline at end of file diff --git a/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp b/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp index 7036e677a0..3ee1e541c3 100644 --- a/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp +++ b/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp @@ -14,13 +14,14 @@ * limitations under the License. */ -#include <gui/TraceUtils.h> - #include <common/FlagManager.h> +#include <common/trace.h> #include <scheduler/FrameTargeter.h> #include <scheduler/IVsyncSource.h> +#include <utils/Log.h> namespace android::scheduler { +using namespace std::chrono_literals; FrameTarget::FrameTarget(const std::string& displayLabel) : mFramePending("PrevFramePending " + displayLabel, false), @@ -28,34 +29,53 @@ FrameTarget::FrameTarget(const std::string& displayLabel) mHwcFrameMissed("PrevHwcFrameMissed " + displayLabel, false), mGpuFrameMissed("PrevGpuFrameMissed " + displayLabel, false) {} -TimePoint FrameTarget::pastVsyncTime(Period minFramePeriod) const { - // TODO(b/267315508): Generalize to N VSYNCs. - const size_t shift = getPresentFenceShift(minFramePeriod); - return mExpectedPresentTime - Period::fromNs(minFramePeriod.ns() << shift); +std::pair<bool /* wouldBackpressure */, FrameTarget::PresentFence> +FrameTarget::expectedSignaledPresentFence(Period vsyncPeriod, Period minFramePeriod) const { + if (!FlagManager::getInstance().allow_n_vsyncs_in_targeter()) { + const size_t i = static_cast<size_t>(targetsVsyncsAhead<2>(minFramePeriod)); + return {true, mPresentFencesLegacy[i]}; + } + + bool wouldBackpressure = true; + auto expectedPresentTime = mExpectedPresentTime; + for (size_t i = mPresentFences.size(); i != 0; --i) { + const auto& fence = mPresentFences[i - 1]; + + if (fence.expectedPresentTime + minFramePeriod < expectedPresentTime - vsyncPeriod / 2) { + wouldBackpressure = false; + } + + if (fence.expectedPresentTime <= mFrameBeginTime) { + return {wouldBackpressure, fence}; + } + + expectedPresentTime = fence.expectedPresentTime; + } + return {wouldBackpressure, PresentFence{}}; } -FenceTimePtr FrameTarget::presentFenceForPastVsync(Period minFramePeriod) const { - if (FlagManager::getInstance().allow_n_vsyncs_in_targeter()) { - return pastVsyncTimePtr(); +bool FrameTarget::wouldPresentEarly(Period vsyncPeriod, Period minFramePeriod) const { + if (targetsVsyncsAhead<3>(minFramePeriod)) { + return true; } - const size_t shift = getPresentFenceShift(minFramePeriod); - ATRACE_FORMAT("mPresentFences shift=%zu", shift); - return mPresentFences[shift].fenceTime; -} + const auto [wouldBackpressure, fence] = + expectedSignaledPresentFence(vsyncPeriod, minFramePeriod); -bool FrameTarget::wouldPresentEarly(Period minFramePeriod) const { - // TODO(b/241285475): Since this is called during `composite`, the calls to `targetsVsyncsAhead` - // should use `TimePoint::now()` in case of delays since `mFrameBeginTime`. + return !wouldBackpressure || + (fence.fenceTime->isValid() && + fence.fenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING); +} - // TODO(b/267315508): Generalize to N VSYNCs. - const bool allowNVsyncs = FlagManager::getInstance().allow_n_vsyncs_in_targeter(); - if (!allowNVsyncs && targetsVsyncsAhead<3>(minFramePeriod)) { - return true; +const FenceTimePtr& FrameTarget::presentFenceForPreviousFrame() const { + if (FlagManager::getInstance().allow_n_vsyncs_in_targeter()) { + if (mPresentFences.size() > 0) { + return mPresentFences.back().fenceTime; + } + return FenceTime::NO_FENCE; } - const auto fence = presentFenceForPastVsync(minFramePeriod); - return fence->isValid() && fence->getSignalTime() != Fence::SIGNAL_TIME_PENDING; + return mPresentFencesLegacy.front().fenceTime; } void FrameTargeter::beginFrame(const BeginFrameArgs& args, const IVsyncSource& vsyncSource) { @@ -89,27 +109,39 @@ void FrameTargeter::beginFrame(const BeginFrameArgs& args, const IVsyncSource& v } if (!mSupportsExpectedPresentTime) { - mEarliestPresentTime = computeEarliestPresentTime(minFramePeriod, args.hwcMinWorkDuration); + mEarliestPresentTime = + computeEarliestPresentTime(vsyncPeriod, minFramePeriod, args.hwcMinWorkDuration); } - ATRACE_FORMAT("%s %" PRId64 " vsyncIn %.2fms%s", __func__, ftl::to_underlying(args.vsyncId), - ticks<std::milli, float>(mExpectedPresentTime - TimePoint::now()), - mExpectedPresentTime == args.expectedVsyncTime ? "" : " (adjusted)"); + SFTRACE_FORMAT("%s %" PRId64 " vsyncIn %.2fms%s", __func__, ftl::to_underlying(args.vsyncId), + ticks<std::milli, float>(mExpectedPresentTime - TimePoint::now()), + mExpectedPresentTime == args.expectedVsyncTime ? "" : " (adjusted)"); - const FenceTimePtr& pastPresentFence = presentFenceForPastVsync(minFramePeriod); + const auto [wouldBackpressure, fence] = + expectedSignaledPresentFence(vsyncPeriod, minFramePeriod); // In cases where the present fence is about to fire, give it a small grace period instead of // giving up on the frame. - // - // TODO(b/280667110): The grace period should depend on `sfWorkDuration` and `vsyncPeriod` being - // approximately equal, not whether backpressure propagation is enabled. - const int graceTimeForPresentFenceMs = static_cast<int>( - mBackpressureGpuComposition || !mCompositionCoverage.test(CompositionCoverage::Gpu)); + const int graceTimeForPresentFenceMs = [&] { + const bool considerBackpressure = + mBackpressureGpuComposition || !mCompositionCoverage.test(CompositionCoverage::Gpu); + + if (!FlagManager::getInstance().allow_n_vsyncs_in_targeter()) { + return static_cast<int>(considerBackpressure); + } + + if (!wouldBackpressure || !considerBackpressure) { + return 0; + } + + return static_cast<int>((std::abs(fence.expectedPresentTime.ns() - mFrameBeginTime.ns()) <= + Duration(1ms).ns())); + }(); // Pending frames may trigger backpressure propagation. const auto& isFencePending = *isFencePendingFuncPtr; - mFramePending = pastPresentFence != FenceTime::NO_FENCE && - isFencePending(pastPresentFence, graceTimeForPresentFenceMs); + mFramePending = fence.fenceTime != FenceTime::NO_FENCE && + isFencePending(fence.fenceTime, graceTimeForPresentFenceMs); // A frame is missed if the prior frame is still pending. If no longer pending, then we still // count the frame as missed if the predicted present time was further in the past than when the @@ -117,9 +149,10 @@ void FrameTargeter::beginFrame(const BeginFrameArgs& args, const IVsyncSource& v // than a typical frame duration, but should not be so small that it reports reasonable drift as // a missed frame. mFrameMissed = mFramePending || [&] { - const nsecs_t pastPresentTime = pastPresentFence->getSignalTime(); + const nsecs_t pastPresentTime = fence.fenceTime->getSignalTime(); if (pastPresentTime < 0) return false; - mLastSignaledFrameTime = TimePoint::fromNs(pastPresentTime); + mLastSignaledFrameTime = {.signalTime = TimePoint::fromNs(pastPresentTime), + .expectedPresentTime = fence.expectedPresentTime}; const nsecs_t frameMissedSlop = vsyncPeriod.ns() / 2; return lastScheduledPresentTime.ns() < pastPresentTime - frameMissedSlop; }(); @@ -130,11 +163,14 @@ void FrameTargeter::beginFrame(const BeginFrameArgs& args, const IVsyncSource& v if (mFrameMissed) mFrameMissedCount++; if (mHwcFrameMissed) mHwcFrameMissedCount++; if (mGpuFrameMissed) mGpuFrameMissedCount++; + + mWouldBackpressureHwc = mFramePending && wouldBackpressure; } -std::optional<TimePoint> FrameTargeter::computeEarliestPresentTime(Period minFramePeriod, +std::optional<TimePoint> FrameTargeter::computeEarliestPresentTime(Period vsyncPeriod, + Period minFramePeriod, Duration hwcMinWorkDuration) { - if (wouldPresentEarly(minFramePeriod)) { + if (wouldPresentEarly(vsyncPeriod, minFramePeriod)) { return previousFrameVsyncTime(minFramePeriod) - hwcMinWorkDuration; } return {}; @@ -153,10 +189,8 @@ FenceTimePtr FrameTargeter::setPresentFence(sp<Fence> presentFence, FenceTimePtr if (FlagManager::getInstance().allow_n_vsyncs_in_targeter()) { addFence(std::move(presentFence), presentFenceTime, mExpectedPresentTime); } else { - for (size_t i = mPresentFences.size()-1; i >= 1; i--) { - mPresentFences[i] = mPresentFences[i-1]; - } - mPresentFences[0] = {std::move(presentFence), presentFenceTime, mExpectedPresentTime}; + mPresentFencesLegacy[1] = mPresentFencesLegacy[0]; + mPresentFencesLegacy[0] = {std::move(presentFence), presentFenceTime, mExpectedPresentTime}; } return presentFenceTime; } @@ -169,7 +203,7 @@ void FrameTargeter::dump(utils::Dumper& dumper) const { } bool FrameTargeter::isFencePending(const FenceTimePtr& fence, int graceTimeMs) { - ATRACE_CALL(); + SFTRACE_CALL(); const status_t status = fence->wait(graceTimeMs); // This is the same as Fence::Status::Unsignaled, but it saves a call to getStatus, diff --git a/services/surfaceflinger/Scheduler/src/Timer.cpp b/services/surfaceflinger/Scheduler/src/Timer.cpp index eeb9c60d15..20c58eb52f 100644 --- a/services/surfaceflinger/Scheduler/src/Timer.cpp +++ b/services/surfaceflinger/Scheduler/src/Timer.cpp @@ -24,10 +24,10 @@ #include <sys/timerfd.h> #include <sys/unistd.h> +#include <common/trace.h> #include <ftl/concat.h> #include <ftl/enum.h> #include <log/log.h> -#include <utils/Trace.h> #include <scheduler/Timer.h> @@ -188,9 +188,9 @@ bool Timer::dispatch() { int nfds = epoll_wait(mEpollFd, events, DispatchType::MAX_DISPATCH_TYPE, -1); setDebugState(DebugState::Running); - if (ATRACE_ENABLED()) { + if (SFTRACE_ENABLED()) { ftl::Concat trace("TimerIteration #", iteration++); - ATRACE_NAME(trace.c_str()); + SFTRACE_NAME(trace.c_str()); } if (nfds == -1) { diff --git a/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp b/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp index 5448eecc60..6f4e1f1dfe 100644 --- a/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp +++ b/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp @@ -53,8 +53,13 @@ public: const auto& target() const { return mTargeter.target(); } - bool wouldPresentEarly(Period minFramePeriod) const { - return target().wouldPresentEarly(minFramePeriod); + bool wouldPresentEarly(Period vsyncPeriod, Period minFramePeriod) const { + return target().wouldPresentEarly(vsyncPeriod, minFramePeriod); + } + + std::pair<bool /*wouldBackpressure*/, FrameTarget::PresentFence> expectedSignaledPresentFence( + Period vsyncPeriod, Period minFramePeriod) const { + return target().expectedSignaledPresentFence(vsyncPeriod, minFramePeriod); } struct Frame { @@ -169,7 +174,6 @@ TEST_F(FrameTargeterTest, inflatesExpectedPresentTime) { } TEST_F(FrameTargeterTest, recallsPastVsync) { - SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, false); VsyncId vsyncId{111}; TimePoint frameBeginTime(1000ms); constexpr Fps kRefreshRate = 60_Hz; @@ -177,37 +181,72 @@ TEST_F(FrameTargeterTest, recallsPastVsync) { constexpr Duration kFrameDuration = 13ms; for (int n = 5; n-- > 0;) { - Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate); - const auto fence = frame.end(); + FenceTimePtr fence; + { + Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, + kRefreshRate); + fence = frame.end(); + } - EXPECT_EQ(target().pastVsyncTime(kPeriod), frameBeginTime + kFrameDuration - kPeriod); - EXPECT_EQ(target().presentFenceForPastVsync(kPeriod), fence); + Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate); + const auto [wouldBackpressure, presentFence] = + expectedSignaledPresentFence(kPeriod, kPeriod); + ASSERT_TRUE(wouldBackpressure); + EXPECT_EQ(presentFence.fenceTime, fence); } } -TEST_F(FrameTargeterTest, recallsPastVsyncTwoVsyncsAhead) { - SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, false); - VsyncId vsyncId{222}; - TimePoint frameBeginTime(2000ms); - constexpr Fps kRefreshRate = 120_Hz; +TEST_F(FrameTargeterTest, wouldBackpressureAfterTime) { + SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, true); + VsyncId vsyncId{111}; + TimePoint frameBeginTime(1000ms); + constexpr Fps kRefreshRate = 60_Hz; constexpr Period kPeriod = kRefreshRate.getPeriod(); - constexpr Duration kFrameDuration = 10ms; + constexpr Duration kFrameDuration = 13ms; - FenceTimePtr previousFence = FenceTime::NO_FENCE; + { Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate); } + { + Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate); - for (int n = 5; n-- > 0;) { + const auto [wouldBackpressure, presentFence] = + expectedSignaledPresentFence(kPeriod, kPeriod); + EXPECT_TRUE(wouldBackpressure); + } + { + frameBeginTime += kPeriod; Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate); - const auto fence = frame.end(); + const auto [wouldBackpressure, presentFence] = + expectedSignaledPresentFence(kPeriod, kPeriod); + EXPECT_FALSE(wouldBackpressure); + } +} + +TEST_F(FrameTargeterTest, wouldBackpressureAfterTimeLegacy) { + SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, false); + VsyncId vsyncId{111}; + TimePoint frameBeginTime(1000ms); + constexpr Fps kRefreshRate = 60_Hz; + constexpr Period kPeriod = kRefreshRate.getPeriod(); + constexpr Duration kFrameDuration = 13ms; - EXPECT_EQ(target().pastVsyncTime(kPeriod), frameBeginTime + kFrameDuration - 2 * kPeriod); - EXPECT_EQ(target().presentFenceForPastVsync(kPeriod), previousFence); + { Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate); } + { + Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate); - previousFence = fence; + const auto [wouldBackpressure, presentFence] = + expectedSignaledPresentFence(kPeriod, kPeriod); + EXPECT_TRUE(wouldBackpressure); + } + { + frameBeginTime += kPeriod; + Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate); + const auto [wouldBackpressure, presentFence] = + expectedSignaledPresentFence(kPeriod, kPeriod); + EXPECT_TRUE(wouldBackpressure); } } -TEST_F(FrameTargeterTest, recallsPastNVsyncTwoVsyncsAhead) { - SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, true); +TEST_F(FrameTargeterTest, recallsPastVsyncTwoVsyncsAhead) { VsyncId vsyncId{222}; TimePoint frameBeginTime(2000ms); constexpr Fps kRefreshRate = 120_Hz; @@ -215,80 +254,66 @@ TEST_F(FrameTargeterTest, recallsPastNVsyncTwoVsyncsAhead) { constexpr Duration kFrameDuration = 10ms; FenceTimePtr previousFence = FenceTime::NO_FENCE; - + FenceTimePtr currentFence = FenceTime::NO_FENCE; for (int n = 5; n-- > 0;) { Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate); - const auto fence = frame.end(); - - const auto pastVsyncTime = frameBeginTime + kFrameDuration - 2 * kPeriod; - EXPECT_EQ(target().pastVsyncTime(kPeriod), pastVsyncTime); - EXPECT_EQ(target().presentFenceForPastVsync(kFrameDuration), previousFence); - - frameBeginTime += kPeriod; - previousFence = fence; + EXPECT_EQ(expectedSignaledPresentFence(kPeriod, kPeriod).second.fenceTime, previousFence); + previousFence = currentFence; + currentFence = frame.end(); } } -TEST_F(FrameTargeterTest, recallsPastVsyncTwoVsyncsAheadVrr) { - SET_FLAG_FOR_TEST(flags::vrr_config, true); - SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, false); +TEST_F(FrameTargeterTest, recallsPastVsyncFiveVsyncsAhead) { + SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, true); VsyncId vsyncId{222}; TimePoint frameBeginTime(2000ms); constexpr Fps kRefreshRate = 120_Hz; - constexpr Fps kPeakRefreshRate = 240_Hz; constexpr Period kPeriod = kRefreshRate.getPeriod(); - constexpr Duration kFrameDuration = 10ms; - - FenceTimePtr previousFence = FenceTime::NO_FENCE; + constexpr Duration kFrameDuration = 40ms; + FenceTimePtr firstFence = FenceTime::NO_FENCE; for (int n = 5; n-- > 0;) { - Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, - kPeakRefreshRate); + Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate); const auto fence = frame.end(); - - EXPECT_EQ(target().pastVsyncTime(kPeriod), frameBeginTime + kFrameDuration - 2 * kPeriod); - EXPECT_EQ(target().presentFenceForPastVsync(kPeriod), previousFence); - - previousFence = fence; + if (firstFence == FenceTime::NO_FENCE) { + firstFence = fence; + } } + + Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate); + EXPECT_EQ(expectedSignaledPresentFence(kPeriod, kPeriod).second.fenceTime, firstFence); } -TEST_F(FrameTargeterTest, recallsPastNVsyncTwoVsyncsAheadVrr) { +TEST_F(FrameTargeterTest, recallsPastVsyncTwoVsyncsAheadVrr) { SET_FLAG_FOR_TEST(flags::vrr_config, true); - SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, true); VsyncId vsyncId{222}; TimePoint frameBeginTime(2000ms); constexpr Fps kRefreshRate = 120_Hz; - constexpr Fps kPeakRefreshRate = 240_Hz; + constexpr Fps kVsyncRate = 240_Hz; constexpr Period kPeriod = kRefreshRate.getPeriod(); + constexpr Period kVsyncPeriod = kVsyncRate.getPeriod(); constexpr Duration kFrameDuration = 10ms; FenceTimePtr previousFence = FenceTime::NO_FENCE; - + FenceTimePtr currentFence = FenceTime::NO_FENCE; for (int n = 5; n-- > 0;) { - Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, - kPeakRefreshRate); - const auto fence = frame.end(); - - const auto pastVsyncTime = frameBeginTime + kFrameDuration - 2 * kPeriod; - EXPECT_EQ(target().pastVsyncTime(kPeriod), pastVsyncTime); - EXPECT_EQ(target().presentFenceForPastVsync(kFrameDuration), previousFence); - - frameBeginTime += kPeriod; - previousFence = fence; + Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate); + EXPECT_EQ(expectedSignaledPresentFence(kVsyncPeriod, kPeriod).second.fenceTime, + previousFence); + previousFence = currentFence; + currentFence = frame.end(); } } TEST_F(FrameTargeterTest, doesNotDetectEarlyPresentIfNoFence) { constexpr Period kPeriod = (60_Hz).getPeriod(); - EXPECT_EQ(target().presentFenceForPastVsync(kPeriod), FenceTime::NO_FENCE); - EXPECT_FALSE(wouldPresentEarly(kPeriod)); + EXPECT_EQ(expectedSignaledPresentFence(kPeriod, kPeriod).second.fenceTime, FenceTime::NO_FENCE); + EXPECT_FALSE(wouldPresentEarly(kPeriod, kPeriod)); } TEST_F(FrameTargeterTest, detectsEarlyPresent) { - SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, false); VsyncId vsyncId{333}; TimePoint frameBeginTime(3000ms); constexpr Fps kRefreshRate = 60_Hz; @@ -296,20 +321,57 @@ TEST_F(FrameTargeterTest, detectsEarlyPresent) { // The target is not early while past present fences are pending. for (int n = 3; n-- > 0;) { - const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); - EXPECT_FALSE(wouldPresentEarly(kPeriod)); + { + const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); + } + EXPECT_FALSE(wouldPresentEarly(kPeriod, kPeriod)); + EXPECT_FALSE(target().earliestPresentTime()); + } + + // The target is early if the past present fence was signaled. + { + Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); + const auto fence = frame.end(); + fence->signalForTest(frameBeginTime.ns()); + } + + Frame finalFrame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); + + // `finalFrame` would present early, so it has an earliest present time. + EXPECT_TRUE(wouldPresentEarly(kPeriod, kPeriod)); + ASSERT_NE(std::nullopt, target().earliestPresentTime()); + EXPECT_EQ(*target().earliestPresentTime(), + target().expectedPresentTime() - kPeriod - kHwcMinWorkDuration); +} + +TEST_F(FrameTargeterTest, detectsEarlyPresentAfterLongPeriod) { + VsyncId vsyncId{333}; + TimePoint frameBeginTime(3000ms); + constexpr Fps kRefreshRate = 60_Hz; + constexpr Period kPeriod = kRefreshRate.getPeriod(); + + // The target is not early while past present fences are pending. + for (int n = 3; n-- > 0;) { + { + const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); + } + EXPECT_FALSE(wouldPresentEarly(kPeriod, kPeriod)); EXPECT_FALSE(target().earliestPresentTime()); } // The target is early if the past present fence was signaled. - Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); - const auto fence = frame.end(); - fence->signalForTest(frameBeginTime.ns()); + { + Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); + const auto fence = frame.end(); + fence->signalForTest(frameBeginTime.ns()); + } + + frameBeginTime += 10 * kPeriod; Frame finalFrame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); // `finalFrame` would present early, so it has an earliest present time. - EXPECT_TRUE(wouldPresentEarly(kPeriod)); + EXPECT_TRUE(wouldPresentEarly(kPeriod, kPeriod)); ASSERT_NE(std::nullopt, target().earliestPresentTime()); EXPECT_EQ(*target().earliestPresentTime(), target().expectedPresentTime() - kPeriod - kHwcMinWorkDuration); @@ -318,7 +380,6 @@ TEST_F(FrameTargeterTest, detectsEarlyPresent) { // Same as `detectsEarlyPresent`, above, but verifies that we do not set an earliest present time // when there is expected present time support. TEST_F(FrameTargeterWithExpectedPresentSupportTest, detectsEarlyPresent) { - SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, false); VsyncId vsyncId{333}; TimePoint frameBeginTime(3000ms); constexpr Fps kRefreshRate = 60_Hz; @@ -326,26 +387,30 @@ TEST_F(FrameTargeterWithExpectedPresentSupportTest, detectsEarlyPresent) { // The target is not early while past present fences are pending. for (int n = 3; n-- > 0;) { - const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); - EXPECT_FALSE(wouldPresentEarly(kPeriod)); + { + const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); + } + EXPECT_FALSE(wouldPresentEarly(kPeriod, kPeriod)); EXPECT_FALSE(target().earliestPresentTime()); } // The target is early if the past present fence was signaled. - Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); - const auto fence = frame.end(); - fence->signalForTest(frameBeginTime.ns()); + { + Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); + + const auto fence = frame.end(); + fence->signalForTest(frameBeginTime.ns()); + } Frame finalFrame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); // `finalFrame` would present early, but we have expected present time support, so it has no // earliest present time. - EXPECT_TRUE(wouldPresentEarly(kPeriod)); + EXPECT_TRUE(wouldPresentEarly(kPeriod, kPeriod)); ASSERT_EQ(std::nullopt, target().earliestPresentTime()); } TEST_F(FrameTargeterTest, detectsEarlyPresentTwoVsyncsAhead) { - SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, false); VsyncId vsyncId{444}; TimePoint frameBeginTime(4000ms); constexpr Fps kRefreshRate = 120_Hz; @@ -353,17 +418,21 @@ TEST_F(FrameTargeterTest, detectsEarlyPresentTwoVsyncsAhead) { // The target is not early while past present fences are pending. for (int n = 3; n-- > 0;) { - const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); - EXPECT_FALSE(wouldPresentEarly(kPeriod)); + { + const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); + } + EXPECT_FALSE(wouldPresentEarly(kPeriod, kPeriod)); EXPECT_FALSE(target().earliestPresentTime()); } + { + Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); - Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); - const auto fence = frame.end(); - fence->signalForTest(frameBeginTime.ns()); + const auto fence = frame.end(); + fence->signalForTest(frameBeginTime.ns()); + } // The target is two VSYNCs ahead, so the past present fence is still pending. - EXPECT_FALSE(wouldPresentEarly(kPeriod)); + EXPECT_FALSE(wouldPresentEarly(kPeriod, kPeriod)); EXPECT_FALSE(target().earliestPresentTime()); { const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); } @@ -371,66 +440,21 @@ TEST_F(FrameTargeterTest, detectsEarlyPresentTwoVsyncsAhead) { Frame finalFrame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); // The target is early if the past present fence was signaled. - EXPECT_TRUE(wouldPresentEarly(kPeriod)); + EXPECT_TRUE(wouldPresentEarly(kPeriod, kPeriod)); ASSERT_NE(std::nullopt, target().earliestPresentTime()); EXPECT_EQ(*target().earliestPresentTime(), target().expectedPresentTime() - kPeriod - kHwcMinWorkDuration); } -TEST_F(FrameTargeterTest, detectsEarlyPresentNVsyncsAhead) { - SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, true); - VsyncId vsyncId{444}; - TimePoint frameBeginTime(4000ms); - Fps refreshRate = 120_Hz; - Period period = refreshRate.getPeriod(); - - // The target is not early while past present fences are pending. - for (int n = 5; n-- > 0;) { - const Frame frame(this, vsyncId++, frameBeginTime, 10ms, refreshRate, refreshRate); - EXPECT_FALSE(wouldPresentEarly(period)); - EXPECT_FALSE(target().earliestPresentTime()); - } - - Frame frame(this, vsyncId++, frameBeginTime, 10ms, refreshRate, refreshRate); - auto fence = frame.end(); - frameBeginTime += period; - fence->signalForTest(frameBeginTime.ns()); - - // The target is two VSYNCs ahead, so the past present fence is still pending. - EXPECT_FALSE(wouldPresentEarly(period)); - EXPECT_FALSE(target().earliestPresentTime()); - - { const Frame frame(this, vsyncId++, frameBeginTime, 10ms, refreshRate, refreshRate); } - - Frame oneEarlyPresentFrame(this, vsyncId++, frameBeginTime, 10ms, refreshRate, refreshRate); - // The target is early if the past present fence was signaled. - EXPECT_TRUE(wouldPresentEarly(period)); - ASSERT_NE(std::nullopt, target().earliestPresentTime()); - EXPECT_EQ(*target().earliestPresentTime(), - target().expectedPresentTime() - period - kHwcMinWorkDuration); - - fence = oneEarlyPresentFrame.end(); - frameBeginTime += period; - fence->signalForTest(frameBeginTime.ns()); - - // Change rate to track frame more than 2 vsyncs ahead - refreshRate = 144_Hz; - period = refreshRate.getPeriod(); - Frame onePresentEarlyFrame(this, vsyncId++, frameBeginTime, 16ms, refreshRate, refreshRate); - // The target is not early as last frame as the past frame is tracked for pending. - EXPECT_FALSE(wouldPresentEarly(period)); -} - TEST_F(FrameTargeterTest, detectsEarlyPresentThreeVsyncsAhead) { - SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, false); TimePoint frameBeginTime(5000ms); constexpr Fps kRefreshRate = 144_Hz; constexpr Period kPeriod = kRefreshRate.getPeriod(); - const Frame frame(this, VsyncId{555}, frameBeginTime, 16ms, kRefreshRate, kRefreshRate); + { const Frame frame(this, VsyncId{555}, frameBeginTime, 16ms, kRefreshRate, kRefreshRate); } // The target is more than two VSYNCs ahead, but present fences are not tracked that far back. - EXPECT_TRUE(wouldPresentEarly(kPeriod)); + EXPECT_TRUE(wouldPresentEarly(kPeriod, kPeriod)); EXPECT_TRUE(target().earliestPresentTime()); EXPECT_EQ(*target().earliestPresentTime(), target().expectedPresentTime() - kPeriod - kHwcMinWorkDuration); diff --git a/services/surfaceflinger/ScreenCaptureOutput.cpp b/services/surfaceflinger/ScreenCaptureOutput.cpp index 8bb72b8470..41a9a1bb22 100644 --- a/services/surfaceflinger/ScreenCaptureOutput.cpp +++ b/services/surfaceflinger/ScreenCaptureOutput.cpp @@ -93,6 +93,12 @@ renderengine::DisplaySettings ScreenCaptureOutput::generateClientCompositionDisp if (mEnableLocalTonemapping) { clientCompositionDisplay.tonemapStrategy = renderengine::DisplaySettings::TonemapStrategy::Local; + if (static_cast<ui::PixelFormat>(buffer->getPixelFormat()) == ui::PixelFormat::RGBA_FP16) { + clientCompositionDisplay.targetHdrSdrRatio = + getState().displayBrightnessNits / getState().sdrWhitePointNits; + } else { + clientCompositionDisplay.targetHdrSdrRatio = 1.f; + } } return clientCompositionDisplay; diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index d4d32aa37a..65a0ed3065 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -40,8 +40,10 @@ #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> #include <binder/PermissionCache.h> +#include <com_android_graphics_libgui_flags.h> #include <com_android_graphics_surfaceflinger_flags.h> #include <common/FlagManager.h> +#include <common/trace.h> #include <compositionengine/CompositionEngine.h> #include <compositionengine/CompositionRefreshArgs.h> #include <compositionengine/Display.h> @@ -63,7 +65,7 @@ #include <ftl/fake_guard.h> #include <ftl/future.h> #include <ftl/unit.h> -#include <gui/AidlStatusUtil.h> +#include <gui/AidlUtil.h> #include <gui/BufferQueue.h> #include <gui/DebugEGLImageTracker.h> #include <gui/IProducerListener.h> @@ -71,9 +73,8 @@ #include <gui/LayerState.h> #include <gui/Surface.h> #include <gui/SurfaceComposerClient.h> -#include <gui/TraceUtils.h> #include <hidl/ServiceManagement.h> -#include <layerproto/LayerProtoParser.h> +#include <layerproto/LayerProtoHeader.h> #include <linux/sched/types.h> #include <log/log.h> #include <private/android_filesystem_config.h> @@ -143,6 +144,7 @@ #include "FrontEnd/LayerLog.h" #include "FrontEnd/LayerSnapshot.h" #include "HdrLayerInfoReporter.h" +#include "Jank/JankTracker.h" #include "Layer.h" #include "LayerProtoHelper.h" #include "LayerRenderArea.h" @@ -204,8 +206,6 @@ using ui::Dataspace; using ui::DisplayPrimaries; using ui::RenderIntent; -using KernelIdleTimerController = scheduler::RefreshRateSelector::KernelIdleTimerController; - namespace hal = android::hardware::graphics::composer::hal; namespace { @@ -373,8 +373,6 @@ const String16 sCaptureBlackoutContent("android.permission.CAPTURE_BLACKOUT_CONT const String16 sInternalSystemWindow("android.permission.INTERNAL_SYSTEM_WINDOW"); const String16 sWakeupSurfaceFlinger("android.permission.WAKEUP_SURFACE_FLINGER"); -const char* KERNEL_IDLE_TIMER_PROP = "graphics.display.kernel_idle_timer.enabled"; - // --------------------------------------------------------------------------- int64_t SurfaceFlinger::dispSyncPresentTimeOffset; bool SurfaceFlinger::useHwcForRgbToYuv; @@ -433,7 +431,7 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory, SkipInitializationTag) } SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipInitialization) { - ATRACE_CALL(); + SFTRACE_CALL(); ALOGI("SurfaceFlinger is starting"); hasSyncFramework = running_without_sync_framework(true); @@ -533,13 +531,7 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipI mIgnoreHdrCameraLayers = ignore_hdr_camera_layers(false); - mLayerLifecycleManagerEnabled = - base::GetBoolProperty("persist.debug.sf.enable_layer_lifecycle_manager"s, true); - // These are set by the HWC implementation to indicate that they will use the workarounds. - mIsHotplugErrViaNegVsync = - base::GetBoolProperty("debug.sf.hwc_hotplug_error_via_neg_vsync"s, false); - mIsHdcpViaNegVsync = base::GetBoolProperty("debug.sf.hwc_hdcp_via_neg_vsync"s, false); } @@ -728,6 +720,7 @@ void SurfaceFlinger::bootFinished() { mBootFinished = true; FlagManager::getMutableInstance().markBootCompleted(); + ::tracing_perfetto::registerWithPerfetto(); mInitBootPropsFuture.wait(); mRenderEnginePrimeCacheFuture.wait(); @@ -854,13 +847,15 @@ renderengine::RenderEngine::BlurAlgorithm chooseBlurAlgorithm(bool supportsBlur) auto const algorithm = base::GetProperty(PROPERTY_DEBUG_RENDERENGINE_BLUR_ALGORITHM, ""); if (algorithm == "gaussian") { return renderengine::RenderEngine::BlurAlgorithm::GAUSSIAN; + } else if (algorithm == "kawase2") { + return renderengine::RenderEngine::BlurAlgorithm::KAWASE_DUAL_FILTER; } else { return renderengine::RenderEngine::BlurAlgorithm::KAWASE; } } void SurfaceFlinger::init() FTL_FAKE_GUARD(kMainThreadContext) { - ATRACE_CALL(); + SFTRACE_CALL(); ALOGI( "SurfaceFlinger's main thread ready to run. " "Initializing graphics H/W..."); addTransactionReadyFilters(); @@ -912,9 +907,11 @@ void SurfaceFlinger::init() FTL_FAKE_GUARD(kMainThreadContext) { LOG_ALWAYS_FATAL_IF(!configureLocked(), "Initial display configuration failed: HWC did not hotplug"); + mActiveDisplayId = getPrimaryDisplayIdLocked(); + // Commit primary display. sp<const DisplayDevice> display; - if (const auto indexOpt = mCurrentState.getDisplayIndex(getPrimaryDisplayIdLocked())) { + if (const auto indexOpt = mCurrentState.getDisplayIndex(mActiveDisplayId)) { const auto& displays = mCurrentState.displays; const auto& token = displays.keyAt(*indexOpt); @@ -1006,6 +1003,8 @@ void SurfaceFlinger::init() FTL_FAKE_GUARD(kMainThreadContext) { // which we maintain for backwards compatibility. config.cacheUltraHDR = base::GetBoolProperty("ro.surface_flinger.prime_shader_cache.ultrahdr"s, false); + config.cacheEdgeExtension = + base::GetBoolProperty("debug.sf.edge_extension_shader"s, true); return getRenderEngine().primeCache(config); }); @@ -1280,20 +1279,14 @@ status_t SurfaceFlinger::getDisplayStats(const sp<IBinder>& displayToken, return BAD_VALUE; } + // TODO: b/277364366 - Require a display token from clients and remove fallback to pacesetter. std::optional<PhysicalDisplayId> displayIdOpt; - { + if (displayToken) { Mutex::Autolock lock(mStateLock); - if (displayToken) { - displayIdOpt = getPhysicalDisplayIdLocked(displayToken); - if (!displayIdOpt) { - ALOGW("%s: Invalid physical display token %p", __func__, displayToken.get()); - return NAME_NOT_FOUND; - } - } else { - // TODO (b/277364366): Clients should be updated to pass in the display they - // want, rather than us picking an arbitrary one (the active display, in this - // case). - displayIdOpt = mActiveDisplayId; + displayIdOpt = getPhysicalDisplayIdLocked(displayToken); + if (!displayIdOpt) { + ALOGW("%s: Invalid physical display token %p", __func__, displayToken.get()); + return NAME_NOT_FOUND; } } @@ -1312,7 +1305,7 @@ void SurfaceFlinger::setDesiredMode(display::DisplayModeRequest&& desiredMode) { const auto mode = desiredMode.mode; const auto displayId = mode.modePtr->getPhysicalDisplayId(); - ATRACE_NAME(ftl::Concat(__func__, ' ', displayId.value).c_str()); + SFTRACE_NAME(ftl::Concat(__func__, ' ', displayId.value).c_str()); const bool emitEvent = desiredMode.emitEvent; @@ -1340,22 +1333,16 @@ void SurfaceFlinger::setDesiredMode(display::DisplayModeRequest&& desiredMode) { // VsyncController model is locked. mScheduler->modulateVsync(displayId, &VsyncModulator::onRefreshRateChangeInitiated); - if (displayId == mActiveDisplayId) { - mScheduler->updatePhaseConfiguration(mode.fps); - } - + mScheduler->updatePhaseConfiguration(displayId, mode.fps); mScheduler->setModeChangePending(true); break; } case DesiredModeAction::InitiateRenderRateSwitch: mScheduler->setRenderRate(displayId, mode.fps, /*applyImmediately*/ false); - - if (displayId == mActiveDisplayId) { - mScheduler->updatePhaseConfiguration(mode.fps); - } + mScheduler->updatePhaseConfiguration(displayId, mode.fps); if (emitEvent) { - dispatchDisplayModeChangeEvent(displayId, mode); + mScheduler->onDisplayModeChanged(displayId, mode); } break; case DesiredModeAction::None: @@ -1365,7 +1352,7 @@ void SurfaceFlinger::setDesiredMode(display::DisplayModeRequest&& desiredMode) { status_t SurfaceFlinger::setActiveModeFromBackdoor(const sp<display::DisplayToken>& displayToken, DisplayModeId modeId, Fps minFps, Fps maxFps) { - ATRACE_CALL(); + SFTRACE_CALL(); if (!displayToken) { return BAD_VALUE; @@ -1417,7 +1404,7 @@ status_t SurfaceFlinger::setActiveModeFromBackdoor(const sp<display::DisplayToke // TODO: b/241285876 - Restore thread safety analysis once mStateLock below is unconditional. [[clang::no_thread_safety_analysis]] void SurfaceFlinger::finalizeDisplayModeChange(PhysicalDisplayId displayId) { - ATRACE_NAME(ftl::Concat(__func__, ' ', displayId.value).c_str()); + SFTRACE_NAME(ftl::Concat(__func__, ' ', displayId.value).c_str()); const auto pendingModeOpt = mDisplayModeController.getPendingMode(displayId); if (!pendingModeOpt) { @@ -1447,12 +1434,10 @@ void SurfaceFlinger::finalizeDisplayModeChange(PhysicalDisplayId displayId) { mDisplayModeController.finalizeModeChange(displayId, activeMode.modePtr->getId(), activeMode.modePtr->getVsyncRate(), activeMode.fps); - if (displayId == mActiveDisplayId) { - mScheduler->updatePhaseConfiguration(activeMode.fps); - } + mScheduler->updatePhaseConfiguration(displayId, activeMode.fps); if (pendingModeOpt->emitEvent) { - dispatchDisplayModeChangeEvent(displayId, activeMode); + mScheduler->onDisplayModeChanged(displayId, activeMode); } } @@ -1473,17 +1458,13 @@ void SurfaceFlinger::applyActiveMode(PhysicalDisplayId displayId) { constexpr bool kAllowToEnable = true; mScheduler->resyncToHardwareVsync(displayId, kAllowToEnable, std::move(activeModePtr).take()); - mScheduler->setRenderRate(displayId, renderFps, /*applyImmediately*/ true); - if (displayId == mActiveDisplayId) { - mScheduler->updatePhaseConfiguration(renderFps); - } + mScheduler->setRenderRate(displayId, renderFps, /*applyImmediately*/ true); + mScheduler->updatePhaseConfiguration(displayId, renderFps); } void SurfaceFlinger::initiateDisplayModeChanges() { - ATRACE_CALL(); - - std::optional<PhysicalDisplayId> displayToUpdateImmediately; + SFTRACE_CALL(); for (const auto& [displayId, physical] : mPhysicalDisplays) { auto desiredModeOpt = mDisplayModeController.getDesiredMode(displayId); @@ -1538,21 +1519,14 @@ void SurfaceFlinger::initiateDisplayModeChanges() { if (outTimeline.refreshRequired) { scheduleComposite(FrameHint::kNone); } else { - // TODO(b/255635711): Remove `displayToUpdateImmediately` to `finalizeDisplayModeChange` - // for all displays. This was only needed when the loop iterated over `mDisplays` rather - // than `mPhysicalDisplays`. - displayToUpdateImmediately = displayId; - } - } - - if (displayToUpdateImmediately) { - const auto displayId = *displayToUpdateImmediately; - finalizeDisplayModeChange(displayId); + // HWC has requested to apply the mode change immediately rather than on the next frame. + finalizeDisplayModeChange(displayId); - const auto desiredModeOpt = mDisplayModeController.getDesiredMode(displayId); - if (desiredModeOpt && - mDisplayModeController.getActiveMode(displayId) == desiredModeOpt->mode) { - applyActiveMode(displayId); + const auto desiredModeOpt = mDisplayModeController.getDesiredMode(displayId); + if (desiredModeOpt && + mDisplayModeController.getActiveMode(displayId) == desiredModeOpt->mode) { + applyActiveMode(displayId); + } } } } @@ -1560,7 +1534,7 @@ void SurfaceFlinger::initiateDisplayModeChanges() { void SurfaceFlinger::disableExpensiveRendering() { const char* const whence = __func__; auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) { - ATRACE_NAME(whence); + SFTRACE_NAME(whence); if (mPowerAdvisor->isUsingExpensiveRendering()) { for (const auto& [_, display] : mDisplays) { constexpr bool kDisable = false; @@ -2171,12 +2145,12 @@ sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection( return mScheduler->createDisplayEventConnection(cycle, eventRegistration, layerHandle); } -void SurfaceFlinger::scheduleCommit(FrameHint hint) { +void SurfaceFlinger::scheduleCommit(FrameHint hint, Duration workDurationSlack) { if (hint == FrameHint::kActive) { mScheduler->resetIdleTimer(); } mPowerAdvisor->notifyDisplayUpdateImminentAndCpuReset(); - mScheduler->scheduleFrame(); + mScheduler->scheduleFrame(workDurationSlack); } void SurfaceFlinger::scheduleComposite(FrameHint hint) { @@ -2193,41 +2167,25 @@ void SurfaceFlinger::scheduleSample() { static_cast<void>(mScheduler->schedule([this] { sample(); })); } -nsecs_t SurfaceFlinger::getVsyncPeriodFromHWC() const { - if (const auto display = getDefaultDisplayDeviceLocked()) { - return display->getVsyncPeriodFromHWC(); - } - - return 0; -} - void SurfaceFlinger::onComposerHalVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp, std::optional<hal::VsyncPeriodNanos> vsyncPeriod) { if (FlagManager::getInstance().connected_display() && timestamp < 0 && vsyncPeriod.has_value()) { - // use ~0 instead of -1 as AidlComposerHal.cpp passes the param as unsigned int32 - if (mIsHotplugErrViaNegVsync && vsyncPeriod.value() == ~0) { - const auto errorCode = static_cast<int32_t>(-timestamp); - ALOGD("%s: Hotplug error %d for display %" PRIu64, __func__, errorCode, hwcDisplayId); - mScheduler->dispatchHotplugError(errorCode); - return; - } - if (mIsHdcpViaNegVsync && vsyncPeriod.value() == ~1) { const int32_t value = static_cast<int32_t>(-timestamp); // one byte is good enough to encode android.hardware.drm.HdcpLevel const int32_t maxLevel = (value >> 8) & 0xFF; const int32_t connectedLevel = value & 0xFF; - ALOGD("%s: HDCP levels changed (connected=%d, max=%d) for display %" PRIu64, __func__, - connectedLevel, maxLevel, hwcDisplayId); + ALOGD("%s: HDCP levels changed (connected=%d, max=%d) for hwcDisplayId %" PRIu64, + __func__, connectedLevel, maxLevel, hwcDisplayId); updateHdcpLevels(hwcDisplayId, connectedLevel, maxLevel); return; } } - ATRACE_NAME(vsyncPeriod - ? ftl::Concat(__func__, ' ', hwcDisplayId, ' ', *vsyncPeriod, "ns").c_str() - : ftl::Concat(__func__, ' ', hwcDisplayId).c_str()); + SFTRACE_NAME(vsyncPeriod + ? ftl::Concat(__func__, ' ', hwcDisplayId, ' ', *vsyncPeriod, "ns").c_str() + : ftl::Concat(__func__, ' ', hwcDisplayId).c_str()); Mutex::Autolock lock(mStateLock); if (const auto displayIdOpt = getHwComposer().onVsync(hwcDisplayId, timestamp)) { @@ -2259,7 +2217,7 @@ void SurfaceFlinger::onComposerHalHotplugEvent(hal::HWDisplayId hwcDisplayId, if (FlagManager::getInstance().hotplug2()) { // TODO(b/311403559): use enum type instead of int const auto errorCode = static_cast<int32_t>(event); - ALOGD("%s: Hotplug error %d for display %" PRIu64, __func__, errorCode, hwcDisplayId); + ALOGD("%s: Hotplug error %d for hwcDisplayId %" PRIu64, __func__, errorCode, hwcDisplayId); mScheduler->dispatchHotplugError(errorCode); } } @@ -2285,12 +2243,12 @@ void SurfaceFlinger::onComposerHalRefresh(hal::HWDisplayId) { } void SurfaceFlinger::onComposerHalVsyncIdle(hal::HWDisplayId) { - ATRACE_CALL(); + SFTRACE_CALL(); mScheduler->forceNextResync(); } void SurfaceFlinger::onRefreshRateChangedDebug(const RefreshRateChangedDebugData& data) { - ATRACE_CALL(); + SFTRACE_CALL(); const char* const whence = __func__; static_cast<void>(mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD( kMainThreadContext) { @@ -2299,7 +2257,7 @@ void SurfaceFlinger::onRefreshRateChangedDebug(const RefreshRateChangedDebugData const Fps refreshRate = Fps::fromPeriodNsecs( getHwComposer().getComposer()->isVrrSupported() ? data.refreshPeriodNanos : data.vsyncPeriodNanos); - ATRACE_FORMAT("%s refresh rate = %d", whence, refreshRate.getIntValue()); + SFTRACE_FORMAT("%s refresh rate = %d", whence, refreshRate.getIntValue()); const auto renderRate = mDisplayModeController.getActiveMode(*displayIdOpt).fps; constexpr bool kSetByHwc = true; @@ -2309,6 +2267,18 @@ void SurfaceFlinger::onRefreshRateChangedDebug(const RefreshRateChangedDebugData })); } +void SurfaceFlinger::onComposerHalHdcpLevelsChanged(hal::HWDisplayId hwcDisplayId, + const HdcpLevels& levels) { + if (FlagManager::getInstance().hdcp_level_hal()) { + // TODO(b/362270040): propagate enum constants + const int32_t maxLevel = static_cast<int32_t>(levels.maxLevel); + const int32_t connectedLevel = static_cast<int32_t>(levels.connectedLevel); + ALOGD("%s: HDCP levels changed (connected=%d, max=%d) for hwcDisplayId %" PRIu64, __func__, + connectedLevel, maxLevel, hwcDisplayId); + updateHdcpLevels(hwcDisplayId, connectedLevel, maxLevel); + } +} + void SurfaceFlinger::configure() { Mutex::Autolock lock(mStateLock); if (configureLocked()) { @@ -2316,37 +2286,6 @@ void SurfaceFlinger::configure() { } } -bool SurfaceFlinger::updateLayerSnapshotsLegacy(VsyncId vsyncId, nsecs_t frameTimeNs, - bool flushTransactions, - bool& outTransactionsAreEmpty) { - ATRACE_CALL(); - frontend::Update update; - if (flushTransactions) { - update = flushLifecycleUpdates(); - if (mTransactionTracing) { - mTransactionTracing->addCommittedTransactions(ftl::to_underlying(vsyncId), frameTimeNs, - update, mFrontEndDisplayInfos, - mFrontEndDisplayInfosChanged); - } - } - - bool needsTraversal = false; - if (flushTransactions) { - needsTraversal |= commitMirrorDisplays(vsyncId); - needsTraversal |= commitCreatedLayers(vsyncId, update.layerCreatedStates); - needsTraversal |= applyTransactions(update.transactions); - } - outTransactionsAreEmpty = !needsTraversal; - const bool shouldCommit = (getTransactionFlags() & ~eTransactionFlushNeeded) || needsTraversal; - if (shouldCommit) { - commitTransactionsLegacy(); - } - - bool mustComposite = latchBuffers() || shouldCommit; - updateLayerGeometry(); - return mustComposite; -} - void SurfaceFlinger::updateLayerHistory(nsecs_t now) { for (const auto& snapshot : mLayerSnapshotBuilder.getSnapshots()) { using Changes = frontend::RequestedLayerState::Changes; @@ -2418,10 +2357,10 @@ void SurfaceFlinger::updateLayerHistory(nsecs_t now) { bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, nsecs_t frameTimeNs, bool flushTransactions, bool& outTransactionsAreEmpty) { using Changes = frontend::RequestedLayerState::Changes; - ATRACE_CALL(); + SFTRACE_CALL(); frontend::Update update; if (flushTransactions) { - ATRACE_NAME("TransactionHandler:flushTransactions"); + SFTRACE_NAME("TransactionHandler:flushTransactions"); // Locking: // 1. to prevent onHandleDestroyed from being called while the state lock is held, // we must keep a copy of the transactions (specifically the composer @@ -2435,7 +2374,7 @@ bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, nsecs_t frameTimeNs, { // TODO(b/238781169) lockless queue this and keep order. std::scoped_lock<std::mutex> lock(mCreatedLayersLock); - update.layerCreatedStates = std::move(mCreatedLayers); + update.legacyLayers = std::move(mCreatedLayers); mCreatedLayers.clear(); update.newLayers = std::move(mNewLayers); mNewLayers.clear(); @@ -2454,11 +2393,8 @@ bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, nsecs_t frameTimeNs, } mLayerLifecycleManager.applyTransactions(update.transactions); mLayerLifecycleManager.onHandlesDestroyed(update.destroyedHandles); - for (auto& legacyLayer : update.layerCreatedStates) { - sp<Layer> layer = legacyLayer.layer.promote(); - if (layer) { - mLegacyLayers[layer->sequence] = layer; - } + for (auto& legacyLayer : update.legacyLayers) { + mLegacyLayers[legacyLayer->sequence] = legacyLayer; } mLayerHierarchyBuilder.update(mLayerLifecycleManager); } @@ -2473,7 +2409,7 @@ bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, nsecs_t frameTimeNs, mustComposite |= applyAndCommitDisplayTransactionStatesLocked(update.transactions); { - ATRACE_NAME("LayerSnapshotBuilder:update"); + SFTRACE_NAME("LayerSnapshotBuilder:update"); frontend::LayerSnapshotBuilder::Args args{.root = mLayerHierarchyBuilder.getHierarchy(), .layerLifecycleManager = mLayerLifecycleManager, @@ -2514,7 +2450,7 @@ bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, nsecs_t frameTimeNs, } bool newDataLatched = false; - ATRACE_NAME("DisplayCallbackAndStatsUpdates"); + SFTRACE_NAME("DisplayCallbackAndStatsUpdates"); mustComposite |= applyTransactionsLocked(update.transactions); traverseLegacyLayers([&](Layer* layer) { layer->commitTransaction(); }); const nsecs_t latchTime = systemTime(); @@ -2562,17 +2498,25 @@ bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, nsecs_t frameTimeNs, it->second->latchBufferImpl(unused, latchTime, bgColorOnly); newDataLatched = true; - mLayersWithQueuedFrames.emplace(it->second); + frontend::LayerSnapshot* snapshot = mLayerSnapshotBuilder.getSnapshot(it->second->sequence); + gui::GameMode gameMode = (snapshot) ? snapshot->gameMode : gui::GameMode::Unsupported; + mLayersWithQueuedFrames.emplace(it->second, gameMode); mLayersIdsWithQueuedFrames.emplace(it->second->sequence); } updateLayerHistory(latchTime); - mLayerSnapshotBuilder.forEachVisibleSnapshot([&](const frontend::LayerSnapshot& snapshot) { - if (mLayersIdsWithQueuedFrames.find(snapshot.path.id) == mLayersIdsWithQueuedFrames.end()) - return; - Region visibleReg; - visibleReg.set(snapshot.transformedBoundsWithoutTransparentRegion); - invalidateLayerStack(snapshot.outputFilter, visibleReg); + mLayerSnapshotBuilder.forEachSnapshot([&](const frontend::LayerSnapshot& snapshot) { + // update output dirty region if we have a queued buffer that is visible or a snapshot + // recently became invisible + // TODO(b/360050020) investigate if we need to update dirty region when layer color changes + if ((snapshot.isVisible && + (mLayersIdsWithQueuedFrames.find(snapshot.path.id) != + mLayersIdsWithQueuedFrames.end())) || + (!snapshot.isVisible && snapshot.changes.test(Changes::Visibility))) { + Region visibleReg; + visibleReg.set(snapshot.transformedBoundsWithoutTransparentRegion); + invalidateLayerStack(snapshot.outputFilter, visibleReg); + } }); for (auto& destroyedLayer : mLayerLifecycleManager.getDestroyedLayers()) { @@ -2580,7 +2524,7 @@ bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, nsecs_t frameTimeNs, } { - ATRACE_NAME("LLM:commitChanges"); + SFTRACE_NAME("LLM:commitChanges"); mLayerLifecycleManager.commitChanges(); } @@ -2603,7 +2547,7 @@ bool SurfaceFlinger::commit(PhysicalDisplayId pacesetterId, const scheduler::FrameTarget& pacesetterFrameTarget = *frameTargets.get(pacesetterId)->get(); const VsyncId vsyncId = pacesetterFrameTarget.vsyncId(); - ATRACE_NAME(ftl::Concat(__func__, ' ', ftl::to_underlying(vsyncId)).c_str()); + SFTRACE_NAME(ftl::Concat(__func__, ' ', ftl::to_underlying(vsyncId)).c_str()); if (pacesetterFrameTarget.didMissFrame()) { mTimeStats->incrementMissedFrames(); @@ -2630,13 +2574,16 @@ bool SurfaceFlinger::commit(PhysicalDisplayId pacesetterId, } } - if (pacesetterFrameTarget.isFramePending()) { + if (pacesetterFrameTarget.wouldBackpressureHwc()) { if (mBackpressureGpuComposition || pacesetterFrameTarget.didMissHwcFrame()) { if (FlagManager::getInstance().vrr_config()) { mScheduler->getVsyncSchedule()->getTracker().onFrameMissed( pacesetterFrameTarget.expectedPresentTime()); } - scheduleCommit(FrameHint::kNone); + const Duration slack = FlagManager::getInstance().allow_n_vsyncs_in_targeter() + ? TimePoint::now() - pacesetterFrameTarget.frameBeginTime() + : Duration::fromNs(0); + scheduleCommit(FrameHint::kNone, slack); return false; } } @@ -2683,11 +2630,8 @@ bool SurfaceFlinger::commit(PhysicalDisplayId pacesetterId, const bool flushTransactions = clearTransactionFlags(eTransactionFlushNeeded); bool transactionsAreEmpty = false; - if (mLayerLifecycleManagerEnabled) { - mustComposite |= - updateLayerSnapshots(vsyncId, pacesetterFrameTarget.frameBeginTime().ns(), - flushTransactions, transactionsAreEmpty); - } + mustComposite |= updateLayerSnapshots(vsyncId, pacesetterFrameTarget.frameBeginTime().ns(), + flushTransactions, transactionsAreEmpty); // Tell VsyncTracker that we are going to present this frame before scheduling // setTransactionFlags which will schedule another SF frame. This was if the tracker @@ -2721,9 +2665,7 @@ bool SurfaceFlinger::commit(PhysicalDisplayId pacesetterId, mUpdateAttachedChoreographer = false; Mutex::Autolock lock(mStateLock); - mScheduler->chooseRefreshRateForContent(mLayerLifecycleManagerEnabled - ? &mLayerHierarchyBuilder.getHierarchy() - : nullptr, + mScheduler->chooseRefreshRateForContent(&mLayerHierarchyBuilder.getHierarchy(), updateAttachedChoreographer); if (FlagManager::getInstance().connected_display()) { @@ -2756,7 +2698,7 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( frameTargeters.get(pacesetterId)->get()->target(); const VsyncId vsyncId = pacesetterTarget.vsyncId(); - ATRACE_NAME(ftl::Concat(__func__, ' ', ftl::to_underlying(vsyncId)).c_str()); + SFTRACE_NAME(ftl::Concat(__func__, ' ', ftl::to_underlying(vsyncId)).c_str()); compositionengine::CompositionRefreshArgs refreshArgs; refreshArgs.powerCallback = this; @@ -2793,17 +2735,14 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( const bool updateTaskMetadata = mCompositionEngine->getFeatureFlags().test( compositionengine::Feature::kSnapshotLayerMetadata); - if (updateTaskMetadata && (mVisibleRegionsDirty || mLayerMetadataSnapshotNeeded)) { - updateLayerMetadataSnapshot(); - mLayerMetadataSnapshotNeeded = false; - } refreshArgs.bufferIdsToUncache = std::move(mBufferIdsToUncache); if (!FlagManager::getInstance().ce_fence_promise()) { refreshArgs.layersWithQueuedFrames.reserve(mLayersWithQueuedFrames.size()); - for (auto& layer : mLayersWithQueuedFrames) { - if (const auto& layerFE = layer->getCompositionEngineLayerFE()) + for (auto& [layer, _] : mLayersWithQueuedFrames) { + if (const auto& layerFE = layer->getCompositionEngineLayerFE( + {static_cast<uint32_t>(layer->sequence)})) refreshArgs.layersWithQueuedFrames.push_back(layerFE); } } @@ -2844,7 +2783,7 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( constexpr bool kCursorOnly = false; const auto layers = moveSnapshotsToCompositionArgs(refreshArgs, kCursorOnly); - if (mLayerLifecycleManagerEnabled && !mVisibleRegionsDirty) { + if (!mVisibleRegionsDirty) { for (const auto& [token, display] : FTL_FAKE_GUARD(mStateLock, mDisplays)) { auto compositionDisplay = display->getCompositionDisplay(); if (!compositionDisplay->getState().isEnabled) continue; @@ -2878,8 +2817,9 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( } refreshArgs.layersWithQueuedFrames.reserve(mLayersWithQueuedFrames.size()); - for (auto& layer : mLayersWithQueuedFrames) { - if (const auto& layerFE = layer->getCompositionEngineLayerFE()) { + for (auto& [layer, _] : mLayersWithQueuedFrames) { + if (const auto& layerFE = layer->getCompositionEngineLayerFE( + {static_cast<uint32_t>(layer->sequence)})) { refreshArgs.layersWithQueuedFrames.push_back(layerFE); // Some layers are not displayed and do not yet have a future release fence if (layerFE->getReleaseFencePromiseStatus() == @@ -2910,9 +2850,7 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( for (auto [layer, layerFE] : layers) { CompositionResult compositionResult{layerFE->stealCompositionResult()}; for (auto& [releaseFence, layerStack] : compositionResult.releaseFences) { - Layer* clonedFrom = layer->getClonedFrom().get(); - auto owningLayer = clonedFrom ? clonedFrom : layer; - owningLayer->onLayerDisplayed(std::move(releaseFence), layerStack); + layer->onLayerDisplayed(std::move(releaseFence), layerStack); } if (compositionResult.lastClientCompositionFence) { layer->setWasClientComposed(compositionResult.lastClientCompositionFence); @@ -2920,6 +2858,7 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( } } + SFTRACE_NAME("postComposition"); mTimeStats->recordFrameDuration(pacesetterTarget.frameBeginTime().ns(), systemTime()); // Send a power hint after presentation is finished. @@ -2927,6 +2866,9 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( // Now that the current frame has been presented above, PowerAdvisor needs the present time // of the previous frame (whose fence is signaled by now) to determine how long the HWC had // waited on that fence to retire before presenting. + // TODO(b/355238809) `presentFenceForPreviousFrame` might not always be signaled (e.g. on + // devices + // where HWC does not block on the previous present fence). Revise this assumtion. const auto& previousPresentFence = pacesetterTarget.presentFenceForPreviousFrame(); mPowerAdvisor->setSfPresentTiming(TimePoint::fromNs(previousPresentFence->getSignalTime()), @@ -3014,21 +2956,6 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( return resultsPerDisplay; } -void SurfaceFlinger::updateLayerGeometry() { - ATRACE_CALL(); - - if (mVisibleRegionsDirty) { - computeLayerBounds(); - } - - for (auto& layer : mLayersPendingRefresh) { - Region visibleReg; - visibleReg.set(layer->getScreenBounds()); - invalidateLayerStack(layer->getOutputFilter(), visibleReg); - } - mLayersPendingRefresh.clear(); -} - bool SurfaceFlinger::isHdrLayer(const frontend::LayerSnapshot& snapshot) const { // Even though the camera layer may be using an HDR transfer function or otherwise be "HDR" // the device may need to avoid boosting the brightness as a result of these layers to @@ -3099,7 +3026,7 @@ ui::Rotation SurfaceFlinger::getPhysicalDisplayOrientation(DisplayId displayId, void SurfaceFlinger::onCompositionPresented(PhysicalDisplayId pacesetterId, const scheduler::FrameTargeters& frameTargeters, nsecs_t presentStartTime) { - ATRACE_CALL(); + SFTRACE_CALL(); ui::PhysicalDisplayMap<PhysicalDisplayId, std::shared_ptr<FenceTime>> presentFences; ui::PhysicalDisplayMap<PhysicalDisplayId, const sp<Fence>> gpuCompositionDoneFences; @@ -3195,10 +3122,10 @@ void SurfaceFlinger::onCompositionPresented(PhysicalDisplayId pacesetterId, } mLayersWithBuffersRemoved.clear(); - for (const auto& layer: mLayersWithQueuedFrames) { + for (const auto& [layer, gameMode] : mLayersWithQueuedFrames) { layer->onCompositionPresented(pacesetterDisplay.get(), pacesetterGpuCompositionDoneFenceTime, - pacesetterPresentFenceTime, compositorTiming); + pacesetterPresentFenceTime, compositorTiming, gameMode); layer->releasePendingBuffer(presentTime.ns()); } @@ -3259,28 +3186,20 @@ void SurfaceFlinger::onCompositionPresented(PhysicalDisplayId pacesetterId, } }; - if (mLayerLifecycleManagerEnabled) { - mLayerSnapshotBuilder.forEachVisibleSnapshot( - [&, compositionDisplay = compositionDisplay]( - std::unique_ptr<frontend::LayerSnapshot>& - snapshot) FTL_FAKE_GUARD(kMainThreadContext) { - auto it = mLegacyLayers.find(snapshot->sequence); - LLOG_ALWAYS_FATAL_WITH_TRACE_IF(it == mLegacyLayers.end(), - "Couldnt find layer object for %s", - snapshot->getDebugString().c_str()); - auto& legacyLayer = it->second; - sp<LayerFE> layerFe = - legacyLayer->getCompositionEngineLayerFE(snapshot->path); - - updateInfoFn(compositionDisplay, *snapshot, layerFe); - }); - } else { - mDrawingState.traverse([&, compositionDisplay = compositionDisplay](Layer* layer) { - const auto layerFe = layer->getCompositionEngineLayerFE(); - const frontend::LayerSnapshot& snapshot = *layer->getLayerSnapshot(); - updateInfoFn(compositionDisplay, snapshot, layerFe); - }); - } + mLayerSnapshotBuilder.forEachVisibleSnapshot( + [&, compositionDisplay = compositionDisplay]( + std::unique_ptr<frontend::LayerSnapshot>& snapshot) + FTL_FAKE_GUARD(kMainThreadContext) { + auto it = mLegacyLayers.find(snapshot->sequence); + LLOG_ALWAYS_FATAL_WITH_TRACE_IF(it == mLegacyLayers.end(), + "Couldnt find layer object for %s", + snapshot->getDebugString().c_str()); + auto& legacyLayer = it->second; + sp<LayerFE> layerFe = + legacyLayer->getCompositionEngineLayerFE(snapshot->path); + + updateInfoFn(compositionDisplay, *snapshot, layerFe); + }); listener->dispatchHdrLayerInfo(info); } } @@ -3326,9 +3245,8 @@ void SurfaceFlinger::onCompositionPresented(PhysicalDisplayId pacesetterId, if (!layer->hasTrustedPresentationListener()) { return; } - const frontend::LayerSnapshot* snapshot = mLayerLifecycleManagerEnabled - ? mLayerSnapshotBuilder.getSnapshot(layer->sequence) - : layer->getLayerSnapshot(); + const frontend::LayerSnapshot* snapshot = + mLayerSnapshotBuilder.getSnapshot(layer->sequence); std::optional<const DisplayDevice*> displayOpt = std::nullopt; if (snapshot) { displayOpt = layerStackToDisplay.get(snapshot->outputFilter.layerStack); @@ -3340,48 +3258,18 @@ void SurfaceFlinger::onCompositionPresented(PhysicalDisplayId pacesetterId, }); } - // Even though ATRACE_INT64 already checks if tracing is enabled, it doesn't prevent the + // Even though SFTRACE_INT64 already checks if tracing is enabled, it doesn't prevent the // side-effect of getTotalSize(), so we check that again here - if (ATRACE_ENABLED()) { + if (SFTRACE_ENABLED()) { // getTotalSize returns the total number of buffers that were allocated by SurfaceFlinger - ATRACE_INT64("Total Buffer Size", GraphicBufferAllocator::get().getTotalSize()); + SFTRACE_INT64("Total Buffer Size", GraphicBufferAllocator::get().getTotalSize()); } logFrameStats(presentTime); } -FloatRect SurfaceFlinger::getMaxDisplayBounds() { - const ui::Size maxSize = [this] { - ftl::FakeGuard guard(mStateLock); - - // The LayerTraceGenerator tool runs without displays. - if (mDisplays.empty()) return ui::Size{5000, 5000}; - - return std::accumulate(mDisplays.begin(), mDisplays.end(), ui::kEmptySize, - [](ui::Size size, const auto& pair) -> ui::Size { - const auto& display = pair.second; - return {std::max(size.getWidth(), display->getWidth()), - std::max(size.getHeight(), display->getHeight())}; - }); - }(); - - // Ignore display bounds for now since they will be computed later. Use a large Rect bound - // to ensure it's bigger than an actual display will be. - const float xMax = maxSize.getWidth() * 10.f; - const float yMax = maxSize.getHeight() * 10.f; - - return {-xMax, -yMax, xMax, yMax}; -} - -void SurfaceFlinger::computeLayerBounds() { - const FloatRect maxBounds = getMaxDisplayBounds(); - for (const auto& layer : mDrawingState.layersSortedByZ) { - layer->computeBounds(maxBounds, ui::Transform(), 0.f /* shadowRadius */); - } -} - void SurfaceFlinger::commitTransactions() { - ATRACE_CALL(); + SFTRACE_CALL(); mDebugInTransaction = systemTime(); // Here we're guaranteed that some transaction flags are set @@ -3393,28 +3281,6 @@ void SurfaceFlinger::commitTransactions() { mDebugInTransaction = 0; } -void SurfaceFlinger::commitTransactionsLegacy() { - ATRACE_CALL(); - - // Keep a copy of the drawing state (that is going to be overwritten - // by commitTransactionsLocked) outside of mStateLock so that the side - // effects of the State assignment don't happen with mStateLock held, - // which can cause deadlocks. - State drawingState(mDrawingState); - - Mutex::Autolock lock(mStateLock); - mDebugInTransaction = systemTime(); - - // Here we're guaranteed that some transaction flags are set - // so we can call commitTransactionsLocked unconditionally. - // We clear the flags with mStateLock held to guarantee that - // mCurrentState won't change until the transaction is committed. - mScheduler->modulateVsync({}, &VsyncModulator::onTransactionCommit); - commitTransactionsLocked(clearTransactionFlags(eTransactionMask)); - - mDebugInTransaction = 0; -} - std::pair<DisplayModes, DisplayModePtr> SurfaceFlinger::loadDisplayModes( PhysicalDisplayId displayId) const { std::vector<HWComposer::HWCDisplayMode> hwcModes; @@ -3673,7 +3539,12 @@ std::optional<DisplayModeId> SurfaceFlinger::processHotplugConnect(PhysicalDispl state.physical = {.id = displayId, .hwcDisplayId = hwcDisplayId, .activeMode = std::move(activeMode)}; - state.isSecure = connectionType == ui::DisplayConnectionType::Internal; + if (mIsHdcpViaNegVsync) { + state.isSecure = connectionType == ui::DisplayConnectionType::Internal; + } else { + // TODO(b/349703362): Remove this when HDCP aidl API becomes ready + state.isSecure = true; // All physical displays are currently considered secure. + } state.isProtected = true; state.displayName = std::move(info.name); @@ -3697,16 +3568,6 @@ void SurfaceFlinger::processHotplugDisconnect(PhysicalDisplayId displayId, mPhysicalDisplays.erase(displayId); } -void SurfaceFlinger::dispatchDisplayModeChangeEvent(PhysicalDisplayId displayId, - const scheduler::FrameRateMode& mode) { - // TODO(b/255635821): Merge code paths and move to Scheduler. - const auto onDisplayModeChanged = displayId == mActiveDisplayId - ? &scheduler::Scheduler::onPrimaryDisplayModeChanged - : &scheduler::Scheduler::onNonPrimaryDisplayModeChanged; - - ((*mScheduler).*onDisplayModeChanged)(scheduler::Cycle::Render, mode); -} - sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal( const wp<IBinder>& displayToken, std::shared_ptr<compositionengine::Display> compositionDisplay, @@ -3782,7 +3643,7 @@ sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal( if (const auto& physical = state.physical) { const auto& mode = *physical->activeMode; mDisplayModeController.setActiveMode(physical->id, mode.getId(), mode.getVsyncRate(), - mode.getVsyncRate()); + mode.getPeakFps()); } display->setLayerFilter(makeLayerFilterForDisplay(display->getId(), state.layerStack)); @@ -3852,11 +3713,20 @@ void SurfaceFlinger::processDisplayAdded(const wp<IBinder>& displayToken, state.surface.get()); const auto displayId = PhysicalDisplayId::tryCast(compositionDisplay->getId()); LOG_FATAL_IF(!displayId); +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) + const auto frameBufferSurface = + sp<FramebufferSurface>::make(getHwComposer(), *displayId, bqProducer, bqConsumer, + state.physical->activeMode->getResolution(), + ui::Size(maxGraphicsWidth, maxGraphicsHeight)); + displaySurface = frameBufferSurface; + producer = frameBufferSurface->getSurface()->getIGraphicBufferProducer(); +#else displaySurface = sp<FramebufferSurface>::make(getHwComposer(), *displayId, bqConsumer, state.physical->activeMode->getResolution(), ui::Size(maxGraphicsWidth, maxGraphicsHeight)); producer = bqProducer; +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) } LOG_FATAL_IF(!displaySurface); @@ -3949,7 +3819,8 @@ void SurfaceFlinger::processDisplayChanged(const wp<IBinder>& displayToken, mDisplays.erase(displayToken); if (const auto& physical = currentState.physical) { - getHwComposer().allocatePhysicalDisplay(physical->hwcDisplayId, physical->id); + getHwComposer().allocatePhysicalDisplay(physical->hwcDisplayId, physical->id, + /*physicalSize=*/std::nullopt); } processDisplayAdded(displayToken, currentState); @@ -3960,11 +3831,8 @@ void SurfaceFlinger::processDisplayChanged(const wp<IBinder>& displayToken, setPowerModeInternal(display, hal::PowerMode::ON); } - // TODO(b/175678251) Call a listener instead. - if (currentState.physical->hwcDisplayId == getHwComposer().getPrimaryHwcDisplayId()) { - const Fps refreshRate = - mDisplayModeController.getActiveMode(display->getPhysicalId()).fps; - mScheduler->resetPhaseConfiguration(refreshRate); + if (display->getPhysicalId() == mActiveDisplayId) { + onActiveDisplayChangedLocked(nullptr, *display); } } return; @@ -4048,14 +3916,6 @@ void SurfaceFlinger::commitTransactionsLocked(uint32_t transactionFlags) { // Commit display transactions. const bool displayTransactionNeeded = transactionFlags & eDisplayTransactionNeeded; mFrontEndDisplayInfosChanged = displayTransactionNeeded; - if (displayTransactionNeeded && !mLayerLifecycleManagerEnabled) { - processDisplayChangesLocked(); - mFrontEndDisplayInfos.clear(); - for (const auto& [_, display] : mDisplays) { - mFrontEndDisplayInfos.try_emplace(display->getLayerStack(), display->getFrontEndInfo()); - } - } - mForceTransactionDisplayChange = displayTransactionNeeded; if (mSomeChildrenChanged) { mVisibleRegionsDirty = true; @@ -4063,51 +3923,6 @@ void SurfaceFlinger::commitTransactionsLocked(uint32_t transactionFlags) { mUpdateInputInfo = true; } - // Update transform hint. - if (transactionFlags & (eTransformHintUpdateNeeded | eDisplayTransactionNeeded)) { - // Layers and/or displays have changed, so update the transform hint for each layer. - // - // NOTE: we do this here, rather than when presenting the display so that - // the hint is set before we acquire a buffer from the surface texture. - // - // NOTE: layer transactions have taken place already, so we use their - // drawing state. However, SurfaceFlinger's own transaction has not - // happened yet, so we must use the current state layer list - // (soon to become the drawing state list). - // - sp<const DisplayDevice> hintDisplay; - ui::LayerStack layerStack; - - mCurrentState.traverse([&](Layer* layer) REQUIRES(mStateLock) { - // NOTE: we rely on the fact that layers are sorted by - // layerStack first (so we don't have to traverse the list - // of displays for every layer). - if (const auto filter = layer->getOutputFilter(); layerStack != filter.layerStack) { - layerStack = filter.layerStack; - hintDisplay = nullptr; - - // Find the display that includes the layer. - for (const auto& [token, display] : mDisplays) { - if (!display->getCompositionDisplay()->includesLayer(filter)) { - continue; - } - - // Pick the primary display if another display mirrors the layer. - if (hintDisplay) { - hintDisplay = nullptr; - break; - } - - hintDisplay = display; - } - } - - if (hintDisplay) { - layer->updateTransformHint(hintDisplay->getTransformHint()); - } - }); - } - if (mLayersAdded) { mLayersAdded = false; // Layers have been added. @@ -4121,14 +3936,6 @@ void SurfaceFlinger::commitTransactionsLocked(uint32_t transactionFlags) { mLayersRemoved = false; mVisibleRegionsDirty = true; mUpdateInputInfo = true; - mDrawingState.traverseInZOrder([&](Layer* layer) { - if (mLayersPendingRemoval.indexOf(sp<Layer>::fromExisting(layer)) >= 0) { - // this layer is not visible anymore - Region visibleReg; - visibleReg.set(layer->getScreenBounds()); - invalidateLayerStack(layer->getOutputFilter(), visibleReg); - } - }); } if (transactionFlags & eInputInfoUpdateNeeded) { @@ -4142,7 +3949,7 @@ void SurfaceFlinger::updateInputFlinger(VsyncId vsyncId, TimePoint frameTime) { if (!mInputFlinger || (!mUpdateInputInfo && mInputWindowCommands.empty())) { return; } - ATRACE_CALL(); + SFTRACE_CALL(); std::vector<WindowInfo> windowInfos; std::vector<DisplayInfo> displayInfos; @@ -4172,7 +3979,7 @@ void SurfaceFlinger::updateInputFlinger(VsyncId vsyncId, TimePoint frameTime) { std::move(mInputWindowCommands), inputFlinger = mInputFlinger, this, visibleWindowsChanged, vsyncId, frameTime]() { - ATRACE_NAME("BackgroundExecutor::updateInputFlinger"); + SFTRACE_NAME("BackgroundExecutor::updateInputFlinger"); if (updateWindowInfo) { mWindowInfosListenerInvoker ->windowInfosChanged(gui::WindowInfosUpdate{std::move(windowInfos), @@ -4235,23 +4042,10 @@ void SurfaceFlinger::buildWindowInfos(std::vector<WindowInfo>& outWindowInfos, outWindowInfos.reserve(sNumWindowInfos); sNumWindowInfos = 0; - if (mLayerLifecycleManagerEnabled) { - mLayerSnapshotBuilder.forEachInputSnapshot( - [&outWindowInfos](const frontend::LayerSnapshot& snapshot) { - outWindowInfos.push_back(snapshot.inputInfo); - }); - } else { - mDrawingState.traverseInReverseZOrder([&](Layer* layer) FTL_FAKE_GUARD(kMainThreadContext) { - if (!layer->needsInputInfo()) return; - const auto opt = - mFrontEndDisplayInfos.get(layer->getLayerStack()) - .transform([](const frontend::DisplayInfo& info) { - return Layer::InputDisplayArgs{&info.transform, info.isSecure}; - }); - - outWindowInfos.push_back(layer->fillInputInfo(opt.value_or(Layer::InputDisplayArgs{}))); - }); - } + mLayerSnapshotBuilder.forEachInputSnapshot( + [&outWindowInfos](const frontend::LayerSnapshot& snapshot) { + outWindowInfos.push_back(snapshot.inputInfo); + }); sNumWindowInfos = outWindowInfos.size(); @@ -4285,7 +4079,7 @@ void SurfaceFlinger::requestDisplayModes(std::vector<display::DisplayModeRequest return; } - ATRACE_CALL(); + SFTRACE_CALL(); // If this is called from the main thread mStateLock must be locked before // Currently the only way to call this function from the main thread is from @@ -4310,25 +4104,14 @@ void SurfaceFlinger::requestDisplayModes(std::vector<display::DisplayModeRequest } } -void SurfaceFlinger::triggerOnFrameRateOverridesChanged() { - PhysicalDisplayId displayId = [&]() { - ConditionalLock lock(mStateLock, std::this_thread::get_id() != mMainThreadId); - return getDefaultDisplayDeviceLocked()->getPhysicalId(); - }(); - - mScheduler->onFrameRateOverridesChanged(scheduler::Cycle::Render, displayId); -} - void SurfaceFlinger::notifyCpuLoadUp() { mPowerAdvisor->notifyCpuLoadUp(); } void SurfaceFlinger::onChoreographerAttached() { - ATRACE_CALL(); - if (mLayerLifecycleManagerEnabled) { - mUpdateAttachedChoreographer = true; - scheduleCommit(FrameHint::kNone); - } + SFTRACE_CALL(); + mUpdateAttachedChoreographer = true; + scheduleCommit(FrameHint::kNone); } void SurfaceFlinger::onExpectedPresentTimePosted(TimePoint expectedPresentTime, @@ -4453,6 +4236,8 @@ void SurfaceFlinger::scheduleNotifyExpectedPresentHint(PhysicalDisplayId display if (data.hintStatus.compare_exchange_strong(scheduleHintOnTx, NotifyExpectedPresentHintStatus::Sent)) { sendHint(); + constexpr bool kAllowToEnable = true; + mScheduler->resyncToHardwareVsync(displayId, kAllowToEnable); } })); } @@ -4552,75 +4337,9 @@ void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) { } void SurfaceFlinger::doCommitTransactions() { - ATRACE_CALL(); - - if (!mLayersPendingRemoval.isEmpty()) { - // Notify removed layers now that they can't be drawn from - for (const auto& l : mLayersPendingRemoval) { - // Ensure any buffers set to display on any children are released. - if (l->isRemovedFromCurrentState()) { - l->latchAndReleaseBuffer(); - } - - // If a layer has a parent, we allow it to out-live it's handle - // with the idea that the parent holds a reference and will eventually - // be cleaned up. However no one cleans up the top-level so we do so - // here. - if (l->isAtRoot()) { - l->setIsAtRoot(false); - mCurrentState.layersSortedByZ.remove(l); - } - - // If the layer has been removed and has no parent, then it will not be reachable - // when traversing layers on screen. Add the layer to the offscreenLayers set to - // ensure we can copy its current to drawing state. - if (!l->getParent()) { - mOffscreenLayers.emplace(l.get()); - } - } - mLayersPendingRemoval.clear(); - } - + SFTRACE_CALL(); mDrawingState = mCurrentState; mCurrentState.colorMatrixChanged = false; - - if (mVisibleRegionsDirty) { - for (const auto& rootLayer : mDrawingState.layersSortedByZ) { - rootLayer->commitChildList(); - } - } - - commitOffscreenLayers(); - if (mLayerMirrorRoots.size() > 0) { - std::deque<Layer*> pendingUpdates; - pendingUpdates.insert(pendingUpdates.end(), mLayerMirrorRoots.begin(), - mLayerMirrorRoots.end()); - std::vector<Layer*> needsUpdating; - for (Layer* cloneRoot : mLayerMirrorRoots) { - pendingUpdates.pop_front(); - if (cloneRoot->isRemovedFromCurrentState()) { - continue; - } - if (cloneRoot->updateMirrorInfo(pendingUpdates)) { - } else { - needsUpdating.push_back(cloneRoot); - } - } - for (Layer* cloneRoot : needsUpdating) { - cloneRoot->updateMirrorInfo({}); - } - } -} - -void SurfaceFlinger::commitOffscreenLayers() { - for (Layer* offscreenLayer : mOffscreenLayers) { - offscreenLayer->traverse(LayerVector::StateSet::Drawing, [](Layer* layer) { - if (layer->clearTransactionFlags(eTransactionNeeded)) { - layer->doTransaction(0); - layer->commitChildList(); - } - }); - } } void SurfaceFlinger::invalidateLayerStack(const ui::LayerFilter& layerFilter, const Region& dirty) { @@ -4632,160 +4351,30 @@ void SurfaceFlinger::invalidateLayerStack(const ui::LayerFilter& layerFilter, co } } -bool SurfaceFlinger::latchBuffers() { - ATRACE_CALL(); - - const nsecs_t latchTime = systemTime(); - - bool visibleRegions = false; - bool frameQueued = false; - bool newDataLatched = false; - - // Store the set of layers that need updates. This set must not change as - // buffers are being latched, as this could result in a deadlock. - // Example: Two producers share the same command stream and: - // 1.) Layer 0 is latched - // 2.) Layer 0 gets a new frame - // 2.) Layer 1 gets a new frame - // 3.) Layer 1 is latched. - // Display is now waiting on Layer 1's frame, which is behind layer 0's - // second frame. But layer 0's second frame could be waiting on display. - mDrawingState.traverse([&](Layer* layer) { - if (layer->clearTransactionFlags(eTransactionNeeded) || mForceTransactionDisplayChange) { - const uint32_t flags = layer->doTransaction(0); - if (flags & Layer::eVisibleRegion) { - mVisibleRegionsDirty = true; - } - } - - if (layer->hasReadyFrame() || layer->willReleaseBufferOnLatch()) { - frameQueued = true; - mLayersWithQueuedFrames.emplace(sp<Layer>::fromExisting(layer)); - } else { - layer->useEmptyDamage(); - if (!layer->hasBuffer()) { - // The last latch time is used to classify a missed frame as buffer stuffing - // instead of a missed frame. This is used to identify scenarios where we - // could not latch a buffer or apply a transaction due to backpressure. - // We only update the latch time for buffer less layers here, the latch time - // is updated for buffer layers when the buffer is latched. - layer->updateLastLatchTime(latchTime); - } - } - }); - mForceTransactionDisplayChange = false; - - // The client can continue submitting buffers for offscreen layers, but they will not - // be shown on screen. Therefore, we need to latch and release buffers of offscreen - // layers to ensure dequeueBuffer doesn't block indefinitely. - for (Layer* offscreenLayer : mOffscreenLayers) { - offscreenLayer->traverse(LayerVector::StateSet::Drawing, - [&](Layer* l) { l->latchAndReleaseBuffer(); }); - } - - if (!mLayersWithQueuedFrames.empty()) { - // mStateLock is needed for latchBuffer as LayerRejecter::reject() - // writes to Layer current state. See also b/119481871 - Mutex::Autolock lock(mStateLock); - - for (const auto& layer : mLayersWithQueuedFrames) { - if (layer->willReleaseBufferOnLatch()) { - mLayersWithBuffersRemoved.emplace(layer); - } - if (layer->latchBuffer(visibleRegions, latchTime)) { - mLayersPendingRefresh.push_back(layer); - newDataLatched = true; - } - layer->useSurfaceDamage(); - } - } - - mVisibleRegionsDirty |= visibleRegions; - - // If we will need to wake up at some time in the future to deal with a - // queued frame that shouldn't be displayed during this vsync period, wake - // up during the next vsync period to check again. - if (frameQueued && (mLayersWithQueuedFrames.empty() || !newDataLatched)) { - scheduleCommit(FrameHint::kNone); - } - - // enter boot animation on first buffer latch - if (CC_UNLIKELY(mBootStage == BootStage::BOOTLOADER && newDataLatched)) { - ALOGI("Enter boot animation"); - mBootStage = BootStage::BOOTANIMATION; - } - - if (mLayerMirrorRoots.size() > 0) { - mDrawingState.traverse([&](Layer* layer) { layer->updateCloneBufferInfo(); }); - } - - // Only continue with the refresh if there is actually new work to do - return !mLayersWithQueuedFrames.empty() && newDataLatched; -} - status_t SurfaceFlinger::addClientLayer(LayerCreationArgs& args, const sp<IBinder>& handle, const sp<Layer>& layer, const wp<Layer>& parent, uint32_t* outTransformHint) { if (mNumLayers >= MAX_LAYERS) { + static std::atomic<nsecs_t> lasttime{0}; + nsecs_t now = systemTime(); + if (lasttime != 0 && ns2s(now - lasttime.load()) < 10) { + ALOGE("AddClientLayer already dumped 10s before"); + return NO_MEMORY; + } else { + lasttime = now; + } + ALOGE("AddClientLayer failed, mNumLayers (%zu) >= MAX_LAYERS (%zu)", mNumLayers.load(), MAX_LAYERS); - static_cast<void>(mScheduler->schedule([=, this] { - ALOGE("Dumping layer keeping > 20 children alive:"); - bool leakingParentLayerFound = false; - mDrawingState.traverse([&](Layer* layer) { - if (leakingParentLayerFound) { - return; - } - if (layer->getChildrenCount() > 20) { - leakingParentLayerFound = true; - sp<Layer> parent = sp<Layer>::fromExisting(layer); - while (parent) { - ALOGE("Parent Layer: %s%s", parent->getName().c_str(), - (parent->isHandleAlive() ? "handleAlive" : "")); - parent = parent->getParent(); - } - // Sample up to 100 layers - ALOGE("Dumping random sampling of child layers total(%zu): ", - layer->getChildrenCount()); - int sampleSize = (layer->getChildrenCount() / 100) + 1; - layer->traverseChildren([&](Layer* layer) { - if (rand() % sampleSize == 0) { - ALOGE("Child Layer: %s%s", layer->getName().c_str(), - (layer->isHandleAlive() ? "handleAlive" : "")); - } - }); - } - }); - - int numLayers = 0; - mDrawingState.traverse([&](Layer* layer) { numLayers++; }); - - ALOGE("Dumping random sampling of on-screen layers total(%u):", numLayers); - mDrawingState.traverse([&](Layer* layer) { - // Aim to dump about 200 layers to avoid totally trashing - // logcat. On the other hand, if there really are 4096 layers - // something has gone totally wrong its probably the most - // useful information in logcat. - if (rand() % 20 == 13) { - ALOGE("Layer: %s%s", layer->getName().c_str(), - (layer->isHandleAlive() ? "handleAlive" : "")); - std::this_thread::sleep_for(std::chrono::milliseconds(5)); - } - }); - ALOGE("Dumping random sampling of off-screen layers total(%zu): ", - mOffscreenLayers.size()); - for (Layer* offscreenLayer : mOffscreenLayers) { - if (rand() % 20 == 13) { - ALOGE("Offscreen-layer: %s%s", offscreenLayer->getName().c_str(), - (offscreenLayer->isHandleAlive() ? "handleAlive" : "")); - std::this_thread::sleep_for(std::chrono::milliseconds(5)); - } - } + static_cast<void>(mScheduler->schedule([&]() FTL_FAKE_GUARD(kMainThreadContext) { + ALOGE("Dumping on-screen layers."); + mLayerHierarchyBuilder.dumpLayerSample(mLayerHierarchyBuilder.getHierarchy()); + ALOGE("Dumping off-screen layers."); + mLayerHierarchyBuilder.dumpLayerSample(mLayerHierarchyBuilder.getOffscreenHierarchy()); })); return NO_MEMORY; } - layer->updateTransformHint(mActiveDisplayTransformHint); if (outTransformHint) { *outTransformHint = mActiveDisplayTransformHint; } @@ -4793,7 +4382,7 @@ status_t SurfaceFlinger::addClientLayer(LayerCreationArgs& args, const sp<IBinde args.layerIdToMirror = LayerHandle::getLayerId(args.mirrorLayerHandle.promote()); { std::scoped_lock<std::mutex> lock(mCreatedLayersLock); - mCreatedLayers.emplace_back(layer, parent, args.addToRoot); + mCreatedLayers.emplace_back(layer); mNewLayers.emplace_back(std::make_unique<frontend::RequestedLayerState>(args)); args.mirrorLayerHandle.clear(); args.parentHandle.clear(); @@ -4810,7 +4399,7 @@ uint32_t SurfaceFlinger::getTransactionFlags() const { uint32_t SurfaceFlinger::clearTransactionFlags(uint32_t mask) { uint32_t transactionFlags = mTransactionFlags.fetch_and(~mask); - ATRACE_INT("mTransactionFlags", transactionFlags); + SFTRACE_INT("mTransactionFlags", transactionFlags); return transactionFlags & mask; } @@ -4818,7 +4407,7 @@ void SurfaceFlinger::setTransactionFlags(uint32_t mask, TransactionSchedule sche const sp<IBinder>& applyToken, FrameHint frameHint) { mScheduler->modulateVsync({}, &VsyncModulator::setTransactionSchedule, schedule, applyToken); uint32_t transactionFlags = mTransactionFlags.fetch_or(mask); - ATRACE_INT("mTransactionFlags", transactionFlags); + SFTRACE_INT("mTransactionFlags", transactionFlags); if (const bool scheduled = transactionFlags & mask; !scheduled) { scheduleCommit(frameHint); @@ -4843,8 +4432,8 @@ TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyTimelin // for stability reasons. if (!transaction.isAutoTimestamp && desiredPresentTime >= expectedPresentTime && desiredPresentTime < expectedPresentTime + 1s) { - ATRACE_FORMAT("not current desiredPresentTime: %" PRId64 " expectedPresentTime: %" PRId64, - desiredPresentTime, expectedPresentTime); + SFTRACE_FORMAT("not current desiredPresentTime: %" PRId64 " expectedPresentTime: %" PRId64, + desiredPresentTime, expectedPresentTime); return TransactionReadiness::NotReady; } @@ -4856,117 +4445,22 @@ TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyTimelin // incorrectly as the frame rate of SF changed before it drained the older transactions. if (ftl::to_underlying(vsyncId) == FrameTimelineInfo::INVALID_VSYNC_ID && !mScheduler->isVsyncValid(expectedPresentTime, transaction.originUid)) { - ATRACE_FORMAT("!isVsyncValid expectedPresentTime: %" PRId64 " uid: %d", expectedPresentTime, - transaction.originUid); + SFTRACE_FORMAT("!isVsyncValid expectedPresentTime: %" PRId64 " uid: %d", + expectedPresentTime, transaction.originUid); return TransactionReadiness::NotReady; } // If the client didn't specify desiredPresentTime, use the vsyncId to determine the // expected present time of this transaction. if (transaction.isAutoTimestamp && frameIsEarly(expectedPresentTime, vsyncId)) { - ATRACE_FORMAT("frameIsEarly vsyncId: %" PRId64 " expectedPresentTime: %" PRId64, - transaction.frameTimelineInfo.vsyncId, expectedPresentTime); + SFTRACE_FORMAT("frameIsEarly vsyncId: %" PRId64 " expectedPresentTime: %" PRId64, + transaction.frameTimelineInfo.vsyncId, expectedPresentTime); return TransactionReadiness::NotReady; } return TransactionReadiness::Ready; } -TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyBufferCheckLegacy( - const TransactionHandler::TransactionFlushState& flushState) { - using TransactionReadiness = TransactionHandler::TransactionReadiness; - auto ready = TransactionReadiness::Ready; - flushState.transaction->traverseStatesWithBuffersWhileTrue([&](const ResolvedComposerState& - resolvedState) -> bool { - sp<Layer> layer = LayerHandle::getLayer(resolvedState.state.surface); - - const auto& transaction = *flushState.transaction; - const auto& s = resolvedState.state; - // check for barrier frames - if (s.bufferData->hasBarrier) { - // The current producerId is already a newer producer than the buffer that has a - // barrier. This means the incoming buffer is older and we can release it here. We - // don't wait on the barrier since we know that's stale information. - if (layer->getDrawingState().barrierProducerId > s.bufferData->producerId) { - layer->callReleaseBufferCallback(s.bufferData->releaseBufferListener, - resolvedState.externalTexture->getBuffer(), - s.bufferData->frameNumber, - s.bufferData->acquireFence); - // Delete the entire state at this point and not just release the buffer because - // everything associated with the Layer in this Transaction is now out of date. - ATRACE_FORMAT("DeleteStaleBuffer %s barrierProducerId:%d > %d", - layer->getDebugName(), layer->getDrawingState().barrierProducerId, - s.bufferData->producerId); - return TraverseBuffersReturnValues::DELETE_AND_CONTINUE_TRAVERSAL; - } - - if (layer->getDrawingState().barrierFrameNumber < s.bufferData->barrierFrameNumber) { - const bool willApplyBarrierFrame = - flushState.bufferLayersReadyToPresent.contains(s.surface.get()) && - ((flushState.bufferLayersReadyToPresent.get(s.surface.get()) >= - s.bufferData->barrierFrameNumber)); - if (!willApplyBarrierFrame) { - ATRACE_FORMAT("NotReadyBarrier %s barrierFrameNumber:%" PRId64 " > %" PRId64, - layer->getDebugName(), - layer->getDrawingState().barrierFrameNumber, - s.bufferData->barrierFrameNumber); - ready = TransactionReadiness::NotReadyBarrier; - return TraverseBuffersReturnValues::STOP_TRAVERSAL; - } - } - } - - // If backpressure is enabled and we already have a buffer to commit, keep - // the transaction in the queue. - const bool hasPendingBuffer = - flushState.bufferLayersReadyToPresent.contains(s.surface.get()); - if (layer->backpressureEnabled() && hasPendingBuffer && transaction.isAutoTimestamp) { - ATRACE_FORMAT("hasPendingBuffer %s", layer->getDebugName()); - ready = TransactionReadiness::NotReady; - return TraverseBuffersReturnValues::STOP_TRAVERSAL; - } - - const bool acquireFenceAvailable = s.bufferData && - s.bufferData->flags.test(BufferData::BufferDataChange::fenceChanged) && - s.bufferData->acquireFence; - const bool fenceSignaled = !acquireFenceAvailable || - s.bufferData->acquireFence->getStatus() != Fence::Status::Unsignaled; - if (!fenceSignaled) { - // check fence status - const bool allowLatchUnsignaled = shouldLatchUnsignaled(s, transaction.states.size(), - flushState.firstTransaction) && - layer->isSimpleBufferUpdate(s); - - if (allowLatchUnsignaled) { - ATRACE_FORMAT("fence unsignaled try allowLatchUnsignaled %s", - layer->getDebugName()); - ready = TransactionReadiness::NotReadyUnsignaled; - } else { - ready = TransactionReadiness::NotReady; - auto& listener = s.bufferData->releaseBufferListener; - if (listener && - (flushState.queueProcessTime - transaction.postTime) > - std::chrono::nanoseconds(4s).count()) { - // Used to add a stalled transaction which uses an internal lock. - ftl::FakeGuard guard(kMainThreadContext); - mTransactionHandler - .onTransactionQueueStalled(transaction.id, - {.pid = layer->getOwnerPid(), - .layerId = static_cast<uint32_t>( - layer->getSequence()), - .layerName = layer->getDebugName(), - .bufferId = s.bufferData->getId(), - .frameNumber = s.bufferData->frameNumber}); - } - ATRACE_FORMAT("fence unsignaled %s", layer->getDebugName()); - return TraverseBuffersReturnValues::STOP_TRAVERSAL; - } - } - return TraverseBuffersReturnValues::CONTINUE_TRAVERSAL; - }); - return ready; -} - TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyBufferCheck( const TransactionHandler::TransactionFlushState& flushState) { using TransactionReadiness = TransactionHandler::TransactionReadiness; @@ -4988,8 +4482,8 @@ TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyBufferC uint32_t currentMaxAcquiredBufferCount = getMaxAcquiredBufferCountForCurrentRefreshRate( layer->ownerUid.val()); - ATRACE_FORMAT_INSTANT("callReleaseBufferCallback %s - %" PRIu64, - layer->name.c_str(), s.bufferData->frameNumber); + SFTRACE_FORMAT_INSTANT("callReleaseBufferCallback %s - %" PRIu64, + layer->name.c_str(), s.bufferData->frameNumber); s.bufferData->releaseBufferListener ->onReleaseBuffer({resolvedState.externalTexture->getBuffer() ->getId(), @@ -5003,9 +4497,9 @@ TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyBufferC // Delete the entire state at this point and not just release the buffer // because everything associated with the Layer in this Transaction is now // out of date. - ATRACE_FORMAT("DeleteStaleBuffer %s barrierProducerId:%d > %d", - layer->name.c_str(), layer->barrierProducerId, - s.bufferData->producerId); + SFTRACE_FORMAT("DeleteStaleBuffer %s barrierProducerId:%d > %d", + layer->name.c_str(), layer->barrierProducerId, + s.bufferData->producerId); return TraverseBuffersReturnValues::DELETE_AND_CONTINUE_TRAVERSAL; } @@ -5015,10 +4509,10 @@ TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyBufferC ((flushState.bufferLayersReadyToPresent.get(s.surface.get()) >= s.bufferData->barrierFrameNumber)); if (!willApplyBarrierFrame) { - ATRACE_FORMAT("NotReadyBarrier %s barrierFrameNumber:%" PRId64 - " > %" PRId64, - layer->name.c_str(), layer->barrierFrameNumber, - s.bufferData->barrierFrameNumber); + SFTRACE_FORMAT("NotReadyBarrier %s barrierFrameNumber:%" PRId64 + " > %" PRId64, + layer->name.c_str(), layer->barrierFrameNumber, + s.bufferData->barrierFrameNumber); ready = TransactionReadiness::NotReadyBarrier; return TraverseBuffersReturnValues::STOP_TRAVERSAL; } @@ -5031,7 +4525,7 @@ TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyBufferC flushState.bufferLayersReadyToPresent.contains(s.surface.get()); if (layer->backpressureEnabled() && hasPendingBuffer && transaction.isAutoTimestamp) { - ATRACE_FORMAT("hasPendingBuffer %s", layer->name.c_str()); + SFTRACE_FORMAT("hasPendingBuffer %s", layer->name.c_str()); ready = TransactionReadiness::NotReady; return TraverseBuffersReturnValues::STOP_TRAVERSAL; } @@ -5048,8 +4542,8 @@ TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyBufferC flushState.firstTransaction) && layer->isSimpleBufferUpdate(s); if (allowLatchUnsignaled) { - ATRACE_FORMAT("fence unsignaled try allowLatchUnsignaled %s", - layer->name.c_str()); + SFTRACE_FORMAT("fence unsignaled try allowLatchUnsignaled %s", + layer->name.c_str()); ready = TransactionReadiness::NotReadyUnsignaled; } else { ready = TransactionReadiness::NotReady; @@ -5066,7 +4560,7 @@ TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyBufferC .frameNumber = s.bufferData->frameNumber}); } - ATRACE_FORMAT("fence unsignaled %s", layer->name.c_str()); + SFTRACE_FORMAT("fence unsignaled %s", layer->name.c_str()); return TraverseBuffersReturnValues::STOP_TRAVERSAL; } } @@ -5078,15 +4572,8 @@ TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyBufferC void SurfaceFlinger::addTransactionReadyFilters() { mTransactionHandler.addTransactionReadyFilter( std::bind(&SurfaceFlinger::transactionReadyTimelineCheck, this, std::placeholders::_1)); - if (mLayerLifecycleManagerEnabled) { - mTransactionHandler.addTransactionReadyFilter( - std::bind(&SurfaceFlinger::transactionReadyBufferCheck, this, - std::placeholders::_1)); - } else { - mTransactionHandler.addTransactionReadyFilter( - std::bind(&SurfaceFlinger::transactionReadyBufferCheckLegacy, this, - std::placeholders::_1)); - } + mTransactionHandler.addTransactionReadyFilter( + std::bind(&SurfaceFlinger::transactionReadyBufferCheck, this, std::placeholders::_1)); } // For tests only @@ -5145,22 +4632,22 @@ bool SurfaceFlinger::frameIsEarly(TimePoint expectedPresentTime, VsyncId vsyncId bool SurfaceFlinger::shouldLatchUnsignaled(const layer_state_t& state, size_t numStates, bool firstTransaction) const { if (enableLatchUnsignaledConfig == LatchUnsignaledConfig::Disabled) { - ATRACE_FORMAT_INSTANT("%s: false (LatchUnsignaledConfig::Disabled)", __func__); + SFTRACE_FORMAT_INSTANT("%s: false (LatchUnsignaledConfig::Disabled)", __func__); return false; } // We only want to latch unsignaled when a single layer is updated in this // transaction (i.e. not a blast sync transaction). if (numStates != 1) { - ATRACE_FORMAT_INSTANT("%s: false (numStates=%zu)", __func__, numStates); + SFTRACE_FORMAT_INSTANT("%s: false (numStates=%zu)", __func__, numStates); return false; } if (enableLatchUnsignaledConfig == LatchUnsignaledConfig::AutoSingleLayer) { if (!firstTransaction) { - ATRACE_FORMAT_INSTANT("%s: false (LatchUnsignaledConfig::AutoSingleLayer; not first " - "transaction)", - __func__); + SFTRACE_FORMAT_INSTANT("%s: false (LatchUnsignaledConfig::AutoSingleLayer; not first " + "transaction)", + __func__); return false; } @@ -5168,9 +4655,9 @@ bool SurfaceFlinger::shouldLatchUnsignaled(const layer_state_t& state, size_t nu // as it leads to jank due to RenderEngine waiting for unsignaled buffer // or window animations being slow. if (mScheduler->vsyncModulator().isVsyncConfigEarly()) { - ATRACE_FORMAT_INSTANT("%s: false (LatchUnsignaledConfig::AutoSingleLayer; " - "isVsyncConfigEarly)", - __func__); + SFTRACE_FORMAT_INSTANT("%s: false (LatchUnsignaledConfig::AutoSingleLayer; " + "isVsyncConfigEarly)", + __func__); return false; } } @@ -5180,12 +4667,12 @@ bool SurfaceFlinger::shouldLatchUnsignaled(const layer_state_t& state, size_t nu status_t SurfaceFlinger::setTransactionState( const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& states, - const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken, + 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) { - ATRACE_CALL(); + SFTRACE_CALL(); IPCThreadState* ipc = IPCThreadState::self(); const int originPid = ipc->getCallingPid(); @@ -5195,7 +4682,7 @@ status_t SurfaceFlinger::setTransactionState( composerState.state.sanitize(permissions); } - for (DisplayState display : displays) { + for (DisplayState& display : displays) { display.sanitize(permissions); } @@ -5316,11 +4803,6 @@ bool SurfaceFlinger::applyTransactionState(const FrameTimelineInfo& frameTimelin const std::vector<ListenerCallbacks>& listenerCallbacks, int originPid, int originUid, uint64_t transactionId) { uint32_t transactionFlags = 0; - if (!mLayerLifecycleManagerEnabled) { - for (DisplayState& display : displays) { - transactionFlags |= setDisplayStateLocked(display); - } - } // start and end registration for listeners w/ no surface so they can get their callback. Note // that listeners with SurfaceControls will start registration during setClientStateLocked @@ -5328,27 +4810,11 @@ bool SurfaceFlinger::applyTransactionState(const FrameTimelineInfo& frameTimelin for (const auto& listener : listenerCallbacks) { mTransactionCallbackInvoker.addEmptyTransaction(listener); } - nsecs_t now = systemTime(); uint32_t clientStateFlags = 0; for (auto& resolvedState : states) { clientStateFlags |= updateLayerCallbacksAndStats(frameTimelineInfo, resolvedState, desiredPresentTime, isAutoTimestamp, postTime, transactionId); - if (!mLayerLifecycleManagerEnabled) { - if ((flags & eAnimation) && resolvedState.state.surface) { - if (const auto layer = LayerHandle::getLayer(resolvedState.state.surface)) { - const auto layerProps = scheduler::LayerProps{ - .visible = layer->isVisible(), - .bounds = layer->getBounds(), - .transform = layer->getTransform(), - .setFrameRateVote = layer->getFrameRateForLayerTree(), - .frameRateSelectionPriority = layer->getFrameRateSelectionPriority(), - .isFrontBuffered = layer->isFrontBuffered(), - }; - layer->recordLayerHistoryAnimationTx(layerProps, now); - } - } - } } transactionFlags |= clientStateFlags; @@ -5483,366 +4949,6 @@ bool SurfaceFlinger::callingThreadHasUnscopedSurfaceFlingerAccess(bool usePermis return true; } -uint32_t SurfaceFlinger::setClientStateLocked(const FrameTimelineInfo& frameTimelineInfo, - ResolvedComposerState& composerState, - int64_t desiredPresentTime, bool isAutoTimestamp, - int64_t postTime, uint64_t transactionId) { - layer_state_t& s = composerState.state; - - std::vector<ListenerCallbacks> filteredListeners; - for (auto& listener : s.listeners) { - // Starts a registration but separates the callback ids according to callback type. This - // allows the callback invoker to send on latch callbacks earlier. - // note that startRegistration will not re-register if the listener has - // already be registered for a prior surface control - - ListenerCallbacks onCommitCallbacks = listener.filter(CallbackId::Type::ON_COMMIT); - if (!onCommitCallbacks.callbackIds.empty()) { - filteredListeners.push_back(onCommitCallbacks); - } - - ListenerCallbacks onCompleteCallbacks = listener.filter(CallbackId::Type::ON_COMPLETE); - if (!onCompleteCallbacks.callbackIds.empty()) { - filteredListeners.push_back(onCompleteCallbacks); - } - } - - const uint64_t what = s.what; - uint32_t flags = 0; - sp<Layer> layer = nullptr; - if (s.surface) { - layer = LayerHandle::getLayer(s.surface); - } else { - // The client may provide us a null handle. Treat it as if the layer was removed. - ALOGW("Attempt to set client state with a null layer handle"); - } - if (layer == nullptr) { - for (auto& [listener, callbackIds] : s.listeners) { - mTransactionCallbackInvoker.addCallbackHandle(sp<CallbackHandle>::make(listener, - callbackIds, - s.surface), - std::vector<JankData>()); - } - return 0; - } - MUTEX_ALIAS(mStateLock, layer->mFlinger->mStateLock); - - ui::LayerStack oldLayerStack = layer->getLayerStack(LayerVector::StateSet::Current); - - // Only set by BLAST adapter layers - if (what & layer_state_t::eProducerDisconnect) { - layer->onDisconnect(); - } - - if (what & layer_state_t::ePositionChanged) { - if (layer->setPosition(s.x, s.y)) { - flags |= eTraversalNeeded; - } - } - if (what & layer_state_t::eLayerChanged) { - // NOTE: index needs to be calculated before we update the state - const auto& p = layer->getParent(); - if (p == nullptr) { - ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer); - if (layer->setLayer(s.z) && idx >= 0) { - mCurrentState.layersSortedByZ.removeAt(idx); - mCurrentState.layersSortedByZ.add(layer); - // we need traversal (state changed) - // AND transaction (list changed) - flags |= eTransactionNeeded|eTraversalNeeded; - } - } else { - if (p->setChildLayer(layer, s.z)) { - flags |= eTransactionNeeded|eTraversalNeeded; - } - } - } - if (what & layer_state_t::eRelativeLayerChanged) { - // NOTE: index needs to be calculated before we update the state - const auto& p = layer->getParent(); - const auto& relativeHandle = s.relativeLayerSurfaceControl ? - s.relativeLayerSurfaceControl->getHandle() : nullptr; - if (p == nullptr) { - ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer); - if (layer->setRelativeLayer(relativeHandle, s.z) && - idx >= 0) { - mCurrentState.layersSortedByZ.removeAt(idx); - mCurrentState.layersSortedByZ.add(layer); - // we need traversal (state changed) - // AND transaction (list changed) - flags |= eTransactionNeeded|eTraversalNeeded; - } - } else { - if (p->setChildRelativeLayer(layer, relativeHandle, s.z)) { - flags |= eTransactionNeeded|eTraversalNeeded; - } - } - } - if (what & layer_state_t::eAlphaChanged) { - if (layer->setAlpha(s.color.a)) flags |= eTraversalNeeded; - } - if (what & layer_state_t::eColorChanged) { - if (layer->setColor(s.color.rgb)) flags |= eTraversalNeeded; - } - if (what & layer_state_t::eColorTransformChanged) { - if (layer->setColorTransform(s.colorTransform)) { - flags |= eTraversalNeeded; - } - } - if (what & layer_state_t::eBackgroundColorChanged) { - if (layer->setBackgroundColor(s.bgColor.rgb, s.bgColor.a, s.bgColorDataspace)) { - flags |= eTraversalNeeded; - } - } - if (what & layer_state_t::eMatrixChanged) { - if (layer->setMatrix(s.matrix)) flags |= eTraversalNeeded; - } - if (what & layer_state_t::eTransparentRegionChanged) { - if (layer->setTransparentRegionHint(s.transparentRegion)) - flags |= eTraversalNeeded; - } - if (what & layer_state_t::eFlagsChanged) { - if (layer->setFlags(s.flags, s.mask)) flags |= eTraversalNeeded; - } - if (what & layer_state_t::eCornerRadiusChanged) { - if (layer->setCornerRadius(s.cornerRadius)) - flags |= eTraversalNeeded; - } - if (what & layer_state_t::eBackgroundBlurRadiusChanged && mSupportsBlur) { - if (layer->setBackgroundBlurRadius(s.backgroundBlurRadius)) flags |= eTraversalNeeded; - } - if (what & layer_state_t::eBlurRegionsChanged) { - if (layer->setBlurRegions(s.blurRegions)) flags |= eTraversalNeeded; - } - if (what & layer_state_t::eLayerStackChanged) { - ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer); - // We only allow setting layer stacks for top level layers, - // everything else inherits layer stack from its parent. - if (layer->hasParent()) { - ALOGE("Attempt to set layer stack on layer with parent (%s) is invalid", - layer->getDebugName()); - } else if (idx < 0) { - ALOGE("Attempt to set layer stack on layer without parent (%s) that " - "that also does not appear in the top level layer list. Something" - " has gone wrong.", - layer->getDebugName()); - } else if (layer->setLayerStack(s.layerStack)) { - mCurrentState.layersSortedByZ.removeAt(idx); - mCurrentState.layersSortedByZ.add(layer); - // we need traversal (state changed) - // AND transaction (list changed) - flags |= eTransactionNeeded | eTraversalNeeded | eTransformHintUpdateNeeded; - } - } - if (what & layer_state_t::eBufferTransformChanged) { - if (layer->setTransform(s.bufferTransform)) flags |= eTraversalNeeded; - } - if (what & layer_state_t::eTransformToDisplayInverseChanged) { - if (layer->setTransformToDisplayInverse(s.transformToDisplayInverse)) - flags |= eTraversalNeeded; - } - if (what & layer_state_t::eCropChanged) { - if (layer->setCrop(s.crop)) flags |= eTraversalNeeded; - } - if (what & layer_state_t::eDataspaceChanged) { - if (layer->setDataspace(s.dataspace)) flags |= eTraversalNeeded; - } - if (what & layer_state_t::eSurfaceDamageRegionChanged) { - if (layer->setSurfaceDamageRegion(s.surfaceDamageRegion)) flags |= eTraversalNeeded; - } - if (what & layer_state_t::eApiChanged) { - if (layer->setApi(s.api)) flags |= eTraversalNeeded; - } - if (what & layer_state_t::eSidebandStreamChanged) { - if (layer->setSidebandStream(s.sidebandStream, frameTimelineInfo, postTime)) - flags |= eTraversalNeeded; - } - if (what & layer_state_t::eInputInfoChanged) { - layer->setInputInfo(*s.windowInfoHandle->getInfo()); - flags |= eTraversalNeeded; - } - if (what & layer_state_t::eMetadataChanged) { - if (const int32_t gameMode = s.metadata.getInt32(gui::METADATA_GAME_MODE, -1); - gameMode != -1) { - // The transaction will be received on the Task layer and needs to be applied to all - // child layers. Child layers that are added at a later point will obtain the game mode - // info through addChild(). - layer->setGameModeForTree(static_cast<GameMode>(gameMode)); - } - - if (layer->setMetadata(s.metadata)) { - flags |= eTraversalNeeded; - mLayerMetadataSnapshotNeeded = true; - } - } - if (what & layer_state_t::eColorSpaceAgnosticChanged) { - if (layer->setColorSpaceAgnostic(s.colorSpaceAgnostic)) { - flags |= eTraversalNeeded; - } - } - if (what & layer_state_t::eShadowRadiusChanged) { - if (layer->setShadowRadius(s.shadowRadius)) flags |= eTraversalNeeded; - } - if (what & layer_state_t::eDefaultFrameRateCompatibilityChanged) { - const auto compatibility = - Layer::FrameRate::convertCompatibility(s.defaultFrameRateCompatibility); - - if (layer->setDefaultFrameRateCompatibility(compatibility)) { - flags |= eTraversalNeeded; - } - } - if (what & layer_state_t::eFrameRateSelectionPriority) { - if (layer->setFrameRateSelectionPriority(s.frameRateSelectionPriority)) { - flags |= eTraversalNeeded; - } - } - if (what & layer_state_t::eFrameRateChanged) { - const auto compatibility = - Layer::FrameRate::convertCompatibility(s.frameRateCompatibility); - const auto strategy = - Layer::FrameRate::convertChangeFrameRateStrategy(s.changeFrameRateStrategy); - - if (layer->setFrameRate(Layer::FrameRate::FrameRateVote(Fps::fromValue(s.frameRate), - compatibility, strategy))) { - flags |= eTraversalNeeded; - } - } - if (what & layer_state_t::eFrameRateCategoryChanged) { - const FrameRateCategory category = Layer::FrameRate::convertCategory(s.frameRateCategory); - if (layer->setFrameRateCategory(category, s.frameRateCategorySmoothSwitchOnly)) { - flags |= eTraversalNeeded; - } - } - if (what & layer_state_t::eFrameRateSelectionStrategyChanged) { - const scheduler::LayerInfo::FrameRateSelectionStrategy strategy = - scheduler::LayerInfo::convertFrameRateSelectionStrategy( - s.frameRateSelectionStrategy); - if (layer->setFrameRateSelectionStrategy(strategy)) { - flags |= eTraversalNeeded; - } - } - if (what & layer_state_t::eFixedTransformHintChanged) { - if (layer->setFixedTransformHint(s.fixedTransformHint)) { - flags |= eTraversalNeeded | eTransformHintUpdateNeeded; - } - } - if (what & layer_state_t::eAutoRefreshChanged) { - layer->setAutoRefresh(s.autoRefresh); - } - if (what & layer_state_t::eDimmingEnabledChanged) { - if (layer->setDimmingEnabled(s.dimmingEnabled)) flags |= eTraversalNeeded; - } - if (what & layer_state_t::eExtendedRangeBrightnessChanged) { - if (layer->setExtendedRangeBrightness(s.currentHdrSdrRatio, s.desiredHdrSdrRatio)) { - flags |= eTraversalNeeded; - } - } - if (what & layer_state_t::eDesiredHdrHeadroomChanged) { - if (layer->setDesiredHdrHeadroom(s.desiredHdrSdrRatio)) { - flags |= eTraversalNeeded; - } - } - if (what & layer_state_t::eCachingHintChanged) { - if (layer->setCachingHint(s.cachingHint)) { - flags |= eTraversalNeeded; - } - } - if (what & layer_state_t::eHdrMetadataChanged) { - if (layer->setHdrMetadata(s.hdrMetadata)) flags |= eTraversalNeeded; - } - if (what & layer_state_t::eTrustedOverlayChanged) { - if (layer->setTrustedOverlay(s.trustedOverlay == gui::TrustedOverlay::ENABLED)) { - flags |= eTraversalNeeded; - } - } - if (what & layer_state_t::eStretchChanged) { - if (layer->setStretchEffect(s.stretchEffect)) { - flags |= eTraversalNeeded; - } - } - if (what & layer_state_t::eBufferCropChanged) { - if (layer->setBufferCrop(s.bufferCrop)) { - flags |= eTraversalNeeded; - } - } - if (what & layer_state_t::eDestinationFrameChanged) { - if (layer->setDestinationFrame(s.destinationFrame)) { - flags |= eTraversalNeeded; - } - } - if (what & layer_state_t::eDropInputModeChanged) { - if (layer->setDropInputMode(s.dropInputMode)) { - flags |= eTraversalNeeded; - mUpdateInputInfo = true; - } - } - // This has to happen after we reparent children because when we reparent to null we remove - // child layers from current state and remove its relative z. If the children are reparented in - // the same transaction, then we have to make sure we reparent the children first so we do not - // lose its relative z order. - if (what & layer_state_t::eReparent) { - bool hadParent = layer->hasParent(); - auto parentHandle = (s.parentSurfaceControlForChild) - ? s.parentSurfaceControlForChild->getHandle() - : nullptr; - if (layer->reparent(parentHandle)) { - if (!hadParent) { - layer->setIsAtRoot(false); - mCurrentState.layersSortedByZ.remove(layer); - } - flags |= eTransactionNeeded | eTraversalNeeded; - } - } - std::vector<sp<CallbackHandle>> callbackHandles; - if ((what & layer_state_t::eHasListenerCallbacksChanged) && (!filteredListeners.empty())) { - for (auto& [listener, callbackIds] : filteredListeners) { - callbackHandles.emplace_back( - sp<CallbackHandle>::make(listener, callbackIds, s.surface)); - } - } - - if (what & layer_state_t::eBufferChanged) { - if (layer->setBuffer(composerState.externalTexture, *s.bufferData, postTime, - desiredPresentTime, isAutoTimestamp, frameTimelineInfo)) { - flags |= eTraversalNeeded; - } - } else if (frameTimelineInfo.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID) { - layer->setFrameTimelineVsyncForBufferlessTransaction(frameTimelineInfo, postTime); - } - - if ((what & layer_state_t::eBufferChanged) == 0) { - layer->setDesiredPresentTime(desiredPresentTime, isAutoTimestamp); - } - - if (what & layer_state_t::eTrustedPresentationInfoChanged) { - if (layer->setTrustedPresentationInfo(s.trustedPresentationThresholds, - s.trustedPresentationListener)) { - flags |= eTraversalNeeded; - } - } - - if (what & layer_state_t::eFlushJankData) { - // Do nothing. Processing the transaction completed listeners currently cause the flush. - } - - if (layer->setTransactionCompletedListeners(callbackHandles, - layer->willPresentCurrentTransaction() || - layer->willReleaseBufferOnLatch())) { - flags |= eTraversalNeeded; - } - - // Do not put anything that updates layer state or modifies flags after - // setTransactionCompletedListener - - // if the layer has been parented on to a new display, update its transform hint. - if (((flags & eTransformHintUpdateNeeded) == 0) && - oldLayerStack != layer->getLayerStack(LayerVector::StateSet::Current)) { - flags |= eTransformHintUpdateNeeded; - } - - return flags; -} - uint32_t SurfaceFlinger::updateLayerCallbacksAndStats(const FrameTimelineInfo& frameTimelineInfo, ResolvedComposerState& composerState, int64_t desiredPresentTime, @@ -5879,10 +4985,8 @@ uint32_t SurfaceFlinger::updateLayerCallbacksAndStats(const FrameTimelineInfo& f } if (layer == nullptr) { for (auto& [listener, callbackIds] : s.listeners) { - mTransactionCallbackInvoker.addCallbackHandle(sp<CallbackHandle>::make(listener, - callbackIds, - s.surface), - std::vector<JankData>()); + mTransactionCallbackInvoker.addCallbackHandle( + sp<CallbackHandle>::make(listener, callbackIds, s.surface)); } return 0; } @@ -5897,6 +5001,17 @@ uint32_t SurfaceFlinger::updateLayerCallbacksAndStats(const FrameTimelineInfo& f sp<CallbackHandle>::make(listener, callbackIds, s.surface)); } } + + frontend::LayerSnapshot* snapshot = nullptr; + gui::GameMode gameMode = gui::GameMode::Unsupported; + if (what & (layer_state_t::eSidebandStreamChanged | layer_state_t::eBufferChanged) || + frameTimelineInfo.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID) { + snapshot = mLayerSnapshotBuilder.getSnapshot(layer->sequence); + if (snapshot) { + gameMode = snapshot->gameMode; + } + } + // TODO(b/238781169) remove after screenshot refactor, currently screenshots // requires to read drawing state from binder thread. So we need to fix that // before removing this. @@ -5911,7 +5026,7 @@ uint32_t SurfaceFlinger::updateLayerCallbacksAndStats(const FrameTimelineInfo& f if (layer->setCrop(s.crop)) flags |= eTraversalNeeded; } if (what & layer_state_t::eSidebandStreamChanged) { - if (layer->setSidebandStream(s.sidebandStream, frameTimelineInfo, postTime)) + if (layer->setSidebandStream(s.sidebandStream, frameTimelineInfo, postTime, gameMode)) flags |= eTraversalNeeded; } if (what & layer_state_t::eDataspaceChanged) { @@ -5929,18 +5044,17 @@ uint32_t SurfaceFlinger::updateLayerCallbacksAndStats(const FrameTimelineInfo& f } if (what & layer_state_t::eBufferChanged) { std::optional<ui::Transform::RotationFlags> transformHint = std::nullopt; - frontend::LayerSnapshot* snapshot = mLayerSnapshotBuilder.getSnapshot(layer->sequence); if (snapshot) { transformHint = snapshot->transformHint; } layer->setTransformHint(transformHint); if (layer->setBuffer(composerState.externalTexture, *s.bufferData, postTime, - desiredPresentTime, isAutoTimestamp, frameTimelineInfo)) { + desiredPresentTime, isAutoTimestamp, frameTimelineInfo, gameMode)) { flags |= eTraversalNeeded; } - mLayersWithQueuedFrames.emplace(layer); + mLayersWithQueuedFrames.emplace(layer, gameMode); } else if (frameTimelineInfo.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID) { - layer->setFrameTimelineVsyncForBufferlessTransaction(frameTimelineInfo, postTime); + layer->setFrameTimelineVsyncForBufferlessTransaction(frameTimelineInfo, postTime, gameMode); } if ((what & layer_state_t::eBufferChanged) == 0) { @@ -5954,6 +5068,10 @@ uint32_t SurfaceFlinger::updateLayerCallbacksAndStats(const FrameTimelineInfo& f } } + if (what & layer_state_t::eBufferReleaseChannelChanged) { + layer->setBufferReleaseChannel(s.bufferReleaseChannel); + } + const auto& requestedLayerState = mLayerLifecycleManager.getLayerFromId(layer->getSequence()); bool willPresentCurrentTransaction = requestedLayerState && (requestedLayerState->hasReadyFrame() || @@ -5992,8 +5110,6 @@ status_t SurfaceFlinger::mirrorLayer(const LayerCreationArgs& args, if (result != NO_ERROR) { return result; } - - mirrorLayer->setClonedChild(mirrorFrom->createClone()); } outResult.layerId = mirrorLayer->sequence; @@ -6107,31 +5223,22 @@ status_t SurfaceFlinger::createEffectLayer(const LayerCreationArgs& args, sp<IBi return NO_ERROR; } -void SurfaceFlinger::markLayerPendingRemovalLocked(const sp<Layer>& layer) { - mLayersPendingRemoval.add(layer); - mLayersRemoved = true; - setTransactionFlags(eTransactionNeeded); -} - void SurfaceFlinger::onHandleDestroyed(BBinder* handle, sp<Layer>& layer, uint32_t layerId) { { - std::scoped_lock<std::mutex> lock(mCreatedLayersLock); - mDestroyedHandles.emplace_back(layerId, layer->getDebugName()); - } - - { // Used to remove stalled transactions which uses an internal lock. ftl::FakeGuard guard(kMainThreadContext); mTransactionHandler.onLayerDestroyed(layerId); } + JankTracker::flushJankData(layerId); - Mutex::Autolock lock(mStateLock); - markLayerPendingRemovalLocked(layer); + std::scoped_lock<std::mutex> lock(mCreatedLayersLock); + mDestroyedHandles.emplace_back(layerId, layer->getDebugName()); + + Mutex::Autolock stateLock(mStateLock); layer->onHandleDestroyed(); mBufferCountTracker.remove(handle); layer.clear(); - - setTransactionFlags(eTransactionFlushNeeded); + setTransactionFlags(eTransactionFlushNeeded | eTransactionNeeded); } void SurfaceFlinger::initializeDisplays() { @@ -6402,15 +5509,23 @@ status_t SurfaceFlinger::doDump(int fd, const DumpArgs& args, bool asProto) { return NO_ERROR; } - // Traversal of drawing state must happen on the main thread. - // Otherwise, SortedVector may have shared ownership during concurrent - // traversals, which can result in use-after-frees. + // Collect debug data from main thread std::string compositionLayers; mScheduler ->schedule([&]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD(kMainThreadContext) { dumpVisibleFrontEnd(compositionLayers); }) .get(); + // get window info listener data without the state lock + auto windowInfosDebug = mWindowInfosListenerInvoker->getDebugInfo(); + compositionLayers.append("Window Infos:\n"); + StringAppendF(&compositionLayers, " max send vsync id: %" PRId64 "\n", + ftl::to_underlying(windowInfosDebug.maxSendDelayVsyncId)); + StringAppendF(&compositionLayers, " max send delay (ns): %" PRId64 " ns\n", + windowInfosDebug.maxSendDelayDuration); + StringAppendF(&compositionLayers, " unsent messages: %zu\n", + windowInfosDebug.pendingMessageCount); + compositionLayers.append("\n"); dumpAll(args, compositionLayers, result); write(fd, result.c_str(), result.size()); return NO_ERROR; @@ -6427,7 +5542,7 @@ void SurfaceFlinger::listLayers(std::string& result) const { } void SurfaceFlinger::dumpStats(const DumpArgs& args, std::string& result) const { - StringAppendF(&result, "%" PRId64 "\n", getVsyncPeriodFromHWC()); + StringAppendF(&result, "%" PRId64 "\n", mScheduler->getPacesetterVsyncPeriod().ns()); if (args.size() < 2) return; const auto name = String8(args[1]); @@ -6462,8 +5577,8 @@ void SurfaceFlinger::logFrameStats(TimePoint now) { if (now - sTimestamp < 30min) return; sTimestamp = now; - ATRACE_CALL(); - mDrawingState.traverse([&](Layer* layer) { layer->logFrameStats(); }); + SFTRACE_CALL(); + traverseLegacyLayers([&](Layer* layer) { layer->logFrameStats(); }); } void SurfaceFlinger::appendSfConfigString(std::string& result) const { @@ -6487,11 +5602,6 @@ void SurfaceFlinger::dumpScheduler(std::string& result) const { // TODO(b/241285876): Move to DisplayModeController. dumper.dump("debugDisplayModeSetByBackdoor"sv, mDebugDisplayModeSetByBackdoor); dumper.eol(); - - StringAppendF(&result, - " present offset: %9" PRId64 " ns\t VSYNC period: %9" PRId64 - " ns\n\n", - dispSyncPresentTimeOffset, getVsyncPeriodFromHWC()); } void SurfaceFlinger::dumpEvents(std::string& result) const { @@ -6654,53 +5764,35 @@ void SurfaceFlinger::dumpFrontEnd(std::string& result) { } void SurfaceFlinger::dumpVisibleFrontEnd(std::string& result) { - if (!mLayerLifecycleManagerEnabled) { - StringAppendF(&result, "Composition layers\n"); - mDrawingState.traverseInZOrder([&](Layer* layer) { - auto* compositionState = layer->getCompositionState(); - if (!compositionState || !compositionState->isVisible) return; - android::base::StringAppendF(&result, "* Layer %p (%s)\n", layer, - layer->getDebugName() ? layer->getDebugName() - : "<unknown>"); - compositionState->dump(result); - }); - - StringAppendF(&result, "Offscreen Layers\n"); - for (Layer* offscreenLayer : mOffscreenLayers) { - offscreenLayer->traverse(LayerVector::StateSet::Drawing, - [&](Layer* layer) { layer->dumpOffscreenDebugInfo(result); }); - } - } else { - std::ostringstream out; - out << "\nComposition list\n"; - ui::LayerStack lastPrintedLayerStackHeader = ui::INVALID_LAYER_STACK; - mLayerSnapshotBuilder.forEachVisibleSnapshot( - [&](std::unique_ptr<frontend::LayerSnapshot>& snapshot) { - if (snapshot->hasSomethingToDraw()) { - if (lastPrintedLayerStackHeader != snapshot->outputFilter.layerStack) { - lastPrintedLayerStackHeader = snapshot->outputFilter.layerStack; - out << "LayerStack=" << lastPrintedLayerStackHeader.id << "\n"; - } - out << " " << *snapshot << "\n"; + std::ostringstream out; + out << "\nComposition list\n"; + ui::LayerStack lastPrintedLayerStackHeader = ui::INVALID_LAYER_STACK; + mLayerSnapshotBuilder.forEachVisibleSnapshot( + [&](std::unique_ptr<frontend::LayerSnapshot>& snapshot) { + if (snapshot->hasSomethingToDraw()) { + if (lastPrintedLayerStackHeader != snapshot->outputFilter.layerStack) { + lastPrintedLayerStackHeader = snapshot->outputFilter.layerStack; + out << "LayerStack=" << lastPrintedLayerStackHeader.id << "\n"; } - }); + out << " " << *snapshot << "\n"; + } + }); - out << "\nInput list\n"; - lastPrintedLayerStackHeader = ui::INVALID_LAYER_STACK; - mLayerSnapshotBuilder.forEachInputSnapshot([&](const frontend::LayerSnapshot& snapshot) { - if (lastPrintedLayerStackHeader != snapshot.outputFilter.layerStack) { - lastPrintedLayerStackHeader = snapshot.outputFilter.layerStack; - out << "LayerStack=" << lastPrintedLayerStackHeader.id << "\n"; - } - out << " " << snapshot << "\n"; - }); + out << "\nInput list\n"; + lastPrintedLayerStackHeader = ui::INVALID_LAYER_STACK; + mLayerSnapshotBuilder.forEachInputSnapshot([&](const frontend::LayerSnapshot& snapshot) { + if (lastPrintedLayerStackHeader != snapshot.outputFilter.layerStack) { + lastPrintedLayerStackHeader = snapshot.outputFilter.layerStack; + out << "LayerStack=" << lastPrintedLayerStackHeader.id << "\n"; + } + out << " " << snapshot << "\n"; + }); - out << "\nLayer Hierarchy\n" - << mLayerHierarchyBuilder.getHierarchy() << "\nOffscreen Hierarchy\n" - << mLayerHierarchyBuilder.getOffscreenHierarchy() << "\n\n"; - result = out.str(); - dumpHwcLayersMinidump(result); - } + out << "\nLayer Hierarchy\n" + << mLayerHierarchyBuilder.getHierarchy() << "\nOffscreen Hierarchy\n" + << mLayerHierarchyBuilder.getOffscreenHierarchy() << "\n\n"; + result = out.str(); + dumpHwcLayersMinidump(result); } perfetto::protos::LayersProto SurfaceFlinger::dumpDrawingStateProto(uint32_t traceFlags) const { @@ -6715,9 +5807,16 @@ perfetto::protos::LayersProto SurfaceFlinger::dumpDrawingStateProto(uint32_t tra } } - return LayerProtoFromSnapshotGenerator(mLayerSnapshotBuilder, mFrontEndDisplayInfos, - mLegacyLayers, traceFlags) - .generate(mLayerHierarchyBuilder.getHierarchy()); + auto traceGenerator = + LayerProtoFromSnapshotGenerator(mLayerSnapshotBuilder, mFrontEndDisplayInfos, + mLegacyLayers, traceFlags) + .with(mLayerHierarchyBuilder.getHierarchy()); + + if (traceFlags & LayerTracing::Flag::TRACE_EXTRA) { + traceGenerator.withOffscreenLayers(mLayerHierarchyBuilder.getOffscreenHierarchy()); + } + + return traceGenerator.generate(); } google::protobuf::RepeatedPtrField<perfetto::protos::DisplayProto> @@ -6751,26 +5850,6 @@ void SurfaceFlinger::dumpHwc(std::string& result) const { getHwComposer().dump(result); } -void SurfaceFlinger::dumpOffscreenLayersProto(perfetto::protos::LayersProto& layersProto, - uint32_t traceFlags) const { - // Add a fake invisible root layer to the proto output and parent all the offscreen layers to - // it. - perfetto::protos::LayerProto* rootProto = layersProto.add_layers(); - const int32_t offscreenRootLayerId = INT32_MAX - 2; - rootProto->set_id(offscreenRootLayerId); - rootProto->set_name("Offscreen Root"); - rootProto->set_parent(-1); - - for (Layer* offscreenLayer : mOffscreenLayers) { - // Add layer as child of the fake root - rootProto->add_children(offscreenLayer->sequence); - - // Add layer - auto* layerProto = offscreenLayer->writeToProto(layersProto, traceFlags); - layerProto->set_parent(offscreenRootLayerId); - } -} - perfetto::protos::LayersProto SurfaceFlinger::dumpProtoFromMainThread(uint32_t traceFlags) { return mScheduler ->schedule([=, this]() FTL_FAKE_GUARD(kMainThreadContext) { @@ -6779,41 +5858,7 @@ perfetto::protos::LayersProto SurfaceFlinger::dumpProtoFromMainThread(uint32_t t .get(); } -void SurfaceFlinger::dumpOffscreenLayers(std::string& result) { - auto future = mScheduler->schedule([this] { - std::string result; - for (Layer* offscreenLayer : mOffscreenLayers) { - offscreenLayer->traverse(LayerVector::StateSet::Drawing, - [&](Layer* layer) { layer->dumpOffscreenDebugInfo(result); }); - } - return result; - }); - - result.append("Offscreen Layers:\n"); - result.append(future.get()); -} - -void SurfaceFlinger::dumpHwcLayersMinidumpLockedLegacy(std::string& result) const { - for (const auto& [token, display] : mDisplays) { - const auto displayId = HalDisplayId::tryCast(display->getId()); - if (!displayId) { - continue; - } - - StringAppendF(&result, "Display %s (%s) HWC layers:\n", to_string(*displayId).c_str(), - displayId == mActiveDisplayId ? "active" : "inactive"); - Layer::miniDumpHeader(result); - - const DisplayDevice& ref = *display; - mDrawingState.traverseInZOrder([&](Layer* layer) { layer->miniDumpLegacy(result, ref); }); - result.append("\n"); - } -} - void SurfaceFlinger::dumpHwcLayersMinidump(std::string& result) const { - if (!mLayerLifecycleManagerEnabled) { - return dumpHwcLayersMinidumpLockedLegacy(result); - } for (const auto& [token, display] : mDisplays) { const auto displayId = HalDisplayId::tryCast(display->getId()); if (!displayId) { @@ -6893,8 +5938,7 @@ void SurfaceFlinger::dumpAll(const DumpArgs& args, const std::string& compositio * Dump the visible layer list */ colorizer.bold(result); - StringAppendF(&result, "SurfaceFlinger New Frontend Enabled:%s\n", - mLayerLifecycleManagerEnabled ? "true" : "false"); + StringAppendF(&result, "SurfaceFlinger New Frontend Enabled:%s\n", "true"); StringAppendF(&result, "Active Layers - layers with client handles (count = %zu)\n", mNumLayers.load()); colorizer.reset(result); @@ -6993,15 +6037,6 @@ void SurfaceFlinger::dumpAll(const DumpArgs& args, const std::string& compositio result.append(mTimeStats->miniDump()); result.append("\n"); - - result.append("Window Infos:\n"); - auto windowInfosDebug = mWindowInfosListenerInvoker->getDebugInfo(); - StringAppendF(&result, " max send vsync id: %" PRId64 "\n", - ftl::to_underlying(windowInfosDebug.maxSendDelayVsyncId)); - StringAppendF(&result, " max send delay (ns): %" PRId64 " ns\n", - windowInfosDebug.maxSendDelayDuration); - StringAppendF(&result, " unsent messages: %zu\n", windowInfosDebug.pendingMessageCount); - result.append("\n"); } mat4 SurfaceFlinger::calculateColorMatrix(float saturation) { @@ -7495,15 +6530,9 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r return NO_ERROR; } case 1039: { - PhysicalDisplayId displayId = [&]() { - Mutex::Autolock lock(mStateLock); - return getDefaultDisplayDeviceLocked()->getPhysicalId(); - }(); - - auto inUid = static_cast<uid_t>(data.readInt32()); + const auto uid = static_cast<uid_t>(data.readInt32()); const auto refreshRate = data.readFloat(); - mScheduler->setPreferredRefreshRateForUid(FrameRateOverride{inUid, refreshRate}); - mScheduler->onFrameRateOverridesChanged(scheduler::Cycle::Render, displayId); + mScheduler->setPreferredRefreshRateForUid(FrameRateOverride{uid, refreshRate}); return NO_ERROR; } // Toggle caching feature @@ -7692,7 +6721,7 @@ void SurfaceFlinger::kernelTimerChanged(bool expired) { // Update the overlay on the main thread to avoid race conditions with // RefreshRateSelector::getActiveMode - static_cast<void>(mScheduler->schedule([=, this] { + static_cast<void>(mScheduler->schedule([=, this]() FTL_FAKE_GUARD(kMainThreadContext) { const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked()); if (!display) { ALOGW("%s: default display is null", __func__); @@ -7700,15 +6729,9 @@ void SurfaceFlinger::kernelTimerChanged(bool expired) { } if (!display->isRefreshRateOverlayEnabled()) return; - const auto desiredModeIdOpt = - mDisplayModeController.getDesiredMode(display->getPhysicalId()) - .transform([](const display::DisplayModeRequest& request) { - return request.mode.modePtr->getId(); - }); - - const bool timerExpired = mKernelIdleTimerEnabled && expired; + const auto state = mDisplayModeController.getKernelIdleTimerState(display->getPhysicalId()); - if (display->onKernelTimerChanged(desiredModeIdOpt, timerExpired)) { + if (display->onKernelTimerChanged(state.desiredModeIdOpt, state.isEnabled && expired)) { mScheduler->scheduleFrame(); } })); @@ -7730,8 +6753,8 @@ void SurfaceFlinger::vrrDisplayIdle(bool idle) { })); } -std::pair<std::optional<KernelIdleTimerController>, std::chrono::milliseconds> -SurfaceFlinger::getKernelIdleTimerProperties(PhysicalDisplayId displayId) { +auto SurfaceFlinger::getKernelIdleTimerProperties(PhysicalDisplayId displayId) + -> std::pair<std::optional<KernelIdleTimerController>, std::chrono::milliseconds> { const bool isKernelIdleTimerHwcSupported = getHwComposer().getComposer()->isSupported( android::Hwc2::Composer::OptionalFeature::KernelIdleTimer); const auto timeout = getIdleTimerTimeout(displayId); @@ -7755,63 +6778,6 @@ SurfaceFlinger::getKernelIdleTimerProperties(PhysicalDisplayId displayId) { return {std::nullopt, timeout}; } -void SurfaceFlinger::updateKernelIdleTimer(std::chrono::milliseconds timeout, - KernelIdleTimerController controller, - PhysicalDisplayId displayId) { - switch (controller) { - case KernelIdleTimerController::HwcApi: { - getHwComposer().setIdleTimerEnabled(displayId, timeout); - break; - } - case KernelIdleTimerController::Sysprop: { - base::SetProperty(KERNEL_IDLE_TIMER_PROP, timeout > 0ms ? "true" : "false"); - break; - } - } -} - -void SurfaceFlinger::toggleKernelIdleTimer() { - using KernelIdleTimerAction = scheduler::RefreshRateSelector::KernelIdleTimerAction; - - const auto display = getDefaultDisplayDeviceLocked(); - if (!display) { - ALOGW("%s: default display is null", __func__); - return; - } - - // If the support for kernel idle timer is disabled for the active display, - // don't do anything. - const std::optional<KernelIdleTimerController> kernelIdleTimerController = - display->refreshRateSelector().kernelIdleTimerController(); - if (!kernelIdleTimerController.has_value()) { - return; - } - - const KernelIdleTimerAction action = display->refreshRateSelector().getIdleTimerAction(); - - switch (action) { - case KernelIdleTimerAction::TurnOff: - if (mKernelIdleTimerEnabled) { - ATRACE_INT("KernelIdleTimer", 0); - std::chrono::milliseconds constexpr kTimerDisabledTimeout = 0ms; - updateKernelIdleTimer(kTimerDisabledTimeout, kernelIdleTimerController.value(), - display->getPhysicalId()); - mKernelIdleTimerEnabled = false; - } - break; - case KernelIdleTimerAction::TurnOn: - if (!mKernelIdleTimerEnabled) { - ATRACE_INT("KernelIdleTimer", 1); - const std::chrono::milliseconds timeout = - display->refreshRateSelector().getIdleTimerTimeout(); - updateKernelIdleTimer(timeout, kernelIdleTimerController.value(), - display->getPhysicalId()); - mKernelIdleTimerEnabled = true; - } - break; - } -} - // A simple RAII class to disconnect from an ANativeWindow* when it goes out of scope class WindowDisconnector { public: @@ -7837,7 +6803,8 @@ static status_t validateScreenshotPermissions(const CaptureArgs& captureArgs) { IPCThreadState* ipc = IPCThreadState::self(); const int pid = ipc->getCallingPid(); const int uid = ipc->getCallingUid(); - if (uid == AID_GRAPHICS || PermissionCache::checkPermission(sReadFramebuffer, pid, uid)) { + if (uid == AID_GRAPHICS || uid == AID_SYSTEM || + PermissionCache::checkPermission(sReadFramebuffer, pid, uid)) { return OK; } @@ -7930,9 +6897,10 @@ static void invokeScreenCaptureError(const status_t status, void SurfaceFlinger::captureDisplay(const DisplayCaptureArgs& args, const sp<IScreenCaptureListener>& captureListener) { - ATRACE_CALL(); + SFTRACE_CALL(); - status_t validate = validateScreenshotPermissions(args); + const auto& captureArgs = args.captureArgs; + status_t validate = validateScreenshotPermissions(captureArgs); if (validate != OK) { ALOGD("Permission denied to captureDisplay"); invokeScreenCaptureError(validate, captureListener); @@ -7945,7 +6913,7 @@ void SurfaceFlinger::captureDisplay(const DisplayCaptureArgs& args, return; } - if (args.captureSecureLayers && !hasCaptureBlackoutContentPermission()) { + if (captureArgs.captureSecureLayers && !hasCaptureBlackoutContentPermission()) { ALOGD("Attempting to capture secure layers without CAPTURE_BLACKOUT_CONTENT"); invokeScreenCaptureError(PERMISSION_DENIED, captureListener); return; @@ -7971,7 +6939,7 @@ void SurfaceFlinger::captureDisplay(const DisplayCaptureArgs& args, reqSize = display->getLayerStackSpaceRect().getSize(); } - for (const auto& handle : args.excludeHandles) { + for (const auto& handle : captureArgs.excludeHandles) { uint32_t excludeLayer = LayerHandle::getLayerId(handle); if (excludeLayer != UNASSIGNED_LAYER_ID) { excludeLayerIds.emplace(excludeLayer); @@ -7984,17 +6952,22 @@ void SurfaceFlinger::captureDisplay(const DisplayCaptureArgs& args, } GetLayerSnapshotsFunction getLayerSnapshotsFn = - getLayerSnapshotsForScreenshots(layerStack, args.uid, std::move(excludeLayerIds)); + getLayerSnapshotsForScreenshots(layerStack, captureArgs.uid, + std::move(excludeLayerIds)); ftl::Flags<RenderArea::Options> options; - if (args.captureSecureLayers) options |= RenderArea::Options::CAPTURE_SECURE_LAYERS; - if (args.hintForSeamlessTransition) + if (captureArgs.captureSecureLayers) options |= RenderArea::Options::CAPTURE_SECURE_LAYERS; + if (captureArgs.hintForSeamlessTransition) options |= RenderArea::Options::HINT_FOR_SEAMLESS_TRANSITION; captureScreenCommon(RenderAreaBuilderVariant(std::in_place_type<DisplayRenderAreaBuilder>, - args.sourceCrop, reqSize, args.dataspace, + gui::aidl_utils::fromARect(captureArgs.sourceCrop), + reqSize, + static_cast<ui::Dataspace>(captureArgs.dataspace), displayWeak, options), - getLayerSnapshotsFn, reqSize, args.pixelFormat, args.allowProtected, - args.grayscale, captureListener); + getLayerSnapshotsFn, reqSize, + static_cast<ui::PixelFormat>(captureArgs.pixelFormat), + captureArgs.allowProtected, captureArgs.grayscale, + captureArgs.attachGainmap, captureListener); } void SurfaceFlinger::captureDisplay(DisplayId displayId, const CaptureArgs& args, @@ -8047,10 +7020,11 @@ void SurfaceFlinger::captureDisplay(DisplayId displayId, const CaptureArgs& args if (args.hintForSeamlessTransition) options |= RenderArea::Options::HINT_FOR_SEAMLESS_TRANSITION; captureScreenCommon(RenderAreaBuilderVariant(std::in_place_type<DisplayRenderAreaBuilder>, - Rect(), size, args.dataspace, displayWeak, - options), - getLayerSnapshotsFn, size, args.pixelFormat, kAllowProtected, kGrayscale, - captureListener); + Rect(), size, + static_cast<ui::Dataspace>(args.dataspace), + displayWeak, options), + getLayerSnapshotsFn, size, static_cast<ui::PixelFormat>(args.pixelFormat), + kAllowProtected, kGrayscale, args.attachGainmap, captureListener); } ScreenCaptureResults SurfaceFlinger::captureLayersSync(const LayerCaptureArgs& args) { @@ -8061,22 +7035,25 @@ ScreenCaptureResults SurfaceFlinger::captureLayersSync(const LayerCaptureArgs& a void SurfaceFlinger::captureLayers(const LayerCaptureArgs& args, const sp<IScreenCaptureListener>& captureListener) { - ATRACE_CALL(); + SFTRACE_CALL(); + + const auto& captureArgs = args.captureArgs; - status_t validate = validateScreenshotPermissions(args); + status_t validate = validateScreenshotPermissions(captureArgs); if (validate != OK) { ALOGD("Permission denied to captureLayers"); invokeScreenCaptureError(validate, captureListener); return; } + auto crop = gui::aidl_utils::fromARect(captureArgs.sourceCrop); + ui::Size reqSize; sp<Layer> parent; - Rect crop(args.sourceCrop); std::unordered_set<uint32_t> excludeLayerIds; - ui::Dataspace dataspace = args.dataspace; + ui::Dataspace dataspace = static_cast<ui::Dataspace>(captureArgs.dataspace); - if (args.captureSecureLayers && !hasCaptureBlackoutContentPermission()) { + if (captureArgs.captureSecureLayers && !hasCaptureBlackoutContentPermission()) { ALOGD("Attempting to capture secure layers without CAPTURE_BLACKOUT_CONTENT"); invokeScreenCaptureError(PERMISSION_DENIED, captureListener); return; @@ -8093,26 +7070,27 @@ void SurfaceFlinger::captureLayers(const LayerCaptureArgs& args, } Rect parentSourceBounds = parent->getCroppedBufferSize(parent->getDrawingState()); - if (args.sourceCrop.width() <= 0) { + if (crop.width() <= 0) { crop.left = 0; crop.right = parentSourceBounds.getWidth(); } - if (args.sourceCrop.height() <= 0) { + if (crop.height() <= 0) { crop.top = 0; crop.bottom = parentSourceBounds.getHeight(); } - if (crop.isEmpty() || args.frameScaleX <= 0.0f || args.frameScaleY <= 0.0f) { + if (crop.isEmpty() || captureArgs.frameScaleX <= 0.0f || captureArgs.frameScaleY <= 0.0f) { // Error out if the layer has no source bounds (i.e. they are boundless) and a source // crop was not specified, or an invalid frame scale was provided. ALOGD("Boundless layer, unspecified crop, or invalid frame scale to captureLayers"); invokeScreenCaptureError(BAD_VALUE, captureListener); return; } - reqSize = ui::Size(crop.width() * args.frameScaleX, crop.height() * args.frameScaleY); + reqSize = ui::Size(crop.width() * captureArgs.frameScaleX, + crop.height() * captureArgs.frameScaleY); - for (const auto& handle : args.excludeHandles) { + for (const auto& handle : captureArgs.excludeHandles) { uint32_t excludeLayer = LayerHandle::getLayerId(handle); if (excludeLayer != UNASSIGNED_LAYER_ID) { excludeLayerIds.emplace(excludeLayer); @@ -8138,8 +7116,9 @@ void SurfaceFlinger::captureLayers(const LayerCaptureArgs& args, } GetLayerSnapshotsFunction getLayerSnapshotsFn = - getLayerSnapshotsForScreenshots(parent->sequence, args.uid, std::move(excludeLayerIds), - args.childrenOnly, parentCrop); + getLayerSnapshotsForScreenshots(parent->sequence, captureArgs.uid, + std::move(excludeLayerIds), args.childrenOnly, + parentCrop); if (captureListener == nullptr) { ALOGD("capture screen must provide a capture listener callback"); @@ -8148,14 +7127,16 @@ void SurfaceFlinger::captureLayers(const LayerCaptureArgs& args, } ftl::Flags<RenderArea::Options> options; - if (args.captureSecureLayers) options |= RenderArea::Options::CAPTURE_SECURE_LAYERS; - if (args.hintForSeamlessTransition) + if (captureArgs.captureSecureLayers) options |= RenderArea::Options::CAPTURE_SECURE_LAYERS; + if (captureArgs.hintForSeamlessTransition) options |= RenderArea::Options::HINT_FOR_SEAMLESS_TRANSITION; captureScreenCommon(RenderAreaBuilderVariant(std::in_place_type<LayerRenderAreaBuilder>, crop, reqSize, dataspace, parent, args.childrenOnly, options), - getLayerSnapshotsFn, reqSize, args.pixelFormat, args.allowProtected, - args.grayscale, captureListener); + getLayerSnapshotsFn, reqSize, + static_cast<ui::PixelFormat>(captureArgs.pixelFormat), + captureArgs.allowProtected, captureArgs.grayscale, + captureArgs.attachGainmap, captureListener); } // Creates a Future release fence for a layer and keeps track of it in a list to @@ -8164,9 +7145,7 @@ void SurfaceFlinger::captureLayers(const LayerCaptureArgs& args, void SurfaceFlinger::attachReleaseFenceFutureToLayer(Layer* layer, LayerFE* layerFE, ui::LayerStack layerStack) { ftl::Future<FenceResult> futureFence = layerFE->createReleaseFenceFuture(); - Layer* clonedFrom = layer->getClonedFrom().get(); - auto owningLayer = clonedFrom ? clonedFrom : layer; - owningLayer->prepareReleaseCallbacks(std::move(futureFence), layerStack); + layer->prepareReleaseCallbacks(std::move(futureFence), layerStack); } // Loop over all visible layers to see whether there's any protected layer. A protected layer is @@ -8189,12 +7168,12 @@ bool SurfaceFlinger::layersHasProtectedLayer(const std::vector<sp<LayerFE>>& lay // Accessing display requires mStateLock, and contention for this lock // is reduced when grabbed from the main thread, thus also reducing // risk of deadlocks. -std::optional<SurfaceFlinger::OutputCompositionState> -SurfaceFlinger::getDisplayAndLayerSnapshotsFromMainThread( +std::optional<SurfaceFlinger::OutputCompositionState> SurfaceFlinger::getSnapshotsFromMainThread( RenderAreaBuilderVariant& renderAreaBuilder, GetLayerSnapshotsFunction getLayerSnapshotsFn, std::vector<sp<LayerFE>>& layerFEs) { return mScheduler ->schedule([=, this, &renderAreaBuilder, &layerFEs]() REQUIRES(kMainThreadContext) { + SFTRACE_NAME("getSnapshotsFromMainThread"); auto layers = getLayerSnapshotsFn(); for (auto& [layer, layerFE] : layers) { attachReleaseFenceFutureToLayer(layer, layerFE.get(), ui::INVALID_LAYER_STACK); @@ -8208,9 +7187,9 @@ SurfaceFlinger::getDisplayAndLayerSnapshotsFromMainThread( void SurfaceFlinger::captureScreenCommon(RenderAreaBuilderVariant renderAreaBuilder, GetLayerSnapshotsFunction getLayerSnapshotsFn, ui::Size bufferSize, ui::PixelFormat reqPixelFormat, - bool allowProtected, bool grayscale, + bool allowProtected, bool grayscale, bool attachGainmap, const sp<IScreenCaptureListener>& captureListener) { - ATRACE_CALL(); + SFTRACE_CALL(); if (exceedsMaxRenderTargetSize(bufferSize.getWidth(), bufferSize.getHeight())) { ALOGE("Attempted to capture screen with size (%" PRId32 ", %" PRId32 @@ -8224,8 +7203,7 @@ void SurfaceFlinger::captureScreenCommon(RenderAreaBuilderVariant renderAreaBuil FlagManager::getInstance().ce_fence_promise() && mRenderEngine->isThreaded()) { std::vector<sp<LayerFE>> layerFEs; auto displayState = - getDisplayAndLayerSnapshotsFromMainThread(renderAreaBuilder, getLayerSnapshotsFn, - layerFEs); + getSnapshotsFromMainThread(renderAreaBuilder, getLayerSnapshotsFn, layerFEs); const bool supportsProtected = getRenderEngine().supportsProtectedContent(); bool hasProtectedLayer = false; @@ -8255,9 +7233,9 @@ void SurfaceFlinger::captureScreenCommon(RenderAreaBuilderVariant renderAreaBuil renderengine::impl::ExternalTexture>(buffer, getRenderEngine(), renderengine::impl::ExternalTexture::Usage:: WRITEABLE); - auto futureFence = - captureScreenshot(renderAreaBuilder, texture, false /* regionSampling */, grayscale, - isProtected, captureListener, displayState, layerFEs); + auto futureFence = captureScreenshot(renderAreaBuilder, texture, false /* regionSampling */, + grayscale, isProtected, attachGainmap, captureListener, + displayState, layerFEs); futureFence.get(); } else { @@ -8292,7 +7270,7 @@ void SurfaceFlinger::captureScreenCommon(RenderAreaBuilderVariant renderAreaBuil WRITEABLE); auto futureFence = captureScreenshotLegacy(renderAreaBuilder, getLayerSnapshotsFn, texture, false /* regionSampling */, grayscale, - isProtected, captureListener); + isProtected, attachGainmap, captureListener); futureFence.get(); } } @@ -8346,9 +7324,10 @@ std::vector<sp<LayerFE>> SurfaceFlinger::extractLayerFEs( ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshot( const RenderAreaBuilderVariant& renderAreaBuilder, const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling, - bool grayscale, bool isProtected, const sp<IScreenCaptureListener>& captureListener, + bool grayscale, bool isProtected, bool attachGainmap, + const sp<IScreenCaptureListener>& captureListener, std::optional<OutputCompositionState>& displayState, std::vector<sp<LayerFE>>& layerFEs) { - ATRACE_CALL(); + SFTRACE_CALL(); ScreenCaptureResults captureResults; std::unique_ptr<const RenderArea> renderArea = @@ -8363,19 +7342,87 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshot( } return ftl::yield<FenceResult>(base::unexpected(NO_ERROR)).share(); } + float displayBrightnessNits = displayState.value().displayBrightnessNits; + float sdrWhitePointNits = displayState.value().sdrWhitePointNits; // Empty vector needed to pass into renderScreenImpl for legacy path std::vector<std::pair<Layer*, sp<android::LayerFE>>> layers; ftl::SharedFuture<FenceResult> renderFuture = - renderScreenImpl(std::move(renderArea), buffer, regionSampling, grayscale, isProtected, - captureResults, displayState, layers, layerFEs); + renderScreenImpl(renderArea.get(), buffer, regionSampling, grayscale, isProtected, + attachGainmap, captureResults, displayState, layers, layerFEs); + + if (captureResults.capturedHdrLayers && attachGainmap && + FlagManager::getInstance().true_hdr_screenshots()) { + sp<GraphicBuffer> hdrBuffer = + getFactory().createGraphicBuffer(buffer->getWidth(), buffer->getHeight(), + HAL_PIXEL_FORMAT_RGBA_FP16, 1 /* layerCount */, + buffer->getUsage(), "screenshot-hdr"); + sp<GraphicBuffer> gainmapBuffer = + getFactory().createGraphicBuffer(buffer->getWidth(), buffer->getHeight(), + buffer->getPixelFormat(), 1 /* layerCount */, + buffer->getUsage(), "screenshot-gainmap"); + + const status_t bufferStatus = hdrBuffer->initCheck(); + const status_t gainmapBufferStatus = gainmapBuffer->initCheck(); + + if (bufferStatus != OK) { + ALOGW("%s: Buffer failed to allocate for hdr: %d. Screenshoting SDR instead.", __func__, + bufferStatus); + } else if (gainmapBufferStatus != OK) { + ALOGW("%s: Buffer failed to allocate for gainmap: %d. Screenshoting SDR instead.", + __func__, gainmapBufferStatus); + } else { + captureResults.optionalGainMap = gainmapBuffer; + const auto hdrTexture = std::make_shared< + renderengine::impl::ExternalTexture>(hdrBuffer, getRenderEngine(), + renderengine::impl::ExternalTexture:: + Usage::WRITEABLE); + const auto gainmapTexture = std::make_shared< + renderengine::impl::ExternalTexture>(gainmapBuffer, getRenderEngine(), + renderengine::impl::ExternalTexture:: + Usage::WRITEABLE); + ScreenCaptureResults unusedResults; + ftl::SharedFuture<FenceResult> hdrRenderFuture = + renderScreenImpl(renderArea.get(), hdrTexture, regionSampling, grayscale, + isProtected, attachGainmap, unusedResults, displayState, + layers, layerFEs); + + renderFuture = + ftl::Future(std::move(renderFuture)) + .then([&, hdrRenderFuture = std::move(hdrRenderFuture), + displayBrightnessNits, sdrWhitePointNits, + dataspace = captureResults.capturedDataspace, buffer, hdrTexture, + gainmapTexture](FenceResult fenceResult) -> FenceResult { + if (!fenceResult.ok()) { + return fenceResult; + } + + auto hdrFenceResult = hdrRenderFuture.get(); + + if (!hdrFenceResult.ok()) { + return hdrFenceResult; + } + + return getRenderEngine() + .drawGainmap(buffer, fenceResult.value()->get(), hdrTexture, + hdrFenceResult.value()->get(), + displayBrightnessNits / sdrWhitePointNits, + static_cast<ui::Dataspace>(dataspace), + gainmapTexture) + .get(); + }) + .share(); + }; + } if (captureListener) { // Defer blocking on renderFuture back to the Binder thread. return ftl::Future(std::move(renderFuture)) - .then([captureListener, captureResults = std::move(captureResults)]( - FenceResult fenceResult) mutable -> FenceResult { + .then([captureListener, captureResults = std::move(captureResults), + displayBrightnessNits, + sdrWhitePointNits](FenceResult fenceResult) mutable -> FenceResult { captureResults.fenceResult = std::move(fenceResult); + captureResults.hdrSdrRatio = displayBrightnessNits / sdrWhitePointNits; captureListener->onScreenCaptureCompleted(captureResults); return base::unexpected(NO_ERROR); }) @@ -8387,8 +7434,9 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshot( ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshotLegacy( RenderAreaBuilderVariant renderAreaBuilder, GetLayerSnapshotsFunction getLayerSnapshotsFn, const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling, - bool grayscale, bool isProtected, const sp<IScreenCaptureListener>& captureListener) { - ATRACE_CALL(); + bool grayscale, bool isProtected, bool attachGainmap, + const sp<IScreenCaptureListener>& captureListener) { + SFTRACE_CALL(); auto takeScreenshotFn = [=, this, renderAreaBuilder = std::move(renderAreaBuilder)]() REQUIRES( kMainThreadContext) mutable -> ftl::SharedFuture<FenceResult> { @@ -8416,8 +7464,8 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshotLegacy( auto layerFEs = extractLayerFEs(layers); ftl::SharedFuture<FenceResult> renderFuture = - renderScreenImpl(std::move(renderArea), buffer, regionSampling, grayscale, - isProtected, captureResults, displayState, layers, layerFEs); + renderScreenImpl(renderArea.get(), buffer, regionSampling, grayscale, isProtected, + attachGainmap, captureResults, displayState, layers, layerFEs); if (captureListener) { // Defer blocking on renderFuture back to the Binder thread. @@ -8447,12 +7495,11 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshotLegacy( } ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl( - std::unique_ptr<const RenderArea> renderArea, - const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling, - bool grayscale, bool isProtected, ScreenCaptureResults& captureResults, - std::optional<OutputCompositionState>& displayState, + const RenderArea* renderArea, const std::shared_ptr<renderengine::ExternalTexture>& buffer, + bool regionSampling, bool grayscale, bool isProtected, bool attachGainmap, + ScreenCaptureResults& captureResults, std::optional<OutputCompositionState>& displayState, std::vector<std::pair<Layer*, sp<LayerFE>>>& layers, std::vector<sp<LayerFE>>& layerFEs) { - ATRACE_CALL(); + SFTRACE_CALL(); for (auto& layerFE : layerFEs) { frontend::LayerSnapshot* snapshot = layerFE->mSnapshot.get(); @@ -8628,65 +7675,13 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl( } void SurfaceFlinger::traverseLegacyLayers(const LayerVector::Visitor& visitor) const { - if (mLayerLifecycleManagerEnabled) { - for (auto& layer : mLegacyLayers) { - visitor(layer.second.get()); - } - } else { - mDrawingState.traverse(visitor); + for (auto& layer : mLegacyLayers) { + visitor(layer.second.get()); } } // --------------------------------------------------------------------------- -void SurfaceFlinger::State::traverse(const LayerVector::Visitor& visitor) const { - layersSortedByZ.traverse(visitor); -} - -void SurfaceFlinger::State::traverseInZOrder(const LayerVector::Visitor& visitor) const { - layersSortedByZ.traverseInZOrder(stateSet, visitor); -} - -void SurfaceFlinger::State::traverseInReverseZOrder(const LayerVector::Visitor& visitor) const { - layersSortedByZ.traverseInReverseZOrder(stateSet, visitor); -} - -void SurfaceFlinger::traverseLayersInLayerStack(ui::LayerStack layerStack, const int32_t uid, - std::unordered_set<uint32_t> excludeLayerIds, - const LayerVector::Visitor& visitor) { - // We loop through the first level of layers without traversing, - // as we need to determine which layers belong to the requested display. - for (const auto& layer : mDrawingState.layersSortedByZ) { - if (layer->getLayerStack() != layerStack) { - continue; - } - // relative layers are traversed in Layer::traverseInZOrder - layer->traverseInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) { - if (layer->isInternalDisplayOverlay()) { - return; - } - if (!layer->isVisible()) { - return; - } - if (uid != CaptureArgs::UNSET_UID && layer->getOwnerUid() != uid) { - return; - } - - if (!excludeLayerIds.empty()) { - auto p = sp<Layer>::fromExisting(layer); - while (p != nullptr) { - if (excludeLayerIds.count(p->sequence) != 0) { - return; - } - p = p->getParent(); - } - } - - visitor(layer); - }); - } -} - ftl::Optional<scheduler::FrameRateMode> SurfaceFlinger::getPreferredDisplayMode( PhysicalDisplayId displayId, DisplayModeId defaultModeId) const { if (const auto schedulerMode = mScheduler->getPreferredDisplayMode(); @@ -8708,7 +7703,7 @@ status_t SurfaceFlinger::setDesiredDisplayModeSpecsInternal( const sp<DisplayDevice>& display, const scheduler::RefreshRateSelector::PolicyVariant& policy) { const auto displayId = display->getPhysicalId(); - ATRACE_NAME(ftl::Concat(__func__, ' ', displayId.value).c_str()); + SFTRACE_NAME(ftl::Concat(__func__, ' ', displayId.value).c_str()); Mutex::Autolock lock(mStateLock); @@ -8737,13 +7732,9 @@ status_t SurfaceFlinger::applyRefreshRateSelectorPolicy( const scheduler::RefreshRateSelector::Policy currentPolicy = selector.getCurrentPolicy(); ALOGV("Setting desired display mode specs: %s", currentPolicy.toString().c_str()); - // TODO(b/140204874): Leave the event in until we do proper testing with all apps that might - // be depending in this callback. - if (const auto activeMode = selector.getActiveMode(); displayId == mActiveDisplayId) { - mScheduler->onPrimaryDisplayModeChanged(scheduler::Cycle::Render, activeMode); - toggleKernelIdleTimer(); - } else { - mScheduler->onNonPrimaryDisplayModeChanged(scheduler::Cycle::Render, activeMode); + if (const bool isPacesetter = + mScheduler->onDisplayModeChanged(displayId, selector.getActiveMode())) { + mDisplayModeController.updateKernelIdleTimer(displayId); } auto preferredModeOpt = getPreferredDisplayMode(displayId, currentPolicy.defaultMode); @@ -8767,10 +7758,7 @@ status_t SurfaceFlinger::applyRefreshRateSelectorPolicy( setDesiredMode({std::move(preferredMode), .emitEvent = true}); // Update the frameRateOverride list as the display render rate might have changed - if (mScheduler->updateFrameRateOverrides(scheduler::GlobalSignals{}, preferredFps)) { - triggerOnFrameRateOverridesChanged(); - } - + mScheduler->updateFrameRateOverrides(scheduler::GlobalSignals{}, preferredFps); return NO_ERROR; } @@ -8801,7 +7789,7 @@ gui::DisplayModeSpecs::RefreshRateRanges translate(const FpsRanges& ranges) { status_t SurfaceFlinger::setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, const gui::DisplayModeSpecs& specs) { - ATRACE_CALL(); + SFTRACE_CALL(); if (!displayToken) { return BAD_VALUE; @@ -8835,7 +7823,7 @@ status_t SurfaceFlinger::setDesiredDisplayModeSpecs(const sp<IBinder>& displayTo status_t SurfaceFlinger::getDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, gui::DisplayModeSpecs* outSpecs) { - ATRACE_CALL(); + SFTRACE_CALL(); if (!displayToken || !outSpecs) { return BAD_VALUE; @@ -8862,17 +7850,12 @@ status_t SurfaceFlinger::getDesiredDisplayModeSpecs(const sp<IBinder>& displayTo void SurfaceFlinger::onLayerFirstRef(Layer* layer) { mNumLayers++; - if (!layer->isRemovedFromCurrentState()) { - mScheduler->registerLayer(layer); - } + mScheduler->registerLayer(layer, scheduler::FrameRateCompatibility::Default); } void SurfaceFlinger::onLayerDestroyed(Layer* layer) { mNumLayers--; - removeHierarchyFromOffscreenLayers(layer); - if (!layer->isRemovedFromCurrentState()) { - mScheduler->deregisterLayer(layer); - } + mScheduler->deregisterLayer(layer); if (mTransactionTracing) { mTransactionTracing->onLayerRemoved(layer->getSequence()); } @@ -8883,24 +7866,6 @@ void SurfaceFlinger::onLayerUpdate() { scheduleCommit(FrameHint::kActive); } -// WARNING: ONLY CALL THIS FROM LAYER DTOR -// Here we add children in the current state to offscreen layers and remove the -// layer itself from the offscreen layer list. Since -// this is the dtor, it is safe to access the current state. This keeps us -// from dangling children layers such that they are not reachable from the -// Drawing state nor the offscreen layer list -// See b/141111965 -void SurfaceFlinger::removeHierarchyFromOffscreenLayers(Layer* layer) { - for (auto& child : layer->getCurrentChildren()) { - mOffscreenLayers.emplace(child.get()); - } - mOffscreenLayers.erase(layer); -} - -void SurfaceFlinger::removeFromOffscreenLayers(Layer* layer) { - mOffscreenLayers.erase(layer); -} - status_t SurfaceFlinger::setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor, float lightPosY, float lightPosZ, float lightRadius) { @@ -8930,13 +7895,7 @@ const std::unordered_map<std::string, uint32_t>& SurfaceFlinger::getGenericLayer } status_t SurfaceFlinger::setGameModeFrameRateOverride(uid_t uid, float frameRate) { - PhysicalDisplayId displayId = [&]() { - Mutex::Autolock lock(mStateLock); - return getDefaultDisplayDeviceLocked()->getPhysicalId(); - }(); - - mScheduler->setGameModeFrameRateForUid(FrameRateOverride{static_cast<uid_t>(uid), frameRate}); - mScheduler->onFrameRateOverridesChanged(scheduler::Cycle::Render, displayId); + mScheduler->setGameModeFrameRateForUid(FrameRateOverride{uid, frameRate}); return NO_ERROR; } @@ -9049,51 +8008,6 @@ int SurfaceFlinger::getMaxAcquiredBufferCountForRefreshRate(Fps refreshRate) con return calculateMaxAcquiredBufferCount(refreshRate, presentLatency); } -void SurfaceFlinger::handleLayerCreatedLocked(const LayerCreatedState& state, VsyncId vsyncId) { - sp<Layer> layer = state.layer.promote(); - if (!layer) { - ALOGD("Layer was destroyed soon after creation %p", state.layer.unsafe_get()); - return; - } - MUTEX_ALIAS(mStateLock, layer->mFlinger->mStateLock); - - sp<Layer> parent; - bool addToRoot = state.addToRoot; - if (state.initialParent != nullptr) { - parent = state.initialParent.promote(); - if (parent == nullptr) { - ALOGD("Parent was destroyed soon after creation %p", state.initialParent.unsafe_get()); - addToRoot = false; - } - } - - if (parent == nullptr && addToRoot) { - layer->setIsAtRoot(true); - mCurrentState.layersSortedByZ.add(layer); - } else if (parent == nullptr) { - layer->onRemovedFromCurrentState(); - } else if (parent->isRemovedFromCurrentState()) { - parent->addChild(layer); - layer->onRemovedFromCurrentState(); - } else { - parent->addChild(layer); - } - - ui::LayerStack layerStack = layer->getLayerStack(LayerVector::StateSet::Current); - sp<const DisplayDevice> hintDisplay; - // Find the display that includes the layer. - for (const auto& [token, display] : mDisplays) { - if (display->getLayerStack() == layerStack) { - hintDisplay = display; - break; - } - } - - if (hintDisplay) { - layer->updateTransformHint(hintDisplay->getTransformHint()); - } -} - void SurfaceFlinger::sample() { if (!mLumaSampling || !mRegionSamplingThread) { return; @@ -9127,7 +8041,7 @@ sp<DisplayDevice> SurfaceFlinger::getActivatableDisplay() const { void SurfaceFlinger::onActiveDisplayChangedLocked(const DisplayDevice* inactiveDisplayPtr, const DisplayDevice& activeDisplay) { - ATRACE_CALL(); + SFTRACE_CALL(); if (inactiveDisplayPtr) { inactiveDisplayPtr->getCompositionDisplay()->setLayerCachingTexturePoolEnabled(false); @@ -9136,8 +8050,6 @@ void SurfaceFlinger::onActiveDisplayChangedLocked(const DisplayDevice* inactiveD mActiveDisplayId = activeDisplay.getPhysicalId(); activeDisplay.getCompositionDisplay()->setLayerCachingTexturePoolEnabled(true); - mScheduler->resetPhaseConfiguration(mDisplayModeController.getActiveMode(mActiveDisplayId).fps); - // TODO(b/255635711): Check for pending mode changes on other displays. mScheduler->setModeChangePending(false); @@ -9261,170 +8173,52 @@ std::shared_ptr<renderengine::ExternalTexture> SurfaceFlinger::getExternalTextur return nullptr; } -bool SurfaceFlinger::commitMirrorDisplays(VsyncId vsyncId) { - std::vector<MirrorDisplayState> mirrorDisplays; - { - std::scoped_lock<std::mutex> lock(mMirrorDisplayLock); - mirrorDisplays = std::move(mMirrorDisplays); - mMirrorDisplays.clear(); - if (mirrorDisplays.size() == 0) { - return false; - } - } - - sp<IBinder> unused; - for (const auto& mirrorDisplay : mirrorDisplays) { - // Set mirror layer's default layer stack to -1 so it doesn't end up rendered on a display - // accidentally. - sp<Layer> rootMirrorLayer = LayerHandle::getLayer(mirrorDisplay.rootHandle); - ssize_t idx = mCurrentState.layersSortedByZ.indexOf(rootMirrorLayer); - bool ret = rootMirrorLayer->setLayerStack(ui::LayerStack::fromValue(-1)); - if (idx >= 0 && ret) { - mCurrentState.layersSortedByZ.removeAt(idx); - mCurrentState.layersSortedByZ.add(rootMirrorLayer); - } - - for (const auto& layer : mDrawingState.layersSortedByZ) { - if (layer->getLayerStack() != mirrorDisplay.layerStack || - layer->isInternalDisplayOverlay()) { - continue; - } - - LayerCreationArgs mirrorArgs(this, mirrorDisplay.client, "MirrorLayerParent", - ISurfaceComposerClient::eNoColorFill, - gui::LayerMetadata()); - sp<Layer> childMirror; - { - Mutex::Autolock lock(mStateLock); - createEffectLayer(mirrorArgs, &unused, &childMirror); - MUTEX_ALIAS(mStateLock, childMirror->mFlinger->mStateLock); - childMirror->setClonedChild(layer->createClone()); - childMirror->reparent(mirrorDisplay.rootHandle); - } - // lock on mStateLock needs to be released before binder handle gets destroyed - unused.clear(); - } - } - return true; -} - -bool SurfaceFlinger::commitCreatedLayers(VsyncId vsyncId, - std::vector<LayerCreatedState>& createdLayers) { - if (createdLayers.size() == 0) { - return false; - } - - Mutex::Autolock _l(mStateLock); - for (const auto& createdLayer : createdLayers) { - handleLayerCreatedLocked(createdLayer, vsyncId); - } - mLayersAdded = true; - return mLayersAdded; -} - -void SurfaceFlinger::updateLayerMetadataSnapshot() { - LayerMetadata parentMetadata; - for (const auto& layer : mDrawingState.layersSortedByZ) { - layer->updateMetadataSnapshot(parentMetadata); - } - - std::unordered_set<Layer*> visited; - mDrawingState.traverse([&visited](Layer* layer) { - if (visited.find(layer) != visited.end()) { - return; - } - - // If the layer isRelativeOf, then either it's relative metadata will be set - // recursively when updateRelativeMetadataSnapshot is called on its relative parent or - // it's relative parent has been deleted. Clear the layer's relativeLayerMetadata to ensure - // that layers with deleted relative parents don't hold stale relativeLayerMetadata. - if (layer->getDrawingState().isRelativeOf) { - layer->editLayerSnapshot()->relativeLayerMetadata = {}; - return; - } - - layer->updateRelativeMetadataSnapshot({}, visited); - }); -} - void SurfaceFlinger::moveSnapshotsFromCompositionArgs( compositionengine::CompositionRefreshArgs& refreshArgs, const std::vector<std::pair<Layer*, LayerFE*>>& layers) { - if (mLayerLifecycleManagerEnabled) { - std::vector<std::unique_ptr<frontend::LayerSnapshot>>& snapshots = - mLayerSnapshotBuilder.getSnapshots(); - for (auto [_, layerFE] : layers) { - auto i = layerFE->mSnapshot->globalZ; - snapshots[i] = std::move(layerFE->mSnapshot); - } - } - if (!mLayerLifecycleManagerEnabled) { - for (auto [layer, layerFE] : layers) { - layer->updateLayerSnapshot(std::move(layerFE->mSnapshot)); - } + std::vector<std::unique_ptr<frontend::LayerSnapshot>>& snapshots = + mLayerSnapshotBuilder.getSnapshots(); + for (auto [_, layerFE] : layers) { + auto i = layerFE->mSnapshot->globalZ; + snapshots[i] = std::move(layerFE->mSnapshot); } } std::vector<std::pair<Layer*, LayerFE*>> SurfaceFlinger::moveSnapshotsToCompositionArgs( compositionengine::CompositionRefreshArgs& refreshArgs, bool cursorOnly) { std::vector<std::pair<Layer*, LayerFE*>> layers; - if (mLayerLifecycleManagerEnabled) { - nsecs_t currentTime = systemTime(); - mLayerSnapshotBuilder.forEachVisibleSnapshot( - [&](std::unique_ptr<frontend::LayerSnapshot>& snapshot) FTL_FAKE_GUARD( - kMainThreadContext) { - if (cursorOnly && - snapshot->compositionType != - aidl::android::hardware::graphics::composer3::Composition::CURSOR) { - return; - } - - if (!snapshot->hasSomethingToDraw()) { - return; - } - - auto it = mLegacyLayers.find(snapshot->sequence); - LLOG_ALWAYS_FATAL_WITH_TRACE_IF(it == mLegacyLayers.end(), - "Couldnt find layer object for %s", - snapshot->getDebugString().c_str()); - auto& legacyLayer = it->second; - sp<LayerFE> layerFE = legacyLayer->getCompositionEngineLayerFE(snapshot->path); - snapshot->fps = getLayerFramerate(currentTime, snapshot->sequence); - layerFE->mSnapshot = std::move(snapshot); - refreshArgs.layers.push_back(layerFE); - layers.emplace_back(legacyLayer.get(), layerFE.get()); - }); - } - if (!mLayerLifecycleManagerEnabled) { - auto moveSnapshots = [&layers, &refreshArgs, cursorOnly](Layer* layer) { - if (const auto& layerFE = layer->getCompositionEngineLayerFE()) { + nsecs_t currentTime = systemTime(); + const bool needsMetadata = mCompositionEngine->getFeatureFlags().test( + compositionengine::Feature::kSnapshotLayerMetadata); + mLayerSnapshotBuilder.forEachSnapshot( + [&](std::unique_ptr<frontend::LayerSnapshot>& snapshot) FTL_FAKE_GUARD( + kMainThreadContext) { if (cursorOnly && - layer->getLayerSnapshot()->compositionType != - aidl::android::hardware::graphics::composer3::Composition::CURSOR) + snapshot->compositionType != + aidl::android::hardware::graphics::composer3::Composition::CURSOR) { return; - layer->updateSnapshot(refreshArgs.updatingGeometryThisFrame); - layerFE->mSnapshot = layer->stealLayerSnapshot(); - refreshArgs.layers.push_back(layerFE); - layers.emplace_back(layer, layerFE.get()); - } - }; + } - if (cursorOnly || !mVisibleRegionsDirty) { - // for hot path avoid traversals by walking though the previous composition list - for (sp<Layer> layer : mPreviouslyComposedLayers) { - moveSnapshots(layer.get()); - } - } else { - mPreviouslyComposedLayers.clear(); - mDrawingState.traverseInZOrder( - [&moveSnapshots](Layer* layer) { moveSnapshots(layer); }); - mPreviouslyComposedLayers.reserve(layers.size()); - for (auto [layer, _] : layers) { - mPreviouslyComposedLayers.push_back(sp<Layer>::fromExisting(layer)); - } - } - } + if (!snapshot->hasSomethingToDraw()) { + return; + } + auto it = mLegacyLayers.find(snapshot->sequence); + LLOG_ALWAYS_FATAL_WITH_TRACE_IF(it == mLegacyLayers.end(), + "Couldnt find layer object for %s", + snapshot->getDebugString().c_str()); + auto& legacyLayer = it->second; + sp<LayerFE> layerFE = legacyLayer->getCompositionEngineLayerFE(snapshot->path); + snapshot->fps = getLayerFramerate(currentTime, snapshot->sequence); + layerFE->mSnapshot = std::move(snapshot); + refreshArgs.layers.push_back(layerFE); + layers.emplace_back(legacyLayer.get(), layerFE.get()); + }, + [needsMetadata](const frontend::LayerSnapshot& snapshot) { + return snapshot.isVisible || + (needsMetadata && + snapshot.changes.test(frontend::RequestedLayerState::Changes::Metadata)); + }); return layers; } @@ -9551,33 +8345,6 @@ SurfaceFlinger::getLayerSnapshotsForScreenshots(uint32_t rootLayerId, uint32_t u }; } -frontend::Update SurfaceFlinger::flushLifecycleUpdates() { - frontend::Update update; - ATRACE_NAME("TransactionHandler:flushTransactions"); - // Locking: - // 1. to prevent onHandleDestroyed from being called while the state lock is held, - // we must keep a copy of the transactions (specifically the composer - // states) around outside the scope of the lock. - // 2. Transactions and created layers do not share a lock. To prevent applying - // transactions with layers still in the createdLayer queue, flush the transactions - // before committing the created layers. - mTransactionHandler.collectTransactions(); - update.transactions = mTransactionHandler.flushTransactions(); - { - // TODO(b/238781169) lockless queue this and keep order. - std::scoped_lock<std::mutex> lock(mCreatedLayersLock); - update.layerCreatedStates = std::move(mCreatedLayers); - mCreatedLayers.clear(); - update.newLayers = std::move(mNewLayers); - mNewLayers.clear(); - update.layerCreationArgs = std::move(mNewLayerArgs); - mNewLayerArgs.clear(); - update.destroyedHandles = std::move(mDestroyedHandles); - mDestroyedHandles.clear(); - } - return update; -} - void SurfaceFlinger::doActiveLayersTracingIfNeeded(bool isCompositionComputed, bool visibleRegionDirty, TimePoint time, VsyncId vsyncId) { @@ -9599,7 +8366,7 @@ void SurfaceFlinger::doActiveLayersTracingIfNeeded(bool isCompositionComputed, perfetto::protos::LayersSnapshotProto SurfaceFlinger::takeLayersSnapshotProto( uint32_t traceFlags, TimePoint time, VsyncId vsyncId, bool visibleRegionDirty) { - ATRACE_CALL(); + SFTRACE_CALL(); perfetto::protos::LayersSnapshotProto snapshot; snapshot.set_elapsed_realtime_nanos(time.ns()); snapshot.set_vsync_id(ftl::to_underlying(vsyncId)); @@ -9608,9 +8375,6 @@ perfetto::protos::LayersSnapshotProto SurfaceFlinger::takeLayersSnapshotProto( 0); auto layers = dumpDrawingStateProto(traceFlags); - if (traceFlags & LayerTracing::Flag::TRACE_EXTRA) { - dumpOffscreenLayersProto(layers); - } *snapshot.mutable_layers() = std::move(layers); if (traceFlags & LayerTracing::Flag::TRACE_HWC) { @@ -10489,6 +9253,28 @@ binder::Status SurfaceComposerAIDL::notifyShutdown() { return ::android::binder::Status::ok(); } +binder::Status SurfaceComposerAIDL::addJankListener(const sp<IBinder>& layerHandle, + const sp<gui::IJankListener>& listener) { + sp<Layer> layer = LayerHandle::getLayer(layerHandle); + if (layer == nullptr) { + return binder::Status::fromExceptionCode(binder::Status::EX_NULL_POINTER); + } + JankTracker::addJankListener(layer->sequence, IInterface::asBinder(listener)); + return binder::Status::ok(); +} + +binder::Status SurfaceComposerAIDL::flushJankData(int32_t layerId) { + JankTracker::flushJankData(layerId); + return binder::Status::ok(); +} + +binder::Status SurfaceComposerAIDL::removeJankListener(int32_t layerId, + const sp<gui::IJankListener>& listener, + int64_t afterVsync) { + JankTracker::removeJankListener(layerId, IInterface::asBinder(listener), afterVsync); + return binder::Status::ok(); +} + status_t SurfaceComposerAIDL::checkAccessPermission(bool usePermissionCache) { if (!mFlinger->callingThreadHasUnscopedSurfaceFlingerAccess(usePermissionCache)) { IPCThreadState* ipc = IPCThreadState::self(); diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 2369043821..3eb72cc4c0 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -27,7 +27,9 @@ #include <android/gui/BnSurfaceComposer.h> #include <android/gui/DisplayStatInfo.h> #include <android/gui/DisplayState.h> +#include <android/gui/IJankListener.h> #include <android/gui/ISurfaceComposerClient.h> +#include <common/trace.h> #include <cutils/atomic.h> #include <cutils/compiler.h> #include <ftl/algorithm.h> @@ -52,7 +54,6 @@ #include <utils/KeyedVector.h> #include <utils/RefBase.h> #include <utils/SortedVector.h> -#include <utils/Trace.h> #include <utils/threads.h> #include <compositionengine/OutputColorSetting.h> @@ -134,6 +135,7 @@ class FrameTracer; class ScreenCapturer; class WindowInfosListenerInvoker; +using ::aidl::android::hardware::drm::HdcpLevels; using ::aidl::android::hardware::graphics::common::DisplayHotplugEvent; using ::aidl::android::hardware::graphics::composer3::RefreshRateChangedDebugData; using frontend::TransactionHandler; @@ -272,7 +274,7 @@ public: enum class FrameHint { kNone, kActive }; // Schedule commit of transactions on the main thread ahead of the next VSYNC. - void scheduleCommit(FrameHint); + void scheduleCommit(FrameHint, Duration workDurationSlack = Duration::fromNs(0)); // As above, but also force composite regardless if transactions were committed. void scheduleComposite(FrameHint); // As above, but also force dirty geometry to repaint. @@ -291,16 +293,11 @@ public: void onLayerDestroyed(Layer*); void onLayerUpdate(); - void removeHierarchyFromOffscreenLayers(Layer* layer); - void removeFromOffscreenLayers(Layer* layer); - // Called when all clients have released all their references to // this layer. The layer may still be kept alive by its parents but // the client can no longer modify this layer directly. void onHandleDestroyed(BBinder* handle, sp<Layer>& layer, uint32_t layerId); - std::vector<Layer*> mLayerMirrorRoots; - TransactionCallbackInvoker& getTransactionCallbackInvoker() { return mTransactionCallbackInvoker; } @@ -312,7 +309,6 @@ public: // Disables expensive rendering for all displays // This is scheduled on the main thread void disableExpensiveRendering(); - FloatRect getMaxDisplayBounds(); // If set, composition engine tries to predict the composition strategy provided by HWC // based on the previous frame. If the strategy can be predicted, gpu composition will @@ -392,11 +388,10 @@ private: class State { public: - explicit State(LayerVector::StateSet set) : stateSet(set), layersSortedByZ(set) {} + explicit State(LayerVector::StateSet set) : stateSet(set) {} State& operator=(const State& other) { // We explicitly don't copy stateSet so that, e.g., mDrawingState // always uses the Drawing StateSet. - layersSortedByZ = other.layersSortedByZ; displays = other.displays; colorMatrixChanged = other.colorMatrixChanged; if (colorMatrixChanged) { @@ -408,7 +403,6 @@ private: } const LayerVector::StateSet stateSet = LayerVector::StateSet::Invalid; - LayerVector layersSortedByZ; // TODO(b/241285876): Replace deprecated DefaultKeyedVector with ftl::SmallMap. DefaultKeyedVector<wp<IBinder>, DisplayDeviceState> displays; @@ -428,10 +422,6 @@ private: mat4 colorMatrix; ShadowSettings globalShadowSettings; - - void traverse(const LayerVector::Visitor& visitor) const; - void traverseInZOrder(const LayerVector::Visitor& visitor) const; - void traverseInReverseZOrder(const LayerVector::Visitor& visitor) const; }; // Keeps track of pending buffers per layer handle in the transaction queue or current/drawing @@ -449,7 +439,7 @@ private: if (it != mCounterByLayerHandle.end()) { auto [name, pendingBuffers] = it->second; int32_t count = ++(*pendingBuffers); - ATRACE_INT(name.c_str(), count); + SFTRACE_INT(name.c_str(), count); } else { ALOGW("Handle not found! %p", layerHandle); } @@ -559,7 +549,7 @@ private: sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId) const; status_t setTransactionState( const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& state, - const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken, + 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, @@ -682,6 +672,7 @@ private: void onComposerHalSeamlessPossible(hal::HWDisplayId) override; void onComposerHalVsyncIdle(hal::HWDisplayId) override; void onRefreshRateChangedDebug(const RefreshRateChangedDebugData&) override; + void onComposerHalHdcpLevelsChanged(hal::HWDisplayId, const HdcpLevels& levels) override; // ICompositor overrides: void configure() override REQUIRES(kMainThreadContext); @@ -697,7 +688,6 @@ private: void requestHardwareVsync(PhysicalDisplayId, bool) override; void requestDisplayModes(std::vector<display::DisplayModeRequest>) override; void kernelTimerChanged(bool expired) override; - void triggerOnFrameRateOverridesChanged() override; void onChoreographerAttached() override; void onExpectedPresentTimePosted(TimePoint expectedPresentTime, ftl::NonNull<DisplayModePtr>, Fps renderRate) override; @@ -708,22 +698,13 @@ private: // ICEPowerCallback overrides: void notifyCpuLoadUp() override; - // Toggles the kernel idle timer on or off depending the policy decisions around refresh rates. - void toggleKernelIdleTimer() REQUIRES(mStateLock); - using KernelIdleTimerController = scheduler::RefreshRateSelector::KernelIdleTimerController; // Get the controller and timeout that will help decide how the kernel idle timer will be // configured and what value to use as the timeout. std::pair<std::optional<KernelIdleTimerController>, std::chrono::milliseconds> getKernelIdleTimerProperties(PhysicalDisplayId) REQUIRES(mStateLock); - // Updates the kernel idle timer either through HWC or through sysprop - // depending on which controller is provided - void updateKernelIdleTimer(std::chrono::milliseconds timeoutMs, KernelIdleTimerController, - PhysicalDisplayId) REQUIRES(mStateLock); - // Keeps track of whether the kernel idle timer is currently enabled, so we don't have to - // make calls to sys prop each time. - bool mKernelIdleTimerEnabled = false; + // Show spinner with refresh rate overlay bool mRefreshRateOverlaySpinner = false; // Show render rate with refresh rate overlay @@ -763,17 +744,11 @@ private: const scheduler::RefreshRateSelector&) REQUIRES(mStateLock, kMainThreadContext); - void commitTransactionsLegacy() EXCLUDES(mStateLock) REQUIRES(kMainThreadContext); void commitTransactions() REQUIRES(kMainThreadContext, mStateLock); void commitTransactionsLocked(uint32_t transactionFlags) REQUIRES(mStateLock, kMainThreadContext); void doCommitTransactions() REQUIRES(mStateLock); - // Returns whether a new buffer has been latched. - bool latchBuffers(); - - void updateLayerGeometry(); - void updateLayerMetadataSnapshot(); std::vector<std::pair<Layer*, LayerFE*>> moveSnapshotsToCompositionArgs( compositionengine::CompositionRefreshArgs& refreshArgs, bool cursorOnly) REQUIRES(kMainThreadContext); @@ -781,13 +756,9 @@ private: const std::vector<std::pair<Layer*, LayerFE*>>& layers) REQUIRES(kMainThreadContext); // Return true if we must composite this frame - bool updateLayerSnapshotsLegacy(VsyncId vsyncId, nsecs_t frameTimeNs, bool transactionsFlushed, - bool& out) REQUIRES(kMainThreadContext); - // Return true if we must composite this frame bool updateLayerSnapshots(VsyncId vsyncId, nsecs_t frameTimeNs, bool transactionsFlushed, bool& out) REQUIRES(kMainThreadContext); void updateLayerHistory(nsecs_t now) REQUIRES(kMainThreadContext); - frontend::Update flushLifecycleUpdates() REQUIRES(kMainThreadContext); void updateInputFlinger(VsyncId vsyncId, TimePoint frameTime) REQUIRES(kMainThreadContext); void persistDisplayBrightness(bool needsComposite) REQUIRES(kMainThreadContext); @@ -826,16 +797,10 @@ private: TransactionHandler::TransactionReadiness transactionReadyTimelineCheck( const TransactionHandler::TransactionFlushState& flushState) REQUIRES(kMainThreadContext); - TransactionHandler::TransactionReadiness transactionReadyBufferCheckLegacy( - const TransactionHandler::TransactionFlushState& flushState) - REQUIRES(kMainThreadContext); TransactionHandler::TransactionReadiness transactionReadyBufferCheck( const TransactionHandler::TransactionFlushState& flushState) REQUIRES(kMainThreadContext); - uint32_t setClientStateLocked(const FrameTimelineInfo&, ResolvedComposerState&, - int64_t desiredPresentTime, bool isAutoTimestamp, - int64_t postTime, uint64_t transactionId) REQUIRES(mStateLock); uint32_t updateLayerCallbacksAndStats(const FrameTimelineInfo&, ResolvedComposerState&, int64_t desiredPresentTime, bool isAutoTimestamp, int64_t postTime, uint64_t transactionId) @@ -850,8 +815,6 @@ private: // Clears and returns the masked bits. uint32_t clearTransactionFlags(uint32_t mask); - void commitOffscreenLayers(); - static LatchUnsignaledConfig getLatchUnsignaledConfig(); bool shouldLatchUnsignaled(const layer_state_t&, size_t numStates, bool firstTransaction) const; bool applyTransactionsLocked(std::vector<TransactionState>& transactions) @@ -878,16 +841,11 @@ private: status_t mirrorDisplay(DisplayId displayId, const LayerCreationArgs& args, gui::CreateSurfaceResult& outResult); - void markLayerPendingRemovalLocked(const sp<Layer>& layer) REQUIRES(mStateLock); - // add a layer to SurfaceFlinger status_t addClientLayer(LayerCreationArgs& args, const sp<IBinder>& handle, const sp<Layer>& layer, const wp<Layer>& parentLayer, uint32_t* outTransformHint); - // Traverse through all the layers and compute and cache its bounds. - void computeLayerBounds(); - // Creates a promise for a future release fence for a layer. This allows for // the layer to keep track of when its buffer can be released. void attachReleaseFenceFutureToLayer(Layer* layer, LayerFE* layerFE, ui::LayerStack layerStack); @@ -897,13 +855,13 @@ private: using OutputCompositionState = compositionengine::impl::OutputCompositionState; - std::optional<OutputCompositionState> getDisplayAndLayerSnapshotsFromMainThread( + std::optional<OutputCompositionState> getSnapshotsFromMainThread( RenderAreaBuilderVariant& renderAreaBuilder, GetLayerSnapshotsFunction getLayerSnapshotsFn, std::vector<sp<LayerFE>>& layerFEs); void captureScreenCommon(RenderAreaBuilderVariant, GetLayerSnapshotsFunction, ui::Size bufferSize, ui::PixelFormat, bool allowProtected, - bool grayscale, const sp<IScreenCaptureListener>&); + bool grayscale, bool attachGainmap, const sp<IScreenCaptureListener>&); std::optional<OutputCompositionState> getDisplayStateFromRenderAreaBuilder( RenderAreaBuilderVariant& renderAreaBuilder) REQUIRES(kMainThreadContext); @@ -917,29 +875,24 @@ private: ftl::SharedFuture<FenceResult> captureScreenshot( const RenderAreaBuilderVariant& renderAreaBuilder, const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling, - bool grayscale, bool isProtected, const sp<IScreenCaptureListener>& captureListener, + bool grayscale, bool isProtected, bool attachGainmap, + const sp<IScreenCaptureListener>& captureListener, std::optional<OutputCompositionState>& displayState, std::vector<sp<LayerFE>>& layerFEs); ftl::SharedFuture<FenceResult> captureScreenshotLegacy( RenderAreaBuilderVariant, GetLayerSnapshotsFunction, const std::shared_ptr<renderengine::ExternalTexture>&, bool regionSampling, - bool grayscale, bool isProtected, const sp<IScreenCaptureListener>&); + bool grayscale, bool isProtected, bool attachGainmap, + const sp<IScreenCaptureListener>&); ftl::SharedFuture<FenceResult> renderScreenImpl( - std::unique_ptr<const RenderArea>, - const std::shared_ptr<renderengine::ExternalTexture>&, bool regionSampling, - bool grayscale, bool isProtected, ScreenCaptureResults&, - std::optional<OutputCompositionState>& displayState, + const RenderArea*, const std::shared_ptr<renderengine::ExternalTexture>&, + bool regionSampling, bool grayscale, bool isProtected, bool attachGainmap, + ScreenCaptureResults&, std::optional<OutputCompositionState>& displayState, std::vector<std::pair<Layer*, sp<LayerFE>>>& layers, std::vector<sp<LayerFE>>& layerFEs); - // If the uid provided is not UNSET_UID, the traverse will skip any layers that don't have a - // matching ownerUid - void traverseLayersInLayerStack(ui::LayerStack, const int32_t uid, - std::unordered_set<uint32_t> excludeLayerIds, - const LayerVector::Visitor&); - void readPersistentProperties(); uint32_t getMaxAcquiredBufferCountForCurrentRefreshRate(uid_t uid) const; @@ -988,18 +941,12 @@ private: return nullptr; } - // Returns the primary display or (for foldables) the active display, assuming that the inner - // and outer displays have mutually exclusive power states. + // Returns the primary display or (for foldables) the active display. sp<const DisplayDevice> getDefaultDisplayDeviceLocked() const REQUIRES(mStateLock) { return const_cast<SurfaceFlinger*>(this)->getDefaultDisplayDeviceLocked(); } sp<DisplayDevice> getDefaultDisplayDeviceLocked() REQUIRES(mStateLock) { - if (const auto display = getDisplayDeviceLocked(mActiveDisplayId)) { - return display; - } - // The active display is outdated, so fall back to the primary display. - mActiveDisplayId = getPrimaryDisplayIdLocked(); return getDisplayDeviceLocked(mActiveDisplayId); } @@ -1099,13 +1046,6 @@ private: const DisplayDeviceState& drawingState) REQUIRES(mStateLock, kMainThreadContext); - void dispatchDisplayModeChangeEvent(PhysicalDisplayId, const scheduler::FrameRateMode&); - - /* - * VSYNC - */ - nsecs_t getVsyncPeriodFromHWC() const REQUIRES(mStateLock); - /* * Display identification */ @@ -1156,7 +1096,6 @@ private: void dumpAll(const DumpArgs& args, const std::string& compositionLayers, std::string& result) const EXCLUDES(mStateLock); void dumpHwcLayersMinidump(std::string& result) const REQUIRES(mStateLock, kMainThreadContext); - void dumpHwcLayersMinidumpLockedLegacy(std::string& result) const REQUIRES(mStateLock); void appendSfConfigString(std::string& result) const; void listLayers(std::string& result) const REQUIRES(kMainThreadContext); @@ -1182,8 +1121,6 @@ private: perfetto::protos::LayersProto dumpDrawingStateProto(uint32_t traceFlags) const REQUIRES(kMainThreadContext); - void dumpOffscreenLayersProto(perfetto::protos::LayersProto& layersProto, - uint32_t traceFlags = LayerTracing::TRACE_ALL) const; google::protobuf::RepeatedPtrField<perfetto::protos::DisplayProto> dumpDisplayProto() const; void doActiveLayersTracingIfNeeded(bool isCompositionComputed, bool visibleRegionDirty, TimePoint, VsyncId) REQUIRES(kMainThreadContext); @@ -1195,7 +1132,6 @@ private: void dumpHwc(std::string& result) const; perfetto::protos::LayersProto dumpProtoFromMainThread( uint32_t traceFlags = LayerTracing::TRACE_ALL) EXCLUDES(mStateLock); - void dumpOffscreenLayers(std::string& result) EXCLUDES(mStateLock); void dumpPlannerInfo(const DumpArgs& args, std::string& result) const REQUIRES(mStateLock); status_t doDump(int fd, const DumpArgs& args, bool asProto); @@ -1256,7 +1192,6 @@ private: State mCurrentState{LayerVector::StateSet::Current}; std::atomic<int32_t> mTransactionFlags = 0; std::atomic<uint32_t> mUniqueTransactionId = 1; - SortedVector<sp<Layer>> mLayersPendingRemoval; // Buffers that have been discarded by clients and need to be evicted from per-layer caches so // the graphics memory can be immediately freed. @@ -1296,21 +1231,22 @@ private: // TODO: Also move visibleRegions over to a boolean system. bool mUpdateInputInfo = false; bool mSomeChildrenChanged; - bool mForceTransactionDisplayChange = false; bool mUpdateAttachedChoreographer = false; - // Set if LayerMetadata has changed since the last LayerMetadata snapshot. - bool mLayerMetadataSnapshotNeeded = false; + struct LayerIntHash { + size_t operator()(const std::pair<sp<Layer>, gui::GameMode>& k) const { + return std::hash<Layer*>()(k.first.get()) ^ + std::hash<int32_t>()(static_cast<int32_t>(k.second)); + } + }; // TODO(b/238781169) validate these on composition // Tracks layers that have pending frames which are candidates for being // latched. - std::unordered_set<sp<Layer>, SpHash<Layer>> mLayersWithQueuedFrames; + std::unordered_set<std::pair<sp<Layer>, gui::GameMode>, LayerIntHash> mLayersWithQueuedFrames; std::unordered_set<sp<Layer>, SpHash<Layer>> mLayersWithBuffersRemoved; std::unordered_set<uint32_t> mLayersIdsWithQueuedFrames; - // Tracks layers that need to update a display's dirty region. - std::vector<sp<Layer>> mLayersPendingRefresh; // Sorted list of layers that were composed during previous frame. This is used to // avoid an expensive traversal of the layer hierarchy when there are no // visible region changes. Because this is a list of strong pointers, this will @@ -1325,7 +1261,6 @@ private: }; bool mIsHdcpViaNegVsync = false; - bool mIsHotplugErrViaNegVsync = false; std::mutex mHotplugMutex; std::vector<HotplugEvent> mPendingHotplugEvents GUARDED_BY(mHotplugMutex); @@ -1338,7 +1273,7 @@ private: display::PhysicalDisplays mPhysicalDisplays GUARDED_BY(mStateLock); - // The inner or outer display for foldables, assuming they have mutually exclusive power states. + // The inner or outer display for foldables, while unfolded or folded, respectively. std::atomic<PhysicalDisplayId> mActiveDisplayId; display::DisplayModeController mDisplayModeController; @@ -1439,38 +1374,11 @@ private: // Flag used to set override desired display mode from backdoor bool mDebugDisplayModeSetByBackdoor = false; - // A set of layers that have no parent so they are not drawn on screen. - // Should only be accessed by the main thread. - // The Layer pointer is removed from the set when the destructor is called so there shouldn't - // be any issues with a raw pointer referencing an invalid object. - std::unordered_set<Layer*> mOffscreenLayers; - BufferCountTracker mBufferCountTracker; std::unordered_map<DisplayId, sp<HdrLayerInfoReporter>> mHdrLayerInfoListeners GUARDED_BY(mStateLock); - mutable std::mutex mCreatedLayersLock; - - // A temporay pool that store the created layers and will be added to current state in main - // thread. - std::vector<LayerCreatedState> mCreatedLayers GUARDED_BY(mCreatedLayersLock); - bool commitCreatedLayers(VsyncId, std::vector<LayerCreatedState>& createdLayers); - void handleLayerCreatedLocked(const LayerCreatedState&, VsyncId) REQUIRES(mStateLock); - - mutable std::mutex mMirrorDisplayLock; - struct MirrorDisplayState { - MirrorDisplayState(ui::LayerStack layerStack, sp<IBinder>& rootHandle, - const sp<Client>& client) - : layerStack(layerStack), rootHandle(rootHandle), client(client) {} - - ui::LayerStack layerStack; - sp<IBinder> rootHandle; - const sp<Client> client; - }; - std::vector<MirrorDisplayState> mMirrorDisplays GUARDED_BY(mMirrorDisplayLock); - bool commitMirrorDisplays(VsyncId); - std::atomic<ui::Transform::RotationFlags> mActiveDisplayTransformHint; // Must only be accessed on the main thread. @@ -1504,8 +1412,6 @@ private: } bool mPowerHintSessionEnabled; - - bool mLayerLifecycleManagerEnabled = false; // Whether a display should be turned on when initialized bool mSkipPowerOnForQuiescent; @@ -1513,6 +1419,8 @@ private: frontend::LayerHierarchyBuilder mLayerHierarchyBuilder GUARDED_BY(kMainThreadContext); frontend::LayerSnapshotBuilder mLayerSnapshotBuilder GUARDED_BY(kMainThreadContext); + mutable std::mutex mCreatedLayersLock; + std::vector<sp<Layer>> mCreatedLayers GUARDED_BY(mCreatedLayersLock); std::vector<std::pair<uint32_t, std::string>> mDestroyedHandles GUARDED_BY(mCreatedLayersLock); std::vector<std::unique_ptr<frontend::RequestedLayerState>> mNewLayers GUARDED_BY(mCreatedLayersLock); @@ -1694,6 +1602,11 @@ public: int pid, std::optional<gui::StalledTransactionInfo>* outInfo) override; binder::Status getSchedulingPolicy(gui::SchedulingPolicy* outPolicy) override; binder::Status notifyShutdown() override; + binder::Status addJankListener(const sp<IBinder>& layer, + const sp<gui::IJankListener>& listener) override; + binder::Status flushJankData(int32_t layerId) override; + binder::Status removeJankListener(int32_t layerId, const sp<gui::IJankListener>& listener, + int64_t afterVsync) override; private: static const constexpr bool kUsePermissionCache = true; diff --git a/services/surfaceflinger/TimeStats/Android.bp b/services/surfaceflinger/TimeStats/Android.bp index a631074f4d..a6a0152044 100644 --- a/services/surfaceflinger/TimeStats/Android.bp +++ b/services/surfaceflinger/TimeStats/Android.bp @@ -20,10 +20,12 @@ cc_defaults { "libtimestats_atoms_proto", "libui", "libutils", + "libtracing_perfetto", ], static_libs: [ "libtimestats_proto", + "libsurfaceflinger_common", ], export_static_lib_headers: [ diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp index 368cb41779..c60ded6e56 100644 --- a/services/surfaceflinger/TimeStats/TimeStats.cpp +++ b/services/surfaceflinger/TimeStats/TimeStats.cpp @@ -19,11 +19,11 @@ #define ATRACE_TAG ATRACE_TAG_GRAPHICS #include <android-base/stringprintf.h> +#include <common/trace.h> #include <log/log.h> #include <timestatsatomsproto/TimeStatsAtomsProtoHeader.h> #include <utils/String8.h> #include <utils/Timers.h> -#include <utils/Trace.h> #include <algorithm> #include <chrono> @@ -271,7 +271,7 @@ bool TimeStats::onPullAtom(const int atomId, std::vector<uint8_t>* pulledData) { } void TimeStats::parseArgs(bool asProto, const Vector<String16>& args, std::string& result) { - ATRACE_CALL(); + SFTRACE_CALL(); std::unordered_map<std::string, int32_t> argsMap; for (size_t index = 0; index < args.size(); ++index) { @@ -304,7 +304,7 @@ void TimeStats::parseArgs(bool asProto, const Vector<String16>& args, std::strin } std::string TimeStats::miniDump() { - ATRACE_CALL(); + SFTRACE_CALL(); std::string result = "TimeStats miniDump:\n"; std::lock_guard<std::mutex> lock(mMutex); @@ -318,7 +318,7 @@ std::string TimeStats::miniDump() { void TimeStats::incrementTotalFrames() { if (!mEnabled.load()) return; - ATRACE_CALL(); + SFTRACE_CALL(); std::lock_guard<std::mutex> lock(mMutex); mTimeStats.totalFramesLegacy++; @@ -327,7 +327,7 @@ void TimeStats::incrementTotalFrames() { void TimeStats::incrementMissedFrames() { if (!mEnabled.load()) return; - ATRACE_CALL(); + SFTRACE_CALL(); std::lock_guard<std::mutex> lock(mMutex); mTimeStats.missedFramesLegacy++; @@ -338,7 +338,7 @@ void TimeStats::pushCompositionStrategyState(const TimeStats::ClientCompositionR return; } - ATRACE_CALL(); + SFTRACE_CALL(); std::lock_guard<std::mutex> lock(mMutex); if (record.changed) mTimeStats.compositionStrategyChangesLegacy++; @@ -351,7 +351,7 @@ void TimeStats::pushCompositionStrategyState(const TimeStats::ClientCompositionR void TimeStats::incrementRefreshRateSwitches() { if (!mEnabled.load()) return; - ATRACE_CALL(); + SFTRACE_CALL(); std::lock_guard<std::mutex> lock(mMutex); mTimeStats.refreshRateSwitchesLegacy++; @@ -445,7 +445,7 @@ void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayR std::optional<Fps> renderRate, SetFrameRateVote frameRateVote, GameMode gameMode) { - ATRACE_CALL(); + SFTRACE_CALL(); ALOGV("[%d]-flushAvailableRecordsToStatsLocked", layerId); LayerRecord& layerRecord = mTimeStatsTracker[layerId]; @@ -568,7 +568,7 @@ void TimeStats::setPostTime(int32_t layerId, uint64_t frameNumber, const std::st uid_t uid, nsecs_t postTime, GameMode gameMode) { if (!mEnabled.load()) return; - ATRACE_CALL(); + SFTRACE_CALL(); ALOGV("[%d]-[%" PRIu64 "]-[%s]-PostTime[%" PRId64 "]", layerId, frameNumber, layerName.c_str(), postTime); @@ -612,7 +612,7 @@ void TimeStats::setPostTime(int32_t layerId, uint64_t frameNumber, const std::st void TimeStats::setLatchTime(int32_t layerId, uint64_t frameNumber, nsecs_t latchTime) { if (!mEnabled.load()) return; - ATRACE_CALL(); + SFTRACE_CALL(); ALOGV("[%d]-[%" PRIu64 "]-LatchTime[%" PRId64 "]", layerId, frameNumber, latchTime); std::lock_guard<std::mutex> lock(mMutex); @@ -630,7 +630,7 @@ void TimeStats::setLatchTime(int32_t layerId, uint64_t frameNumber, nsecs_t latc void TimeStats::incrementLatchSkipped(int32_t layerId, LatchSkipReason reason) { if (!mEnabled.load()) return; - ATRACE_CALL(); + SFTRACE_CALL(); ALOGV("[%d]-LatchSkipped-Reason[%d]", layerId, static_cast<std::underlying_type<LatchSkipReason>::type>(reason)); @@ -648,7 +648,7 @@ void TimeStats::incrementLatchSkipped(int32_t layerId, LatchSkipReason reason) { void TimeStats::incrementBadDesiredPresent(int32_t layerId) { if (!mEnabled.load()) return; - ATRACE_CALL(); + SFTRACE_CALL(); ALOGV("[%d]-BadDesiredPresent", layerId); std::lock_guard<std::mutex> lock(mMutex); @@ -660,7 +660,7 @@ void TimeStats::incrementBadDesiredPresent(int32_t layerId) { void TimeStats::setDesiredTime(int32_t layerId, uint64_t frameNumber, nsecs_t desiredTime) { if (!mEnabled.load()) return; - ATRACE_CALL(); + SFTRACE_CALL(); ALOGV("[%d]-[%" PRIu64 "]-DesiredTime[%" PRId64 "]", layerId, frameNumber, desiredTime); std::lock_guard<std::mutex> lock(mMutex); @@ -678,7 +678,7 @@ void TimeStats::setDesiredTime(int32_t layerId, uint64_t frameNumber, nsecs_t de void TimeStats::setAcquireTime(int32_t layerId, uint64_t frameNumber, nsecs_t acquireTime) { if (!mEnabled.load()) return; - ATRACE_CALL(); + SFTRACE_CALL(); ALOGV("[%d]-[%" PRIu64 "]-AcquireTime[%" PRId64 "]", layerId, frameNumber, acquireTime); std::lock_guard<std::mutex> lock(mMutex); @@ -697,7 +697,7 @@ void TimeStats::setAcquireFence(int32_t layerId, uint64_t frameNumber, const std::shared_ptr<FenceTime>& acquireFence) { if (!mEnabled.load()) return; - ATRACE_CALL(); + SFTRACE_CALL(); ALOGV("[%d]-[%" PRIu64 "]-AcquireFenceTime[%" PRId64 "]", layerId, frameNumber, acquireFence->getSignalTime()); @@ -718,7 +718,7 @@ void TimeStats::setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t pr SetFrameRateVote frameRateVote, GameMode gameMode) { if (!mEnabled.load()) return; - ATRACE_CALL(); + SFTRACE_CALL(); ALOGV("[%d]-[%" PRIu64 "]-PresentTime[%" PRId64 "]", layerId, frameNumber, presentTime); std::lock_guard<std::mutex> lock(mMutex); @@ -744,7 +744,7 @@ void TimeStats::setPresentFence(int32_t layerId, uint64_t frameNumber, SetFrameRateVote frameRateVote, GameMode gameMode) { if (!mEnabled.load()) return; - ATRACE_CALL(); + SFTRACE_CALL(); ALOGV("[%d]-[%" PRIu64 "]-PresentFenceTime[%" PRId64 "]", layerId, frameNumber, presentFence->getSignalTime()); @@ -805,7 +805,7 @@ static void updateJankPayload(T& t, int32_t reasons) { void TimeStats::incrementJankyFrames(const JankyFramesInfo& info) { if (!mEnabled.load()) return; - ATRACE_CALL(); + SFTRACE_CALL(); std::lock_guard<std::mutex> lock(mMutex); // Only update layer stats if we're already tracking the layer in TimeStats. @@ -861,7 +861,7 @@ void TimeStats::incrementJankyFrames(const JankyFramesInfo& info) { } void TimeStats::onDestroy(int32_t layerId) { - ATRACE_CALL(); + SFTRACE_CALL(); ALOGV("[%d]-onDestroy", layerId); std::lock_guard<std::mutex> lock(mMutex); mTimeStatsTracker.erase(layerId); @@ -870,7 +870,7 @@ void TimeStats::onDestroy(int32_t layerId) { void TimeStats::removeTimeRecord(int32_t layerId, uint64_t frameNumber) { if (!mEnabled.load()) return; - ATRACE_CALL(); + SFTRACE_CALL(); ALOGV("[%d]-[%" PRIu64 "]-removeTimeRecord", layerId, frameNumber); std::lock_guard<std::mutex> lock(mMutex); @@ -935,7 +935,7 @@ void TimeStats::recordRefreshRate(uint32_t fps, nsecs_t duration) { } void TimeStats::flushAvailableGlobalRecordsToStatsLocked() { - ATRACE_CALL(); + SFTRACE_CALL(); while (!mGlobalRecord.presentFences.empty()) { const nsecs_t curPresentTime = mGlobalRecord.presentFences.front()->getSignalTime(); @@ -992,7 +992,7 @@ void TimeStats::flushAvailableGlobalRecordsToStatsLocked() { void TimeStats::setPresentFenceGlobal(const std::shared_ptr<FenceTime>& presentFence) { if (!mEnabled.load()) return; - ATRACE_CALL(); + SFTRACE_CALL(); std::lock_guard<std::mutex> lock(mMutex); if (presentFence == nullptr || !presentFence->isValid()) { mGlobalRecord.prevPresentTime = 0; @@ -1022,7 +1022,7 @@ void TimeStats::setPresentFenceGlobal(const std::shared_ptr<FenceTime>& presentF void TimeStats::enable() { if (mEnabled.load()) return; - ATRACE_CALL(); + SFTRACE_CALL(); std::lock_guard<std::mutex> lock(mMutex); mEnabled.store(true); @@ -1034,7 +1034,7 @@ void TimeStats::enable() { void TimeStats::disable() { if (!mEnabled.load()) return; - ATRACE_CALL(); + SFTRACE_CALL(); std::lock_guard<std::mutex> lock(mMutex); flushPowerTimeLocked(); @@ -1051,7 +1051,7 @@ void TimeStats::clearAll() { } void TimeStats::clearGlobalLocked() { - ATRACE_CALL(); + SFTRACE_CALL(); mTimeStats.statsStartLegacy = (mEnabled.load() ? static_cast<int64_t>(std::time(0)) : 0); mTimeStats.statsEndLegacy = 0; @@ -1078,7 +1078,7 @@ void TimeStats::clearGlobalLocked() { } void TimeStats::clearLayersLocked() { - ATRACE_CALL(); + SFTRACE_CALL(); mTimeStatsTracker.clear(); @@ -1093,7 +1093,7 @@ bool TimeStats::isEnabled() { } void TimeStats::dump(bool asProto, std::optional<uint32_t> maxLayers, std::string& result) { - ATRACE_CALL(); + SFTRACE_CALL(); std::lock_guard<std::mutex> lock(mMutex); if (mTimeStats.statsStartLegacy == 0) { diff --git a/services/surfaceflinger/TracedOrdinal.h b/services/surfaceflinger/TracedOrdinal.h index 1adc3a531d..51f33a6988 100644 --- a/services/surfaceflinger/TracedOrdinal.h +++ b/services/surfaceflinger/TracedOrdinal.h @@ -21,8 +21,8 @@ #include <functional> #include <string> +#include <common/trace.h> #include <cutils/compiler.h> -#include <utils/Trace.h> namespace android { @@ -79,7 +79,7 @@ public: private: void trace() { - if (CC_LIKELY(!ATRACE_ENABLED())) { + if (CC_LIKELY(!SFTRACE_ENABLED())) { return; } @@ -88,13 +88,13 @@ private: } if (!signbit(mData)) { - ATRACE_INT64(mName.c_str(), to_int64(mData)); + SFTRACE_INT64(mName.c_str(), to_int64(mData)); if (mHasGoneNegative) { - ATRACE_INT64(mNameNegative.c_str(), 0); + SFTRACE_INT64(mNameNegative.c_str(), 0); } } else { - ATRACE_INT64(mNameNegative.c_str(), -to_int64(mData)); - ATRACE_INT64(mName.c_str(), 0); + SFTRACE_INT64(mNameNegative.c_str(), -to_int64(mData)); + SFTRACE_INT64(mName.c_str(), 0); } } diff --git a/services/surfaceflinger/Tracing/LayerTracing.cpp b/services/surfaceflinger/Tracing/LayerTracing.cpp index 41bcdf05c3..d40b888504 100644 --- a/services/surfaceflinger/Tracing/LayerTracing.cpp +++ b/services/surfaceflinger/Tracing/LayerTracing.cpp @@ -24,10 +24,10 @@ #include "Tracing/tools/LayerTraceGenerator.h" #include "TransactionTracing.h" +#include <common/trace.h> #include <log/log.h> #include <perfetto/tracing.h> #include <utils/Timers.h> -#include <utils/Trace.h> namespace android { @@ -134,7 +134,7 @@ void LayerTracing::onStop(Mode mode) { void LayerTracing::addProtoSnapshotToOstream(perfetto::protos::LayersSnapshotProto&& snapshot, Mode mode) { - ATRACE_CALL(); + SFTRACE_CALL(); if (mOutStream) { writeSnapshotToStream(std::move(snapshot)); } else { diff --git a/services/surfaceflinger/Tracing/TransactionRingBuffer.h b/services/surfaceflinger/Tracing/TransactionRingBuffer.h index 7d1d3fd7f2..2b66391853 100644 --- a/services/surfaceflinger/Tracing/TransactionRingBuffer.h +++ b/services/surfaceflinger/Tracing/TransactionRingBuffer.h @@ -19,13 +19,12 @@ #include <android-base/file.h> #include <android-base/stringprintf.h> +#include <common/trace.h> #include <log/log.h> #include <utils/Errors.h> #include <utils/Timers.h> -#include <utils/Trace.h> #include <chrono> #include <fstream> -#include <queue> namespace android { @@ -57,7 +56,7 @@ public: } status_t appendToStream(FileProto& fileProto, std::ofstream& out) { - ATRACE_CALL(); + SFTRACE_CALL(); writeToProto(fileProto); std::string output; if (!fileProto.SerializeToString(&output)) { diff --git a/services/surfaceflinger/Tracing/tools/Android.bp b/services/surfaceflinger/Tracing/tools/Android.bp index 8afca4139c..63c1b37166 100644 --- a/services/surfaceflinger/Tracing/tools/Android.bp +++ b/services/surfaceflinger/Tracing/tools/Android.bp @@ -28,6 +28,7 @@ cc_binary { "libsurfaceflinger_mocks_defaults", "librenderengine_deps", "surfaceflinger_defaults", + "libsurfaceflinger_common_deps", ], srcs: [ ":libsurfaceflinger_sources", diff --git a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp index 617ea2c566..1dba17585c 100644 --- a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp +++ b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp @@ -162,7 +162,10 @@ bool LayerTraceGenerator::generate(const perfetto::protos::TransactionTraceFile& auto layersProto = LayerProtoFromSnapshotGenerator(snapshotBuilder, displayInfos, {}, traceFlags) - .generate(hierarchyBuilder.getHierarchy()); + .with(hierarchyBuilder.getHierarchy()) + .withOffscreenLayers(hierarchyBuilder.getOffscreenHierarchy()) + .generate(); + auto displayProtos = LayerProtoHelper::writeDisplayInfoToProto(displayInfos); if (!onlyLastEntry || (i == traceFile.entry_size() - 1)) { perfetto::protos::LayersSnapshotProto snapshotProto{}; diff --git a/services/surfaceflinger/TransactionCallbackInvoker.cpp b/services/surfaceflinger/TransactionCallbackInvoker.cpp index 222ae30acb..c6856aea75 100644 --- a/services/surfaceflinger/TransactionCallbackInvoker.cpp +++ b/services/surfaceflinger/TransactionCallbackInvoker.cpp @@ -27,10 +27,9 @@ #include "BackgroundExecutor.h" #include "Utils/FenceUtils.h" -#include <cinttypes> - #include <binder/IInterface.h> #include <common/FlagManager.h> +#include <common/trace.h> #include <utils/RefBase.h> namespace android { @@ -64,13 +63,12 @@ status_t TransactionCallbackInvoker::addOnCommitCallbackHandles( if (handles.empty()) { return NO_ERROR; } - const std::vector<JankData>& jankData = std::vector<JankData>(); for (const auto& handle : handles) { if (!containsOnCommitCallbacks(handle->callbackIds)) { outRemainingHandles.push_back(handle); continue; } - status_t err = addCallbackHandle(handle, jankData); + status_t err = addCallbackHandle(handle); if (err != NO_ERROR) { return err; } @@ -80,12 +78,12 @@ status_t TransactionCallbackInvoker::addOnCommitCallbackHandles( } status_t TransactionCallbackInvoker::addCallbackHandles( - const std::deque<sp<CallbackHandle>>& handles, const std::vector<JankData>& jankData) { + const std::deque<sp<CallbackHandle>>& handles) { if (handles.empty()) { return NO_ERROR; } for (const auto& handle : handles) { - status_t err = addCallbackHandle(handle, jankData); + status_t err = addCallbackHandle(handle); if (err != NO_ERROR) { return err; } @@ -111,8 +109,7 @@ status_t TransactionCallbackInvoker::findOrCreateTransactionStats( return NO_ERROR; } -status_t TransactionCallbackInvoker::addCallbackHandle(const sp<CallbackHandle>& handle, - const std::vector<JankData>& jankData) { +status_t TransactionCallbackInvoker::addCallbackHandle(const sp<CallbackHandle>& handle) { // If we can't find the transaction stats something has gone wrong. The client should call // startRegistration before trying to add a callback handle. TransactionStats* transactionStats; @@ -151,8 +148,14 @@ status_t TransactionCallbackInvoker::addCallbackHandle(const sp<CallbackHandle>& handle->previousReleaseFence, handle->transformHint, handle->currentMaxAcquiredBufferCount, - eventStats, jankData, - handle->previousReleaseCallbackId); + eventStats, handle->previousReleaseCallbackId); + if (handle->bufferReleaseChannel && + handle->previousReleaseCallbackId != ReleaseCallbackId::INVALID_ID) { + mBufferReleases.emplace_back(handle->bufferReleaseChannel, + handle->previousReleaseCallbackId, + handle->previousReleaseFence, + handle->currentMaxAcquiredBufferCount); + } } return NO_ERROR; } @@ -162,9 +165,15 @@ void TransactionCallbackInvoker::addPresentFence(sp<Fence> presentFence) { } void TransactionCallbackInvoker::sendCallbacks(bool onCommitOnly) { + for (const auto& bufferRelease : mBufferReleases) { + bufferRelease.channel->writeReleaseFence(bufferRelease.callbackId, bufferRelease.fence, + bufferRelease.currentMaxAcquiredBufferCount); + } + mBufferReleases.clear(); + // For each listener auto completedTransactionsItr = mCompletedTransactions.begin(); - BackgroundExecutor::Callbacks callbacks; + ftl::SmallVector<ListenerStats, 10> listenerStatsToSend; while (completedTransactionsItr != mCompletedTransactions.end()) { auto& [listener, transactionStatsDeque] = *completedTransactionsItr; ListenerStats listenerStats; @@ -199,10 +208,7 @@ void TransactionCallbackInvoker::sendCallbacks(bool onCommitOnly) { // keep it as an IBinder due to consistency reasons: if we // interface_cast at the IPC boundary when reading a Parcel, // we get pointers that compare unequal in the SF process. - callbacks.emplace_back([stats = std::move(listenerStats)]() { - interface_cast<ITransactionCompletedListener>(stats.listener) - ->onTransactionCompleted(stats); - }); + listenerStatsToSend.emplace_back(std::move(listenerStats)); } } completedTransactionsItr++; @@ -212,7 +218,14 @@ void TransactionCallbackInvoker::sendCallbacks(bool onCommitOnly) { mPresentFence.clear(); } - BackgroundExecutor::getInstance().sendCallbacks(std::move(callbacks)); + BackgroundExecutor::getInstance().sendCallbacks( + {[listenerStatsToSend = std::move(listenerStatsToSend)]() { + SFTRACE_NAME("TransactionCallbackInvoker::sendCallbacks"); + for (auto& stats : listenerStatsToSend) { + interface_cast<ITransactionCompletedListener>(stats.listener) + ->onTransactionCompleted(stats); + } + }}); } // ----------------------------------------------------------------------- diff --git a/services/surfaceflinger/TransactionCallbackInvoker.h b/services/surfaceflinger/TransactionCallbackInvoker.h index cb7150b943..14a7487156 100644 --- a/services/surfaceflinger/TransactionCallbackInvoker.h +++ b/services/surfaceflinger/TransactionCallbackInvoker.h @@ -16,18 +16,14 @@ #pragma once -#include <condition_variable> #include <deque> -#include <mutex> #include <optional> -#include <queue> -#include <thread> #include <unordered_map> -#include <unordered_set> #include <android-base/thread_annotations.h> #include <binder/IBinder.h> #include <ftl/future.h> +#include <gui/BufferReleaseChannel.h> #include <gui/ITransactionCompletedListener.h> #include <ui/Fence.h> #include <ui/FenceResult.h> @@ -59,12 +55,12 @@ public: uint64_t frameNumber = 0; uint64_t previousFrameNumber = 0; ReleaseCallbackId previousReleaseCallbackId = ReleaseCallbackId::INVALID_ID; + std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint> bufferReleaseChannel; }; class TransactionCallbackInvoker { public: - status_t addCallbackHandles(const std::deque<sp<CallbackHandle>>& handles, - const std::vector<JankData>& jankData); + status_t addCallbackHandles(const std::deque<sp<CallbackHandle>>& handles); status_t addOnCommitCallbackHandles(const std::deque<sp<CallbackHandle>>& handles, std::deque<sp<CallbackHandle>>& outRemainingHandles); @@ -77,9 +73,7 @@ public: mCompletedTransactions.clear(); } - status_t addCallbackHandle(const sp<CallbackHandle>& handle, - const std::vector<JankData>& jankData); - + status_t addCallbackHandle(const sp<CallbackHandle>& handle); private: status_t findOrCreateTransactionStats(const sp<IBinder>& listener, @@ -89,6 +83,14 @@ private: std::unordered_map<sp<IBinder>, std::deque<TransactionStats>, IListenerHash> mCompletedTransactions; + struct BufferRelease { + std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint> channel; + ReleaseCallbackId callbackId; + sp<Fence> fence; + uint32_t currentMaxAcquiredBufferCount; + }; + std::vector<BufferRelease> mBufferReleases; + sp<Fence> mPresentFence; }; diff --git a/services/surfaceflinger/Utils/RingBuffer.h b/services/surfaceflinger/Utils/RingBuffer.h index 198e7b2cf1..215472b388 100644 --- a/services/surfaceflinger/Utils/RingBuffer.h +++ b/services/surfaceflinger/Utils/RingBuffer.h @@ -43,8 +43,10 @@ public: } T& front() { return (*this)[0]; } + const T& front() const { return (*this)[0]; } T& back() { return (*this)[size() - 1]; } + const T& back() const { return (*this)[size() - 1]; } T& operator[](size_t index) { return mBuffer[(static_cast<size_t>(mHead + 1) + index) % mCount]; diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.cpp b/services/surfaceflinger/WindowInfosListenerInvoker.cpp index effbfdb896..895e0543e1 100644 --- a/services/surfaceflinger/WindowInfosListenerInvoker.cpp +++ b/services/surfaceflinger/WindowInfosListenerInvoker.cpp @@ -17,8 +17,8 @@ #include <android/gui/BnWindowInfosPublisher.h> #include <android/gui/IWindowInfosPublisher.h> #include <android/gui/WindowInfosListenerInfo.h> +#include <common/trace.h> #include <gui/ISurfaceComposer.h> -#include <gui/TraceUtils.h> #include <gui/WindowInfosUpdate.h> #include <scheduler/Time.h> @@ -42,7 +42,7 @@ void WindowInfosListenerInvoker::addWindowInfosListener(sp<IWindowInfosListener> BackgroundExecutor::getInstance().sendCallbacks( {[this, listener = std::move(listener), listenerId]() { - ATRACE_NAME("WindowInfosListenerInvoker::addWindowInfosListener"); + SFTRACE_NAME("WindowInfosListenerInvoker::addWindowInfosListener"); sp<IBinder> asBinder = IInterface::asBinder(listener); asBinder->linkToDeath(sp<DeathRecipient>::fromExisting(this)); mWindowInfosListeners.try_emplace(asBinder, @@ -53,7 +53,7 @@ void WindowInfosListenerInvoker::addWindowInfosListener(sp<IWindowInfosListener> void WindowInfosListenerInvoker::removeWindowInfosListener( const sp<IWindowInfosListener>& listener) { BackgroundExecutor::getInstance().sendCallbacks({[this, listener]() { - ATRACE_NAME("WindowInfosListenerInvoker::removeWindowInfosListener"); + SFTRACE_NAME("WindowInfosListenerInvoker::removeWindowInfosListener"); sp<IBinder> asBinder = IInterface::asBinder(listener); asBinder->unlinkToDeath(sp<DeathRecipient>::fromExisting(this)); eraseListenerAndAckMessages(asBinder); @@ -62,7 +62,7 @@ void WindowInfosListenerInvoker::removeWindowInfosListener( void WindowInfosListenerInvoker::binderDied(const wp<IBinder>& who) { BackgroundExecutor::getInstance().sendCallbacks({[this, who]() { - ATRACE_NAME("WindowInfosListenerInvoker::binderDied"); + SFTRACE_NAME("WindowInfosListenerInvoker::binderDied"); eraseListenerAndAckMessages(who); }}); } @@ -146,7 +146,7 @@ void WindowInfosListenerInvoker::windowInfosChanged( WindowInfosListenerInvoker::DebugInfo WindowInfosListenerInvoker::getDebugInfo() { DebugInfo result; BackgroundExecutor::getInstance().sendCallbacks({[&, this]() { - ATRACE_NAME("WindowInfosListenerInvoker::getDebugInfo"); + SFTRACE_NAME("WindowInfosListenerInvoker::getDebugInfo"); updateMaxSendDelay(); result = mDebugInfo; result.pendingMessageCount = mUnackedState.size(); @@ -169,7 +169,7 @@ void WindowInfosListenerInvoker::updateMaxSendDelay() { binder::Status WindowInfosListenerInvoker::ackWindowInfosReceived(int64_t vsyncId, int64_t listenerId) { BackgroundExecutor::getInstance().sendCallbacks({[this, vsyncId, listenerId]() { - ATRACE_NAME("WindowInfosListenerInvoker::ackWindowInfosReceived"); + SFTRACE_NAME("WindowInfosListenerInvoker::ackWindowInfosReceived"); auto it = mUnackedState.find(vsyncId); if (it == mUnackedState.end()) { return; diff --git a/services/surfaceflinger/common/Android.bp b/services/surfaceflinger/common/Android.bp index bcf18869f8..f9c99bf2d2 100644 --- a/services/surfaceflinger/common/Android.bp +++ b/services/surfaceflinger/common/Android.bp @@ -18,6 +18,7 @@ cc_defaults { "libSurfaceFlingerProp", "server_configurable_flags", "libaconfig_storage_read_api_cc", + "libtracing_perfetto", ], static_libs: [ "librenderengine_includes", @@ -27,6 +28,7 @@ cc_defaults { ], local_include_dirs: ["include"], export_include_dirs: ["include"], + export_shared_lib_headers: ["libtracing_perfetto"], } cc_library_static { @@ -60,6 +62,7 @@ cc_defaults { shared_libs: [ "server_configurable_flags", "libaconfig_storage_read_api_cc", + "libtracing_perfetto", ], static_libs: [ "libsurfaceflinger_common", @@ -75,6 +78,7 @@ cc_defaults { shared_libs: [ "server_configurable_flags", "libaconfig_storage_read_api_cc", + "libtracing_perfetto", ], static_libs: [ "libsurfaceflinger_common_test", diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp index 2e3273c579..12d6138675 100644 --- a/services/surfaceflinger/common/FlagManager.cpp +++ b/services/surfaceflinger/common/FlagManager.cpp @@ -136,23 +136,27 @@ void FlagManager::dump(std::string& result) const { DUMP_READ_ONLY_FLAG(vulkan_renderengine); DUMP_READ_ONLY_FLAG(renderable_buffer_usage); DUMP_READ_ONLY_FLAG(vrr_bugfix_24q4); + DUMP_READ_ONLY_FLAG(vrr_bugfix_dropped_frame); DUMP_READ_ONLY_FLAG(restore_blur_step); DUMP_READ_ONLY_FLAG(dont_skip_on_early_ro); DUMP_READ_ONLY_FLAG(protected_if_client); DUMP_READ_ONLY_FLAG(ce_fence_promise); DUMP_READ_ONLY_FLAG(idle_screen_refresh_rate_timeout); DUMP_READ_ONLY_FLAG(graphite_renderengine); + DUMP_READ_ONLY_FLAG(filter_frames_before_trace_starts); DUMP_READ_ONLY_FLAG(latch_unsignaled_with_auto_refresh_changed); DUMP_READ_ONLY_FLAG(deprecate_vsync_sf); DUMP_READ_ONLY_FLAG(allow_n_vsyncs_in_targeter); DUMP_READ_ONLY_FLAG(detached_mirror); DUMP_READ_ONLY_FLAG(commit_not_composited); + DUMP_READ_ONLY_FLAG(correct_dpi_with_display_size); DUMP_READ_ONLY_FLAG(local_tonemap_screenshots); DUMP_READ_ONLY_FLAG(override_trusted_overlay); DUMP_READ_ONLY_FLAG(flush_buffer_slots_to_uncache); DUMP_READ_ONLY_FLAG(force_compile_graphite_renderengine); DUMP_READ_ONLY_FLAG(single_hop_screenshot); DUMP_READ_ONLY_FLAG(trace_frame_rate_override); + DUMP_READ_ONLY_FLAG(true_hdr_screenshots); #undef DUMP_READ_ONLY_FLAG #undef DUMP_SERVER_FLAG @@ -242,18 +246,22 @@ FLAG_MANAGER_READ_ONLY_FLAG(restore_blur_step, "debug.renderengine.restore_blur_ FLAG_MANAGER_READ_ONLY_FLAG(dont_skip_on_early_ro, "") FLAG_MANAGER_READ_ONLY_FLAG(protected_if_client, "") FLAG_MANAGER_READ_ONLY_FLAG(vrr_bugfix_24q4, ""); +FLAG_MANAGER_READ_ONLY_FLAG(vrr_bugfix_dropped_frame, "") FLAG_MANAGER_READ_ONLY_FLAG(ce_fence_promise, ""); FLAG_MANAGER_READ_ONLY_FLAG(graphite_renderengine, "debug.renderengine.graphite") +FLAG_MANAGER_READ_ONLY_FLAG(filter_frames_before_trace_starts, "") FLAG_MANAGER_READ_ONLY_FLAG(latch_unsignaled_with_auto_refresh_changed, ""); FLAG_MANAGER_READ_ONLY_FLAG(deprecate_vsync_sf, ""); FLAG_MANAGER_READ_ONLY_FLAG(allow_n_vsyncs_in_targeter, ""); FLAG_MANAGER_READ_ONLY_FLAG(detached_mirror, ""); FLAG_MANAGER_READ_ONLY_FLAG(commit_not_composited, ""); +FLAG_MANAGER_READ_ONLY_FLAG(correct_dpi_with_display_size, ""); FLAG_MANAGER_READ_ONLY_FLAG(local_tonemap_screenshots, "debug.sf.local_tonemap_screenshots"); FLAG_MANAGER_READ_ONLY_FLAG(override_trusted_overlay, ""); FLAG_MANAGER_READ_ONLY_FLAG(flush_buffer_slots_to_uncache, ""); FLAG_MANAGER_READ_ONLY_FLAG(force_compile_graphite_renderengine, ""); FLAG_MANAGER_READ_ONLY_FLAG(single_hop_screenshot, ""); +FLAG_MANAGER_READ_ONLY_FLAG(true_hdr_screenshots, "debug.sf.true_hdr_screenshots"); /// Trunk stable server flags /// FLAG_MANAGER_SERVER_FLAG(refresh_rate_overlay_on_external_display, "") diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h index ab7a474d2c..a1be19421b 100644 --- a/services/surfaceflinger/common/include/common/FlagManager.h +++ b/services/surfaceflinger/common/include/common/FlagManager.h @@ -73,6 +73,7 @@ public: bool screenshot_fence_preservation() const; bool vulkan_renderengine() const; bool vrr_bugfix_24q4() const; + bool vrr_bugfix_dropped_frame() const; bool renderable_buffer_usage() const; bool restore_blur_step() const; bool dont_skip_on_early_ro() const; @@ -80,17 +81,20 @@ public: bool ce_fence_promise() const; bool idle_screen_refresh_rate_timeout() const; bool graphite_renderengine() const; + bool filter_frames_before_trace_starts() const; bool latch_unsignaled_with_auto_refresh_changed() const; bool deprecate_vsync_sf() const; bool allow_n_vsyncs_in_targeter() const; bool detached_mirror() const; bool commit_not_composited() const; + bool correct_dpi_with_display_size() const; bool local_tonemap_screenshots() const; bool override_trusted_overlay() const; bool flush_buffer_slots_to_uncache() const; bool force_compile_graphite_renderengine() const; bool single_hop_screenshot() const; bool trace_frame_rate_override() const; + bool true_hdr_screenshots() const; protected: // overridden for unit tests diff --git a/services/surfaceflinger/common/include/common/trace.h b/services/surfaceflinger/common/include/common/trace.h new file mode 100644 index 0000000000..dc5716ba05 --- /dev/null +++ b/services/surfaceflinger/common/include/common/trace.h @@ -0,0 +1,90 @@ + +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#ifndef ATRACE_TAG +#define ATRACE_TAG ATRACE_TAG_GRAPHICS +#endif + +#include <cutils/trace.h> +#include <tracing_perfetto.h> + +// prevent using atrace directly, calls should go through tracing_perfetto lib +#undef ATRACE_ENABLED +#undef ATRACE_BEGIN +#undef ATRACE_END +#undef ATRACE_ASYNC_BEGIN +#undef ATRACE_ASYNC_END +#undef ATRACE_ASYNC_FOR_TRACK_BEGIN +#undef ATRACE_ASYNC_FOR_TRACK_END +#undef ATRACE_INSTANT +#undef ATRACE_INSTANT_FOR_TRACK +#undef ATRACE_INT +#undef ATRACE_INT64 +#undef ATRACE_CALL +#undef ATRACE_NAME +#undef ATRACE_FORMAT +#undef ATRACE_FORMAT_INSTANT + +#define SFTRACE_ENABLED() ::tracing_perfetto::isTagEnabled(ATRACE_TAG) +#define SFTRACE_BEGIN(name) ::tracing_perfetto::traceBegin(ATRACE_TAG, name) +#define SFTRACE_END() ::tracing_perfetto::traceEnd(ATRACE_TAG) +#define SFTRACE_ASYNC_BEGIN(name, cookie) \ + ::tracing_perfetto::traceAsyncBegin(ATRACE_TAG, name, cookie) +#define SFTRACE_ASYNC_END(name, cookie) ::tracing_perfetto::traceAsyncEnd(ATRACE_TAG, name, cookie) +#define SFTRACE_ASYNC_FOR_TRACK_BEGIN(track_name, name, cookie) \ + ::tracing_perfetto::traceAsyncBeginForTrack(ATRACE_TAG, name, track_name, cookie) +#define SFTRACE_ASYNC_FOR_TRACK_END(track_name, cookie) \ + ::tracing_perfetto::traceAsyncEndForTrack(ATRACE_TAG, track_name, cookie) +#define SFTRACE_INSTANT(name) ::tracing_perfetto::traceInstant(ATRACE_TAG, name) +#define SFTRACE_FORMAT_INSTANT(fmt, ...) \ + ::tracing_perfetto::traceFormatInstant(ATRACE_TAG, fmt, ##__VA_ARGS__) +#define SFTRACE_INSTANT_FOR_TRACK(trackName, name) \ + ::tracing_perfetto::traceInstantForTrack(ATRACE_TAG, trackName, name) +#define SFTRACE_INT(name, value) ::tracing_perfetto::traceCounter32(ATRACE_TAG, name, value) +#define SFTRACE_INT64(name, value) ::tracing_perfetto::traceCounter(ATRACE_TAG, name, value) + +// SFTRACE_NAME traces from its location until the end of its enclosing scope. +#define _PASTE(x, y) x##y +#define PASTE(x, y) _PASTE(x, y) +#define SFTRACE_NAME(name) ::android::ScopedTrace PASTE(___tracer, __LINE__)(name) +// SFTRACE_CALL is an SFTRACE_NAME that uses the current function name. +#define SFTRACE_CALL() SFTRACE_NAME(__FUNCTION__) + +#define SFTRACE_FORMAT(fmt, ...) \ + ::android::ScopedTrace PASTE(___tracer, __LINE__)(fmt, ##__VA_ARGS__) + +#define ALOGE_AND_TRACE(fmt, ...) \ + do { \ + ALOGE(fmt, ##__VA_ARGS__); \ + SFTRACE_FORMAT_INSTANT(fmt, ##__VA_ARGS__); \ + } while (false) + +namespace android { + +class ScopedTrace { +public: + template <typename... Args> + inline ScopedTrace(const char* fmt, Args&&... args) { + ::tracing_perfetto::traceFormatBegin(ATRACE_TAG, fmt, std::forward<Args>(args)...); + } + inline ScopedTrace(const char* name) { SFTRACE_BEGIN(name); } + inline ~ScopedTrace() { SFTRACE_END(); } +}; + +} // namespace android diff --git a/services/surfaceflinger/layerproto/Android.bp b/services/surfaceflinger/layerproto/Android.bp index f77b137e4d..0a69a72c1d 100644 --- a/services/surfaceflinger/layerproto/Android.bp +++ b/services/surfaceflinger/layerproto/Android.bp @@ -8,14 +8,9 @@ package { default_team: "trendy_team_android_core_graphics_stack", } -cc_library { - name: "liblayers_proto", +cc_defaults { + name: "libsurfaceflinger_proto_deps", export_include_dirs: ["include"], - - srcs: [ - "LayerProtoParser.cpp", - ], - static_libs: [ "libperfetto_client_experimental", ], @@ -31,24 +26,15 @@ cc_library { ], shared_libs: [ - "android.hardware.graphics.common@1.1", - "libgui", - "libui", "libprotobuf-cpp-lite", - "libbase", ], - cppflags: [ - "-Werror", - "-Wno-unused-parameter", - "-Wno-format", - "-Wno-c++98-compat-pedantic", - "-Wno-float-conversion", - "-Wno-disabled-macro-expansion", - "-Wno-float-equal", - "-Wno-sign-conversion", - "-Wno-padded", - "-Wno-old-style-cast", - "-Wno-undef", + header_libs: [ + "libsurfaceflinger_proto_headers", ], } + +cc_library_headers { + name: "libsurfaceflinger_proto_headers", + export_include_dirs: ["include"], +} diff --git a/services/surfaceflinger/layerproto/LayerProtoParser.cpp b/services/surfaceflinger/layerproto/LayerProtoParser.cpp deleted file mode 100644 index c3d0a40261..0000000000 --- a/services/surfaceflinger/layerproto/LayerProtoParser.cpp +++ /dev/null @@ -1,328 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include <android-base/stringprintf.h> -#include <layerproto/LayerProtoParser.h> -#include <ui/DebugUtils.h> - -using android::base::StringAppendF; -using android::base::StringPrintf; - -namespace android { -namespace surfaceflinger { - -bool sortLayers(LayerProtoParser::Layer* lhs, const LayerProtoParser::Layer* rhs) { - uint32_t ls = lhs->layerStack; - uint32_t rs = rhs->layerStack; - if (ls != rs) return ls < rs; - - int32_t lz = lhs->z; - int32_t rz = rhs->z; - if (lz != rz) { - return lz < rz; - } - - return lhs->id < rhs->id; -} - -LayerProtoParser::LayerTree LayerProtoParser::generateLayerTree( - const perfetto::protos::LayersProto& layersProto) { - LayerTree layerTree; - layerTree.allLayers = generateLayerList(layersProto); - - // find and sort the top-level layers - for (Layer& layer : layerTree.allLayers) { - if (layer.parent == nullptr) { - layerTree.topLevelLayers.push_back(&layer); - } - } - std::sort(layerTree.topLevelLayers.begin(), layerTree.topLevelLayers.end(), sortLayers); - - return layerTree; -} - -std::vector<LayerProtoParser::Layer> LayerProtoParser::generateLayerList( - const perfetto::protos::LayersProto& layersProto) { - std::vector<Layer> layerList; - std::unordered_map<int32_t, Layer*> layerMap; - - // build the layer list and the layer map - layerList.reserve(layersProto.layers_size()); - layerMap.reserve(layersProto.layers_size()); - for (int i = 0; i < layersProto.layers_size(); i++) { - layerList.emplace_back(generateLayer(layersProto.layers(i))); - // this works because layerList never changes capacity - layerMap[layerList.back().id] = &layerList.back(); - } - - // fix up children and relatives - for (int i = 0; i < layersProto.layers_size(); i++) { - updateChildrenAndRelative(layersProto.layers(i), layerMap); - } - - return layerList; -} - -LayerProtoParser::Layer LayerProtoParser::generateLayer( - const perfetto::protos::LayerProto& layerProto) { - Layer layer; - layer.id = layerProto.id(); - layer.name = layerProto.name(); - layer.type = layerProto.type(); - layer.transparentRegion = generateRegion(layerProto.transparent_region()); - layer.visibleRegion = generateRegion(layerProto.visible_region()); - layer.damageRegion = generateRegion(layerProto.damage_region()); - layer.layerStack = layerProto.layer_stack(); - layer.z = layerProto.z(); - layer.position = {layerProto.position().x(), layerProto.position().y()}; - layer.requestedPosition = {layerProto.requested_position().x(), - layerProto.requested_position().y()}; - layer.size = {layerProto.size().w(), layerProto.size().h()}; - layer.crop = generateRect(layerProto.crop()); - layer.isOpaque = layerProto.is_opaque(); - layer.invalidate = layerProto.invalidate(); - layer.dataspace = layerProto.dataspace(); - layer.pixelFormat = layerProto.pixel_format(); - layer.color = {layerProto.color().r(), layerProto.color().g(), layerProto.color().b(), - layerProto.color().a()}; - layer.requestedColor = {layerProto.requested_color().r(), layerProto.requested_color().g(), - layerProto.requested_color().b(), layerProto.requested_color().a()}; - layer.flags = layerProto.flags(); - layer.transform = generateTransform(layerProto.transform()); - layer.requestedTransform = generateTransform(layerProto.requested_transform()); - layer.activeBuffer = generateActiveBuffer(layerProto.active_buffer()); - layer.bufferTransform = generateTransform(layerProto.buffer_transform()); - layer.queuedFrames = layerProto.queued_frames(); - layer.refreshPending = layerProto.refresh_pending(); - layer.isProtected = layerProto.is_protected(); - layer.isTrustedOverlay = layerProto.is_trusted_overlay(); - layer.cornerRadius = layerProto.corner_radius(); - layer.backgroundBlurRadius = layerProto.background_blur_radius(); - for (const auto& entry : layerProto.metadata()) { - const std::string& dataStr = entry.second; - std::vector<uint8_t>& outData = layer.metadata.mMap[entry.first]; - outData.resize(dataStr.size()); - memcpy(outData.data(), dataStr.data(), dataStr.size()); - } - layer.cornerRadiusCrop = generateFloatRect(layerProto.corner_radius_crop()); - layer.shadowRadius = layerProto.shadow_radius(); - layer.ownerUid = layerProto.owner_uid(); - return layer; -} - -LayerProtoParser::Region LayerProtoParser::generateRegion( - const perfetto::protos::RegionProto& regionProto) { - LayerProtoParser::Region region; - for (int i = 0; i < regionProto.rect_size(); i++) { - const perfetto::protos::RectProto& rectProto = regionProto.rect(i); - region.rects.push_back(generateRect(rectProto)); - } - - return region; -} - -LayerProtoParser::Rect LayerProtoParser::generateRect( - const perfetto::protos::RectProto& rectProto) { - LayerProtoParser::Rect rect; - rect.left = rectProto.left(); - rect.top = rectProto.top(); - rect.right = rectProto.right(); - rect.bottom = rectProto.bottom(); - - return rect; -} - -LayerProtoParser::FloatRect LayerProtoParser::generateFloatRect( - const perfetto::protos::FloatRectProto& rectProto) { - LayerProtoParser::FloatRect rect; - rect.left = rectProto.left(); - rect.top = rectProto.top(); - rect.right = rectProto.right(); - rect.bottom = rectProto.bottom(); - - return rect; -} - -LayerProtoParser::Transform LayerProtoParser::generateTransform( - const perfetto::protos::TransformProto& transformProto) { - LayerProtoParser::Transform transform; - transform.dsdx = transformProto.dsdx(); - transform.dtdx = transformProto.dtdx(); - transform.dsdy = transformProto.dsdy(); - transform.dtdy = transformProto.dtdy(); - - return transform; -} - -LayerProtoParser::ActiveBuffer LayerProtoParser::generateActiveBuffer( - const perfetto::protos::ActiveBufferProto& activeBufferProto) { - LayerProtoParser::ActiveBuffer activeBuffer; - activeBuffer.width = activeBufferProto.width(); - activeBuffer.height = activeBufferProto.height(); - activeBuffer.stride = activeBufferProto.stride(); - activeBuffer.format = activeBufferProto.format(); - - return activeBuffer; -} - -void LayerProtoParser::updateChildrenAndRelative(const perfetto::protos::LayerProto& layerProto, - std::unordered_map<int32_t, Layer*>& layerMap) { - auto currLayer = layerMap[layerProto.id()]; - - for (int i = 0; i < layerProto.children_size(); i++) { - if (layerMap.count(layerProto.children(i)) > 0) { - currLayer->children.push_back(layerMap[layerProto.children(i)]); - } - } - - for (int i = 0; i < layerProto.relatives_size(); i++) { - if (layerMap.count(layerProto.relatives(i)) > 0) { - currLayer->relatives.push_back(layerMap[layerProto.relatives(i)]); - } - } - - if (layerProto.has_parent()) { - if (layerMap.count(layerProto.parent()) > 0) { - currLayer->parent = layerMap[layerProto.parent()]; - } - } - - if (layerProto.has_z_order_relative_of()) { - if (layerMap.count(layerProto.z_order_relative_of()) > 0) { - currLayer->zOrderRelativeOf = layerMap[layerProto.z_order_relative_of()]; - } - } -} - -std::string LayerProtoParser::layerTreeToString(const LayerTree& layerTree) { - std::string result; - for (const LayerProtoParser::Layer* layer : layerTree.topLevelLayers) { - if (layer->zOrderRelativeOf != nullptr) { - continue; - } - result.append(layerToString(layer)); - } - - return result; -} - -std::string LayerProtoParser::layerToString(const LayerProtoParser::Layer* layer) { - std::string result; - - std::vector<Layer*> traverse(layer->relatives); - for (LayerProtoParser::Layer* child : layer->children) { - if (child->zOrderRelativeOf != nullptr) { - continue; - } - - traverse.push_back(child); - } - - std::sort(traverse.begin(), traverse.end(), sortLayers); - - size_t i = 0; - for (; i < traverse.size(); i++) { - auto& relative = traverse[i]; - if (relative->z >= 0) { - break; - } - result.append(layerToString(relative)); - } - result.append(layer->to_string()); - result.append("\n"); - for (; i < traverse.size(); i++) { - auto& relative = traverse[i]; - result.append(layerToString(relative)); - } - - return result; -} - -std::string LayerProtoParser::ActiveBuffer::to_string() const { - return StringPrintf("[%4ux%4u:%4u,%s]", width, height, stride, - decodePixelFormat(format).c_str()); -} - -std::string LayerProtoParser::Transform::to_string() const { - return StringPrintf("[%.2f, %.2f][%.2f, %.2f]", static_cast<double>(dsdx), - static_cast<double>(dtdx), static_cast<double>(dsdy), - static_cast<double>(dtdy)); -} - -std::string LayerProtoParser::Rect::to_string() const { - return StringPrintf("[%3d, %3d, %3d, %3d]", left, top, right, bottom); -} - -std::string LayerProtoParser::FloatRect::to_string() const { - return StringPrintf("[%.2f, %.2f, %.2f, %.2f]", left, top, right, bottom); -} - -std::string LayerProtoParser::Region::to_string(const char* what) const { - std::string result = - StringPrintf(" Region %s (this=%lx count=%d)\n", what, static_cast<unsigned long>(id), - static_cast<int>(rects.size())); - - for (auto& rect : rects) { - StringAppendF(&result, " %s\n", rect.to_string().c_str()); - } - - return result; -} - -std::string LayerProtoParser::Layer::to_string() const { - std::string result; - StringAppendF(&result, "+ %s (%s) uid=%d\n", type.c_str(), name.c_str(), ownerUid); - result.append(transparentRegion.to_string("TransparentRegion").c_str()); - result.append(visibleRegion.to_string("VisibleRegion").c_str()); - result.append(damageRegion.to_string("SurfaceDamageRegion").c_str()); - - StringAppendF(&result, " layerStack=%4d, z=%9d, pos=(%g,%g), size=(%4d,%4d), ", layerStack, - z, static_cast<double>(position.x), static_cast<double>(position.y), size.x, - size.y); - - StringAppendF(&result, "crop=%s, ", crop.to_string().c_str()); - StringAppendF(&result, "cornerRadius=%f, ", cornerRadius); - StringAppendF(&result, "isProtected=%1d, ", isProtected); - StringAppendF(&result, "isTrustedOverlay=%1d, ", isTrustedOverlay); - StringAppendF(&result, "isOpaque=%1d, invalidate=%1d, ", isOpaque, invalidate); - StringAppendF(&result, "dataspace=%s, ", dataspace.c_str()); - StringAppendF(&result, "defaultPixelFormat=%s, ", pixelFormat.c_str()); - StringAppendF(&result, "backgroundBlurRadius=%1d, ", backgroundBlurRadius); - StringAppendF(&result, "color=(%.3f,%.3f,%.3f,%.3f), flags=0x%08x, ", - static_cast<double>(color.r), static_cast<double>(color.g), - static_cast<double>(color.b), static_cast<double>(color.a), flags); - StringAppendF(&result, "tr=%s", transform.to_string().c_str()); - result.append("\n"); - StringAppendF(&result, " parent=%s\n", parent == nullptr ? "none" : parent->name.c_str()); - StringAppendF(&result, " zOrderRelativeOf=%s\n", - zOrderRelativeOf == nullptr ? "none" : zOrderRelativeOf->name.c_str()); - StringAppendF(&result, " activeBuffer=%s,", activeBuffer.to_string().c_str()); - StringAppendF(&result, " tr=%s", bufferTransform.to_string().c_str()); - StringAppendF(&result, " queued-frames=%d", queuedFrames); - StringAppendF(&result, " metadata={"); - bool first = true; - for (const auto& entry : metadata.mMap) { - if (!first) result.append(", "); - first = false; - result.append(metadata.itemToString(entry.first, ":")); - } - result.append("},"); - StringAppendF(&result, " cornerRadiusCrop=%s, ", cornerRadiusCrop.to_string().c_str()); - StringAppendF(&result, " shadowRadius=%.3f, ", shadowRadius); - return result; -} - -} // namespace surfaceflinger -} // namespace android diff --git a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h deleted file mode 100644 index 79c3982dbd..0000000000 --- a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright (C) 2017 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 <layerproto/LayerProtoHeader.h> - -#include <gui/LayerMetadata.h> -#include <math/vec4.h> - -#include <memory> -#include <unordered_map> -#include <vector> - -using android::gui::LayerMetadata; - -namespace android { -namespace surfaceflinger { - -class LayerProtoParser { -public: - class ActiveBuffer { - public: - uint32_t width; - uint32_t height; - uint32_t stride; - int32_t format; - - std::string to_string() const; - }; - - class Transform { - public: - float dsdx; - float dtdx; - float dsdy; - float dtdy; - - std::string to_string() const; - }; - - class Rect { - public: - int32_t left; - int32_t top; - int32_t right; - int32_t bottom; - - std::string to_string() const; - }; - - class FloatRect { - public: - float left; - float top; - float right; - float bottom; - - std::string to_string() const; - }; - - class Region { - public: - uint64_t id; - std::vector<Rect> rects; - - std::string to_string(const char* what) const; - }; - - class Layer { - public: - int32_t id; - std::string name; - std::vector<Layer*> children; - std::vector<Layer*> relatives; - std::string type; - LayerProtoParser::Region transparentRegion; - LayerProtoParser::Region visibleRegion; - LayerProtoParser::Region damageRegion; - uint32_t layerStack; - int32_t z; - float2 position; - float2 requestedPosition; - int2 size; - LayerProtoParser::Rect crop; - bool isOpaque; - bool invalidate; - std::string dataspace; - std::string pixelFormat; - half4 color; - half4 requestedColor; - uint32_t flags; - Transform transform; - Transform requestedTransform; - Layer* parent = 0; - Layer* zOrderRelativeOf = 0; - LayerProtoParser::ActiveBuffer activeBuffer; - Transform bufferTransform; - int32_t queuedFrames; - bool refreshPending; - bool isProtected; - bool isTrustedOverlay; - float cornerRadius; - int backgroundBlurRadius; - LayerMetadata metadata; - LayerProtoParser::FloatRect cornerRadiusCrop; - float shadowRadius; - uid_t ownerUid; - - std::string to_string() const; - }; - - class LayerTree { - public: - // all layers in LayersProto and in the original order - std::vector<Layer> allLayers; - - // pointers to top-level layers in allLayers - std::vector<Layer*> topLevelLayers; - }; - - static LayerTree generateLayerTree(const perfetto::protos::LayersProto& layersProto); - static std::string layerTreeToString(const LayerTree& layerTree); - -private: - static std::vector<Layer> generateLayerList(const perfetto::protos::LayersProto& layersProto); - static LayerProtoParser::Layer generateLayer(const perfetto::protos::LayerProto& layerProto); - static LayerProtoParser::Region generateRegion( - const perfetto::protos::RegionProto& regionProto); - static LayerProtoParser::Rect generateRect(const perfetto::protos::RectProto& rectProto); - static LayerProtoParser::FloatRect generateFloatRect( - const perfetto::protos::FloatRectProto& rectProto); - static LayerProtoParser::Transform generateTransform( - const perfetto::protos::TransformProto& transformProto); - static LayerProtoParser::ActiveBuffer generateActiveBuffer( - const perfetto::protos::ActiveBufferProto& activeBufferProto); - static void updateChildrenAndRelative(const perfetto::protos::LayerProto& layerProto, - std::unordered_map<int32_t, Layer*>& layerMap); - - static std::string layerToString(const LayerProtoParser::Layer* layer); -}; - -} // namespace surfaceflinger -} // namespace android diff --git a/services/surfaceflinger/surfaceflinger_flags_new.aconfig b/services/surfaceflinger/surfaceflinger_flags_new.aconfig index f4d4ee978d..102e2b643c 100644 --- a/services/surfaceflinger/surfaceflinger_flags_new.aconfig +++ b/services/surfaceflinger/surfaceflinger_flags_new.aconfig @@ -4,10 +4,10 @@ package: "com.android.graphics.surfaceflinger.flags" container: "system" flag { - name: "adpf_gpu_sf" - namespace: "game" - description: "Guards use of the sending ADPF GPU duration hint and load hints from SurfaceFlinger to Power HAL" - bug: "284324521" + name: "adpf_gpu_sf" + namespace: "game" + description: "Guards use of the sending ADPF GPU duration hint and load hints from SurfaceFlinger to Power HAL" + bug: "284324521" } # adpf_gpu_sf flag { @@ -21,18 +21,29 @@ flag { } } # ce_fence_promise - flag { - name: "commit_not_composited" - namespace: "core_graphics" - description: "mark frames as non janky if the transaction resulted in no composition" - bug: "340633280" - is_fixed_read_only: true - metadata { - purpose: PURPOSE_BUGFIX - } - } # commit_not_composited +flag { + name: "commit_not_composited" + namespace: "core_graphics" + description: "mark frames as non janky if the transaction resulted in no composition" + bug: "340633280" + is_fixed_read_only: true + metadata { + purpose: PURPOSE_BUGFIX + } +} # commit_not_composited - flag { +flag { + name: "correct_dpi_with_display_size" + namespace: "core_graphics" + description: "indicate whether missing or likely incorrect dpi should be corrected using the display size." + bug: "328425848" + is_fixed_read_only: true + metadata { + purpose: PURPOSE_BUGFIX + } +} # correct_dpi_with_display_size + +flag { name: "deprecate_vsync_sf" namespace: "core_graphics" description: "Depracate eVsyncSourceSurfaceFlinger and use vsync_app everywhere" @@ -43,7 +54,7 @@ flag { } } # deprecate_vsync_sf - flag { +flag { name: "detached_mirror" namespace: "window_surfaces" description: "Ignore local transform when mirroring a partial hierarchy" @@ -55,6 +66,17 @@ flag { } # detached_mirror flag { + name: "filter_frames_before_trace_starts" + namespace: "core_graphics" + description: "Do not trace FrameTimeline events for frames started before the trace started" + bug: "364194637" + is_fixed_read_only: true + metadata { + purpose: PURPOSE_BUGFIX + } +} # filter_frames_before_trace_starts + +flag { name: "flush_buffer_slots_to_uncache" namespace: "core_graphics" description: "Flush DisplayCommands for disabled displays in order to uncache requested buffers." @@ -96,11 +118,11 @@ flag { } # latch_unsignaled_with_auto_refresh_changed flag { - name: "local_tonemap_screenshots" - namespace: "core_graphics" - description: "Enables local tonemapping when capturing screenshots" - bug: "329464641" - is_fixed_read_only: true + name: "local_tonemap_screenshots" + namespace: "core_graphics" + description: "Enables local tonemapping when capturing screenshots" + bug: "329464641" + is_fixed_read_only: true } # local_tonemap_screenshots flag { @@ -114,6 +136,14 @@ flag { } } # single_hop_screenshot +flag { + name: "true_hdr_screenshots" + namespace: "core_graphics" + description: "Enables screenshotting display content in HDR, sans tone mapping" + bug: "329470026" + is_fixed_read_only: true +} # true_hdr_screenshots + flag { name: "override_trusted_overlay" namespace: "window_surfaces" @@ -126,6 +156,14 @@ flag { } # override_trusted_overlay flag { + name: "view_set_requested_frame_rate_mrr" + namespace: "core_graphics" + description: "Enable to use frame rate category NoPreference with fixed frame rate vote on MRR devices" + bug: "352206100" + is_fixed_read_only: true +} # view_set_requested_frame_rate_mrr + +flag { name: "vrr_bugfix_24q4" namespace: "core_graphics" description: "bug fixes for VRR" @@ -136,4 +174,15 @@ flag { } } # vrr_bugfix_24q4 +flag { + name: "vrr_bugfix_dropped_frame" + namespace: "core_graphics" + description: "bug fix for VRR dropped frame" + bug: "343603085" + is_fixed_read_only: true + metadata { + purpose: PURPOSE_BUGFIX + } +} # vrr_bugfix_dropped_frame + # IMPORTANT - please keep alphabetize to reduce merge conflicts diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp index 38fc977931..4d5c0fd1de 100644 --- a/services/surfaceflinger/tests/Android.bp +++ b/services/surfaceflinger/tests/Android.bp @@ -28,6 +28,7 @@ cc_test { "android.hardware.graphics.common-ndk_shared", "surfaceflinger_defaults", "libsurfaceflinger_common_test_deps", + "libsurfaceflinger_proto_deps", ], test_suites: ["device-tests"], srcs: [ @@ -58,14 +59,12 @@ cc_test { "ScreenCapture_test.cpp", "SetFrameRate_test.cpp", "SetGeometry_test.cpp", - "Stress_test.cpp", "TextureFiltering_test.cpp", "VirtualDisplay_test.cpp", "WindowInfosListener_test.cpp", ], data: ["SurfaceFlinger_test.filter"], static_libs: [ - "liblayers_proto", "android.hardware.graphics.composer@2.1", "libsurfaceflinger_common", ], @@ -121,7 +120,6 @@ cc_test { "libEGL", "libGLESv2", "libgui", - "liblayers_proto", "liblog", "libprotobuf-cpp-full", "libui", diff --git a/services/surfaceflinger/tests/BootDisplayMode_test.cpp b/services/surfaceflinger/tests/BootDisplayMode_test.cpp index 4f41a81011..222642f50c 100644 --- a/services/surfaceflinger/tests/BootDisplayMode_test.cpp +++ b/services/surfaceflinger/tests/BootDisplayMode_test.cpp @@ -18,7 +18,7 @@ #include <gtest/gtest.h> -#include <gui/AidlStatusUtil.h> +#include <gui/AidlUtil.h> #include <gui/SurfaceComposerClient.h> #include <private/gui/ComposerService.h> #include <private/gui/ComposerServiceAIDL.h> diff --git a/services/surfaceflinger/tests/BufferGenerator.cpp b/services/surfaceflinger/tests/BufferGenerator.cpp index d74bd55987..efab7b856a 100644 --- a/services/surfaceflinger/tests/BufferGenerator.cpp +++ b/services/surfaceflinger/tests/BufferGenerator.cpp @@ -42,8 +42,13 @@ public: * through saved callback. */ class BufferListener : public ConsumerBase::FrameAvailableListener { public: +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) + BufferListener(sp<BufferItemConsumer> consumer, BufferCallback callback) +#else BufferListener(sp<IGraphicBufferConsumer> consumer, BufferCallback callback) - : mConsumer(consumer), mCallback(callback) {} +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) + : mConsumer(consumer), mCallback(callback) { + } void onFrameAvailable(const BufferItem& /*item*/) { BufferItem item; @@ -55,7 +60,11 @@ public: } private: +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) + sp<BufferItemConsumer> mConsumer; +#else sp<IGraphicBufferConsumer> mConsumer; +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) BufferCallback mCallback; }; @@ -63,6 +72,16 @@ public: * queue. */ void initialize(uint32_t width, uint32_t height, android_pixel_format_t format, BufferCallback callback) { +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) + mBufferItemConsumer = sp<BufferItemConsumer>::make(GraphicBuffer::USAGE_HW_TEXTURE); + mBufferItemConsumer->setDefaultBufferSize(width, height); + mBufferItemConsumer->setDefaultBufferFormat(format); + + mListener = sp<BufferListener>::make(mBufferItemConsumer, callback); + mBufferItemConsumer->setFrameAvailableListener(mListener); + + mSurface = mBufferItemConsumer->getSurface(); +#else sp<IGraphicBufferProducer> producer; sp<IGraphicBufferConsumer> consumer; BufferQueue::createBufferQueue(&producer, &consumer); @@ -77,6 +96,7 @@ public: mBufferItemConsumer->setFrameAvailableListener(mListener); mSurface = sp<Surface>::make(producer, true); +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) } /* Used by Egl manager. The surface is never displayed. */ diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp index ebe11fb0f3..e6fed63d96 100644 --- a/services/surfaceflinger/tests/Credentials_test.cpp +++ b/services/surfaceflinger/tests/Credentials_test.cpp @@ -20,12 +20,13 @@ #include <android/gui/ISurfaceComposer.h> #include <gtest/gtest.h> -#include <gui/AidlStatusUtil.h> +#include <gui/AidlUtil.h> #include <gui/Surface.h> #include <gui/SurfaceComposerClient.h> #include <private/android_filesystem_config.h> #include <private/gui/ComposerServiceAIDL.h> #include <ui/DisplayMode.h> +#include <ui/DisplayState.h> #include <ui/DynamicDisplayInfo.h> #include <utils/String8.h> #include <functional> @@ -276,10 +277,10 @@ TEST_F(CredentialsTest, CreateDisplayTest) { TEST_F(CredentialsTest, CaptureLayersTest) { setupBackgroundSurface(); sp<GraphicBuffer> outBuffer; - std::function<status_t()> condition = [=]() { + std::function<status_t()> condition = [=, this]() { LayerCaptureArgs captureArgs; captureArgs.layerHandle = mBGSurfaceControl->getHandle(); - captureArgs.sourceCrop = {0, 0, 1, 1}; + captureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(0, 0, 1, 1); ScreenCaptureResults captureResults; return ScreenCapture::captureLayers(captureArgs, captureResults); @@ -396,6 +397,56 @@ TEST_F(CredentialsTest, TransactionPermissionTest) { } } +TEST_F(CredentialsTest, DisplayTransactionPermissionTest) { + const auto display = getFirstDisplayToken(); + + ui::DisplayState displayState; + ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayState(display, &displayState)); + const ui::Rotation initialOrientation = displayState.orientation; + + // Set display orientation from an untrusted process. This should fail silently. + { + UIDFaker f{AID_BIN}; + Transaction transaction; + Rect layerStackRect; + Rect displayRect; + transaction.setDisplayProjection(display, initialOrientation + ui::ROTATION_90, + layerStackRect, displayRect); + transaction.apply(/*synchronous=*/true); + } + + // Verify that the display orientation did not change. + ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayState(display, &displayState)); + ASSERT_EQ(initialOrientation, displayState.orientation); + + // Set display orientation from a trusted process. + { + UIDFaker f{AID_SYSTEM}; + Transaction transaction; + Rect layerStackRect; + Rect displayRect; + transaction.setDisplayProjection(display, initialOrientation + ui::ROTATION_90, + layerStackRect, displayRect); + transaction.apply(/*synchronous=*/true); + } + + // Verify that the display orientation did change. + ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayState(display, &displayState)); + ASSERT_EQ(initialOrientation + ui::ROTATION_90, displayState.orientation); + + // Reset orientation + { + UIDFaker f{AID_SYSTEM}; + Transaction transaction; + Rect layerStackRect; + Rect displayRect; + transaction.setDisplayProjection(display, initialOrientation, layerStackRect, displayRect); + transaction.apply(/*synchronous=*/true); + } + ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayState(display, &displayState)); + ASSERT_EQ(initialOrientation, displayState.orientation); +} + } // namespace android // TODO(b/129481165): remove the #pragma below and fix conversion issues diff --git a/services/surfaceflinger/tests/LayerState_test.cpp b/services/surfaceflinger/tests/LayerState_test.cpp index 15a98df275..cc57e11206 100644 --- a/services/surfaceflinger/tests/LayerState_test.cpp +++ b/services/surfaceflinger/tests/LayerState_test.cpp @@ -28,66 +28,6 @@ using gui::ScreenCaptureResults; namespace test { -TEST(LayerStateTest, ParcellingDisplayCaptureArgs) { - DisplayCaptureArgs args; - args.pixelFormat = ui::PixelFormat::RGB_565; - args.sourceCrop = Rect(0, 0, 500, 200); - args.frameScaleX = 2; - args.frameScaleY = 4; - args.captureSecureLayers = true; - args.displayToken = sp<BBinder>::make(); - args.width = 10; - args.height = 20; - args.grayscale = true; - - Parcel p; - args.writeToParcel(&p); - p.setDataPosition(0); - - DisplayCaptureArgs args2; - args2.readFromParcel(&p); - - ASSERT_EQ(args.pixelFormat, args2.pixelFormat); - ASSERT_EQ(args.sourceCrop, args2.sourceCrop); - ASSERT_EQ(args.frameScaleX, args2.frameScaleX); - ASSERT_EQ(args.frameScaleY, args2.frameScaleY); - ASSERT_EQ(args.captureSecureLayers, args2.captureSecureLayers); - ASSERT_EQ(args.displayToken, args2.displayToken); - ASSERT_EQ(args.width, args2.width); - ASSERT_EQ(args.height, args2.height); - ASSERT_EQ(args.grayscale, args2.grayscale); -} - -TEST(LayerStateTest, ParcellingLayerCaptureArgs) { - LayerCaptureArgs args; - args.pixelFormat = ui::PixelFormat::RGB_565; - args.sourceCrop = Rect(0, 0, 500, 200); - args.frameScaleX = 2; - args.frameScaleY = 4; - args.captureSecureLayers = true; - args.layerHandle = sp<BBinder>::make(); - args.excludeHandles = {sp<BBinder>::make(), sp<BBinder>::make()}; - args.childrenOnly = false; - args.grayscale = true; - - Parcel p; - args.writeToParcel(&p); - p.setDataPosition(0); - - LayerCaptureArgs args2; - args2.readFromParcel(&p); - - ASSERT_EQ(args.pixelFormat, args2.pixelFormat); - ASSERT_EQ(args.sourceCrop, args2.sourceCrop); - ASSERT_EQ(args.frameScaleX, args2.frameScaleX); - ASSERT_EQ(args.frameScaleY, args2.frameScaleY); - ASSERT_EQ(args.captureSecureLayers, args2.captureSecureLayers); - ASSERT_EQ(args.layerHandle, args2.layerHandle); - ASSERT_EQ(args.excludeHandles, args2.excludeHandles); - ASSERT_EQ(args.childrenOnly, args2.childrenOnly); - ASSERT_EQ(args.grayscale, args2.grayscale); -} - TEST(LayerStateTest, ParcellingScreenCaptureResultsWithFence) { ScreenCaptureResults results; results.buffer = sp<GraphicBuffer>::make(100u, 200u, PIXEL_FORMAT_RGBA_8888, 1u, 0u); diff --git a/services/surfaceflinger/tests/LayerTransactionTest.h b/services/surfaceflinger/tests/LayerTransactionTest.h index 5b056d0765..03f900526d 100644 --- a/services/surfaceflinger/tests/LayerTransactionTest.h +++ b/services/surfaceflinger/tests/LayerTransactionTest.h @@ -23,7 +23,7 @@ #include <cutils/properties.h> #include <gtest/gtest.h> -#include <gui/AidlStatusUtil.h> +#include <gui/AidlUtil.h> #include <gui/ISurfaceComposer.h> #include <gui/SurfaceComposerClient.h> #include <private/gui/ComposerService.h> diff --git a/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp index f9b4bbac22..76bae4116c 100644 --- a/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp +++ b/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp @@ -18,6 +18,7 @@ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wconversion" +#include <gui/AidlUtil.h> #include <gui/BufferItemConsumer.h> #include <private/android_filesystem_config.h> #include "TransactionTestHarnesses.h" @@ -64,7 +65,7 @@ TEST_P(LayerTypeTransactionTest, SetRelativeZNegative) { // only layerB is in this range LayerCaptureArgs captureArgs; captureArgs.layerHandle = parent->getHandle(); - captureArgs.sourceCrop = {0, 0, 32, 32}; + captureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(32, 32); ScreenCapture::captureLayers(&screenshot, captureArgs); screenshot->expectColor(Rect(0, 0, 32, 32), Color::BLUE); } diff --git a/services/surfaceflinger/tests/MirrorLayer_test.cpp b/services/surfaceflinger/tests/MirrorLayer_test.cpp index d97d433160..6cc1c51cde 100644 --- a/services/surfaceflinger/tests/MirrorLayer_test.cpp +++ b/services/surfaceflinger/tests/MirrorLayer_test.cpp @@ -20,6 +20,7 @@ #include <android-base/properties.h> #include <common/FlagManager.h> +#include <gui/AidlUtil.h> #include <private/android_filesystem_config.h> #include "LayerTransactionTest.h" #include "utils/TransactionUtils.h" @@ -350,7 +351,7 @@ TEST_F(MirrorLayerTest, OffscreenMirrorScreenshot) { // Capture just the mirror layer and child. LayerCaptureArgs captureArgs; captureArgs.layerHandle = mirrorParent->getHandle(); - captureArgs.sourceCrop = childBounds; + captureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(childBounds); std::unique_ptr<ScreenCapture> shot; ScreenCapture::captureLayers(&shot, captureArgs); shot->expectSize(childBounds.width(), childBounds.height()); diff --git a/services/surfaceflinger/tests/OWNERS b/services/surfaceflinger/tests/OWNERS index 56f2f1b07a..7857961ce4 100644 --- a/services/surfaceflinger/tests/OWNERS +++ b/services/surfaceflinger/tests/OWNERS @@ -4,5 +4,5 @@ per-file HdrSdrRatioOverlay_test.cpp = alecmouri@google.com, sallyqi@google.com, per-file Layer* = set noparent per-file Layer* = pdwilliams@google.com, vishnun@google.com, melodymhsu@google.com -per-file LayerHistoryTest.cpp = file:/services/surfaceflinger/OWNERS +per-file LayerHistoryIntegrationTest.cpp = file:/services/surfaceflinger/OWNERS per-file LayerInfoTest.cpp = file:/services/surfaceflinger/OWNERS
\ No newline at end of file diff --git a/services/surfaceflinger/tests/ScreenCapture_test.cpp b/services/surfaceflinger/tests/ScreenCapture_test.cpp index 9a78550d00..c62f493941 100644 --- a/services/surfaceflinger/tests/ScreenCapture_test.cpp +++ b/services/surfaceflinger/tests/ScreenCapture_test.cpp @@ -20,6 +20,7 @@ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wconversion" +#include <gui/AidlUtil.h> #include <private/android_filesystem_config.h> #include <ui/DisplayState.h> @@ -65,7 +66,7 @@ protected: .show(mFGSurfaceControl); }); - mCaptureArgs.sourceCrop = mDisplayRect; + mCaptureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(mDisplayRect); mCaptureArgs.layerHandle = mRootSurfaceControl->getHandle(); } @@ -112,7 +113,7 @@ TEST_F(ScreenCaptureTest, SetFlagsSecureEUidSystem) { shot->expectColor(Rect(0, 0, 32, 32), Color::BLACK); } - mCaptureArgs.captureSecureLayers = true; + mCaptureArgs.captureArgs.captureSecureLayers = true; // AID_SYSTEM is allowed to capture secure content. ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults)); ASSERT_TRUE(mCaptureResults.capturedSecureLayers); @@ -164,7 +165,7 @@ TEST_F(ScreenCaptureTest, CaptureChildSetParentFlagsSecureEUidSystem) { // Here we pass captureSecureLayers = true and since we are AID_SYSTEM we should be able // to receive them...we are expected to take care with the results. - mCaptureArgs.captureSecureLayers = true; + mCaptureArgs.captureArgs.captureSecureLayers = true; ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults)); ASSERT_TRUE(mCaptureResults.capturedSecureLayers); ScreenCapture sc(mCaptureResults.buffer, mCaptureResults.capturedHdrLayers); @@ -198,8 +199,8 @@ TEST_F(ScreenCaptureTest, CaptureChildRespectsParentSecureFlag) { .apply(); LayerCaptureArgs captureArgs; captureArgs.layerHandle = childLayer->getHandle(); - captureArgs.sourceCrop = size; - captureArgs.captureSecureLayers = false; + captureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(size); + captureArgs.captureArgs.captureSecureLayers = false; { SCOPED_TRACE("parent hidden"); ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(captureArgs, mCaptureResults)); @@ -208,7 +209,7 @@ TEST_F(ScreenCaptureTest, CaptureChildRespectsParentSecureFlag) { sc.expectColor(size, Color::BLACK); } - captureArgs.captureSecureLayers = true; + captureArgs.captureArgs.captureSecureLayers = true; { SCOPED_TRACE("capture secure parent not visible"); ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(captureArgs, mCaptureResults)); @@ -218,7 +219,7 @@ TEST_F(ScreenCaptureTest, CaptureChildRespectsParentSecureFlag) { } Transaction().show(parentLayer).apply(); - captureArgs.captureSecureLayers = false; + captureArgs.captureArgs.captureSecureLayers = false; { SCOPED_TRACE("parent visible"); ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(captureArgs, mCaptureResults)); @@ -227,7 +228,7 @@ TEST_F(ScreenCaptureTest, CaptureChildRespectsParentSecureFlag) { sc.expectColor(size, Color::BLACK); } - captureArgs.captureSecureLayers = true; + captureArgs.captureArgs.captureSecureLayers = true; { SCOPED_TRACE("capture secure parent visible"); ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(captureArgs, mCaptureResults)); @@ -259,8 +260,8 @@ TEST_F(ScreenCaptureTest, CaptureOffscreenChildRespectsParentSecureFlag) { .apply(); LayerCaptureArgs captureArgs; captureArgs.layerHandle = childLayer->getHandle(); - captureArgs.sourceCrop = size; - captureArgs.captureSecureLayers = false; + captureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(size); + captureArgs.captureArgs.captureSecureLayers = false; { SCOPED_TRACE("parent hidden"); ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(captureArgs, mCaptureResults)); @@ -269,7 +270,7 @@ TEST_F(ScreenCaptureTest, CaptureOffscreenChildRespectsParentSecureFlag) { sc.expectColor(size, Color::BLACK); } - captureArgs.captureSecureLayers = true; + captureArgs.captureArgs.captureSecureLayers = true; { SCOPED_TRACE("capture secure parent not visible"); ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(captureArgs, mCaptureResults)); @@ -279,7 +280,7 @@ TEST_F(ScreenCaptureTest, CaptureOffscreenChildRespectsParentSecureFlag) { } Transaction().show(parentLayer).apply(); - captureArgs.captureSecureLayers = false; + captureArgs.captureArgs.captureSecureLayers = false; { SCOPED_TRACE("parent visible"); ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(captureArgs, mCaptureResults)); @@ -288,7 +289,7 @@ TEST_F(ScreenCaptureTest, CaptureOffscreenChildRespectsParentSecureFlag) { sc.expectColor(size, Color::BLACK); } - captureArgs.captureSecureLayers = true; + captureArgs.captureArgs.captureSecureLayers = true; { SCOPED_TRACE("capture secure parent visible"); ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(captureArgs, mCaptureResults)); @@ -361,14 +362,14 @@ TEST_F(ScreenCaptureTest, CaptureLayerExclude) { LayerCaptureArgs captureArgs; captureArgs.layerHandle = fgHandle; captureArgs.childrenOnly = true; - captureArgs.excludeHandles = {child2->getHandle()}; + captureArgs.captureArgs.excludeHandles = {child2->getHandle()}; ScreenCapture::captureLayers(&mCapture, captureArgs); mCapture->checkPixel(10, 10, 0, 0, 0); mCapture->checkPixel(0, 0, 200, 200, 200); } TEST_F(ScreenCaptureTest, CaptureLayerExcludeThroughDisplayArgs) { - mCaptureArgs.excludeHandles = {mFGSurfaceControl->getHandle()}; + mCaptureArgs.captureArgs.excludeHandles = {mFGSurfaceControl->getHandle()}; ScreenCapture::captureLayers(&mCapture, mCaptureArgs); mCapture->expectBGColor(0, 0); // Doesn't capture FG layer which is at 64, 64 @@ -401,7 +402,7 @@ TEST_F(ScreenCaptureTest, CaptureLayerExcludeTree) { LayerCaptureArgs captureArgs; captureArgs.layerHandle = fgHandle; captureArgs.childrenOnly = true; - captureArgs.excludeHandles = {child2->getHandle()}; + captureArgs.captureArgs.excludeHandles = {child2->getHandle()}; ScreenCapture::captureLayers(&mCapture, captureArgs); mCapture->checkPixel(10, 10, 0, 0, 0); mCapture->checkPixel(0, 0, 200, 200, 200); @@ -418,7 +419,7 @@ TEST_F(ScreenCaptureTest, CaptureTransparent) { // Captures child LayerCaptureArgs captureArgs; captureArgs.layerHandle = child->getHandle(); - captureArgs.sourceCrop = {0, 0, 10, 20}; + captureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(10, 20); ScreenCapture::captureLayers(&mCapture, captureArgs); mCapture->expectColor(Rect(0, 0, 9, 9), {200, 200, 200, 255}); // Area outside of child's bounds is transparent. @@ -481,7 +482,7 @@ TEST_F(ScreenCaptureTest, CaptureBoundlessLayerWithSourceCrop) { LayerCaptureArgs captureArgs; captureArgs.layerHandle = child->getHandle(); - captureArgs.sourceCrop = {0, 0, 10, 10}; + captureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(10, 10); ScreenCapture::captureLayers(&mCapture, captureArgs); mCapture->expectColor(Rect(0, 0, 9, 9), Color::RED); @@ -623,7 +624,7 @@ TEST_F(ScreenCaptureTest, CaptureCrop) { // red area to the right of the blue area mCapture->expectColor(Rect(30, 0, 59, 59), Color::RED); - captureArgs.sourceCrop = {0, 0, 30, 30}; + captureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(30, 30); ScreenCapture::captureLayers(&mCapture, captureArgs); // Capturing the cropped screen, cropping out the shown red area, should leave only the blue // area visible. @@ -658,8 +659,8 @@ TEST_F(ScreenCaptureTest, CaptureSize) { // red area to the right of the blue area mCapture->expectColor(Rect(30, 0, 59, 59), Color::RED); - captureArgs.frameScaleX = 0.5f; - captureArgs.frameScaleY = 0.5f; + captureArgs.captureArgs.frameScaleX = 0.5f; + captureArgs.captureArgs.frameScaleY = 0.5f; sleep(1); ScreenCapture::captureLayers(&mCapture, captureArgs); @@ -689,8 +690,8 @@ TEST_F(ScreenCaptureTest, CaptureTooLargeLayer) { LayerCaptureArgs captureArgs; captureArgs.layerHandle = redLayer->getHandle(); - captureArgs.frameScaleX = INT32_MAX / 60; - captureArgs.frameScaleY = INT32_MAX / 60; + captureArgs.captureArgs.frameScaleX = INT32_MAX / 60; + captureArgs.captureArgs.frameScaleY = INT32_MAX / 60; ScreenCaptureResults captureResults; ASSERT_EQ(BAD_VALUE, ScreenCapture::captureLayers(captureArgs, captureResults)); @@ -736,7 +737,7 @@ TEST_F(ScreenCaptureTest, CaptureSecureLayer) { mCapture->expectColor(Rect(30, 30, 60, 60), Color::RED); // Passing flag secure so the blue layer should be screenshot too. - args.captureSecureLayers = true; + args.captureArgs.captureSecureLayers = true; ScreenCapture::captureLayers(&mCapture, args); mCapture->expectColor(Rect(0, 0, 30, 30), Color::BLUE); mCapture->expectColor(Rect(30, 30, 60, 60), Color::RED); @@ -780,7 +781,7 @@ TEST_F(ScreenCaptureTest, ScreenshotProtectedBuffer) { // Reading color data will expectedly result in crash, only check usage bit // b/309965549 Checking that the usage bit is protected does not work for // devices that do not support usage protected. - mCaptureArgs.allowProtected = true; + mCaptureArgs.captureArgs.allowProtected = true; ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, captureResults)); // ASSERT_EQ(GRALLOC_USAGE_PROTECTED, GRALLOC_USAGE_PROTECTED & // captureResults.buffer->getUsage()); @@ -898,7 +899,7 @@ TEST_F(ScreenCaptureTest, CaptureLayerWithUid) { // Make screenshot request with current uid set. No layers were created with the current // uid so screenshot will be black. - captureArgs.uid = fakeUid; + captureArgs.captureArgs.uid = fakeUid; ScreenCapture::captureLayers(&mCapture, captureArgs); mCapture->expectColor(Rect(0, 0, 32, 32), Color::TRANSPARENT); mCapture->expectBorder(Rect(0, 0, 32, 32), Color::TRANSPARENT); @@ -935,7 +936,7 @@ TEST_F(ScreenCaptureTest, CaptureLayerWithUid) { mCapture->expectBorder(Rect(128, 128, 160, 160), Color::TRANSPARENT); // Screenshot from the fakeUid caller with no uid requested allows everything to be screenshot. - captureArgs.uid = -1; + captureArgs.captureArgs.uid = -1; ScreenCapture::captureLayers(&mCapture, captureArgs); mCapture->expectColor(Rect(128, 128, 160, 160), Color::RED); mCapture->expectBorder(Rect(128, 128, 160, 160), {63, 63, 195, 255}); @@ -955,7 +956,7 @@ TEST_F(ScreenCaptureTest, CaptureWithGrayscale) { ScreenCapture::captureLayers(&mCapture, captureArgs); mCapture->expectColor(Rect(0, 0, 32, 32), Color::RED); - captureArgs.grayscale = true; + captureArgs.captureArgs.grayscale = true; const uint8_t tolerance = 1; @@ -1052,7 +1053,7 @@ TEST_F(ScreenCaptureTest, captureOffscreenNullSnapshot) { LayerCaptureArgs captureArgs; captureArgs.layerHandle = mirroredLayer->getHandle(); - captureArgs.sourceCrop = Rect(0, 0, 1, 1); + captureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(1, 1); // Screenshot path should only use the children of the layer hierarchy so // that it will not create a new snapshot. A snapshot would otherwise be diff --git a/services/surfaceflinger/tests/Stress_test.cpp b/services/surfaceflinger/tests/Stress_test.cpp deleted file mode 100644 index b30df5ef15..0000000000 --- a/services/surfaceflinger/tests/Stress_test.cpp +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wconversion" - -#include <gtest/gtest.h> - -#include <gui/SurfaceComposerClient.h> - -#include <utils/String8.h> - -#include <thread> -#include <functional> -#include <layerproto/LayerProtoParser.h> - -namespace android { - -TEST(SurfaceFlingerStress, create_and_destroy) { - auto do_stress = []() { - sp<SurfaceComposerClient> client = sp<SurfaceComposerClient>::make(); - ASSERT_EQ(NO_ERROR, client->initCheck()); - for (int j = 0; j < 1000; j++) { - auto surf = client->createSurface(String8("t"), 100, 100, - PIXEL_FORMAT_RGBA_8888, 0); - ASSERT_TRUE(surf != nullptr); - surf.clear(); - } - }; - - std::vector<std::thread> threads; - for (int i = 0; i < 10; i++) { - threads.push_back(std::thread(do_stress)); - } - for (auto& thread : threads) { - thread.join(); - } -} - -perfetto::protos::LayersProto generateLayerProto() { - perfetto::protos::LayersProto layersProto; - std::array<perfetto::protos::LayerProto*, 10> layers = {}; - for (size_t i = 0; i < layers.size(); ++i) { - layers[i] = layersProto.add_layers(); - layers[i]->set_id(i); - } - - layers[0]->add_children(1); - layers[1]->set_parent(0); - layers[0]->add_children(2); - layers[2]->set_parent(0); - layers[0]->add_children(3); - layers[3]->set_parent(0); - layers[2]->add_children(4); - layers[4]->set_parent(2); - layers[3]->add_children(5); - layers[5]->set_parent(3); - layers[5]->add_children(6); - layers[6]->set_parent(5); - layers[5]->add_children(7); - layers[7]->set_parent(5); - layers[6]->add_children(8); - layers[8]->set_parent(6); - - layers[4]->set_z_order_relative_of(3); - layers[3]->add_relatives(4); - layers[8]->set_z_order_relative_of(9); - layers[9]->add_relatives(8); - layers[3]->set_z_order_relative_of(1); - layers[1]->add_relatives(3); - -/* ---------------------------- - * - 0 - - 9 - - * / | \ - * 1 2 3(1) - * | | - * 4(3) 5 - * / \ - * 6 7 - * | - * 8(9) - * -------------------------- */ - - return layersProto; -} - -TEST(LayerProtoStress, mem_info) { - std::string cmd = "dumpsys meminfo "; - cmd += std::to_string(getpid()); - system(cmd.c_str()); - for (int i = 0; i < 100000; i++) { - perfetto::protos::LayersProto layersProto = generateLayerProto(); - auto layerTree = surfaceflinger::LayerProtoParser::generateLayerTree(layersProto); - surfaceflinger::LayerProtoParser::layerTreeToString(layerTree); - } - system(cmd.c_str()); -} - -} - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic pop // ignored "-Wconversion" diff --git a/services/surfaceflinger/tests/TextureFiltering_test.cpp b/services/surfaceflinger/tests/TextureFiltering_test.cpp index c5d118c1aa..3f39cf6eea 100644 --- a/services/surfaceflinger/tests/TextureFiltering_test.cpp +++ b/services/surfaceflinger/tests/TextureFiltering_test.cpp @@ -14,9 +14,10 @@ * limitations under the License. */ +#include <android/gui/DisplayCaptureArgs.h> #include <android/gui/ISurfaceComposerClient.h> #include <gtest/gtest.h> -#include <gui/DisplayCaptureArgs.h> +#include <gui/AidlUtil.h> #include <ui/GraphicTypes.h> #include <ui/Rect.h> @@ -84,7 +85,7 @@ protected: }; TEST_F(TextureFilteringTest, NoFiltering) { - captureArgs.sourceCrop = Rect{0, 0, 100, 100}; + captureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(100, 100); captureArgs.layerHandle = mParent->getHandle(); ScreenCapture::captureLayers(&mCapture, captureArgs); @@ -93,7 +94,7 @@ TEST_F(TextureFilteringTest, NoFiltering) { } TEST_F(TextureFilteringTest, BufferCropNoFiltering) { - captureArgs.sourceCrop = Rect{0, 0, 100, 100}; + captureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(100, 100); captureArgs.layerHandle = mParent->getHandle(); ScreenCapture::captureLayers(&mCapture, captureArgs); @@ -105,7 +106,7 @@ TEST_F(TextureFilteringTest, BufferCropNoFiltering) { TEST_F(TextureFilteringTest, BufferCropIsFiltered) { Transaction().setBufferCrop(mLayer, Rect{25, 25, 75, 75}).apply(); - captureArgs.sourceCrop = Rect{0, 0, 100, 100}; + captureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(100, 100); captureArgs.layerHandle = mParent->getHandle(); ScreenCapture::captureLayers(&mCapture, captureArgs); @@ -114,9 +115,9 @@ TEST_F(TextureFilteringTest, BufferCropIsFiltered) { // Expect filtering because the output source crop is stretched to the output buffer's size. TEST_F(TextureFilteringTest, OutputSourceCropIsFiltered) { - captureArgs.frameScaleX = 2; - captureArgs.frameScaleY = 2; - captureArgs.sourceCrop = Rect{25, 25, 75, 75}; + captureArgs.captureArgs.frameScaleX = 2; + captureArgs.captureArgs.frameScaleY = 2; + captureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(25, 25, 75, 75); captureArgs.layerHandle = mParent->getHandle(); ScreenCapture::captureLayers(&mCapture, captureArgs); @@ -127,9 +128,9 @@ TEST_F(TextureFilteringTest, OutputSourceCropIsFiltered) { // buffer's size. TEST_F(TextureFilteringTest, LayerCropOutputSourceCropIsFiltered) { Transaction().setCrop(mLayer, Rect{25, 25, 75, 75}).apply(); - captureArgs.frameScaleX = 2; - captureArgs.frameScaleY = 2; - captureArgs.sourceCrop = Rect{25, 25, 75, 75}; + captureArgs.captureArgs.frameScaleX = 2; + captureArgs.captureArgs.frameScaleY = 2; + captureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(25, 25, 75, 75); captureArgs.layerHandle = mParent->getHandle(); ScreenCapture::captureLayers(&mCapture, captureArgs); @@ -139,8 +140,8 @@ TEST_F(TextureFilteringTest, LayerCropOutputSourceCropIsFiltered) { // Expect filtering because the layer is scaled up. TEST_F(TextureFilteringTest, LayerCaptureWithScalingIsFiltered) { captureArgs.layerHandle = mLayer->getHandle(); - captureArgs.frameScaleX = 2; - captureArgs.frameScaleY = 2; + captureArgs.captureArgs.frameScaleX = 2; + captureArgs.captureArgs.frameScaleY = 2; ScreenCapture::captureLayers(&mCapture, captureArgs); expectFiltered({0, 0, 100, 200}, {100, 0, 200, 200}); @@ -149,7 +150,7 @@ TEST_F(TextureFilteringTest, LayerCaptureWithScalingIsFiltered) { // Expect no filtering because the output buffer's size matches the source crop. TEST_F(TextureFilteringTest, LayerCaptureOutputSourceCropNoFiltering) { captureArgs.layerHandle = mLayer->getHandle(); - captureArgs.sourceCrop = Rect{25, 25, 75, 75}; + captureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(25, 25, 75, 75); ScreenCapture::captureLayers(&mCapture, captureArgs); mCapture->expectColor(Rect{0, 0, 25, 50}, Color::RED); @@ -162,7 +163,7 @@ TEST_F(TextureFilteringTest, LayerCaptureWithCropNoFiltering) { Transaction().setCrop(mLayer, Rect{10, 10, 90, 90}).apply(); captureArgs.layerHandle = mLayer->getHandle(); - captureArgs.sourceCrop = Rect{25, 25, 75, 75}; + captureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(25, 25, 75, 75); ScreenCapture::captureLayers(&mCapture, captureArgs); mCapture->expectColor(Rect{0, 0, 25, 50}, Color::RED); @@ -172,7 +173,7 @@ TEST_F(TextureFilteringTest, LayerCaptureWithCropNoFiltering) { // Expect no filtering because the output source crop and output buffer are the same size. TEST_F(TextureFilteringTest, OutputSourceCropDisplayFrameMatchNoFiltering) { captureArgs.layerHandle = mLayer->getHandle(); - captureArgs.sourceCrop = Rect{25, 25, 75, 75}; + captureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(25, 25, 75, 75); ScreenCapture::captureLayers(&mCapture, captureArgs); mCapture->expectColor(Rect{0, 0, 25, 50}, Color::RED); @@ -206,7 +207,7 @@ TEST_F(TextureFilteringTest, ParentHasTransformNoFiltering) { Transaction().setPosition(mParent, 100, 100).apply(); captureArgs.layerHandle = mParent->getHandle(); - captureArgs.sourceCrop = Rect{0, 0, 100, 100}; + captureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(100, 100); ScreenCapture::captureLayers(&mCapture, captureArgs); mCapture->expectColor(Rect{0, 0, 50, 100}, Color::RED); diff --git a/services/surfaceflinger/tests/TransactionTestHarnesses.h b/services/surfaceflinger/tests/TransactionTestHarnesses.h index af3cb9aec1..67a524799d 100644 --- a/services/surfaceflinger/tests/TransactionTestHarnesses.h +++ b/services/surfaceflinger/tests/TransactionTestHarnesses.h @@ -16,6 +16,7 @@ #ifndef ANDROID_TRANSACTION_TEST_HARNESSES #define ANDROID_TRANSACTION_TEST_HARNESSES +#include <com_android_graphics_libgui_flags.h> #include <common/FlagManager.h> #include <ui/DisplayState.h> @@ -51,6 +52,16 @@ public: const ui::Size& resolution = displayMode.resolution; sp<IBinder> vDisplay; + +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) + sp<BufferItemConsumer> itemConsumer = sp<BufferItemConsumer>::make( + // Sample usage bits from screenrecord + GRALLOC_USAGE_HW_VIDEO_ENCODER | GRALLOC_USAGE_SW_READ_OFTEN); + sp<BufferListener> listener = sp<BufferListener>::make(this); + itemConsumer->setFrameAvailableListener(listener); + itemConsumer->setName(String8("Virtual disp consumer")); + itemConsumer->setDefaultBufferSize(resolution.getWidth(), resolution.getHeight()); +#else sp<IGraphicBufferProducer> producer; sp<IGraphicBufferConsumer> consumer; sp<BufferItemConsumer> itemConsumer; @@ -65,6 +76,7 @@ public: GRALLOC_USAGE_SW_READ_OFTEN); sp<BufferListener> listener = sp<BufferListener>::make(this); itemConsumer->setFrameAvailableListener(listener); +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) static const std::string kDisplayName("VirtualDisplay"); vDisplay = SurfaceComposerClient::createVirtualDisplay(kDisplayName, @@ -76,7 +88,12 @@ public: SurfaceComposerClient::getDefault()->mirrorDisplay(displayId); SurfaceComposerClient::Transaction t; +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) + t.setDisplaySurface(vDisplay, + itemConsumer->getSurface()->getIGraphicBufferProducer()); +#else t.setDisplaySurface(vDisplay, producer); +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) t.setDisplayProjection(vDisplay, displayState.orientation, Rect(displayState.layerStackSpaceRect), Rect(resolution)); if (FlagManager::getInstance().ce_fence_promise()) { diff --git a/services/surfaceflinger/tests/VirtualDisplay_test.cpp b/services/surfaceflinger/tests/VirtualDisplay_test.cpp index cd66dd20bb..d69378cec2 100644 --- a/services/surfaceflinger/tests/VirtualDisplay_test.cpp +++ b/services/surfaceflinger/tests/VirtualDisplay_test.cpp @@ -27,6 +27,12 @@ namespace { class VirtualDisplayTest : public ::testing::Test { protected: void SetUp() override { +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) + mGLConsumer = sp<GLConsumer>::make(GLConsumer::TEXTURE_EXTERNAL, true, false, false); + mGLConsumer->setName(String8("Virtual disp consumer")); + mGLConsumer->setDefaultBufferSize(100, 100); + mProducer = mGLConsumer->getSurface()->getIGraphicBufferProducer(); +#else sp<IGraphicBufferConsumer> consumer; BufferQueue::createBufferQueue(&mProducer, &consumer); @@ -34,6 +40,7 @@ protected: consumer->setDefaultBufferSize(100, 100); mGLConsumer = sp<GLConsumer>::make(consumer, GLConsumer::TEXTURE_EXTERNAL, true, false); +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) } sp<IGraphicBufferProducer> mProducer; diff --git a/services/surfaceflinger/tests/benchmarks/Android.bp b/services/surfaceflinger/tests/benchmarks/Android.bp new file mode 100644 index 0000000000..1c47be343e --- /dev/null +++ b/services/surfaceflinger/tests/benchmarks/Android.bp @@ -0,0 +1,31 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + +cc_benchmark { + name: "surfaceflinger_microbenchmarks", + srcs: [ + ":libsurfaceflinger_mock_sources", + ":libsurfaceflinger_sources", + "*.cpp", + ], + defaults: [ + "libsurfaceflinger_mocks_defaults", + "skia_renderengine_deps", + "surfaceflinger_defaults", + ], + static_libs: [ + "libgmock", + "libgtest", + "libc++fs", + ], + header_libs: [ + "libsurfaceflinger_mocks_headers", + "surfaceflinger_tests_common_headers", + ], +} diff --git a/services/surfaceflinger/tests/benchmarks/LayerLifecycleManager_benchmarks.cpp b/services/surfaceflinger/tests/benchmarks/LayerLifecycleManager_benchmarks.cpp new file mode 100644 index 0000000000..7641a45ba4 --- /dev/null +++ b/services/surfaceflinger/tests/benchmarks/LayerLifecycleManager_benchmarks.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <memory> +#include <optional> + +#include <benchmark/benchmark.h> + +#include <Client.h> // temporarily needed for LayerCreationArgs +#include <FrontEnd/LayerCreationArgs.h> +#include <FrontEnd/LayerLifecycleManager.h> +#include <LayerLifecycleManagerHelper.h> + +namespace android::surfaceflinger { + +namespace { + +using namespace android::surfaceflinger::frontend; + +static void addRemoveLayers(benchmark::State& state) { + LayerLifecycleManager lifecycleManager; + for (auto _ : state) { + std::vector<std::unique_ptr<RequestedLayerState>> layers; + layers.emplace_back(LayerLifecycleManagerHelper::rootLayer(1)); + layers.emplace_back(LayerLifecycleManagerHelper::rootLayer(2)); + layers.emplace_back(LayerLifecycleManagerHelper::rootLayer(3)); + lifecycleManager.addLayers(std::move(layers)); + lifecycleManager.onHandlesDestroyed({{1, "1"}, {2, "2"}, {3, "3"}}); + lifecycleManager.commitChanges(); + } +} +BENCHMARK(addRemoveLayers); + +static void updateClientStates(benchmark::State& state) { + LayerLifecycleManager lifecycleManager; + std::vector<std::unique_ptr<RequestedLayerState>> layers; + layers.emplace_back(LayerLifecycleManagerHelper::rootLayer(1)); + lifecycleManager.addLayers(std::move(layers)); + lifecycleManager.commitChanges(); + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + auto& transactionState = transactions.back().states.front(); + transactionState.state.what = layer_state_t::eColorChanged; + transactionState.state.color.rgb = {0.f, 0.f, 0.f}; + transactionState.layerId = 1; + lifecycleManager.applyTransactions(transactions); + lifecycleManager.commitChanges(); + int i = 0; + for (auto s : state) { + if (i++ % 100 == 0) i = 0; + transactionState.state.color.b = static_cast<float>(i / 100.f); + lifecycleManager.applyTransactions(transactions); + lifecycleManager.commitChanges(); + } +} +BENCHMARK(updateClientStates); + +static void updateClientStatesNoChanges(benchmark::State& state) { + LayerLifecycleManager lifecycleManager; + std::vector<std::unique_ptr<RequestedLayerState>> layers; + layers.emplace_back(LayerLifecycleManagerHelper::rootLayer(1)); + lifecycleManager.addLayers(std::move(layers)); + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + auto& transactionState = transactions.back().states.front(); + transactionState.state.what = layer_state_t::eColorChanged; + transactionState.state.color.rgb = {0.f, 0.f, 0.f}; + transactionState.layerId = 1; + lifecycleManager.applyTransactions(transactions); + lifecycleManager.commitChanges(); + for (auto _ : state) { + lifecycleManager.applyTransactions(transactions); + lifecycleManager.commitChanges(); + } +} +BENCHMARK(updateClientStatesNoChanges); + +} // namespace +} // namespace android::surfaceflinger diff --git a/services/surfaceflinger/tests/benchmarks/LocklessQueue_benchmarks.cpp b/services/surfaceflinger/tests/benchmarks/LocklessQueue_benchmarks.cpp new file mode 100644 index 0000000000..60bd58acd5 --- /dev/null +++ b/services/surfaceflinger/tests/benchmarks/LocklessQueue_benchmarks.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <memory> +#include <optional> + +#include <benchmark/benchmark.h> + +#include <LocklessQueue.h> + +namespace android::surfaceflinger { + +namespace { +static void pushPop(benchmark::State& state) { + LocklessQueue<std::vector<uint32_t>> queue; + for (auto _ : state) { + queue.push({10, 5}); + std::vector<uint32_t> poppedValue = *queue.pop(); + benchmark::DoNotOptimize(poppedValue); + } +} +BENCHMARK(pushPop); + +} // namespace +} // namespace android::surfaceflinger diff --git a/services/surfaceflinger/tests/benchmarks/main.cpp b/services/surfaceflinger/tests/benchmarks/main.cpp new file mode 100644 index 0000000000..685c7c6a26 --- /dev/null +++ b/services/surfaceflinger/tests/benchmarks/main.cpp @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <benchmark/benchmark.h> +BENCHMARK_MAIN(); diff --git a/services/surfaceflinger/tests/common/Android.bp b/services/surfaceflinger/tests/common/Android.bp new file mode 100644 index 0000000000..2dfa8afe72 --- /dev/null +++ b/services/surfaceflinger/tests/common/Android.bp @@ -0,0 +1,13 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + +cc_library_headers { + name: "surfaceflinger_tests_common_headers", + export_include_dirs: ["."], +} diff --git a/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h b/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h new file mode 100644 index 0000000000..ae380ad459 --- /dev/null +++ b/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h @@ -0,0 +1,539 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <gui/fake/BufferData.h> +#include <renderengine/mock/FakeExternalTexture.h> +#include <ui/ShadowSettings.h> + +#include <Client.h> // temporarily needed for LayerCreationArgs +#include <FrontEnd/LayerCreationArgs.h> +#include <FrontEnd/LayerHierarchy.h> +#include <FrontEnd/LayerLifecycleManager.h> +#include <FrontEnd/LayerSnapshotBuilder.h> +#include <Layer.h> // needed for framerate + +namespace android::surfaceflinger::frontend { + +class LayerLifecycleManagerHelper { +public: + LayerLifecycleManagerHelper(LayerLifecycleManager& layerLifecycleManager) + : mLifecycleManager(layerLifecycleManager) {} + ~LayerLifecycleManagerHelper() = default; + + static LayerCreationArgs createArgs(uint32_t id, bool canBeRoot, uint32_t parentId, + uint32_t layerIdToMirror) { + LayerCreationArgs args(std::make_optional(id)); + args.name = "testlayer"; + args.addToRoot = canBeRoot; + args.parentId = parentId; + args.layerIdToMirror = layerIdToMirror; + return args; + } + + static LayerCreationArgs createDisplayMirrorArgs(uint32_t id, + ui::LayerStack layerStackToMirror) { + LayerCreationArgs args(std::make_optional(id)); + args.name = "testlayer"; + args.addToRoot = true; + args.layerStackToMirror = layerStackToMirror; + return args; + } + + static std::unique_ptr<RequestedLayerState> rootLayer(uint32_t id) { + return std::make_unique<RequestedLayerState>(createArgs(/*id=*/id, /*canBeRoot=*/true, + /*parent=*/UNASSIGNED_LAYER_ID, + /*mirror=*/UNASSIGNED_LAYER_ID)); + } + + static std::unique_ptr<RequestedLayerState> childLayer(uint32_t id, uint32_t parentId) { + return std::make_unique<RequestedLayerState>(createArgs(/*id=*/id, /*canBeRoot=*/false, + parentId, + /*mirror=*/UNASSIGNED_LAYER_ID)); + } + + static std::vector<TransactionState> setZTransaction(uint32_t id, int32_t z) { + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + + transactions.back().states.front().state.what = layer_state_t::eLayerChanged; + transactions.back().states.front().layerId = id; + transactions.back().states.front().state.z = z; + return transactions; + } + + void createRootLayer(uint32_t id) { + std::vector<std::unique_ptr<RequestedLayerState>> layers; + layers.emplace_back(std::make_unique<RequestedLayerState>( + createArgs(/*id=*/id, /*canBeRoot=*/true, /*parent=*/UNASSIGNED_LAYER_ID, + /*mirror=*/UNASSIGNED_LAYER_ID))); + mLifecycleManager.addLayers(std::move(layers)); + } + + void createRootLayerWithUid(uint32_t id, gui::Uid uid) { + std::vector<std::unique_ptr<RequestedLayerState>> layers; + auto args = createArgs(/*id=*/id, /*canBeRoot=*/true, /*parent=*/UNASSIGNED_LAYER_ID, + /*mirror=*/UNASSIGNED_LAYER_ID); + args.ownerUid = uid.val(); + layers.emplace_back(std::make_unique<RequestedLayerState>(args)); + mLifecycleManager.addLayers(std::move(layers)); + } + + void createDisplayMirrorLayer(uint32_t id, ui::LayerStack layerStack) { + std::vector<std::unique_ptr<RequestedLayerState>> layers; + layers.emplace_back(std::make_unique<RequestedLayerState>( + createDisplayMirrorArgs(/*id=*/id, layerStack))); + mLifecycleManager.addLayers(std::move(layers)); + } + + void createLayer(uint32_t id, uint32_t parentId) { + std::vector<std::unique_ptr<RequestedLayerState>> layers; + layers.emplace_back(std::make_unique<RequestedLayerState>( + createArgs(/*id=*/id, /*canBeRoot=*/false, /*parent=*/parentId, + /*mirror=*/UNASSIGNED_LAYER_ID))); + mLifecycleManager.addLayers(std::move(layers)); + } + + std::vector<TransactionState> reparentLayerTransaction(uint32_t id, uint32_t newParentId) { + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + transactions.back().states.front().parentId = newParentId; + transactions.back().states.front().state.what = layer_state_t::eReparent; + transactions.back().states.front().relativeParentId = UNASSIGNED_LAYER_ID; + transactions.back().states.front().layerId = id; + return transactions; + } + + void reparentLayer(uint32_t id, uint32_t newParentId) { + mLifecycleManager.applyTransactions(reparentLayerTransaction(id, newParentId)); + } + + std::vector<TransactionState> relativeLayerTransaction(uint32_t id, uint32_t relativeParentId) { + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + transactions.back().states.front().relativeParentId = relativeParentId; + transactions.back().states.front().state.what = layer_state_t::eRelativeLayerChanged; + transactions.back().states.front().layerId = id; + return transactions; + } + + void reparentRelativeLayer(uint32_t id, uint32_t relativeParentId) { + mLifecycleManager.applyTransactions(relativeLayerTransaction(id, relativeParentId)); + } + + void removeRelativeZ(uint32_t id) { + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + transactions.back().states.front().state.what = layer_state_t::eLayerChanged; + transactions.back().states.front().layerId = id; + mLifecycleManager.applyTransactions(transactions); + } + + void setPosition(uint32_t id, float x, float y) { + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + transactions.back().states.front().state.what = layer_state_t::ePositionChanged; + transactions.back().states.front().state.x = x; + transactions.back().states.front().state.y = y; + transactions.back().states.front().layerId = id; + mLifecycleManager.applyTransactions(transactions); + } + + void mirrorLayer(uint32_t id, uint32_t parentId, uint32_t layerIdToMirror) { + std::vector<std::unique_ptr<RequestedLayerState>> layers; + layers.emplace_back(std::make_unique<RequestedLayerState>( + createArgs(/*id=*/id, /*canBeRoot=*/false, /*parent=*/parentId, + /*mirror=*/layerIdToMirror))); + mLifecycleManager.addLayers(std::move(layers)); + } + + void updateBackgroundColor(uint32_t id, half alpha) { + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + transactions.back().states.front().state.what = layer_state_t::eBackgroundColorChanged; + transactions.back().states.front().state.bgColor.a = alpha; + transactions.back().states.front().layerId = id; + mLifecycleManager.applyTransactions(transactions); + } + + void destroyLayerHandle(uint32_t id) { mLifecycleManager.onHandlesDestroyed({{id, "test"}}); } + + void setZ(uint32_t id, int32_t z) { + mLifecycleManager.applyTransactions(setZTransaction(id, z)); + } + + void setCrop(uint32_t id, const Rect& crop) { + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + + transactions.back().states.front().state.what = layer_state_t::eCropChanged; + transactions.back().states.front().layerId = id; + transactions.back().states.front().state.crop = crop; + mLifecycleManager.applyTransactions(transactions); + } + + void setFlags(uint32_t id, uint32_t mask, uint32_t flags) { + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + + transactions.back().states.front().state.what = layer_state_t::eFlagsChanged; + transactions.back().states.front().state.flags = flags; + transactions.back().states.front().state.mask = mask; + transactions.back().states.front().layerId = id; + mLifecycleManager.applyTransactions(transactions); + } + + void setAlpha(uint32_t id, float alpha) { + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + + transactions.back().states.front().state.what = layer_state_t::eAlphaChanged; + transactions.back().states.front().layerId = id; + transactions.back().states.front().state.color.a = static_cast<half>(alpha); + mLifecycleManager.applyTransactions(transactions); + } + + void hideLayer(uint32_t id) { + setFlags(id, layer_state_t::eLayerHidden, layer_state_t::eLayerHidden); + } + + void showLayer(uint32_t id) { setFlags(id, layer_state_t::eLayerHidden, 0); } + + void setColor(uint32_t id, half3 rgb = half3(1._hf, 1._hf, 1._hf)) { + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + transactions.back().states.front().state.what = layer_state_t::eColorChanged; + transactions.back().states.front().state.color.rgb = rgb; + transactions.back().states.front().layerId = id; + mLifecycleManager.applyTransactions(transactions); + } + + void setLayerStack(uint32_t id, int32_t layerStack) { + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + + transactions.back().states.front().state.what = layer_state_t::eLayerStackChanged; + transactions.back().states.front().layerId = id; + transactions.back().states.front().state.layerStack = ui::LayerStack::fromValue(layerStack); + mLifecycleManager.applyTransactions(transactions); + } + + void setTouchableRegion(uint32_t id, Region region) { + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + + transactions.back().states.front().state.what = layer_state_t::eInputInfoChanged; + transactions.back().states.front().layerId = id; + transactions.back().states.front().state.windowInfoHandle = + sp<gui::WindowInfoHandle>::make(); + auto inputInfo = transactions.back().states.front().state.windowInfoHandle->editInfo(); + inputInfo->touchableRegion = region; + inputInfo->token = sp<BBinder>::make(); + mLifecycleManager.applyTransactions(transactions); + } + + void setInputInfo(uint32_t id, std::function<void(gui::WindowInfo&)> configureInput) { + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + + transactions.back().states.front().state.what = layer_state_t::eInputInfoChanged; + transactions.back().states.front().layerId = id; + transactions.back().states.front().state.windowInfoHandle = + sp<gui::WindowInfoHandle>::make(); + auto inputInfo = transactions.back().states.front().state.windowInfoHandle->editInfo(); + if (!inputInfo->token) { + inputInfo->token = sp<BBinder>::make(); + } + configureInput(*inputInfo); + + mLifecycleManager.applyTransactions(transactions); + } + + void setTouchableRegionCrop(uint32_t id, Region region, uint32_t touchCropId, + bool replaceTouchableRegionWithCrop) { + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + + transactions.back().states.front().state.what = layer_state_t::eInputInfoChanged; + transactions.back().states.front().layerId = id; + transactions.back().states.front().state.windowInfoHandle = + sp<gui::WindowInfoHandle>::make(); + auto inputInfo = transactions.back().states.front().state.windowInfoHandle->editInfo(); + inputInfo->touchableRegion = region; + inputInfo->replaceTouchableRegionWithCrop = replaceTouchableRegionWithCrop; + transactions.back().states.front().touchCropId = touchCropId; + + inputInfo->token = sp<BBinder>::make(); + mLifecycleManager.applyTransactions(transactions); + } + + void setBackgroundBlurRadius(uint32_t id, uint32_t backgroundBlurRadius) { + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + + transactions.back().states.front().state.what = layer_state_t::eBackgroundBlurRadiusChanged; + transactions.back().states.front().layerId = id; + transactions.back().states.front().state.backgroundBlurRadius = backgroundBlurRadius; + mLifecycleManager.applyTransactions(transactions); + } + + void setFrameRateSelectionPriority(uint32_t id, int32_t priority) { + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + + transactions.back().states.front().state.what = layer_state_t::eFrameRateSelectionPriority; + transactions.back().states.front().layerId = id; + transactions.back().states.front().state.frameRateSelectionPriority = priority; + mLifecycleManager.applyTransactions(transactions); + } + + void setFrameRate(uint32_t id, float frameRate, int8_t compatibility, + int8_t changeFrameRateStrategy) { + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + + transactions.back().states.front().state.what = layer_state_t::eFrameRateChanged; + transactions.back().states.front().layerId = id; + transactions.back().states.front().state.frameRate = frameRate; + transactions.back().states.front().state.frameRateCompatibility = compatibility; + transactions.back().states.front().state.changeFrameRateStrategy = changeFrameRateStrategy; + mLifecycleManager.applyTransactions(transactions); + } + + void setFrameRate(uint32_t id, Layer::FrameRate framerate) { + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + + transactions.back().states.front().state.what = layer_state_t::eFrameRateChanged; + transactions.back().states.front().layerId = id; + transactions.back().states.front().state.frameRate = framerate.vote.rate.getValue(); + transactions.back().states.front().state.frameRateCompatibility = 0; + transactions.back().states.front().state.changeFrameRateStrategy = 0; + mLifecycleManager.applyTransactions(transactions); + } + + void setFrameRateCategory(uint32_t id, int8_t frameRateCategory) { + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + + transactions.back().states.front().state.what = layer_state_t::eFrameRateCategoryChanged; + transactions.back().states.front().layerId = id; + transactions.back().states.front().state.frameRateCategory = frameRateCategory; + mLifecycleManager.applyTransactions(transactions); + } + + void setFrameRateSelectionStrategy(uint32_t id, int8_t strategy) { + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + + transactions.back().states.front().state.what = + layer_state_t::eFrameRateSelectionStrategyChanged; + transactions.back().states.front().layerId = id; + transactions.back().states.front().state.frameRateSelectionStrategy = strategy; + mLifecycleManager.applyTransactions(transactions); + } + + void setDefaultFrameRateCompatibility(uint32_t id, int8_t defaultFrameRateCompatibility) { + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + + transactions.back().states.front().state.what = + layer_state_t::eDefaultFrameRateCompatibilityChanged; + transactions.back().states.front().layerId = id; + transactions.back().states.front().state.defaultFrameRateCompatibility = + defaultFrameRateCompatibility; + mLifecycleManager.applyTransactions(transactions); + } + + void setRoundedCorners(uint32_t id, float radius) { + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + + transactions.back().states.front().state.what = layer_state_t::eCornerRadiusChanged; + transactions.back().states.front().layerId = id; + transactions.back().states.front().state.cornerRadius = radius; + mLifecycleManager.applyTransactions(transactions); + } + + void setBuffer(uint32_t id, std::shared_ptr<renderengine::ExternalTexture> texture) { + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + + transactions.back().states.front().state.what = layer_state_t::eBufferChanged; + transactions.back().states.front().layerId = id; + transactions.back().states.front().externalTexture = texture; + transactions.back().states.front().state.bufferData = + std::make_shared<fake::BufferData>(texture->getId(), texture->getWidth(), + texture->getHeight(), texture->getPixelFormat(), + texture->getUsage()); + mLifecycleManager.applyTransactions(transactions); + } + + void setBuffer(uint32_t id) { + static uint64_t sBufferId = 1; + setBuffer(id, + std::make_shared<renderengine::mock:: + FakeExternalTexture>(1U /*width*/, 1U /*height*/, + sBufferId++, + HAL_PIXEL_FORMAT_RGBA_8888, + GRALLOC_USAGE_PROTECTED /*usage*/)); + } + + void setFrontBuffer(uint32_t id) { + static uint64_t sBufferId = 1; + setBuffer(id, + std::make_shared<renderengine::mock::FakeExternalTexture>( + 1U /*width*/, 1U /*height*/, sBufferId++, HAL_PIXEL_FORMAT_RGBA_8888, + GRALLOC_USAGE_PROTECTED | AHARDWAREBUFFER_USAGE_FRONT_BUFFER /*usage*/)); + } + + void setBufferCrop(uint32_t id, const Rect& bufferCrop) { + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + + transactions.back().states.front().state.what = layer_state_t::eBufferCropChanged; + transactions.back().states.front().layerId = id; + transactions.back().states.front().state.bufferCrop = bufferCrop; + mLifecycleManager.applyTransactions(transactions); + } + + void setDamageRegion(uint32_t id, const Region& damageRegion) { + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + + transactions.back().states.front().state.what = layer_state_t::eSurfaceDamageRegionChanged; + transactions.back().states.front().layerId = id; + transactions.back().states.front().state.surfaceDamageRegion = damageRegion; + mLifecycleManager.applyTransactions(transactions); + } + + void setDataspace(uint32_t id, ui::Dataspace dataspace) { + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + + transactions.back().states.front().state.what = layer_state_t::eDataspaceChanged; + transactions.back().states.front().layerId = id; + transactions.back().states.front().state.dataspace = dataspace; + mLifecycleManager.applyTransactions(transactions); + } + + void setMatrix(uint32_t id, float dsdx, float dtdx, float dtdy, float dsdy) { + layer_state_t::matrix22_t matrix{dsdx, dtdx, dtdy, dsdy}; + + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + + transactions.back().states.front().state.what = layer_state_t::eMatrixChanged; + transactions.back().states.front().layerId = id; + transactions.back().states.front().state.matrix = matrix; + mLifecycleManager.applyTransactions(transactions); + } + + void setShadowRadius(uint32_t id, float shadowRadius) { + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + + transactions.back().states.front().state.what = layer_state_t::eShadowRadiusChanged; + transactions.back().states.front().layerId = id; + transactions.back().states.front().state.shadowRadius = shadowRadius; + mLifecycleManager.applyTransactions(transactions); + } + + void setTrustedOverlay(uint32_t id, gui::TrustedOverlay trustedOverlay) { + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + + transactions.back().states.front().state.what = layer_state_t::eTrustedOverlayChanged; + transactions.back().states.front().layerId = id; + transactions.back().states.front().state.trustedOverlay = trustedOverlay; + mLifecycleManager.applyTransactions(transactions); + } + + void setDropInputMode(uint32_t id, gui::DropInputMode dropInputMode) { + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + + transactions.back().states.front().state.what = layer_state_t::eDropInputModeChanged; + transactions.back().states.front().layerId = id; + transactions.back().states.front().state.dropInputMode = dropInputMode; + mLifecycleManager.applyTransactions(transactions); + } + + void setGameMode(uint32_t id, gui::GameMode gameMode) { + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + transactions.back().states.front().state.what = layer_state_t::eMetadataChanged; + transactions.back().states.front().state.metadata = LayerMetadata(); + transactions.back().states.front().state.metadata.setInt32(METADATA_GAME_MODE, + static_cast<int32_t>(gameMode)); + transactions.back().states.front().layerId = id; + mLifecycleManager.applyTransactions(transactions); + } + + void setEdgeExtensionEffect(uint32_t id, int edge) { + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + + transactions.back().states.front().layerId = id; + transactions.back().states.front().state.what |= layer_state_t::eEdgeExtensionChanged; + transactions.back().states.front().state.edgeExtensionParameters = + gui::EdgeExtensionParameters(); + transactions.back().states.front().state.edgeExtensionParameters.extendLeft = edge & LEFT; + transactions.back().states.front().state.edgeExtensionParameters.extendRight = edge & RIGHT; + transactions.back().states.front().state.edgeExtensionParameters.extendTop = edge & TOP; + transactions.back().states.front().state.edgeExtensionParameters.extendBottom = + edge & BOTTOM; + mLifecycleManager.applyTransactions(transactions); + } + +private: + LayerLifecycleManager& mLifecycleManager; +}; + +} // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp index 98d5754271..f1bd87ccd1 100644 --- a/services/surfaceflinger/tests/unittests/Android.bp +++ b/services/surfaceflinger/tests/unittests/Android.bp @@ -58,6 +58,7 @@ cc_test { ], test_suites: ["device-tests"], static_libs: ["libc++fs"], + header_libs: ["surfaceflinger_tests_common_headers"], srcs: [ ":libsurfaceflinger_mock_sources", ":libsurfaceflinger_sources", @@ -79,20 +80,16 @@ cc_test { "FpsTest.cpp", "FramebufferSurfaceTest.cpp", "FrameRateOverrideMappingsTest.cpp", - "FrameRateSelectionPriorityTest.cpp", - "FrameRateSelectionStrategyTest.cpp", "FrameTimelineTest.cpp", - "GameModeTest.cpp", "HWComposerTest.cpp", + "JankTrackerTest.cpp", "OneShotTimerTest.cpp", - "LayerHistoryTest.cpp", "LayerHistoryIntegrationTest.cpp", "LayerInfoTest.cpp", "LayerMetadataTest.cpp", "LayerHierarchyTest.cpp", "LayerLifecycleManagerTest.cpp", "LayerSnapshotTest.cpp", - "LayerTest.cpp", "LayerTestUtils.cpp", "MessageQueueTest.cpp", "PowerAdvisorTest.cpp", @@ -115,9 +112,7 @@ cc_test { "SurfaceFlinger_SetDisplayStateTest.cpp", "SurfaceFlinger_SetPowerModeInternalTest.cpp", "SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp", - "SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp", "SchedulerTest.cpp", - "SetFrameRateTest.cpp", "RefreshRateSelectorTest.cpp", "RefreshRateStatsTest.cpp", "RegionSamplingTest.cpp", @@ -151,6 +146,7 @@ cc_defaults { "android.hardware.power-ndk_static", "librenderengine_deps", "libsurfaceflinger_common_test_deps", + "libsurfaceflinger_proto_deps", ], static_libs: [ "android.hardware.common-V2-ndk", @@ -169,7 +165,6 @@ cc_defaults { "libframetimeline", "libgmock", "libgui_mocks", - "liblayers_proto", "libperfetto_client_experimental", "librenderengine", "librenderengine_mocks", @@ -208,6 +203,7 @@ cc_defaults { "libsync", "libui", "libutils", + "libtracing_perfetto", ], header_libs: [ "android.hardware.graphics.composer3-command-buffer", diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp index cdd77fec95..23d3c168bd 100644 --- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp +++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp @@ -15,7 +15,6 @@ */ // TODO(b/129481165): remove the #pragma below and fix conversion issues -#include "renderengine/ExternalTexture.h" #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wconversion" #pragma clang diagnostic ignored "-Wextra" @@ -31,6 +30,7 @@ #include <gui/IProducerListener.h> #include <gui/LayerMetadata.h> #include <log/log.h> +#include <renderengine/ExternalTexture.h> #include <renderengine/mock/FakeExternalTexture.h> #include <renderengine/mock/RenderEngine.h> #include <system/window.h> @@ -149,7 +149,6 @@ public: sp<compositionengine::mock::DisplaySurface> mDisplaySurface = sp<compositionengine::mock::DisplaySurface>::make(); sp<mock::NativeWindow> mNativeWindow = sp<mock::NativeWindow>::make(); - std::vector<sp<Layer>> mAuxiliaryLayers; sp<GraphicBuffer> mBuffer = sp<GraphicBuffer>::make(1u, 1u, PIXEL_FORMAT_RGBA_8888, @@ -194,6 +193,7 @@ void CompositionTest::displayRefreshCompositionDirtyFrame() { template <typename LayerCase> void CompositionTest::captureScreenComposition() { LayerCase::setupForScreenCapture(this); + mFlinger.commit(); const Rect sourceCrop(0, 0, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT); constexpr bool regionSampling = false; @@ -204,13 +204,8 @@ void CompositionTest::captureScreenComposition() { RenderArea::Options::CAPTURE_SECURE_LAYERS | RenderArea::Options::HINT_FOR_SEAMLESS_TRANSITION); - auto traverseLayers = [this](const LayerVector::Visitor& visitor) { - return mFlinger.traverseLayersInLayerStack(mDisplay->getLayerStack(), - CaptureArgs::UNSET_UID, {}, visitor); - }; - - // TODO: Use SurfaceFlinger::getLayerSnapshotsForScreenshots instead of this legacy function - auto getLayerSnapshotsFn = RenderArea::fromTraverseLayersLambda(traverseLayers); + auto getLayerSnapshotsFn = mFlinger.getLayerSnapshotsForScreenshotsFn(mDisplay->getLayerStack(), + CaptureArgs::UNSET_UID); const uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE; @@ -462,7 +457,7 @@ struct BaseLayerProperties { static constexpr IComposerClient::BlendMode BLENDMODE = IComposerClient::BlendMode::PREMULTIPLIED; - static void setupLatchedBuffer(CompositionTest* test, sp<Layer> layer) { + static void setupLatchedBuffer(CompositionTest* test, frontend::RequestedLayerState& layer) { Mock::VerifyAndClear(test->mRenderEngine); const auto buffer = std::make_shared< @@ -472,21 +467,15 @@ struct BaseLayerProperties { LayerProperties::FORMAT, LayerProperties::USAGE | GraphicBuffer::USAGE_HW_TEXTURE); - - auto& layerDrawingState = test->mFlinger.mutableLayerDrawingState(layer); - layerDrawingState.crop = Rect(0, 0, LayerProperties::HEIGHT, LayerProperties::WIDTH); - layerDrawingState.buffer = buffer; - layerDrawingState.acquireFence = Fence::NO_FENCE; - layerDrawingState.dataspace = ui::Dataspace::UNKNOWN; - layer->setSurfaceDamageRegion( - Region(Rect(LayerProperties::HEIGHT, LayerProperties::WIDTH))); - - bool ignoredRecomputeVisibleRegions; - layer->latchBuffer(ignoredRecomputeVisibleRegions, 0); + layer.crop = Rect(0, 0, LayerProperties::HEIGHT, LayerProperties::WIDTH); + layer.externalTexture = buffer; + layer.bufferData->acquireFence = Fence::NO_FENCE; + layer.dataspace = ui::Dataspace::UNKNOWN; + layer.surfaceDamageRegion = Region(Rect(LayerProperties::HEIGHT, LayerProperties::WIDTH)); Mock::VerifyAndClear(test->mRenderEngine); } - static void setupLayerState(CompositionTest* test, sp<Layer> layer) { + static void setupLayerState(CompositionTest* test, frontend::RequestedLayerState& layer) { setupLatchedBuffer(test, layer); } @@ -670,14 +659,12 @@ struct SidebandLayerProperties : public BaseLayerProperties<SidebandLayerPropert using Base = BaseLayerProperties<SidebandLayerProperties>; static constexpr IComposerClient::BlendMode BLENDMODE = IComposerClient::BlendMode::NONE; - static void setupLayerState(CompositionTest* test, sp<Layer> layer) { + static void setupLayerState(CompositionTest* test, frontend::RequestedLayerState& layer) { sp<NativeHandle> stream = NativeHandle::create(reinterpret_cast<native_handle_t*>(DEFAULT_SIDEBAND_STREAM), false); - test->mFlinger.setLayerSidebandStream(layer, stream); - auto& layerDrawingState = test->mFlinger.mutableLayerDrawingState(layer); - layerDrawingState.crop = - Rect(0, 0, SidebandLayerProperties::HEIGHT, SidebandLayerProperties::WIDTH); + layer.sidebandStream = stream; + layer.crop = Rect(0, 0, SidebandLayerProperties::HEIGHT, SidebandLayerProperties::WIDTH); } static void setupHwcSetSourceCropBufferCallExpectations(CompositionTest* test) { @@ -755,17 +742,17 @@ struct SecureLayerProperties : public CommonSecureLayerProperties<SecureLayerPro struct CursorLayerProperties : public BaseLayerProperties<CursorLayerProperties> { using Base = BaseLayerProperties<CursorLayerProperties>; - static void setupLayerState(CompositionTest* test, sp<Layer> layer) { + static void setupLayerState(CompositionTest* test, frontend::RequestedLayerState& layer) { Base::setupLayerState(test, layer); - test->mFlinger.setLayerPotentialCursor(layer, true); + layer.potentialCursor = true; } }; struct NoLayerVariant { - using FlingerLayerType = sp<Layer>; - - static FlingerLayerType createLayer(CompositionTest*) { return FlingerLayerType(); } - static void injectLayer(CompositionTest*, FlingerLayerType) {} + static frontend::RequestedLayerState createLayer(CompositionTest*) { + return {LayerCreationArgs()}; + } + static void injectLayer(CompositionTest*, frontend::RequestedLayerState&) {} static void cleanupInjectedLayers(CompositionTest*) {} static void setupCallExpectationsForDirtyGeometry(CompositionTest*) {} @@ -775,10 +762,10 @@ struct NoLayerVariant { template <typename LayerProperties> struct BaseLayerVariant { template <typename L, typename F> - static sp<L> createLayerWithFactory(CompositionTest* test, F factory) { + static frontend::RequestedLayerState createLayerWithFactory(CompositionTest* test, F factory) { EXPECT_CALL(*test->mFlinger.scheduler(), postMessage(_)).Times(0); - sp<L> layer = factory(); + auto layer = factory(); // Layer should be registered with scheduler. EXPECT_EQ(1u, test->mFlinger.scheduler()->layerHistorySize()); @@ -792,27 +779,26 @@ struct BaseLayerVariant { return layer; } - template <typename L> - static void initLayerDrawingStateAndComputeBounds(CompositionTest* test, sp<L> layer) { - auto& layerDrawingState = test->mFlinger.mutableLayerDrawingState(layer); - layerDrawingState.layerStack = LAYER_STACK; - layerDrawingState.color = half4(LayerProperties::COLOR[0], LayerProperties::COLOR[1], - LayerProperties::COLOR[2], LayerProperties::COLOR[3]); - layer->computeBounds(FloatRect(0, 0, 100, 100), ui::Transform(), 0.f /* shadowRadius */); + static void initLayerDrawingStateAndComputeBounds(CompositionTest* test, + frontend::RequestedLayerState& layer) { + layer.layerStack = LAYER_STACK; + layer.color = half4(LayerProperties::COLOR[0], LayerProperties::COLOR[1], + LayerProperties::COLOR[2], LayerProperties::COLOR[3]); } - static void injectLayer(CompositionTest* test, sp<Layer> layer) { + static void injectLayer(CompositionTest* test, frontend::RequestedLayerState& layer) { EXPECT_CALL(*test->mComposer, createLayer(HWC_DISPLAY, _)) .WillOnce(DoAll(SetArgPointee<1>(HWC_LAYER), Return(Error::NONE))); - + auto legacyLayer = test->mFlinger.getLegacyLayer(layer.id); auto outputLayer = test->mDisplay->getCompositionDisplay()->injectOutputLayerForTest( - layer->getCompositionEngineLayerFE()); + legacyLayer->getCompositionEngineLayerFE({.id = layer.id})); outputLayer->editState().visibleRegion = Region(Rect(0, 0, 100, 100)); outputLayer->editState().outputSpaceVisibleRegion = Region(Rect(0, 0, 100, 100)); Mock::VerifyAndClear(test->mComposer); - test->mFlinger.mutableDrawingState().layersSortedByZ.add(layer); + auto layerCopy = std::make_unique<frontend::RequestedLayerState>(layer); + test->mFlinger.addLayer(layerCopy); test->mFlinger.mutableVisibleRegionsDirty() = true; } @@ -820,10 +806,9 @@ struct BaseLayerVariant { EXPECT_CALL(*test->mComposer, destroyLayer(HWC_DISPLAY, HWC_LAYER)) .WillOnce(Return(Error::NONE)); + test->mFlinger.destroyAllLayerHandles(); test->mDisplay->getCompositionDisplay()->clearOutputLayers(); - test->mFlinger.mutableDrawingState().layersSortedByZ.clear(); test->mFlinger.mutablePreviouslyComposedLayers().clear(); - // Layer should be unregistered with scheduler. test->mFlinger.commit(); EXPECT_EQ(0u, test->mFlinger.scheduler()->layerHistorySize()); @@ -833,17 +818,17 @@ struct BaseLayerVariant { template <typename LayerProperties> struct EffectLayerVariant : public BaseLayerVariant<LayerProperties> { using Base = BaseLayerVariant<LayerProperties>; - using FlingerLayerType = sp<Layer>; - - static FlingerLayerType createLayer(CompositionTest* test) { - FlingerLayerType layer = Base::template createLayerWithFactory<Layer>(test, [test]() { - return sp<Layer>::make(LayerCreationArgs(test->mFlinger.flinger(), sp<Client>(), - "test-layer", LayerProperties::LAYER_FLAGS, - LayerMetadata())); + static frontend::RequestedLayerState createLayer(CompositionTest* test) { + frontend::RequestedLayerState layer = Base::template createLayerWithFactory< + frontend::RequestedLayerState>(test, [test]() { + auto args = LayerCreationArgs(test->mFlinger.flinger(), sp<Client>(), "test-layer", + LayerProperties::LAYER_FLAGS, LayerMetadata()); + auto legacyLayer = sp<Layer>::make(args); + test->mFlinger.injectLegacyLayer(legacyLayer); + return frontend::RequestedLayerState(args); }); - auto& layerDrawingState = test->mFlinger.mutableLayerDrawingState(layer); - layerDrawingState.crop = Rect(0, 0, LayerProperties::HEIGHT, LayerProperties::WIDTH); + layer.crop = Rect(0, 0, LayerProperties::HEIGHT, LayerProperties::WIDTH); return layer; } @@ -869,13 +854,15 @@ struct EffectLayerVariant : public BaseLayerVariant<LayerProperties> { template <typename LayerProperties> struct BufferLayerVariant : public BaseLayerVariant<LayerProperties> { using Base = BaseLayerVariant<LayerProperties>; - using FlingerLayerType = sp<Layer>; - static FlingerLayerType createLayer(CompositionTest* test) { - FlingerLayerType layer = Base::template createLayerWithFactory<Layer>(test, [test]() { + static frontend::RequestedLayerState createLayer(CompositionTest* test) { + frontend::RequestedLayerState layer = Base::template createLayerWithFactory< + frontend::RequestedLayerState>(test, [test]() { LayerCreationArgs args(test->mFlinger.flinger(), sp<Client>(), "test-layer", LayerProperties::LAYER_FLAGS, LayerMetadata()); - return sp<Layer>::make(args); + auto legacyLayer = sp<Layer>::make(args); + test->mFlinger.injectLegacyLayer(legacyLayer); + return frontend::RequestedLayerState(args); }); LayerProperties::setupLayerState(test, layer); @@ -917,13 +904,14 @@ struct BufferLayerVariant : public BaseLayerVariant<LayerProperties> { template <typename LayerProperties> struct ContainerLayerVariant : public BaseLayerVariant<LayerProperties> { using Base = BaseLayerVariant<LayerProperties>; - using FlingerLayerType = sp<Layer>; - static FlingerLayerType createLayer(CompositionTest* test) { + static frontend::RequestedLayerState createLayer(CompositionTest* test) { LayerCreationArgs args(test->mFlinger.flinger(), sp<Client>(), "test-container-layer", LayerProperties::LAYER_FLAGS, LayerMetadata()); - FlingerLayerType layer = sp<Layer>::make(args); - Base::template initLayerDrawingStateAndComputeBounds(test, layer); + sp<Layer> legacyLayer = sp<Layer>::make(args); + test->mFlinger.injectLegacyLayer(legacyLayer); + frontend::RequestedLayerState layer(args); + Base::initLayerDrawingStateAndComputeBounds(test, layer); return layer; } }; @@ -931,29 +919,19 @@ struct ContainerLayerVariant : public BaseLayerVariant<LayerProperties> { template <typename LayerVariant, typename ParentLayerVariant> struct ChildLayerVariant : public LayerVariant { using Base = LayerVariant; - using FlingerLayerType = typename LayerVariant::FlingerLayerType; using ParentBase = ParentLayerVariant; - static FlingerLayerType createLayer(CompositionTest* test) { + static frontend::RequestedLayerState createLayer(CompositionTest* test) { // Need to create child layer first. Otherwise layer history size will be 2. - FlingerLayerType layer = Base::createLayer(test); - - typename ParentBase::FlingerLayerType parentLayer = ParentBase::createLayer(test); - parentLayer->addChild(layer); - test->mFlinger.setLayerDrawingParent(layer, parentLayer); - - test->mAuxiliaryLayers.push_back(parentLayer); - + frontend::RequestedLayerState layer = Base::createLayer(test); + frontend::RequestedLayerState parentLayer = ParentBase::createLayer(test); + layer.parentId = parentLayer.id; + auto layerCopy = std::make_unique<frontend::RequestedLayerState>(parentLayer); + test->mFlinger.addLayer(layerCopy); return layer; } - static void cleanupInjectedLayers(CompositionTest* test) { - // Clear auxiliary layers first so that child layer can be successfully destroyed in the - // following call. - test->mAuxiliaryLayers.clear(); - - Base::cleanupInjectedLayers(test); - } + static void cleanupInjectedLayers(CompositionTest* test) { Base::cleanupInjectedLayers(test); } }; /* ------------------------------------------------------------------------ @@ -1016,7 +994,7 @@ struct ChangeCompositionTypeVariant { */ struct CompositionResultBaseVariant { - static void setupLayerState(CompositionTest*, sp<Layer>) {} + static void setupLayerState(CompositionTest*, frontend::RequestedLayerState&) {} template <typename Case> static void setupCallExpectationsForDirtyGeometry(CompositionTest* test) { @@ -1056,9 +1034,8 @@ struct RECompositionResultVariant : public CompositionResultBaseVariant { }; struct ForcedClientCompositionResultVariant : public CompositionResultBaseVariant { - static void setupLayerState(CompositionTest* test, sp<Layer> layer) { - const auto outputLayer = - TestableSurfaceFlinger::findOutputLayerForDisplay(layer, test->mDisplay); + static void setupLayerState(CompositionTest* test, frontend::RequestedLayerState& layer) { + const auto outputLayer = test->mFlinger.findOutputLayerForDisplay(layer.id, test->mDisplay); LOG_FATAL_IF(!outputLayer); outputLayer->editState().forceClientComposition = true; } @@ -1079,7 +1056,7 @@ struct ForcedClientCompositionResultVariant : public CompositionResultBaseVarian }; struct ForcedClientCompositionViaDebugOptionResultVariant : public CompositionResultBaseVariant { - static void setupLayerState(CompositionTest* test, sp<Layer>) { + static void setupLayerState(CompositionTest* test, frontend::RequestedLayerState&) { test->mFlinger.mutableDebugDisableHWC() = true; } @@ -1099,7 +1076,7 @@ struct ForcedClientCompositionViaDebugOptionResultVariant : public CompositionRe }; struct EmptyScreenshotResultVariant { - static void setupLayerState(CompositionTest*, sp<Layer>) {} + static void setupLayerState(CompositionTest*, frontend::RequestedLayerState&) {} template <typename Case> static void setupCallExpectations(CompositionTest*) {} @@ -1365,28 +1342,6 @@ TEST_F(CompositionTest, captureScreenSecureBufferLayerOnInsecureDisplay) { * Layers with a parent layer with ISurfaceComposerClient::eSecure, on a non-secure display */ -TEST_F(CompositionTest, - HWCComposedBufferLayerWithSecureParentLayerOnInsecureDisplayWithDirtyGeometry) { - displayRefreshCompositionDirtyGeometry<CompositionCase< - InsecureDisplaySetupVariant, - ChildLayerVariant<BufferLayerVariant<ParentSecureLayerProperties>, - ContainerLayerVariant<SecureLayerProperties>>, - KeepCompositionTypeVariant< - aidl::android::hardware::graphics::composer3::Composition::CLIENT>, - ForcedClientCompositionResultVariant>>(); -} - -TEST_F(CompositionTest, - HWCComposedBufferLayerWithSecureParentLayerOnInsecureDisplayWithDirtyFrame) { - displayRefreshCompositionDirtyFrame<CompositionCase< - InsecureDisplaySetupVariant, - ChildLayerVariant<BufferLayerVariant<ParentSecureLayerProperties>, - ContainerLayerVariant<SecureLayerProperties>>, - KeepCompositionTypeVariant< - aidl::android::hardware::graphics::composer3::Composition::CLIENT>, - ForcedClientCompositionResultVariant>>(); -} - TEST_F(CompositionTest, captureScreenBufferLayerWithSecureParentLayerOnInsecureDisplay) { captureScreenComposition< CompositionCase<InsecureDisplaySetupVariant, diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp index fa31643df1..9b10c94bcd 100644 --- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp +++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp @@ -64,17 +64,6 @@ DisplayTransactionTest::~DisplayTransactionTest() { void DisplayTransactionTest::injectMockScheduler(PhysicalDisplayId displayId) { LOG_ALWAYS_FATAL_IF(mFlinger.scheduler()); - - EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_)); - EXPECT_CALL(*mEventThread, createEventConnection(_, _)) - .WillOnce(Return( - sp<EventThreadConnection>::make(mEventThread, mock::EventThread::kCallingUid))); - - EXPECT_CALL(*mSFEventThread, registerDisplayEventConnection(_)); - EXPECT_CALL(*mSFEventThread, createEventConnection(_, _)) - .WillOnce(Return(sp<EventThreadConnection>::make(mSFEventThread, - mock::EventThread::kCallingUid))); - mFlinger.setupScheduler(std::make_unique<mock::VsyncController>(), std::make_shared<mock::VSyncTracker>(), std::unique_ptr<EventThread>(mEventThread), diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h index f26336a655..db3c0a1d69 100644 --- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h +++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h @@ -498,9 +498,7 @@ constexpr uint32_t GRALLOC_USAGE_PHYSICAL_DISPLAY = constexpr int PHYSICAL_DISPLAY_FLAGS = 0x1; template <typename PhysicalDisplay, int width, int height, - Secure secure = (PhysicalDisplay::CONNECTION_TYPE == ui::DisplayConnectionType::Internal) - ? Secure::TRUE - : Secure::FALSE> + Secure secure = (PhysicalDisplay::SECURE) ? Secure::TRUE : Secure::FALSE> struct PhysicalDisplayVariant : DisplayVariant<PhysicalDisplayIdType<PhysicalDisplay>, width, height, Async::FALSE, secure, PhysicalDisplay::PRIMARY, GRALLOC_USAGE_PHYSICAL_DISPLAY, @@ -515,16 +513,18 @@ template <bool hasIdentificationData> struct PrimaryDisplay { static constexpr auto CONNECTION_TYPE = ui::DisplayConnectionType::Internal; static constexpr Primary PRIMARY = Primary::TRUE; + static constexpr bool SECURE = true; static constexpr uint8_t PORT = 255; static constexpr HWDisplayId HWC_DISPLAY_ID = 1001; static constexpr bool HAS_IDENTIFICATION_DATA = hasIdentificationData; static constexpr auto GET_IDENTIFICATION_DATA = getInternalEdid; }; -template <ui::DisplayConnectionType connectionType, bool hasIdentificationData> +template <ui::DisplayConnectionType connectionType, bool hasIdentificationData, bool secure> struct SecondaryDisplay { static constexpr auto CONNECTION_TYPE = connectionType; static constexpr Primary PRIMARY = Primary::FALSE; + static constexpr bool SECURE = secure; static constexpr uint8_t PORT = 254; static constexpr HWDisplayId HWC_DISPLAY_ID = 1002; static constexpr bool HAS_IDENTIFICATION_DATA = hasIdentificationData; @@ -533,9 +533,14 @@ struct SecondaryDisplay { : getExternalEdid; }; +constexpr bool kSecure = true; +constexpr bool kNonSecure = false; + +template <bool secure> struct TertiaryDisplay { static constexpr auto CONNECTION_TYPE = ui::DisplayConnectionType::External; static constexpr Primary PRIMARY = Primary::FALSE; + static constexpr bool SECURE = secure; static constexpr uint8_t PORT = 253; static constexpr HWDisplayId HWC_DISPLAY_ID = 1003; static constexpr auto GET_IDENTIFICATION_DATA = getExternalEdid; @@ -545,14 +550,26 @@ using PrimaryDisplayVariant = PhysicalDisplayVariant<PrimaryDisplay<false>, 3840 using InnerDisplayVariant = PhysicalDisplayVariant<PrimaryDisplay<true>, 1840, 2208>; using OuterDisplayVariant = - PhysicalDisplayVariant<SecondaryDisplay<ui::DisplayConnectionType::Internal, true>, 1080, - 2092>; + PhysicalDisplayVariant<SecondaryDisplay<ui::DisplayConnectionType::Internal, + /*hasIdentificationData=*/true, kSecure>, + 1080, 2092>; +using OuterDisplayNonSecureVariant = + PhysicalDisplayVariant<SecondaryDisplay<ui::DisplayConnectionType::Internal, + /*hasIdentificationData=*/true, kNonSecure>, + 1080, 2092>; using ExternalDisplayVariant = - PhysicalDisplayVariant<SecondaryDisplay<ui::DisplayConnectionType::External, false>, 1920, - 1280>; - -using TertiaryDisplayVariant = PhysicalDisplayVariant<TertiaryDisplay, 1600, 1200>; + PhysicalDisplayVariant<SecondaryDisplay<ui::DisplayConnectionType::External, + /*hasIdentificationData=*/false, kSecure>, + 1920, 1280>; +using ExternalDisplayNonSecureVariant = + PhysicalDisplayVariant<SecondaryDisplay<ui::DisplayConnectionType::External, + /*hasIdentificationData=*/false, kNonSecure>, + 1920, 1280>; + +using TertiaryDisplayVariant = PhysicalDisplayVariant<TertiaryDisplay<kSecure>, 1600, 1200>; +using TertiaryDisplayNonSecureVariant = + PhysicalDisplayVariant<TertiaryDisplay<kNonSecure>, 1600, 1200>; // A virtual display not supported by the HWC. constexpr uint32_t GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY = 0; @@ -750,10 +767,18 @@ using SimpleExternalDisplayCase = Case<ExternalDisplayVariant, WideColorNotSupportedVariant<ExternalDisplayVariant>, HdrNotSupportedVariant<ExternalDisplayVariant>, NoPerFrameMetadataSupportVariant<ExternalDisplayVariant>>; +using SimpleExternalDisplayNonSecureCase = + Case<ExternalDisplayVariant, WideColorNotSupportedVariant<ExternalDisplayNonSecureVariant>, + HdrNotSupportedVariant<ExternalDisplayNonSecureVariant>, + NoPerFrameMetadataSupportVariant<ExternalDisplayNonSecureVariant>>; using SimpleTertiaryDisplayCase = Case<TertiaryDisplayVariant, WideColorNotSupportedVariant<TertiaryDisplayVariant>, HdrNotSupportedVariant<TertiaryDisplayVariant>, NoPerFrameMetadataSupportVariant<TertiaryDisplayVariant>>; +using SimpleTertiaryDisplayNonSecureCase = + Case<TertiaryDisplayVariant, WideColorNotSupportedVariant<TertiaryDisplayNonSecureVariant>, + HdrNotSupportedVariant<TertiaryDisplayNonSecureVariant>, + NoPerFrameMetadataSupportVariant<TertiaryDisplayNonSecureVariant>>; using NonHwcVirtualDisplayCase = Case<NonHwcVirtualDisplayVariant<1024, 768, Secure::FALSE>, diff --git a/services/surfaceflinger/tests/unittests/FrameRateSelectionPriorityTest.cpp b/services/surfaceflinger/tests/unittests/FrameRateSelectionPriorityTest.cpp deleted file mode 100644 index d30d5b8223..0000000000 --- a/services/surfaceflinger/tests/unittests/FrameRateSelectionPriorityTest.cpp +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#undef LOG_TAG -#define LOG_TAG "LibSurfaceFlingerUnittests" - -#include <gmock/gmock.h> -#include <gtest/gtest.h> -#include <gui/LayerMetadata.h> - -#include "Layer.h" -#include "TestableSurfaceFlinger.h" -#include "mock/DisplayHardware/MockComposer.h" - -namespace android { - -using testing::_; -using testing::DoAll; -using testing::Mock; -using testing::Return; -using testing::SetArgPointee; - -using android::Hwc2::IComposer; -using android::Hwc2::IComposerClient; - -/** - * This class covers all the test that are related to refresh rate selection. - */ -class RefreshRateSelectionTest : public testing::Test { -public: - RefreshRateSelectionTest(); - ~RefreshRateSelectionTest() override; - -protected: - static constexpr int DEFAULT_DISPLAY_WIDTH = 1920; - static constexpr int DEFAULT_DISPLAY_HEIGHT = 1024; - static constexpr uint32_t WIDTH = 100; - static constexpr uint32_t HEIGHT = 100; - static constexpr uint32_t LAYER_FLAGS = 0; - static constexpr int32_t PRIORITY_UNSET = -1; - - sp<Layer> createBufferStateLayer(); - sp<Layer> createEffectLayer(); - - void setParent(Layer* child, Layer* parent); - void commitTransaction(Layer* layer); - - TestableSurfaceFlinger mFlinger; - - sp<Client> mClient; - sp<Layer> mParent; - sp<Layer> mChild; - sp<Layer> mGrandChild; -}; - -RefreshRateSelectionTest::RefreshRateSelectionTest() { - const ::testing::TestInfo* const test_info = - ::testing::UnitTest::GetInstance()->current_test_info(); - ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); - - mFlinger.setupMockScheduler(); - mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>()); -} - -RefreshRateSelectionTest::~RefreshRateSelectionTest() { - const ::testing::TestInfo* const test_info = - ::testing::UnitTest::GetInstance()->current_test_info(); - ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name()); -} - -sp<Layer> RefreshRateSelectionTest::createBufferStateLayer() { - sp<Client> client; - LayerCreationArgs args(mFlinger.flinger(), client, "buffer-queue-layer", LAYER_FLAGS, - LayerMetadata()); - return sp<Layer>::make(args); -} - -sp<Layer> RefreshRateSelectionTest::createEffectLayer() { - sp<Client> client; - LayerCreationArgs args(mFlinger.flinger(), client, "color-layer", LAYER_FLAGS, LayerMetadata()); - return sp<Layer>::make(args); -} - -void RefreshRateSelectionTest::setParent(Layer* child, Layer* parent) { - child->setParent(sp<Layer>::fromExisting(parent)); -} - -void RefreshRateSelectionTest::commitTransaction(Layer* layer) { - layer->commitTransaction(); -} - -namespace { - -TEST_F(RefreshRateSelectionTest, testPriorityOnBufferStateLayers) { - mParent = createBufferStateLayer(); - mChild = createBufferStateLayer(); - setParent(mChild.get(), mParent.get()); - mGrandChild = createBufferStateLayer(); - setParent(mGrandChild.get(), mChild.get()); - - ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority()); - ASSERT_EQ(PRIORITY_UNSET, mChild->getFrameRateSelectionPriority()); - ASSERT_EQ(PRIORITY_UNSET, mGrandChild->getFrameRateSelectionPriority()); - - // Child has its own priority. - mGrandChild->setFrameRateSelectionPriority(1); - commitTransaction(mGrandChild.get()); - ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority()); - ASSERT_EQ(PRIORITY_UNSET, mChild->getFrameRateSelectionPriority()); - ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority()); - - // Child inherits from his parent. - mChild->setFrameRateSelectionPriority(1); - commitTransaction(mChild.get()); - mGrandChild->setFrameRateSelectionPriority(PRIORITY_UNSET); - commitTransaction(mGrandChild.get()); - ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority()); - ASSERT_EQ(1, mChild->getFrameRateSelectionPriority()); - ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority()); - - // Grandchild inherits from his grand parent. - mParent->setFrameRateSelectionPriority(1); - commitTransaction(mParent.get()); - mChild->setFrameRateSelectionPriority(PRIORITY_UNSET); - commitTransaction(mChild.get()); - mGrandChild->setFrameRateSelectionPriority(PRIORITY_UNSET); - commitTransaction(mGrandChild.get()); - ASSERT_EQ(1, mParent->getFrameRateSelectionPriority()); - ASSERT_EQ(1, mChild->getFrameRateSelectionPriority()); - ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority()); -} - -TEST_F(RefreshRateSelectionTest, testPriorityOnEffectLayers) { - mParent = createEffectLayer(); - mChild = createEffectLayer(); - setParent(mChild.get(), mParent.get()); - mGrandChild = createEffectLayer(); - setParent(mGrandChild.get(), mChild.get()); - - ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority()); - ASSERT_EQ(PRIORITY_UNSET, mChild->getFrameRateSelectionPriority()); - ASSERT_EQ(PRIORITY_UNSET, mGrandChild->getFrameRateSelectionPriority()); - - // Child has its own priority. - mGrandChild->setFrameRateSelectionPriority(1); - commitTransaction(mGrandChild.get()); - ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority()); - ASSERT_EQ(PRIORITY_UNSET, mChild->getFrameRateSelectionPriority()); - ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority()); - - // Child inherits from his parent. - mChild->setFrameRateSelectionPriority(1); - commitTransaction(mChild.get()); - mGrandChild->setFrameRateSelectionPriority(PRIORITY_UNSET); - commitTransaction(mGrandChild.get()); - ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority()); - ASSERT_EQ(1, mChild->getFrameRateSelectionPriority()); - ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority()); - - // Grandchild inherits from his grand parent. - mParent->setFrameRateSelectionPriority(1); - commitTransaction(mParent.get()); - mChild->setFrameRateSelectionPriority(PRIORITY_UNSET); - commitTransaction(mChild.get()); - mGrandChild->setFrameRateSelectionPriority(PRIORITY_UNSET); - commitTransaction(mGrandChild.get()); - ASSERT_EQ(1, mParent->getFrameRateSelectionPriority()); - ASSERT_EQ(1, mChild->getFrameRateSelectionPriority()); - ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority()); -} - -} // namespace -} // namespace android diff --git a/services/surfaceflinger/tests/unittests/FrameRateSelectionStrategyTest.cpp b/services/surfaceflinger/tests/unittests/FrameRateSelectionStrategyTest.cpp deleted file mode 100644 index 5c742d7360..0000000000 --- a/services/surfaceflinger/tests/unittests/FrameRateSelectionStrategyTest.cpp +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Copyright 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#undef LOG_TAG -#define LOG_TAG "LibSurfaceFlingerUnittests" - -#include <gmock/gmock.h> -#include <gtest/gtest.h> -#include <gui/LayerMetadata.h> - -#include "Layer.h" -#include "LayerTestUtils.h" -#include "TestableSurfaceFlinger.h" -#include "mock/DisplayHardware/MockComposer.h" - -namespace android { - -using testing::DoAll; -using testing::Mock; -using testing::SetArgPointee; - -using android::Hwc2::IComposer; -using android::Hwc2::IComposerClient; - -using scheduler::LayerHistory; - -using FrameRate = Layer::FrameRate; -using FrameRateCompatibility = Layer::FrameRateCompatibility; -using FrameRateSelectionStrategy = scheduler::LayerInfo::FrameRateSelectionStrategy; - -/** - * This class tests the behaviour of Layer::setFrameRateSelectionStrategy. - */ -class FrameRateSelectionStrategyTest : public BaseLayerTest { -protected: - const FrameRate FRAME_RATE_VOTE1 = FrameRate(11_Hz, FrameRateCompatibility::Default); - const FrameRate FRAME_RATE_VOTE2 = FrameRate(22_Hz, FrameRateCompatibility::Default); - const FrameRate FRAME_RATE_VOTE3 = FrameRate(33_Hz, FrameRateCompatibility::Default); - const FrameRate FRAME_RATE_DEFAULT = FrameRate(Fps(), FrameRateCompatibility::Default); - const FrameRate FRAME_RATE_TREE = FrameRate(Fps(), FrameRateCompatibility::NoVote); - - FrameRateSelectionStrategyTest(); - - void addChild(sp<Layer> layer, sp<Layer> child); - void removeChild(sp<Layer> layer, sp<Layer> child); - void commitTransaction(); - - std::vector<sp<Layer>> mLayers; -}; - -FrameRateSelectionStrategyTest::FrameRateSelectionStrategyTest() { - const ::testing::TestInfo* const test_info = - ::testing::UnitTest::GetInstance()->current_test_info(); - ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); - - mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>()); -} - -void FrameRateSelectionStrategyTest::addChild(sp<Layer> layer, sp<Layer> child) { - layer->addChild(child); -} - -void FrameRateSelectionStrategyTest::removeChild(sp<Layer> layer, sp<Layer> child) { - layer->removeChild(child); -} - -void FrameRateSelectionStrategyTest::commitTransaction() { - for (auto layer : mLayers) { - layer->commitTransaction(); - } -} - -namespace { - -INSTANTIATE_TEST_SUITE_P(PerLayerType, FrameRateSelectionStrategyTest, - testing::Values(std::make_shared<BufferStateLayerFactory>(), - std::make_shared<EffectLayerFactory>()), - PrintToStringParamName); - -TEST_P(FrameRateSelectionStrategyTest, SetAndGet) { - EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); - - const auto& layerFactory = GetParam(); - auto layer = mLayers.emplace_back(layerFactory->createLayer(mFlinger)); - layer->setFrameRate(FRAME_RATE_VOTE1.vote); - layer->setFrameRateSelectionStrategy(FrameRateSelectionStrategy::OverrideChildren); - commitTransaction(); - EXPECT_EQ(FRAME_RATE_VOTE1, layer->getFrameRateForLayerTree()); - EXPECT_EQ(FrameRateSelectionStrategy::OverrideChildren, - layer->getDrawingState().frameRateSelectionStrategy); -} - -TEST_P(FrameRateSelectionStrategyTest, SetChildOverrideChildren) { - EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); - - const auto& layerFactory = GetParam(); - auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger)); - auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger)); - auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger)); - addChild(parent, child1); - addChild(child1, child2); - - child2->setFrameRate(FRAME_RATE_VOTE1.vote); - child2->setFrameRateSelectionStrategy(FrameRateSelectionStrategy::OverrideChildren); - commitTransaction(); - EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree()); - EXPECT_EQ(FrameRateSelectionStrategy::Propagate, - parent->getDrawingState().frameRateSelectionStrategy); - EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree()); - EXPECT_EQ(FrameRateSelectionStrategy::Propagate, - child1->getDrawingState().frameRateSelectionStrategy); - EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree()); - EXPECT_EQ(FrameRateSelectionStrategy::OverrideChildren, - child2->getDrawingState().frameRateSelectionStrategy); -} - -TEST_P(FrameRateSelectionStrategyTest, SetParentOverrideChildren) { - EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); - - const auto& layerFactory = GetParam(); - auto layer1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger)); - auto layer2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger)); - auto layer3 = mLayers.emplace_back(layerFactory->createLayer(mFlinger)); - addChild(layer1, layer2); - addChild(layer2, layer3); - - layer1->setFrameRate(FRAME_RATE_VOTE1.vote); - layer1->setFrameRateSelectionStrategy(FrameRateSelectionStrategy::OverrideChildren); - layer2->setFrameRate(FRAME_RATE_VOTE2.vote); - layer2->setFrameRateSelectionStrategy(FrameRateSelectionStrategy::OverrideChildren); - layer3->setFrameRate(FRAME_RATE_VOTE3.vote); - commitTransaction(); - - EXPECT_EQ(FRAME_RATE_VOTE1, layer1->getFrameRateForLayerTree()); - EXPECT_EQ(FrameRateSelectionStrategy::OverrideChildren, - layer1->getDrawingState().frameRateSelectionStrategy); - EXPECT_EQ(FRAME_RATE_VOTE1, layer2->getFrameRateForLayerTree()); - EXPECT_EQ(FrameRateSelectionStrategy::OverrideChildren, - layer2->getDrawingState().frameRateSelectionStrategy); - EXPECT_EQ(FRAME_RATE_VOTE1, layer3->getFrameRateForLayerTree()); - EXPECT_EQ(FrameRateSelectionStrategy::Propagate, - layer3->getDrawingState().frameRateSelectionStrategy); - - layer1->setFrameRateSelectionStrategy(FrameRateSelectionStrategy::Propagate); - commitTransaction(); - - EXPECT_EQ(FRAME_RATE_VOTE1, layer1->getFrameRateForLayerTree()); - EXPECT_EQ(FrameRateSelectionStrategy::Propagate, - layer1->getDrawingState().frameRateSelectionStrategy); - EXPECT_EQ(FRAME_RATE_VOTE2, layer2->getFrameRateForLayerTree()); - EXPECT_EQ(FrameRateSelectionStrategy::OverrideChildren, - layer2->getDrawingState().frameRateSelectionStrategy); - EXPECT_EQ(FRAME_RATE_VOTE2, layer3->getFrameRateForLayerTree()); - EXPECT_EQ(FrameRateSelectionStrategy::Propagate, - layer3->getDrawingState().frameRateSelectionStrategy); -} - -TEST_P(FrameRateSelectionStrategyTest, OverrideChildrenAndSelf) { - EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); - - const auto& layerFactory = GetParam(); - auto layer1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger)); - auto layer2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger)); - auto layer3 = mLayers.emplace_back(layerFactory->createLayer(mFlinger)); - addChild(layer1, layer2); - addChild(layer2, layer3); - - layer1->setFrameRate(FRAME_RATE_VOTE1.vote); - layer2->setFrameRate(FRAME_RATE_VOTE2.vote); - layer2->setFrameRateSelectionStrategy(FrameRateSelectionStrategy::Self); - commitTransaction(); - - EXPECT_EQ(FRAME_RATE_VOTE1, layer1->getFrameRateForLayerTree()); - EXPECT_EQ(FrameRateSelectionStrategy::Propagate, - layer1->getDrawingState().frameRateSelectionStrategy); - EXPECT_EQ(FRAME_RATE_VOTE2, layer2->getFrameRateForLayerTree()); - EXPECT_EQ(FrameRateSelectionStrategy::Self, - layer2->getDrawingState().frameRateSelectionStrategy); - EXPECT_EQ(FRAME_RATE_DEFAULT, layer3->getFrameRateForLayerTree()); - EXPECT_EQ(FrameRateSelectionStrategy::Propagate, - layer3->getDrawingState().frameRateSelectionStrategy); - - layer1->setFrameRateSelectionStrategy(FrameRateSelectionStrategy::OverrideChildren); - commitTransaction(); - - EXPECT_EQ(FRAME_RATE_VOTE1, layer1->getFrameRateForLayerTree()); - EXPECT_EQ(FrameRateSelectionStrategy::OverrideChildren, - layer1->getDrawingState().frameRateSelectionStrategy); - EXPECT_EQ(FRAME_RATE_VOTE1, layer2->getFrameRateForLayerTree()); - EXPECT_EQ(FrameRateSelectionStrategy::Self, - layer2->getDrawingState().frameRateSelectionStrategy); - EXPECT_EQ(FRAME_RATE_VOTE1, layer3->getFrameRateForLayerTree()); - EXPECT_EQ(FrameRateSelectionStrategy::Propagate, - layer3->getDrawingState().frameRateSelectionStrategy); - - layer1->setFrameRate(FRAME_RATE_DEFAULT.vote); - commitTransaction(); - - EXPECT_EQ(FRAME_RATE_TREE, layer1->getFrameRateForLayerTree()); - EXPECT_EQ(FrameRateSelectionStrategy::OverrideChildren, - layer1->getDrawingState().frameRateSelectionStrategy); - EXPECT_EQ(FRAME_RATE_VOTE2, layer2->getFrameRateForLayerTree()); - EXPECT_EQ(FrameRateSelectionStrategy::Self, - layer2->getDrawingState().frameRateSelectionStrategy); - EXPECT_EQ(FRAME_RATE_VOTE2, layer3->getFrameRateForLayerTree()); - EXPECT_EQ(FrameRateSelectionStrategy::Propagate, - layer3->getDrawingState().frameRateSelectionStrategy); -} - -} // namespace -} // namespace android
\ No newline at end of file diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp index dac9265b71..0dfbd6185e 100644 --- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp +++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp @@ -15,6 +15,8 @@ */ #include <common/test/FlagUtils.h> +#include "BackgroundExecutor.h" +#include "Jank/JankTracker.h" #include "com_android_graphics_surfaceflinger_flags.h" #include "gmock/gmock-spec-builders.h" #include "mock/MockTimeStats.h" @@ -82,16 +84,22 @@ public: void SetUp() override { constexpr bool kUseBootTimeClock = true; + constexpr bool kFilterFramesBeforeTraceStarts = false; mTimeStats = std::make_shared<mock::TimeStats>(); mFrameTimeline = std::make_unique<impl::FrameTimeline>(mTimeStats, kSurfaceFlingerPid, - kTestThresholds, !kUseBootTimeClock); + kTestThresholds, !kUseBootTimeClock, + kFilterFramesBeforeTraceStarts); mFrameTimeline->registerDataSource(); mTokenManager = &mFrameTimeline->mTokenManager; mTraceCookieCounter = &mFrameTimeline->mTraceCookieCounter; maxDisplayFrames = &mFrameTimeline->mMaxDisplayFrames; maxTokens = mTokenManager->kMaxTokens; + + JankTracker::clearAndStartCollectingAllJankDataForTesting(); } + void TearDown() override { JankTracker::clearAndStopCollectingAllJankDataForTesting(); } + // Each tracing session can be used for a single block of Start -> Stop. static std::unique_ptr<perfetto::TracingSession> getTracingSessionForTest() { perfetto::TraceConfig cfg; @@ -175,6 +183,16 @@ public: [&](FrameTimelineDataSource::TraceContext ctx) { ctx.Flush(); }); } + std::vector<gui::JankData> getLayerOneJankData() { + BackgroundExecutor::getLowPriorityInstance().flushQueue(); + return JankTracker::getCollectedJankDataForTesting(sLayerIdOne); + } + + std::vector<gui::JankData> getLayerTwoJankData() { + BackgroundExecutor::getLowPriorityInstance().flushQueue(); + return JankTracker::getCollectedJankDataForTesting(sLayerIdTwo); + } + std::shared_ptr<mock::TimeStats> mTimeStats; std::unique_ptr<impl::FrameTimeline> mFrameTimeline; impl::TokenManager* mTokenManager; @@ -339,6 +357,9 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_presentedFramesUpdated) { EXPECT_NE(surfaceFrame1->getJankSeverityType(), std::nullopt); EXPECT_NE(surfaceFrame2->getJankType(), std::nullopt); EXPECT_NE(surfaceFrame2->getJankSeverityType(), std::nullopt); + + EXPECT_EQ(getLayerOneJankData().size(), 1u); + EXPECT_EQ(getLayerTwoJankData().size(), 1u); } TEST_F(FrameTimelineTest, displayFrameSkippedComposition) { @@ -446,6 +467,8 @@ TEST_F(FrameTimelineTest, displayFramesSlidingWindowMovesAfterLimit) { // The window should have slided by 1 now and the previous 0th display frame // should have been removed from the deque EXPECT_EQ(compareTimelineItems(displayFrame0->getActuals(), TimelineItem(52, 57, 62)), true); + + EXPECT_EQ(getLayerOneJankData().size(), *maxDisplayFrames); } TEST_F(FrameTimelineTest, surfaceFrameEndTimeAcquireFenceAfterQueue) { @@ -458,6 +481,16 @@ TEST_F(FrameTimelineTest, surfaceFrameEndTimeAcquireFenceAfterQueue) { EXPECT_EQ(surfaceFrame->getActuals().endTime, 456); } +TEST_F(FrameTimelineTest, surfaceFrameEndTimeAcquireFenceUnsignaled) { + auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, 0, sLayerIdOne, + "acquireFenceAfterQueue", + "acquireFenceAfterQueue", + /*isBuffer*/ true, sGameMode); + surfaceFrame->setActualQueueTime(123); + surfaceFrame->setAcquireFenceTime(Fence::SIGNAL_TIME_PENDING); + EXPECT_EQ(surfaceFrame->getActuals().endTime, 123); +} + TEST_F(FrameTimelineTest, surfaceFrameEndTimeAcquireFenceBeforeQueue) { auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, 0, sLayerIdOne, "acquireFenceAfterQueue", @@ -575,6 +608,8 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_doesNotReportForInvalidTokens) { presentFence1->signalForTest(70); mFrameTimeline->setSfPresent(59, presentFence1); + + EXPECT_EQ(getLayerOneJankData().size(), 0u); } TEST_F(FrameTimelineTest, presentFenceSignaled_reportsLongSfCpu) { @@ -603,6 +638,10 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_reportsLongSfCpu) { presentFence1->signalForTest(70); mFrameTimeline->setSfPresent(62, presentFence1); + + auto jankData = getLayerOneJankData(); + EXPECT_EQ(jankData.size(), 1u); + EXPECT_EQ(jankData[0].jankType, JankType::SurfaceFlingerCpuDeadlineMissed); } TEST_F(FrameTimelineTest, presentFenceSignaled_reportsLongSfGpu) { @@ -633,6 +672,10 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_reportsLongSfGpu) { presentFence1->signalForTest(70); mFrameTimeline->setSfPresent(59, presentFence1, gpuFence1); + + auto jankData = getLayerOneJankData(); + EXPECT_EQ(jankData.size(), 1u); + EXPECT_EQ(jankData[0].jankType, JankType::SurfaceFlingerGpuDeadlineMissed); } TEST_F(FrameTimelineTest, presentFenceSignaled_reportsDisplayMiss) { @@ -661,6 +704,10 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_reportsDisplayMiss) { mFrameTimeline->setSfPresent(56, presentFence1); EXPECT_EQ(surfaceFrame1->getJankType(), JankType::DisplayHAL); EXPECT_EQ(surfaceFrame1->getJankSeverityType(), JankSeverityType::Full); + + auto jankData = getLayerOneJankData(); + EXPECT_EQ(jankData.size(), 1u); + EXPECT_EQ(jankData[0].jankType, JankType::DisplayHAL); } TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppMiss) { @@ -691,6 +738,10 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppMiss) { EXPECT_EQ(surfaceFrame1->getJankType(), JankType::AppDeadlineMissed); EXPECT_EQ(surfaceFrame1->getJankSeverityType(), JankSeverityType::Partial); + + auto jankData = getLayerOneJankData(); + EXPECT_EQ(jankData.size(), 1u); + EXPECT_EQ(jankData[0].jankType, JankType::AppDeadlineMissed); } TEST_F(FrameTimelineTest, presentFenceSignaled_reportsSfScheduling) { @@ -721,6 +772,10 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_reportsSfScheduling) { EXPECT_EQ(surfaceFrame1->getJankType(), JankType::SurfaceFlingerScheduling); EXPECT_EQ(surfaceFrame1->getJankSeverityType(), JankSeverityType::Full); + + auto jankData = getLayerOneJankData(); + EXPECT_EQ(jankData.size(), 1u); + EXPECT_EQ(jankData[0].jankType, JankType::SurfaceFlingerScheduling); } TEST_F(FrameTimelineTest, presentFenceSignaled_reportsSfPredictionError) { @@ -751,6 +806,10 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_reportsSfPredictionError) { EXPECT_EQ(surfaceFrame1->getJankType(), JankType::PredictionError); EXPECT_EQ(surfaceFrame1->getJankSeverityType(), JankSeverityType::Partial); + + auto jankData = getLayerOneJankData(); + EXPECT_EQ(jankData.size(), 1u); + EXPECT_EQ(jankData[0].jankType, JankType::PredictionError); } TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppBufferStuffing) { @@ -782,6 +841,10 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppBufferStuffing) { EXPECT_EQ(surfaceFrame1->getJankType(), JankType::BufferStuffing); EXPECT_EQ(surfaceFrame1->getJankSeverityType(), JankSeverityType::Full); + + auto jankData = getLayerOneJankData(); + EXPECT_EQ(jankData.size(), 1u); + EXPECT_EQ(jankData[0].jankType, JankType::BufferStuffing); } TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppMissWithRenderRate) { @@ -814,6 +877,10 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppMissWithRenderRate) { EXPECT_EQ(surfaceFrame1->getJankType(), JankType::AppDeadlineMissed); EXPECT_EQ(surfaceFrame1->getJankSeverityType(), JankSeverityType::Full); + + auto jankData = getLayerOneJankData(); + EXPECT_EQ(jankData.size(), 1u); + EXPECT_EQ(jankData[0].jankType, JankType::AppDeadlineMissed); } TEST_F(FrameTimelineTest, presentFenceSignaled_displayFramePredictionExpiredPresentsSurfaceFrame) { @@ -858,6 +925,10 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_displayFramePredictionExpiredPres EXPECT_EQ(surfaceFrame1->getActuals().presentTime, 90); EXPECT_EQ(surfaceFrame1->getJankType(), JankType::Unknown | JankType::AppDeadlineMissed); EXPECT_EQ(surfaceFrame1->getJankSeverityType(), JankSeverityType::Full); + + auto jankData = getLayerOneJankData(); + EXPECT_EQ(jankData.size(), 1u); + EXPECT_EQ(jankData[0].jankType, JankType::Unknown | JankType::AppDeadlineMissed); } /* @@ -1789,6 +1860,10 @@ TEST_F(FrameTimelineTest, jankClassification_presentOnTimeDoesNotClassify) { EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish); EXPECT_EQ(displayFrame->getJankType(), JankType::None); EXPECT_EQ(displayFrame->getJankSeverityType(), JankSeverityType::None); + + auto jankData = getLayerOneJankData(); + EXPECT_EQ(jankData.size(), 1u); + EXPECT_EQ(jankData[0].jankType, JankType::None); } TEST_F(FrameTimelineTest, jankClassification_displayFrameOnTimeFinishEarlyPresent) { diff --git a/services/surfaceflinger/tests/unittests/GameModeTest.cpp b/services/surfaceflinger/tests/unittests/GameModeTest.cpp deleted file mode 100644 index 1b5c6e70f8..0000000000 --- a/services/surfaceflinger/tests/unittests/GameModeTest.cpp +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#undef LOG_TAG -#define LOG_TAG "LibSurfaceFlingerUnittests" - -#include <gmock/gmock.h> -#include <gtest/gtest.h> -#include <gui/LayerMetadata.h> -#include <gui/SurfaceComposerClient.h> -#include <log/log.h> - -#include "TestableSurfaceFlinger.h" -#include "mock/DisplayHardware/MockComposer.h" - -namespace android { - -using testing::_; -using testing::Mock; -using testing::Return; - -using gui::GameMode; -using gui::LayerMetadata; - -class GameModeTest : public testing::Test { -public: - GameModeTest() { - const ::testing::TestInfo* const test_info = - ::testing::UnitTest::GetInstance()->current_test_info(); - ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); - mFlinger.setupMockScheduler(); - setupComposer(); - } - - ~GameModeTest() { - const ::testing::TestInfo* const test_info = - ::testing::UnitTest::GetInstance()->current_test_info(); - ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name()); - } - - sp<Layer> createLayer() { - sp<Client> client; - LayerCreationArgs args(mFlinger.flinger(), client, "layer", 0, LayerMetadata()); - return sp<Layer>::make(args); - } - - void setupComposer() { - mComposer = new Hwc2::mock::Composer(); - mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer)); - - Mock::VerifyAndClear(mComposer); - } - - // Mocks the behavior of applying a transaction from WMShell - void setGameModeMetadata(sp<Layer> layer, GameMode gameMode) { - mLayerMetadata.setInt32(gui::METADATA_GAME_MODE, static_cast<int32_t>(gameMode)); - layer->setMetadata(mLayerMetadata); - layer->setGameModeForTree(gameMode); - } - - TestableSurfaceFlinger mFlinger; - Hwc2::mock::Composer* mComposer = nullptr; - client_cache_t mClientCache; - LayerMetadata mLayerMetadata; -}; - -TEST_F(GameModeTest, SetGameModeSetsForAllCurrentChildren) { - sp<Layer> rootLayer = createLayer(); - sp<Layer> childLayer1 = createLayer(); - sp<Layer> childLayer2 = createLayer(); - rootLayer->addChild(childLayer1); - rootLayer->addChild(childLayer2); - rootLayer->setGameModeForTree(GameMode::Performance); - - EXPECT_EQ(rootLayer->getGameMode(), GameMode::Performance); - EXPECT_EQ(childLayer1->getGameMode(), GameMode::Performance); - EXPECT_EQ(childLayer2->getGameMode(), GameMode::Performance); -} - -TEST_F(GameModeTest, AddChildAppliesGameModeFromParent) { - sp<Layer> rootLayer = createLayer(); - sp<Layer> childLayer = createLayer(); - rootLayer->setGameModeForTree(GameMode::Performance); - rootLayer->addChild(childLayer); - - EXPECT_EQ(rootLayer->getGameMode(), GameMode::Performance); - EXPECT_EQ(childLayer->getGameMode(), GameMode::Performance); -} - -TEST_F(GameModeTest, RemoveChildResetsGameMode) { - sp<Layer> rootLayer = createLayer(); - sp<Layer> childLayer = createLayer(); - rootLayer->setGameModeForTree(GameMode::Performance); - rootLayer->addChild(childLayer); - - EXPECT_EQ(rootLayer->getGameMode(), GameMode::Performance); - EXPECT_EQ(childLayer->getGameMode(), GameMode::Performance); - - rootLayer->removeChild(childLayer); - EXPECT_EQ(childLayer->getGameMode(), GameMode::Unsupported); -} - -TEST_F(GameModeTest, ReparentingDoesNotOverrideMetadata) { - sp<Layer> rootLayer = createLayer(); - sp<Layer> childLayer1 = createLayer(); - sp<Layer> childLayer2 = createLayer(); - rootLayer->setGameModeForTree(GameMode::Standard); - rootLayer->addChild(childLayer1); - - setGameModeMetadata(childLayer2, GameMode::Performance); - rootLayer->addChild(childLayer2); - - EXPECT_EQ(rootLayer->getGameMode(), GameMode::Standard); - EXPECT_EQ(childLayer1->getGameMode(), GameMode::Standard); - EXPECT_EQ(childLayer2->getGameMode(), GameMode::Performance); - - rootLayer->removeChild(childLayer2); - EXPECT_EQ(childLayer2->getGameMode(), GameMode::Performance); -} - -} // namespace android diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp index 2cff2f2929..e0753a3cfb 100644 --- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp +++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp @@ -58,6 +58,7 @@ using namespace std::chrono_literals; using Hwc2::Config; +using ::aidl::android::hardware::drm::HdcpLevels; using ::aidl::android::hardware::graphics::common::DisplayHotplugEvent; using ::aidl::android::hardware::graphics::composer3::RefreshRateChangedDebugData; using hal::IComposerClient; @@ -165,6 +166,7 @@ TEST_F(HWComposerTest, getModesWithLegacyDisplayConfigs) { expectHotplugConnect(kHwcDisplayId); const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED); ASSERT_TRUE(info); + ASSERT_TRUE(info->preferredDetailedTimingDescriptor.has_value()); EXPECT_CALL(*mHal, isVrrSupported()).WillRepeatedly(Return(false)); @@ -178,6 +180,10 @@ TEST_F(HWComposerTest, getModesWithLegacyDisplayConfigs) { constexpr int32_t kHeight = 720; constexpr int32_t kConfigGroup = 1; constexpr int32_t kVsyncPeriod = 16666667; + constexpr float kMmPerInch = 25.4f; + const ui::Size size = info->preferredDetailedTimingDescriptor->physicalSizeInMm; + const float expectedDpiX = (kWidth * kMmPerInch / size.width); + const float expectedDpiY = (kHeight * kMmPerInch / size.height); EXPECT_CALL(*mHal, getDisplayAttribute(kHwcDisplayId, kConfigId, IComposerClient::Attribute::WIDTH, @@ -217,8 +223,13 @@ TEST_F(HWComposerTest, getModesWithLegacyDisplayConfigs) { EXPECT_EQ(modes.front().height, kHeight); EXPECT_EQ(modes.front().configGroup, kConfigGroup); EXPECT_EQ(modes.front().vsyncPeriod, kVsyncPeriod); - EXPECT_EQ(modes.front().dpiX, -1); - EXPECT_EQ(modes.front().dpiY, -1); + if (!FlagManager::getInstance().correct_dpi_with_display_size()) { + EXPECT_EQ(modes.front().dpiX, -1); + EXPECT_EQ(modes.front().dpiY, -1); + } else { + EXPECT_EQ(modes.front().dpiX, expectedDpiX); + EXPECT_EQ(modes.front().dpiY, expectedDpiY); + } // Optional parameters are supported constexpr int32_t kDpi = 320; @@ -270,6 +281,10 @@ TEST_F(HWComposerTest, getModesWithDisplayConfigurations_VRR_OFF) { constexpr int32_t kHeight = 720; constexpr int32_t kConfigGroup = 1; constexpr int32_t kVsyncPeriod = 16666667; + constexpr float kMmPerInch = 25.4f; + const ui::Size size = info->preferredDetailedTimingDescriptor->physicalSizeInMm; + const float expectedDpiX = (kWidth * kMmPerInch / size.width); + const float expectedDpiY = (kHeight * kMmPerInch / size.height); EXPECT_CALL(*mHal, getDisplayAttribute(kHwcDisplayId, kConfigId, IComposerClient::Attribute::WIDTH, @@ -309,8 +324,13 @@ TEST_F(HWComposerTest, getModesWithDisplayConfigurations_VRR_OFF) { EXPECT_EQ(modes.front().height, kHeight); EXPECT_EQ(modes.front().configGroup, kConfigGroup); EXPECT_EQ(modes.front().vsyncPeriod, kVsyncPeriod); - EXPECT_EQ(modes.front().dpiX, -1); - EXPECT_EQ(modes.front().dpiY, -1); + if (!FlagManager::getInstance().correct_dpi_with_display_size()) { + EXPECT_EQ(modes.front().dpiX, -1); + EXPECT_EQ(modes.front().dpiY, -1); + } else { + EXPECT_EQ(modes.front().dpiX, expectedDpiX); + EXPECT_EQ(modes.front().dpiY, expectedDpiY); + } // Optional parameters are supported constexpr int32_t kDpi = 320; @@ -360,6 +380,10 @@ TEST_F(HWComposerTest, getModesWithDisplayConfigurations_VRR_ON) { constexpr int32_t kHeight = 720; constexpr int32_t kConfigGroup = 1; constexpr int32_t kVsyncPeriod = 16666667; + constexpr float kMmPerInch = 25.4f; + const ui::Size size = info->preferredDetailedTimingDescriptor->physicalSizeInMm; + const float expectedDpiX = (kWidth * kMmPerInch / size.width); + const float expectedDpiY = (kHeight * kMmPerInch / size.height); const hal::VrrConfig vrrConfig = hal::VrrConfig{.minFrameIntervalNs = static_cast<Fps>(120_Hz).getPeriodNsecs(), .notifyExpectedPresentConfig = hal::VrrConfig:: @@ -386,8 +410,13 @@ TEST_F(HWComposerTest, getModesWithDisplayConfigurations_VRR_ON) { EXPECT_EQ(modes.front().configGroup, kConfigGroup); EXPECT_EQ(modes.front().vsyncPeriod, kVsyncPeriod); EXPECT_EQ(modes.front().vrrConfig, vrrConfig); - EXPECT_EQ(modes.front().dpiX, -1); - EXPECT_EQ(modes.front().dpiY, -1); + if (!FlagManager::getInstance().correct_dpi_with_display_size()) { + EXPECT_EQ(modes.front().dpiX, -1); + EXPECT_EQ(modes.front().dpiY, -1); + } else { + EXPECT_EQ(modes.front().dpiX, expectedDpiX); + EXPECT_EQ(modes.front().dpiY, expectedDpiY); + } // Supports optional dpi parameter constexpr int32_t kDpi = 320; @@ -454,6 +483,8 @@ struct MockHWC2ComposerCallback final : StrictMock<HWC2::ComposerCallback> { MOCK_METHOD1(onComposerHalSeamlessPossible, void(hal::HWDisplayId)); MOCK_METHOD1(onComposerHalVsyncIdle, void(hal::HWDisplayId)); MOCK_METHOD(void, onRefreshRateChangedDebug, (const RefreshRateChangedDebugData&), (override)); + MOCK_METHOD(void, onComposerHalHdcpLevelsChanged, (hal::HWDisplayId, const HdcpLevels&), + (override)); }; struct HWComposerSetCallbackTest : HWComposerTest { diff --git a/services/surfaceflinger/tests/unittests/JankTrackerTest.cpp b/services/surfaceflinger/tests/unittests/JankTrackerTest.cpp new file mode 100644 index 0000000000..2941a14ef9 --- /dev/null +++ b/services/surfaceflinger/tests/unittests/JankTrackerTest.cpp @@ -0,0 +1,216 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <android/gui/BnJankListener.h> +#include <binder/IInterface.h> +#include "BackgroundExecutor.h" +#include "Jank/JankTracker.h" + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +namespace android { + +namespace { + +using namespace testing; + +class MockJankListener : public gui::BnJankListener { +public: + MockJankListener() = default; + ~MockJankListener() override = default; + + MOCK_METHOD(binder::Status, onJankData, (const std::vector<gui::JankData>& jankData), + (override)); +}; + +} // anonymous namespace + +class JankTrackerTest : public Test { +public: + JankTrackerTest() {} + + void SetUp() override { mListener = sp<StrictMock<MockJankListener>>::make(); } + + void addJankListener(int32_t layerId) { + JankTracker::addJankListener(layerId, IInterface::asBinder(mListener)); + } + + void removeJankListener(int32_t layerId, int64_t after) { + JankTracker::removeJankListener(layerId, IInterface::asBinder(mListener), after); + } + + void addJankData(int32_t layerId, int jankType) { + gui::JankData data; + data.frameVsyncId = mVsyncId++; + data.jankType = jankType; + data.frameIntervalNs = 8333333; + JankTracker::onJankData(layerId, data); + } + + void flushBackgroundThread() { BackgroundExecutor::getLowPriorityInstance().flushQueue(); } + + size_t listenerCount() { return JankTracker::sListenerCount; } + + std::vector<gui::JankData> getCollectedJankData(int32_t layerId) { + return JankTracker::getCollectedJankDataForTesting(layerId); + } + + sp<StrictMock<MockJankListener>> mListener = nullptr; + int64_t mVsyncId = 1000; +}; + +TEST_F(JankTrackerTest, jankDataIsTrackedAndPropagated) { + ASSERT_EQ(listenerCount(), 0u); + + EXPECT_CALL(*mListener.get(), onJankData(SizeIs(3))) + .WillOnce([](const std::vector<gui::JankData>& jankData) { + EXPECT_EQ(jankData[0].frameVsyncId, 1000); + EXPECT_EQ(jankData[0].jankType, 1); + EXPECT_EQ(jankData[0].frameIntervalNs, 8333333); + + EXPECT_EQ(jankData[1].frameVsyncId, 1001); + EXPECT_EQ(jankData[1].jankType, 2); + EXPECT_EQ(jankData[1].frameIntervalNs, 8333333); + + EXPECT_EQ(jankData[2].frameVsyncId, 1002); + EXPECT_EQ(jankData[2].jankType, 3); + EXPECT_EQ(jankData[2].frameIntervalNs, 8333333); + return binder::Status::ok(); + }); + EXPECT_CALL(*mListener.get(), onJankData(SizeIs(2))) + .WillOnce([](const std::vector<gui::JankData>& jankData) { + EXPECT_EQ(jankData[0].frameVsyncId, 1003); + EXPECT_EQ(jankData[0].jankType, 4); + EXPECT_EQ(jankData[0].frameIntervalNs, 8333333); + + EXPECT_EQ(jankData[1].frameVsyncId, 1004); + EXPECT_EQ(jankData[1].jankType, 5); + EXPECT_EQ(jankData[1].frameIntervalNs, 8333333); + + return binder::Status::ok(); + }); + + addJankListener(123); + addJankData(123, 1); + addJankData(123, 2); + addJankData(123, 3); + JankTracker::flushJankData(123); + addJankData(123, 4); + removeJankListener(123, mVsyncId); + addJankData(123, 5); + JankTracker::flushJankData(123); + addJankData(123, 6); + JankTracker::flushJankData(123); + removeJankListener(123, 0); + + flushBackgroundThread(); +} + +TEST_F(JankTrackerTest, jankDataIsAutomaticallyFlushedInBatches) { + ASSERT_EQ(listenerCount(), 0u); + + // needs to be larger than kJankDataBatchSize in JankTracker.cpp. + constexpr size_t kNumberOfJankDataToSend = 234; + + size_t jankDataReceived = 0; + size_t numBatchesReceived = 0; + + EXPECT_CALL(*mListener.get(), onJankData(_)) + .WillRepeatedly([&](const std::vector<gui::JankData>& jankData) { + jankDataReceived += jankData.size(); + numBatchesReceived++; + return binder::Status::ok(); + }); + + addJankListener(123); + for (size_t i = 0; i < kNumberOfJankDataToSend; i++) { + addJankData(123, 0); + } + + flushBackgroundThread(); + // Check that we got some data, without explicitly flushing. + EXPECT_GT(jankDataReceived, 0u); + EXPECT_GT(numBatchesReceived, 0u); + EXPECT_LT(numBatchesReceived, jankDataReceived); // batches should be > size 1. + + removeJankListener(123, 0); + JankTracker::flushJankData(123); + flushBackgroundThread(); + EXPECT_EQ(jankDataReceived, kNumberOfJankDataToSend); +} + +TEST_F(JankTrackerTest, jankListenerIsRemovedWhenReturningNullError) { + ASSERT_EQ(listenerCount(), 0u); + + EXPECT_CALL(*mListener.get(), onJankData(SizeIs(3))) + .WillOnce(Return(binder::Status::fromExceptionCode(binder::Status::EX_NULL_POINTER))); + + addJankListener(123); + addJankData(123, 1); + addJankData(123, 2); + addJankData(123, 3); + JankTracker::flushJankData(123); + addJankData(123, 4); + addJankData(123, 5); + JankTracker::flushJankData(123); + flushBackgroundThread(); + + EXPECT_EQ(listenerCount(), 0u); +} + +TEST_F(JankTrackerTest, jankDataIsDroppedIfNobodyIsListening) { + ASSERT_EQ(listenerCount(), 0u); + + addJankData(123, 1); + addJankData(123, 2); + addJankData(123, 3); + flushBackgroundThread(); + + EXPECT_EQ(getCollectedJankData(123).size(), 0u); +} + +TEST_F(JankTrackerTest, listenerCountTracksRegistrations) { + ASSERT_EQ(listenerCount(), 0u); + + addJankListener(123); + addJankListener(456); + flushBackgroundThread(); + EXPECT_EQ(listenerCount(), 2u); + + removeJankListener(123, 0); + JankTracker::flushJankData(123); + removeJankListener(456, 0); + JankTracker::flushJankData(456); + flushBackgroundThread(); + EXPECT_EQ(listenerCount(), 0u); +} + +TEST_F(JankTrackerTest, listenerCountIsAccurateOnDuplicateRegistration) { + ASSERT_EQ(listenerCount(), 0u); + + addJankListener(123); + addJankListener(123); + flushBackgroundThread(); + EXPECT_EQ(listenerCount(), 1u); + + removeJankListener(123, 0); + JankTracker::flushJankData(123); + flushBackgroundThread(); + EXPECT_EQ(listenerCount(), 0u); +} + +} // namespace android
\ No newline at end of file diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h index 8b3303c809..37cda3efcb 100644 --- a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h +++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h @@ -25,13 +25,15 @@ #include "FrontEnd/LayerCreationArgs.h" #include "FrontEnd/LayerHierarchy.h" #include "FrontEnd/LayerLifecycleManager.h" +#include "LayerLifecycleManagerHelper.h" + #include "FrontEnd/LayerSnapshotBuilder.h" namespace android::surfaceflinger::frontend { -class LayerHierarchyTestBase : public testing::Test { +class LayerHierarchyTestBase : public testing::Test, public LayerLifecycleManagerHelper { protected: - LayerHierarchyTestBase() { + LayerHierarchyTestBase() : LayerLifecycleManagerHelper(mLifecycleManager) { // tree with 3 levels of children // ROOT // ├── 1 @@ -55,24 +57,6 @@ protected: createLayer(1221, 122); } - LayerCreationArgs createArgs(uint32_t id, bool canBeRoot, uint32_t parentId, - uint32_t layerIdToMirror) { - LayerCreationArgs args(std::make_optional(id)); - args.name = "testlayer"; - args.addToRoot = canBeRoot; - args.parentId = parentId; - args.layerIdToMirror = layerIdToMirror; - return args; - } - - LayerCreationArgs createDisplayMirrorArgs(uint32_t id, ui::LayerStack layerStackToMirror) { - LayerCreationArgs args(std::make_optional(id)); - args.name = "testlayer"; - args.addToRoot = true; - args.layerStackToMirror = layerStackToMirror; - return args; - } - std::vector<uint32_t> getTraversalPath(const LayerHierarchy& hierarchy) const { std::vector<uint32_t> layerIds; hierarchy.traverse([&layerIds = layerIds](const LayerHierarchy& hierarchy, @@ -94,98 +78,6 @@ protected: return layerIds; } - virtual void createRootLayer(uint32_t id) { - std::vector<std::unique_ptr<RequestedLayerState>> layers; - layers.emplace_back(std::make_unique<RequestedLayerState>( - createArgs(/*id=*/id, /*canBeRoot=*/true, /*parent=*/UNASSIGNED_LAYER_ID, - /*mirror=*/UNASSIGNED_LAYER_ID))); - mLifecycleManager.addLayers(std::move(layers)); - } - - void createDisplayMirrorLayer(uint32_t id, ui::LayerStack layerStack) { - std::vector<std::unique_ptr<RequestedLayerState>> layers; - layers.emplace_back(std::make_unique<RequestedLayerState>( - createDisplayMirrorArgs(/*id=*/id, layerStack))); - mLifecycleManager.addLayers(std::move(layers)); - } - - virtual void createLayer(uint32_t id, uint32_t parentId) { - std::vector<std::unique_ptr<RequestedLayerState>> layers; - layers.emplace_back(std::make_unique<RequestedLayerState>( - createArgs(/*id=*/id, /*canBeRoot=*/false, /*parent=*/parentId, - /*mirror=*/UNASSIGNED_LAYER_ID))); - mLifecycleManager.addLayers(std::move(layers)); - } - - std::vector<TransactionState> reparentLayerTransaction(uint32_t id, uint32_t newParentId) { - std::vector<TransactionState> transactions; - transactions.emplace_back(); - transactions.back().states.push_back({}); - transactions.back().states.front().parentId = newParentId; - transactions.back().states.front().state.what = layer_state_t::eReparent; - transactions.back().states.front().relativeParentId = UNASSIGNED_LAYER_ID; - transactions.back().states.front().layerId = id; - return transactions; - } - - void reparentLayer(uint32_t id, uint32_t newParentId) { - mLifecycleManager.applyTransactions(reparentLayerTransaction(id, newParentId)); - } - - std::vector<TransactionState> relativeLayerTransaction(uint32_t id, uint32_t relativeParentId) { - std::vector<TransactionState> transactions; - transactions.emplace_back(); - transactions.back().states.push_back({}); - transactions.back().states.front().relativeParentId = relativeParentId; - transactions.back().states.front().state.what = layer_state_t::eRelativeLayerChanged; - transactions.back().states.front().layerId = id; - return transactions; - } - - void reparentRelativeLayer(uint32_t id, uint32_t relativeParentId) { - mLifecycleManager.applyTransactions(relativeLayerTransaction(id, relativeParentId)); - } - - void removeRelativeZ(uint32_t id) { - std::vector<TransactionState> transactions; - transactions.emplace_back(); - transactions.back().states.push_back({}); - transactions.back().states.front().state.what = layer_state_t::eLayerChanged; - transactions.back().states.front().layerId = id; - mLifecycleManager.applyTransactions(transactions); - } - - void setPosition(uint32_t id, float x, float y) { - std::vector<TransactionState> transactions; - transactions.emplace_back(); - transactions.back().states.push_back({}); - transactions.back().states.front().state.what = layer_state_t::ePositionChanged; - transactions.back().states.front().state.x = x; - transactions.back().states.front().state.y = y; - transactions.back().states.front().layerId = id; - mLifecycleManager.applyTransactions(transactions); - } - - virtual void mirrorLayer(uint32_t id, uint32_t parentId, uint32_t layerIdToMirror) { - std::vector<std::unique_ptr<RequestedLayerState>> layers; - layers.emplace_back(std::make_unique<RequestedLayerState>( - createArgs(/*id=*/id, /*canBeRoot=*/false, /*parent=*/parentId, - /*mirror=*/layerIdToMirror))); - mLifecycleManager.addLayers(std::move(layers)); - } - - void updateBackgroundColor(uint32_t id, half alpha) { - std::vector<TransactionState> transactions; - transactions.emplace_back(); - transactions.back().states.push_back({}); - transactions.back().states.front().state.what = layer_state_t::eBackgroundColorChanged; - transactions.back().states.front().state.bgColor.a = alpha; - transactions.back().states.front().layerId = id; - mLifecycleManager.applyTransactions(transactions); - } - - void destroyLayerHandle(uint32_t id) { mLifecycleManager.onHandlesDestroyed({{id, "test"}}); } - void updateAndVerify(LayerHierarchyBuilder& hierarchyBuilder) { hierarchyBuilder.update(mLifecycleManager); mLifecycleManager.commitChanges(); @@ -201,321 +93,6 @@ protected: mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)); } - std::vector<TransactionState> setZTransaction(uint32_t id, int32_t z) { - std::vector<TransactionState> transactions; - transactions.emplace_back(); - transactions.back().states.push_back({}); - - transactions.back().states.front().state.what = layer_state_t::eLayerChanged; - transactions.back().states.front().layerId = id; - transactions.back().states.front().state.z = z; - return transactions; - } - - void setZ(uint32_t id, int32_t z) { - mLifecycleManager.applyTransactions(setZTransaction(id, z)); - } - - void setCrop(uint32_t id, const Rect& crop) { - std::vector<TransactionState> transactions; - transactions.emplace_back(); - transactions.back().states.push_back({}); - - transactions.back().states.front().state.what = layer_state_t::eCropChanged; - transactions.back().states.front().layerId = id; - transactions.back().states.front().state.crop = crop; - mLifecycleManager.applyTransactions(transactions); - } - - void setFlags(uint32_t id, uint32_t mask, uint32_t flags) { - std::vector<TransactionState> transactions; - transactions.emplace_back(); - transactions.back().states.push_back({}); - - transactions.back().states.front().state.what = layer_state_t::eFlagsChanged; - transactions.back().states.front().state.flags = flags; - transactions.back().states.front().state.mask = mask; - transactions.back().states.front().layerId = id; - mLifecycleManager.applyTransactions(transactions); - } - - void setAlpha(uint32_t id, float alpha) { - std::vector<TransactionState> transactions; - transactions.emplace_back(); - transactions.back().states.push_back({}); - - transactions.back().states.front().state.what = layer_state_t::eAlphaChanged; - transactions.back().states.front().layerId = id; - transactions.back().states.front().state.color.a = static_cast<half>(alpha); - mLifecycleManager.applyTransactions(transactions); - } - - void hideLayer(uint32_t id) { - setFlags(id, layer_state_t::eLayerHidden, layer_state_t::eLayerHidden); - } - - void showLayer(uint32_t id) { setFlags(id, layer_state_t::eLayerHidden, 0); } - - void setColor(uint32_t id, half3 rgb = half3(1._hf, 1._hf, 1._hf)) { - std::vector<TransactionState> transactions; - transactions.emplace_back(); - transactions.back().states.push_back({}); - transactions.back().states.front().state.what = layer_state_t::eColorChanged; - transactions.back().states.front().state.color.rgb = rgb; - transactions.back().states.front().layerId = id; - mLifecycleManager.applyTransactions(transactions); - } - - void setLayerStack(uint32_t id, int32_t layerStack) { - std::vector<TransactionState> transactions; - transactions.emplace_back(); - transactions.back().states.push_back({}); - - transactions.back().states.front().state.what = layer_state_t::eLayerStackChanged; - transactions.back().states.front().layerId = id; - transactions.back().states.front().state.layerStack = ui::LayerStack::fromValue(layerStack); - mLifecycleManager.applyTransactions(transactions); - } - - void setTouchableRegion(uint32_t id, Region region) { - std::vector<TransactionState> transactions; - transactions.emplace_back(); - transactions.back().states.push_back({}); - - transactions.back().states.front().state.what = layer_state_t::eInputInfoChanged; - transactions.back().states.front().layerId = id; - transactions.back().states.front().state.windowInfoHandle = - sp<gui::WindowInfoHandle>::make(); - auto inputInfo = transactions.back().states.front().state.windowInfoHandle->editInfo(); - inputInfo->touchableRegion = region; - inputInfo->token = sp<BBinder>::make(); - mLifecycleManager.applyTransactions(transactions); - } - - void setInputInfo(uint32_t id, std::function<void(gui::WindowInfo&)> configureInput) { - std::vector<TransactionState> transactions; - transactions.emplace_back(); - transactions.back().states.push_back({}); - - transactions.back().states.front().state.what = layer_state_t::eInputInfoChanged; - transactions.back().states.front().layerId = id; - transactions.back().states.front().state.windowInfoHandle = - sp<gui::WindowInfoHandle>::make(); - auto inputInfo = transactions.back().states.front().state.windowInfoHandle->editInfo(); - if (!inputInfo->token) { - inputInfo->token = sp<BBinder>::make(); - } - configureInput(*inputInfo); - - mLifecycleManager.applyTransactions(transactions); - } - - void setTouchableRegionCrop(uint32_t id, Region region, uint32_t touchCropId, - bool replaceTouchableRegionWithCrop) { - std::vector<TransactionState> transactions; - transactions.emplace_back(); - transactions.back().states.push_back({}); - - transactions.back().states.front().state.what = layer_state_t::eInputInfoChanged; - transactions.back().states.front().layerId = id; - transactions.back().states.front().state.windowInfoHandle = - sp<gui::WindowInfoHandle>::make(); - auto inputInfo = transactions.back().states.front().state.windowInfoHandle->editInfo(); - inputInfo->touchableRegion = region; - inputInfo->replaceTouchableRegionWithCrop = replaceTouchableRegionWithCrop; - transactions.back().states.front().touchCropId = touchCropId; - - inputInfo->token = sp<BBinder>::make(); - mLifecycleManager.applyTransactions(transactions); - } - - void setBackgroundBlurRadius(uint32_t id, uint32_t backgroundBlurRadius) { - std::vector<TransactionState> transactions; - transactions.emplace_back(); - transactions.back().states.push_back({}); - - transactions.back().states.front().state.what = layer_state_t::eBackgroundBlurRadiusChanged; - transactions.back().states.front().layerId = id; - transactions.back().states.front().state.backgroundBlurRadius = backgroundBlurRadius; - mLifecycleManager.applyTransactions(transactions); - } - - void setFrameRateSelectionPriority(uint32_t id, int32_t priority) { - std::vector<TransactionState> transactions; - transactions.emplace_back(); - transactions.back().states.push_back({}); - - transactions.back().states.front().state.what = layer_state_t::eFrameRateSelectionPriority; - transactions.back().states.front().layerId = id; - transactions.back().states.front().state.frameRateSelectionPriority = priority; - mLifecycleManager.applyTransactions(transactions); - } - - void setFrameRate(uint32_t id, float frameRate, int8_t compatibility, - int8_t changeFrameRateStrategy) { - std::vector<TransactionState> transactions; - transactions.emplace_back(); - transactions.back().states.push_back({}); - - transactions.back().states.front().state.what = layer_state_t::eFrameRateChanged; - transactions.back().states.front().layerId = id; - transactions.back().states.front().state.frameRate = frameRate; - transactions.back().states.front().state.frameRateCompatibility = compatibility; - transactions.back().states.front().state.changeFrameRateStrategy = changeFrameRateStrategy; - mLifecycleManager.applyTransactions(transactions); - } - - void setFrameRateCategory(uint32_t id, int8_t frameRateCategory) { - std::vector<TransactionState> transactions; - transactions.emplace_back(); - transactions.back().states.push_back({}); - - transactions.back().states.front().state.what = layer_state_t::eFrameRateCategoryChanged; - transactions.back().states.front().layerId = id; - transactions.back().states.front().state.frameRateCategory = frameRateCategory; - mLifecycleManager.applyTransactions(transactions); - } - - void setFrameRateSelectionStrategy(uint32_t id, int8_t strategy) { - std::vector<TransactionState> transactions; - transactions.emplace_back(); - transactions.back().states.push_back({}); - - transactions.back().states.front().state.what = - layer_state_t::eFrameRateSelectionStrategyChanged; - transactions.back().states.front().layerId = id; - transactions.back().states.front().state.frameRateSelectionStrategy = strategy; - mLifecycleManager.applyTransactions(transactions); - } - - void setDefaultFrameRateCompatibility(uint32_t id, int8_t defaultFrameRateCompatibility) { - std::vector<TransactionState> transactions; - transactions.emplace_back(); - transactions.back().states.push_back({}); - - transactions.back().states.front().state.what = - layer_state_t::eDefaultFrameRateCompatibilityChanged; - transactions.back().states.front().layerId = id; - transactions.back().states.front().state.defaultFrameRateCompatibility = - defaultFrameRateCompatibility; - mLifecycleManager.applyTransactions(transactions); - } - - void setRoundedCorners(uint32_t id, float radius) { - std::vector<TransactionState> transactions; - transactions.emplace_back(); - transactions.back().states.push_back({}); - - transactions.back().states.front().state.what = layer_state_t::eCornerRadiusChanged; - transactions.back().states.front().layerId = id; - transactions.back().states.front().state.cornerRadius = radius; - mLifecycleManager.applyTransactions(transactions); - } - - void setBuffer(uint32_t id, std::shared_ptr<renderengine::ExternalTexture> texture) { - std::vector<TransactionState> transactions; - transactions.emplace_back(); - transactions.back().states.push_back({}); - - transactions.back().states.front().state.what = layer_state_t::eBufferChanged; - transactions.back().states.front().layerId = id; - transactions.back().states.front().externalTexture = texture; - transactions.back().states.front().state.bufferData = - std::make_shared<fake::BufferData>(texture->getId(), texture->getWidth(), - texture->getHeight(), texture->getPixelFormat(), - texture->getUsage()); - mLifecycleManager.applyTransactions(transactions); - } - - void setBuffer(uint32_t id) { - static uint64_t sBufferId = 1; - setBuffer(id, - std::make_shared<renderengine::mock:: - FakeExternalTexture>(1U /*width*/, 1U /*height*/, - sBufferId++, - HAL_PIXEL_FORMAT_RGBA_8888, - GRALLOC_USAGE_PROTECTED /*usage*/)); - } - - void setBufferCrop(uint32_t id, const Rect& bufferCrop) { - std::vector<TransactionState> transactions; - transactions.emplace_back(); - transactions.back().states.push_back({}); - - transactions.back().states.front().state.what = layer_state_t::eBufferCropChanged; - transactions.back().states.front().layerId = id; - transactions.back().states.front().state.bufferCrop = bufferCrop; - mLifecycleManager.applyTransactions(transactions); - } - - void setDamageRegion(uint32_t id, const Region& damageRegion) { - std::vector<TransactionState> transactions; - transactions.emplace_back(); - transactions.back().states.push_back({}); - - transactions.back().states.front().state.what = layer_state_t::eSurfaceDamageRegionChanged; - transactions.back().states.front().layerId = id; - transactions.back().states.front().state.surfaceDamageRegion = damageRegion; - mLifecycleManager.applyTransactions(transactions); - } - - void setDataspace(uint32_t id, ui::Dataspace dataspace) { - std::vector<TransactionState> transactions; - transactions.emplace_back(); - transactions.back().states.push_back({}); - - transactions.back().states.front().state.what = layer_state_t::eDataspaceChanged; - transactions.back().states.front().layerId = id; - transactions.back().states.front().state.dataspace = dataspace; - mLifecycleManager.applyTransactions(transactions); - } - - void setMatrix(uint32_t id, float dsdx, float dtdx, float dtdy, float dsdy) { - layer_state_t::matrix22_t matrix{dsdx, dtdx, dtdy, dsdy}; - - std::vector<TransactionState> transactions; - transactions.emplace_back(); - transactions.back().states.push_back({}); - - transactions.back().states.front().state.what = layer_state_t::eMatrixChanged; - transactions.back().states.front().layerId = id; - transactions.back().states.front().state.matrix = matrix; - mLifecycleManager.applyTransactions(transactions); - } - - void setShadowRadius(uint32_t id, float shadowRadius) { - std::vector<TransactionState> transactions; - transactions.emplace_back(); - transactions.back().states.push_back({}); - - transactions.back().states.front().state.what = layer_state_t::eShadowRadiusChanged; - transactions.back().states.front().layerId = id; - transactions.back().states.front().state.shadowRadius = shadowRadius; - mLifecycleManager.applyTransactions(transactions); - } - - void setTrustedOverlay(uint32_t id, gui::TrustedOverlay trustedOverlay) { - std::vector<TransactionState> transactions; - transactions.emplace_back(); - transactions.back().states.push_back({}); - - transactions.back().states.front().state.what = layer_state_t::eTrustedOverlayChanged; - transactions.back().states.front().layerId = id; - transactions.back().states.front().state.trustedOverlay = trustedOverlay; - mLifecycleManager.applyTransactions(transactions); - } - - void setDropInputMode(uint32_t id, gui::DropInputMode dropInputMode) { - std::vector<TransactionState> transactions; - transactions.emplace_back(); - transactions.back().states.push_back({}); - - transactions.back().states.front().state.what = layer_state_t::eDropInputModeChanged; - transactions.back().states.front().layerId = id; - transactions.back().states.front().state.dropInputMode = dropInputMode; - mLifecycleManager.applyTransactions(transactions); - } - LayerLifecycleManager mLifecycleManager; }; @@ -523,21 +100,6 @@ class LayerSnapshotTestBase : public LayerHierarchyTestBase { protected: LayerSnapshotTestBase() : LayerHierarchyTestBase() {} - void createRootLayer(uint32_t id) override { - LayerHierarchyTestBase::createRootLayer(id); - setColor(id); - } - - void createLayer(uint32_t id, uint32_t parentId) override { - LayerHierarchyTestBase::createLayer(id, parentId); - setColor(parentId); - } - - void mirrorLayer(uint32_t id, uint32_t parent, uint32_t layerToMirror) override { - LayerHierarchyTestBase::mirrorLayer(id, parent, layerToMirror); - setColor(id); - } - void update(LayerSnapshotBuilder& snapshotBuilder) { mHierarchyBuilder.update(mLifecycleManager); LayerSnapshotBuilder::Args args{.root = mHierarchyBuilder.getHierarchy(), diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp index a61fa1edb8..de37b6342c 100644 --- a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp @@ -38,6 +38,7 @@ namespace android::scheduler { using android::mock::createDisplayMode; +using android::mock::createVrrDisplayMode; using namespace com::android::graphics::surfaceflinger; class LayerHistoryIntegrationTest : public surfaceflinger::frontend::LayerSnapshotTestBase { @@ -53,6 +54,8 @@ protected: static constexpr Fps HI_FPS = 90_Hz; static constexpr auto HI_FPS_PERIOD = HI_FPS.getPeriodNsecs(); + static constexpr auto kVrrModeId = DisplayModeId(2); + LayerHistoryIntegrationTest() : LayerSnapshotTestBase() { mFlinger.resetScheduler(mScheduler); mLifecycleManager = {}; @@ -71,6 +74,13 @@ protected: updateLayerSnapshotsAndLayerHistory(time); } + void setFrontBufferWithPresentTime(sp<Layer>& layer, nsecs_t time) { + uint32_t sequence = static_cast<uint32_t>(layer->sequence); + setFrontBuffer(sequence); + layer->setDesiredPresentTime(time, false /*autotimestamp*/); + updateLayerSnapshotsAndLayerHistory(time); + } + LayerHistory& history() { return mScheduler->mutableLayerHistory(); } const LayerHistory& history() const { return mScheduler->mutableLayerHistory(); } @@ -135,6 +145,21 @@ protected: return layer; } + auto createLegacyAndFrontedEndLayerWithUid(uint32_t sequence, gui::Uid uid) { + std::string layerName = "test layer:" + std::to_string(sequence); + auto args = LayerCreationArgs{mFlinger.flinger(), + nullptr, + layerName, + 0, + {}, + std::make_optional<uint32_t>(sequence)}; + args.ownerUid = uid.val(); + const auto layer = sp<Layer>::make(args); + mFlinger.injectLegacyLayer(layer); + createRootLayerWithUid(sequence, uid); + return layer; + } + auto destroyLayer(sp<Layer>& layer) { uint32_t sequence = static_cast<uint32_t>(layer->sequence); mFlinger.releaseLegacyLayer(sequence); @@ -157,12 +182,13 @@ protected: ASSERT_EQ(desiredRefreshRate, summary[0].desiredRefreshRate); } - std::shared_ptr<RefreshRateSelector> mSelector = - std::make_shared<RefreshRateSelector>(makeModes(createDisplayMode(DisplayModeId(0), - LO_FPS), - createDisplayMode(DisplayModeId(1), - HI_FPS)), - DisplayModeId(0)); + std::shared_ptr<RefreshRateSelector> mSelector = std::make_shared<RefreshRateSelector>( + makeModes(createDisplayMode(DisplayModeId(0), LO_FPS), + createDisplayMode(DisplayModeId(1), HI_FPS), + createVrrDisplayMode(kVrrModeId, HI_FPS, + hal::VrrConfig{.minFrameIntervalNs = + HI_FPS.getPeriodNsecs()})), + DisplayModeId(0)); mock::SchedulerCallback mSchedulerCallback; TestableSurfaceFlinger mFlinger; @@ -214,6 +240,146 @@ TEST_F(LayerHistoryIntegrationTest, singleLayerMinVoteDefaultCompatibility) { EXPECT_EQ(1u, activeLayerCount()); } +TEST_F(LayerHistoryIntegrationTest, oneLayer) { + createLegacyAndFrontedEndLayer(1); + nsecs_t time = systemTime(); + updateLayerSnapshotsAndLayerHistory(time); + + EXPECT_EQ(1u, layerCount()); + EXPECT_EQ(0u, activeLayerCount()); + + // No layers returned if no layers are active. + EXPECT_TRUE(summarizeLayerHistory(time).empty()); + EXPECT_EQ(0u, activeLayerCount()); + + // Max returned if active layers have insufficient history. + for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) { + setBuffer(1); + updateLayerSnapshotsAndLayerHistory(time); + ASSERT_EQ(1u, summarizeLayerHistory(time).size()); + EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote); + EXPECT_EQ(1u, activeLayerCount()); + time += LO_FPS_PERIOD; + } + + // Max is returned since we have enough history but there is no timestamp votes. + for (size_t i = 0; i < 10; i++) { + setBuffer(1); + updateLayerSnapshotsAndLayerHistory(time); + ASSERT_EQ(1u, summarizeLayerHistory(time).size()); + EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote); + EXPECT_EQ(1u, activeLayerCount()); + time += LO_FPS_PERIOD; + } +} + +TEST_F(LayerHistoryIntegrationTest, gameFrameRateOverrideMapping) { + SET_FLAG_FOR_TEST(flags::game_default_frame_rate, true); + + history().updateGameDefaultFrameRateOverride(FrameRateOverride({0, 60.0f})); + + auto overridePair = history().getGameFrameRateOverride(0); + EXPECT_EQ(0_Hz, overridePair.first); + EXPECT_EQ(60_Hz, overridePair.second); + + history().updateGameModeFrameRateOverride(FrameRateOverride({0, 40.0f})); + history().updateGameModeFrameRateOverride(FrameRateOverride({1, 120.0f})); + + overridePair = history().getGameFrameRateOverride(0); + EXPECT_EQ(40_Hz, overridePair.first); + EXPECT_EQ(60_Hz, overridePair.second); + + overridePair = history().getGameFrameRateOverride(1); + EXPECT_EQ(120_Hz, overridePair.first); + EXPECT_EQ(0_Hz, overridePair.second); + + history().updateGameDefaultFrameRateOverride(FrameRateOverride({0, 0.0f})); + history().updateGameModeFrameRateOverride(FrameRateOverride({1, 0.0f})); + + overridePair = history().getGameFrameRateOverride(0); + EXPECT_EQ(40_Hz, overridePair.first); + EXPECT_EQ(0_Hz, overridePair.second); + + overridePair = history().getGameFrameRateOverride(1); + EXPECT_EQ(0_Hz, overridePair.first); + EXPECT_EQ(0_Hz, overridePair.second); +} + +TEST_F(LayerHistoryIntegrationTest, oneLayerGameFrameRateOverride) { + SET_FLAG_FOR_TEST(flags::game_default_frame_rate, true); + + const uid_t uid = 0; + const Fps gameDefaultFrameRate = Fps::fromValue(30.0f); + const Fps gameModeFrameRate = Fps::fromValue(60.0f); + + auto layer = createLegacyAndFrontedEndLayerWithUid(1, gui::Uid(uid)); + showLayer(1); + + nsecs_t time = systemTime(); + updateLayerSnapshotsAndLayerHistory(time); + + EXPECT_EQ(1u, layerCount()); + EXPECT_EQ(0u, activeLayerCount()); + + // update game default frame rate override + history().updateGameDefaultFrameRateOverride( + FrameRateOverride({uid, gameDefaultFrameRate.getValue()})); + + LayerHistory::Summary summary; + scheduler::LayerProps layerProps = { + .visible = true, + .bounds = {0, 0, 100, 100}, + .transform = {}, + .setFrameRateVote = {}, + .frameRateSelectionPriority = Layer::PRIORITY_UNSET, + .isSmallDirty = false, + .isFrontBuffered = false, + }; + + for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { + setBufferWithPresentTime(layer, time); + time += gameDefaultFrameRate.getPeriodNsecs(); + summary = summarizeLayerHistory(time); + } + + ASSERT_EQ(1u, summary.size()); + ASSERT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summary[0].vote); + ASSERT_EQ(30.0_Hz, summary[0].desiredRefreshRate); + + // test against setFrameRate vote + setFrameRate(1, + Layer::FrameRate(Fps::fromValue(120.0f), Layer::FrameRateCompatibility::Default)); + updateLayerSnapshotsAndLayerHistory(time); + + const Fps setFrameRate = Fps::fromValue(120.0f); + layerProps.setFrameRateVote = + Layer::FrameRate(setFrameRate, Layer::FrameRateCompatibility::Default); + + for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { + setBufferWithPresentTime(layer, time); + time += setFrameRate.getPeriodNsecs(); + summary = summarizeLayerHistory(time); + } + + ASSERT_EQ(1u, summary.size()); + ASSERT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summary[0].vote); + ASSERT_EQ(120.0_Hz, summary[0].desiredRefreshRate); + + // update game mode frame rate override + history().updateGameModeFrameRateOverride( + FrameRateOverride({uid, gameModeFrameRate.getValue()})); + + for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { + setBufferWithPresentTime(layer, time); + time += gameModeFrameRate.getPeriodNsecs(); + summary = summarizeLayerHistory(time); + } + + ASSERT_EQ(1u, summary.size()); + ASSERT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summary[0].vote); + ASSERT_EQ(60.0_Hz, summary[0].desiredRefreshRate); +} + TEST_F(LayerHistoryIntegrationTest, oneInvisibleLayer) { createLegacyAndFrontedEndLayer(1); nsecs_t time = systemTime(); @@ -238,6 +404,110 @@ TEST_F(LayerHistoryIntegrationTest, oneInvisibleLayer) { EXPECT_EQ(0u, activeLayerCount()); } +TEST_F(LayerHistoryIntegrationTest, explicitTimestamp) { + auto layer = createLegacyAndFrontedEndLayer(1); + showLayer(1); + nsecs_t time = systemTime(); + updateLayerSnapshotsAndLayerHistory(time); + + EXPECT_EQ(1u, layerCount()); + EXPECT_EQ(0u, activeLayerCount()); + + for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { + setBufferWithPresentTime(layer, time); + time += LO_FPS_PERIOD; + } + + ASSERT_EQ(1u, summarizeLayerHistory(time).size()); + EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summarizeLayerHistory(time)[0].vote); + EXPECT_EQ(LO_FPS, summarizeLayerHistory(time)[0].desiredRefreshRate); + EXPECT_EQ(1u, activeLayerCount()); + EXPECT_EQ(1, frequentLayerCount(time)); +} + +TEST_F(LayerHistoryIntegrationTest, oneLayerNoVote) { + auto layer = createLegacyAndFrontedEndLayer(1); + showLayer(1); + nsecs_t time = systemTime(); + updateLayerSnapshotsAndLayerHistory(time); + + setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::NoVote); + + EXPECT_EQ(1u, layerCount()); + EXPECT_EQ(0u, activeLayerCount()); + + for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { + setBufferWithPresentTime(layer, time); + time += HI_FPS_PERIOD; + } + + ASSERT_TRUE(summarizeLayerHistory(time).empty()); + EXPECT_EQ(1u, activeLayerCount()); + EXPECT_EQ(1, frequentLayerCount(time)); + + // layer became inactive + time += MAX_ACTIVE_LAYER_PERIOD_NS.count(); + ASSERT_TRUE(summarizeLayerHistory(time).empty()); + EXPECT_EQ(0u, activeLayerCount()); + EXPECT_EQ(0, frequentLayerCount(time)); +} + +TEST_F(LayerHistoryIntegrationTest, oneLayerMinVote) { + auto layer = createLegacyAndFrontedEndLayer(1); + showLayer(1); + nsecs_t time = systemTime(); + updateLayerSnapshotsAndLayerHistory(time); + + setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Min); + + EXPECT_EQ(1u, layerCount()); + EXPECT_EQ(0u, activeLayerCount()); + + for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { + setBufferWithPresentTime(layer, time); + time += HI_FPS_PERIOD; + } + + ASSERT_EQ(1u, summarizeLayerHistory(time).size()); + EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote); + EXPECT_EQ(1u, activeLayerCount()); + EXPECT_EQ(1, frequentLayerCount(time)); + + // layer became inactive + time += MAX_ACTIVE_LAYER_PERIOD_NS.count(); + ASSERT_TRUE(summarizeLayerHistory(time).empty()); + EXPECT_EQ(0u, activeLayerCount()); + EXPECT_EQ(0, frequentLayerCount(time)); +} + +TEST_F(LayerHistoryIntegrationTest, oneLayerMaxVote) { + auto layer = createLegacyAndFrontedEndLayer(1); + showLayer(1); + nsecs_t time = systemTime(); + updateLayerSnapshotsAndLayerHistory(time); + + setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Max); + + EXPECT_EQ(1u, layerCount()); + EXPECT_EQ(0u, activeLayerCount()); + + for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { + setBufferWithPresentTime(layer, time); + time += LO_FPS_PERIOD; + } + + ASSERT_EQ(1u, summarizeLayerHistory(time).size()); + EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote); + EXPECT_EQ(1u, activeLayerCount()); + EXPECT_EQ(1, frequentLayerCount(time)); + + // layer became inactive + time += MAX_ACTIVE_LAYER_PERIOD_NS.count(); + ASSERT_TRUE(summarizeLayerHistory(time).empty()); + EXPECT_EQ(0u, activeLayerCount()); + EXPECT_EQ(0, frequentLayerCount(time)); +} + TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitVote) { createLegacyAndFrontedEndLayer(1); setFrameRate(1, 73.4f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT, @@ -273,6 +543,335 @@ TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitExactVote) { EXPECT_EQ(1, frequentLayerCount(time)); } +TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitExactVote2) { + auto layer = createLegacyAndFrontedEndLayer(1); + setFrameRate(1, 73.4f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, + ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS); + + EXPECT_EQ(1u, layerCount()); + EXPECT_EQ(0u, activeLayerCount()); + + nsecs_t time = systemTime(); + updateLayerSnapshotsAndLayerHistory(time); + + for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { + setBufferWithPresentTime(layer, time); + time += HI_FPS_PERIOD; + } + + ASSERT_EQ(1u, summarizeLayerHistory(time).size()); + EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple, + summarizeLayerHistory(time)[0].vote); + EXPECT_EQ(73.4_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate); + EXPECT_EQ(1u, activeLayerCount()); + EXPECT_EQ(1, frequentLayerCount(time)); + + // layer became infrequent, but the vote stays + setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic); + time += MAX_ACTIVE_LAYER_PERIOD_NS.count(); + ASSERT_EQ(1u, summarizeLayerHistory(time).size()); + EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple, + summarizeLayerHistory(time)[0].vote); + EXPECT_EQ(73.4_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate); + EXPECT_EQ(1u, activeLayerCount()); + EXPECT_EQ(0, frequentLayerCount(time)); +} + +TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitGte_vrr) { + // Set the test to be on a vrr mode. + SET_FLAG_FOR_TEST(flags::vrr_config, true); + mSelector->setActiveMode(kVrrModeId, HI_FPS); + + auto layer = createLegacyAndFrontedEndLayer(1); + showLayer(1); + setFrameRate(1, (33_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_GTE, + ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS); + setFrameRateCategory(1, 0); + + EXPECT_EQ(1u, layerCount()); + EXPECT_EQ(0u, activeLayerCount()); + + nsecs_t time = systemTime(); + for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { + setBufferWithPresentTime(layer, time); + time += HI_FPS_PERIOD; + } + + ASSERT_EQ(1u, summarizeLayerHistory(time).size()); + EXPECT_EQ(1u, activeLayerCount()); + EXPECT_EQ(1, frequentLayerCount(time)); + EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitGte, summarizeLayerHistory(time)[0].vote); + EXPECT_EQ(33_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate); + EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory); + + // layer became inactive, but the vote stays + setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic); + time += MAX_ACTIVE_LAYER_PERIOD_NS.count(); + ASSERT_EQ(1u, summarizeLayerHistory(time).size()); + EXPECT_EQ(1u, activeLayerCount()); + EXPECT_EQ(0, frequentLayerCount(time)); + EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitGte, summarizeLayerHistory(time)[0].vote); + EXPECT_EQ(33_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate); + EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory); +} + +// Test for MRR device with VRR features enabled. +TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitGte_nonVrr) { + SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, true); + // The vrr_config flag is explicitly not set false because this test for an MRR device + // should still work in a VRR-capable world. + + auto layer = createLegacyAndFrontedEndLayer(1); + showLayer(1); + setFrameRate(1, (33_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_GTE, + ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS); + setFrameRateCategory(1, 0); + + EXPECT_EQ(1u, layerCount()); + EXPECT_EQ(0u, activeLayerCount()); + + nsecs_t time = systemTime(); + for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { + setBufferWithPresentTime(layer, time); + time += HI_FPS_PERIOD; + } + + ASSERT_EQ(1u, summarizeLayerHistory(time).size()); + EXPECT_EQ(1u, activeLayerCount()); + EXPECT_EQ(1, frequentLayerCount(time)); + EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote); + EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate); + EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory); + + // layer became infrequent, but the vote stays + setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic); + time += MAX_ACTIVE_LAYER_PERIOD_NS.count(); + ASSERT_EQ(1u, summarizeLayerHistory(time).size()); + EXPECT_EQ(1u, activeLayerCount()); + EXPECT_EQ(0, frequentLayerCount(time)); + EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote); + EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate); + EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory); +} + +TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitVoteWithCategory_vrrFeatureOff) { + SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, false); + + auto layer = createLegacyAndFrontedEndLayer(1); + showLayer(1); + setFrameRate(1, (73.4_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT, + ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS); + setFrameRateCategory(1, ANATIVEWINDOW_FRAME_RATE_CATEGORY_HIGH); + + // Set default to Min so it is obvious that the vote reset triggered. + setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Min); + + EXPECT_EQ(1u, layerCount()); + EXPECT_EQ(0u, activeLayerCount()); + + nsecs_t time = systemTime(); + for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { + setBufferWithPresentTime(layer, time); + time += HI_FPS_PERIOD; + } + + // There is only 1 LayerRequirement due to the disabled flag frame_rate_category_mrr. + ASSERT_EQ(1u, summarizeLayerHistory(time).size()); + EXPECT_EQ(1u, activeLayerCount()); + EXPECT_EQ(1, frequentLayerCount(time)); + EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote); + EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate); + EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory); +} + +// This test case should be the same as oneLayerNoVote except instead of layer vote is NoVote, +// the category is NoPreference. +TEST_F(LayerHistoryIntegrationTest, oneLayerCategoryNoPreference) { + SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, true); + + auto layer = createLegacyAndFrontedEndLayer(1); + showLayer(1); + setFrameRate(1, (0_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT, + ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS); + setFrameRateCategory(1, ANATIVEWINDOW_FRAME_RATE_CATEGORY_NO_PREFERENCE); + + EXPECT_EQ(1u, layerCount()); + EXPECT_EQ(0u, activeLayerCount()); + + nsecs_t time = systemTime(); + for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { + setBufferWithPresentTime(layer, time); + time += HI_FPS_PERIOD; + } + + EXPECT_EQ(1u, summarizeLayerHistory(time).size()); + EXPECT_EQ(1u, activeLayerCount()); + EXPECT_EQ(1, frequentLayerCount(time)); + + // layer became infrequent + time += MAX_ACTIVE_LAYER_PERIOD_NS.count(); + EXPECT_EQ(1u, summarizeLayerHistory(time).size()); + EXPECT_EQ(1u, activeLayerCount()); + EXPECT_EQ(0, frequentLayerCount(time)); +} + +TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitVoteWithCategory) { + SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, true); + + auto layer = createLegacyAndFrontedEndLayer(1); + showLayer(1); + setFrameRate(1, (73.4_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT, + ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS); + setFrameRateCategory(1, ANATIVEWINDOW_FRAME_RATE_CATEGORY_HIGH); + + EXPECT_EQ(1u, layerCount()); + EXPECT_EQ(0u, activeLayerCount()); + + nsecs_t time = systemTime(); + for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { + setBufferWithPresentTime(layer, time); + time += HI_FPS_PERIOD; + } + + // There are 2 LayerRequirement's due to the frame rate category. + ASSERT_EQ(2u, summarizeLayerHistory(time).size()); + EXPECT_EQ(1u, activeLayerCount()); + EXPECT_EQ(1, frequentLayerCount(time)); + // First LayerRequirement is the layer's category specification + EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitCategory, summarizeLayerHistory(time)[0].vote); + EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate); + EXPECT_EQ(FrameRateCategory::High, summarizeLayerHistory(time)[0].frameRateCategory); + + // Second LayerRequirement is the frame rate specification + EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summarizeLayerHistory(time)[1].vote); + EXPECT_EQ(73.4_Hz, summarizeLayerHistory(time)[1].desiredRefreshRate); + EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[1].frameRateCategory); + + // layer became infrequent, but the vote stays + setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic); + time += MAX_ACTIVE_LAYER_PERIOD_NS.count(); + ASSERT_EQ(2u, summarizeLayerHistory(time).size()); + EXPECT_EQ(1u, activeLayerCount()); + EXPECT_EQ(0, frequentLayerCount(time)); + EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitCategory, summarizeLayerHistory(time)[0].vote); + EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate); + EXPECT_EQ(FrameRateCategory::High, summarizeLayerHistory(time)[0].frameRateCategory); +} + +TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitVoteWithCategoryNotVisibleDoesNotVote) { + SET_FLAG_FOR_TEST(flags::misc1, true); + + auto layer = createLegacyAndFrontedEndLayer(1); + hideLayer(1); + setFrameRate(1, (12.34_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT, + ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS); + setFrameRateCategory(1, ANATIVEWINDOW_FRAME_RATE_CATEGORY_HIGH); + + EXPECT_EQ(1u, layerCount()); + EXPECT_EQ(0u, activeLayerCount()); + + nsecs_t time = systemTime(); + for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { + setBufferWithPresentTime(layer, time); + time += HI_FPS_PERIOD; + } + + // Layer is not visible, so the layer is moved to inactive, infrequent, and it will not have + // votes to consider for refresh rate selection. + ASSERT_EQ(0u, summarizeLayerHistory(time).size()); + EXPECT_EQ(0u, activeLayerCount()); + EXPECT_EQ(0, frequentLayerCount(time)); +} + +TEST_F(LayerHistoryIntegrationTest, invisibleExplicitLayer) { + SET_FLAG_FOR_TEST(flags::misc1, false); + + auto explicitVisiblelayer = createLegacyAndFrontedEndLayer(1); + showLayer(1); + setFrameRate(1, (60_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, 0); + + auto explicitInvisiblelayer = createLegacyAndFrontedEndLayer(2); + hideLayer(2); + setFrameRate(2, (90_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, 0); + + nsecs_t time = systemTime(); + + // Post a buffer to the layers to make them active + setBuffer(1); + setBuffer(2); + updateLayerSnapshotsAndLayerHistory(time); + + EXPECT_EQ(2u, layerCount()); + ASSERT_EQ(1u, summarizeLayerHistory(time).size()); + EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple, + summarizeLayerHistory(time)[0].vote); + EXPECT_EQ(60_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate); + EXPECT_EQ(2u, activeLayerCount()); + EXPECT_EQ(2, frequentLayerCount(time)); +} + +TEST_F(LayerHistoryIntegrationTest, invisibleExplicitLayerDoesNotVote) { + SET_FLAG_FOR_TEST(flags::misc1, true); + + auto explicitVisiblelayer = createLegacyAndFrontedEndLayer(1); + showLayer(1); + setFrameRate(1, (60_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, 0); + + auto explicitInvisiblelayer = createLegacyAndFrontedEndLayer(2); + hideLayer(2); + setFrameRate(2, (90_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, 0); + + nsecs_t time = systemTime(); + + // Post a buffer to the layers to make them active + setBuffer(1); + setBuffer(2); + updateLayerSnapshotsAndLayerHistory(time); + + EXPECT_EQ(2u, layerCount()); + ASSERT_EQ(1u, summarizeLayerHistory(time).size()); + EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple, + summarizeLayerHistory(time)[0].vote); + EXPECT_EQ(60_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate); + EXPECT_EQ(1u, activeLayerCount()); + EXPECT_EQ(1, frequentLayerCount(time)); +} + +TEST_F(LayerHistoryIntegrationTest, frontBufferedLayerVotesMax) { + SET_FLAG_FOR_TEST(flags::vrr_config, true); + + auto layer = createLegacyAndFrontedEndLayer(1); + setFrontBuffer(1); + showLayer(1); + + nsecs_t time = systemTime(); + + EXPECT_EQ(1u, layerCount()); + EXPECT_EQ(0u, activeLayerCount()); + EXPECT_EQ(0, frequentLayerCount(time)); + EXPECT_EQ(0, animatingLayerCount(time)); + + // layer is active but infrequent. + for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { + setFrontBufferWithPresentTime(layer, time); + time += MAX_FREQUENT_LAYER_PERIOD_NS.count(); + } + + ASSERT_EQ(1u, summarizeLayerHistory(time).size()); + EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote); + EXPECT_EQ(1u, activeLayerCount()); + EXPECT_EQ(0, frequentLayerCount(time)); + EXPECT_EQ(0, animatingLayerCount(time)); + + // Layer still active due to front buffering, but it's infrequent. + time += MAX_ACTIVE_LAYER_PERIOD_NS.count(); + ASSERT_EQ(1u, summarizeLayerHistory(time).size()); + EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote); + EXPECT_EQ(1u, activeLayerCount()); + EXPECT_EQ(0, frequentLayerCount(time)); + EXPECT_EQ(0, animatingLayerCount(time)); +} + TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitCategory) { SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, true); @@ -293,6 +892,49 @@ TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitCategory) { EXPECT_EQ(FrameRateCategory::High, summarizeLayerHistory(time)[0].frameRateCategory); } +TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitVoteWithFixedSourceAndNoPreferenceCategory) { + SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, false); + + auto layer = createLegacyAndFrontedEndLayer(1); + setFrameRate(1, (45.6_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, + ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS); + setFrameRateCategory(1, ANATIVEWINDOW_FRAME_RATE_CATEGORY_NO_PREFERENCE); + + EXPECT_EQ(1u, layerCount()); + EXPECT_EQ(0u, activeLayerCount()); + + nsecs_t time = systemTime(); + updateLayerSnapshotsAndLayerHistory(time); + for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { + setBufferWithPresentTime(layer, time); + time += HI_FPS_PERIOD; + } + + // There are 2 LayerRequirement's due to the frame rate category. + ASSERT_EQ(2u, summarizeLayerHistory(time).size()); + EXPECT_EQ(1u, activeLayerCount()); + EXPECT_EQ(1, frequentLayerCount(time)); + // First LayerRequirement is the layer's category specification + EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitCategory, summarizeLayerHistory(time)[0].vote); + EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate); + EXPECT_EQ(FrameRateCategory::NoPreference, summarizeLayerHistory(time)[0].frameRateCategory); + + // Second LayerRequirement is the frame rate specification + EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple, + summarizeLayerHistory(time)[1].vote); + EXPECT_EQ(45.6_Hz, summarizeLayerHistory(time)[1].desiredRefreshRate); + EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[1].frameRateCategory); + + // layer became infrequent, but the vote stays + time += MAX_ACTIVE_LAYER_PERIOD_NS.count(); + ASSERT_EQ(2u, summarizeLayerHistory(time).size()); + EXPECT_EQ(1u, activeLayerCount()); + EXPECT_EQ(0, frequentLayerCount(time)); + EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitCategory, summarizeLayerHistory(time)[0].vote); + EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate); + EXPECT_EQ(FrameRateCategory::NoPreference, summarizeLayerHistory(time)[0].frameRateCategory); +} + TEST_F(LayerHistoryIntegrationTest, multipleLayers) { auto layer1 = createLegacyAndFrontedEndLayer(1); auto layer2 = createLegacyAndFrontedEndLayer(2); @@ -805,7 +1447,15 @@ TEST_F(LayerHistoryIntegrationTest, smallDirtyLayer) { // layer is active but infrequent. for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { - auto props = layer->getLayerProps(); + scheduler::LayerProps props = { + .visible = false, + .bounds = {0, 0, 100, 100}, + .transform = {}, + .setFrameRateVote = {}, + .frameRateSelectionPriority = Layer::PRIORITY_UNSET, + .isSmallDirty = false, + .isFrontBuffered = false, + }; if (i % 3 == 0) { props.isSmallDirty = false; } else { @@ -838,8 +1488,15 @@ TEST_F(LayerHistoryIntegrationTest, DISABLED_smallDirtyInMultiLayer) { // uiLayer is updating small dirty. for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE + FREQUENT_LAYER_WINDOW_SIZE + 1; i++) { - auto props = uiLayer->getLayerProps(); - props.isSmallDirty = true; + scheduler::LayerProps props = { + .visible = false, + .bounds = {0, 0, 100, 100}, + .transform = {}, + .setFrameRateVote = {}, + .frameRateSelectionPriority = Layer::PRIORITY_UNSET, + .isSmallDirty = true, + .isFrontBuffered = false, + }; setBuffer(1); uiLayer->setDesiredPresentTime(0, false /*autotimestamp*/); updateLayerSnapshotsAndLayerHistory(time); diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp deleted file mode 100644 index 088d0d233c..0000000000 --- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp +++ /dev/null @@ -1,1573 +0,0 @@ -/* - * Copyright 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wextra" - -#undef LOG_TAG -#define LOG_TAG "LayerHistoryTest" - -#include <Layer.h> -#include <com_android_graphics_surfaceflinger_flags.h> -#include <gmock/gmock.h> -#include <gtest/gtest.h> -#include <log/log.h> - -#include <common/test/FlagUtils.h> -#include "FpsOps.h" -#include "Scheduler/LayerHistory.h" -#include "Scheduler/LayerInfo.h" -#include "TestableScheduler.h" -#include "TestableSurfaceFlinger.h" -#include "mock/DisplayHardware/MockDisplayMode.h" -#include "mock/MockLayer.h" -#include "mock/MockSchedulerCallback.h" - -using testing::_; -using testing::Return; -using testing::ReturnRef; - -namespace android::scheduler { - -using MockLayer = android::mock::MockLayer; - -using android::mock::createDisplayMode; -using android::mock::createVrrDisplayMode; - -// WARNING: LEGACY TESTS FOR LEGACY FRONT END -// Update LayerHistoryIntegrationTest instead -class LayerHistoryTest : public testing::Test { -protected: - static constexpr auto PRESENT_TIME_HISTORY_SIZE = LayerInfo::HISTORY_SIZE; - static constexpr auto MAX_FREQUENT_LAYER_PERIOD_NS = LayerInfo::kMaxPeriodForFrequentLayerNs; - static constexpr auto FREQUENT_LAYER_WINDOW_SIZE = LayerInfo::kFrequentLayerWindowSize; - static constexpr auto PRESENT_TIME_HISTORY_DURATION = LayerInfo::HISTORY_DURATION; - - static constexpr Fps LO_FPS = 30_Hz; - static constexpr auto LO_FPS_PERIOD = LO_FPS.getPeriodNsecs(); - - static constexpr Fps HI_FPS = 90_Hz; - static constexpr auto HI_FPS_PERIOD = HI_FPS.getPeriodNsecs(); - - LayerHistoryTest() { mFlinger.resetScheduler(mScheduler); } - - LayerHistory& history() { return mScheduler->mutableLayerHistory(); } - const LayerHistory& history() const { return mScheduler->mutableLayerHistory(); } - - LayerHistory::Summary summarizeLayerHistory(nsecs_t now) { - // LayerHistory::summarize makes no guarantee of the order of the elements in the summary - // however, for testing only, a stable order is required, therefore we sort the list here. - // Any tests requiring ordered results must create layers with names. - auto summary = history().summarize(*mScheduler->refreshRateSelector(), now); - std::sort(summary.begin(), summary.end(), - [](const RefreshRateSelector::LayerRequirement& lhs, - const RefreshRateSelector::LayerRequirement& rhs) -> bool { - return lhs.name < rhs.name; - }); - return summary; - } - - size_t layerCount() const { return mScheduler->layerHistorySize(); } - size_t activeLayerCount() const NO_THREAD_SAFETY_ANALYSIS { - return history().mActiveLayerInfos.size(); - } - - auto frequentLayerCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS { - const auto& infos = history().mActiveLayerInfos; - return std::count_if(infos.begin(), infos.end(), [now](const auto& pair) { - return pair.second.second->isFrequent(now).isFrequent; - }); - } - - auto animatingLayerCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS { - const auto& infos = history().mActiveLayerInfos; - return std::count_if(infos.begin(), infos.end(), [now](const auto& pair) { - return pair.second.second->isAnimating(now); - }); - } - - auto clearLayerHistoryCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS { - const auto& infos = history().mActiveLayerInfos; - return std::count_if(infos.begin(), infos.end(), [now](const auto& pair) { - return pair.second.second->isFrequent(now).clearHistory; - }); - } - - void setDefaultLayerVote(Layer* layer, - LayerHistory::LayerVoteType vote) NO_THREAD_SAFETY_ANALYSIS { - auto [found, layerPair] = history().findLayer(layer->getSequence()); - if (found != LayerHistory::LayerStatus::NotFound) { - layerPair->second->setDefaultLayerVote(vote); - } - } - - auto createLayer() { return sp<MockLayer>::make(mFlinger.flinger()); } - auto createLayer(std::string name) { - return sp<MockLayer>::make(mFlinger.flinger(), std::move(name)); - } - auto createLayer(std::string name, uint32_t uid) { - return sp<MockLayer>::make(mFlinger.flinger(), std::move(name), std::move(uid)); - } - - void recordFramesAndExpect(const sp<MockLayer>& layer, nsecs_t& time, Fps frameRate, - Fps desiredRefreshRate, int numFrames) { - LayerHistory::Summary summary; - for (int i = 0; i < numFrames; i++) { - history().record(layer->getSequence(), layer->getLayerProps(), time, time, - LayerHistory::LayerUpdateType::Buffer); - time += frameRate.getPeriodNsecs(); - - summary = summarizeLayerHistory(time); - } - - ASSERT_EQ(1, summary.size()); - ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote); - ASSERT_EQ(desiredRefreshRate, summary[0].desiredRefreshRate); - } - - static constexpr auto kVrrModeId = DisplayModeId(2); - std::shared_ptr<RefreshRateSelector> mSelector = std::make_shared<RefreshRateSelector>( - makeModes(createDisplayMode(DisplayModeId(0), LO_FPS), - createDisplayMode(DisplayModeId(1), HI_FPS), - createVrrDisplayMode(kVrrModeId, HI_FPS, - hal::VrrConfig{.minFrameIntervalNs = - HI_FPS.getPeriodNsecs()})), - DisplayModeId(0)); - - mock::SchedulerCallback mSchedulerCallback; - TestableSurfaceFlinger mFlinger; - TestableScheduler* mScheduler = new TestableScheduler(mSelector, mFlinger, mSchedulerCallback); -}; - -namespace { - -using namespace com::android::graphics::surfaceflinger; - -TEST_F(LayerHistoryTest, singleLayerNoVoteDefaultCompatibility) { - const auto layer = createLayer(); - EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true)); - EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate())); - EXPECT_CALL(*layer, getDefaultFrameRateCompatibility()) - .WillOnce(Return(FrameRateCompatibility::NoVote)); - - EXPECT_EQ(1, layerCount()); - EXPECT_EQ(0, activeLayerCount()); - - nsecs_t time = systemTime(); - - // No layers returned if no layers are active. - EXPECT_TRUE(summarizeLayerHistory(time).empty()); - EXPECT_EQ(0, activeLayerCount()); - - history().record(layer->getSequence(), layer->getLayerProps(), 0, time, - LayerHistory::LayerUpdateType::Buffer); - history().setDefaultFrameRateCompatibility(layer->getSequence(), - - layer->getDefaultFrameRateCompatibility(), - true /* contentDetectionEnabled */); - - EXPECT_TRUE(summarizeLayerHistory(time).empty()); - EXPECT_EQ(1, activeLayerCount()); -} - -TEST_F(LayerHistoryTest, singleLayerMinVoteDefaultCompatibility) { - const auto layer = createLayer(); - EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true)); - EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate())); - EXPECT_CALL(*layer, getDefaultFrameRateCompatibility()) - .WillOnce(Return(FrameRateCompatibility::Min)); - - EXPECT_EQ(1, layerCount()); - EXPECT_EQ(0, activeLayerCount()); - - nsecs_t time = systemTime(); - - EXPECT_TRUE(summarizeLayerHistory(time).empty()); - EXPECT_EQ(0, activeLayerCount()); - - history().record(layer->getSequence(), layer->getLayerProps(), 0, time, - LayerHistory::LayerUpdateType::Buffer); - history().setDefaultFrameRateCompatibility(layer->getSequence(), - layer->getDefaultFrameRateCompatibility(), - true /* contentDetectionEnabled */); - - auto summary = summarizeLayerHistory(time); - ASSERT_EQ(1, summarizeLayerHistory(time).size()); - - EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote); - EXPECT_EQ(1, activeLayerCount()); -} - -TEST_F(LayerHistoryTest, oneLayer) { - const auto layer = createLayer(); - EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true)); - EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate())); - - // history().registerLayer(layer, LayerHistory::LayerVoteType::Max); - - EXPECT_EQ(1, layerCount()); - EXPECT_EQ(0, activeLayerCount()); - - nsecs_t time = systemTime(); - - // No layers returned if no layers are active. - EXPECT_TRUE(summarizeLayerHistory(time).empty()); - EXPECT_EQ(0, activeLayerCount()); - - // Max returned if active layers have insufficient history. - for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) { - history().record(layer->getSequence(), layer->getLayerProps(), 0, time, - LayerHistory::LayerUpdateType::Buffer); - ASSERT_EQ(1, summarizeLayerHistory(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote); - EXPECT_EQ(1, activeLayerCount()); - time += LO_FPS_PERIOD; - } - - // Max is returned since we have enough history but there is no timestamp votes. - for (int i = 0; i < 10; i++) { - history().record(layer->getSequence(), layer->getLayerProps(), 0, time, - LayerHistory::LayerUpdateType::Buffer); - ASSERT_EQ(1, summarizeLayerHistory(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote); - EXPECT_EQ(1, activeLayerCount()); - time += LO_FPS_PERIOD; - } -} - -TEST_F(LayerHistoryTest, gameFrameRateOverrideMapping) { - SET_FLAG_FOR_TEST(flags::game_default_frame_rate, true); - - history().updateGameDefaultFrameRateOverride(FrameRateOverride({0, 60.0f})); - - auto overridePair = history().getGameFrameRateOverride(0); - EXPECT_EQ(0_Hz, overridePair.first); - EXPECT_EQ(60_Hz, overridePair.second); - - history().updateGameModeFrameRateOverride(FrameRateOverride({0, 40.0f})); - history().updateGameModeFrameRateOverride(FrameRateOverride({1, 120.0f})); - - overridePair = history().getGameFrameRateOverride(0); - EXPECT_EQ(40_Hz, overridePair.first); - EXPECT_EQ(60_Hz, overridePair.second); - - overridePair = history().getGameFrameRateOverride(1); - EXPECT_EQ(120_Hz, overridePair.first); - EXPECT_EQ(0_Hz, overridePair.second); - - history().updateGameDefaultFrameRateOverride(FrameRateOverride({0, 0.0f})); - history().updateGameModeFrameRateOverride(FrameRateOverride({1, 0.0f})); - - overridePair = history().getGameFrameRateOverride(0); - EXPECT_EQ(40_Hz, overridePair.first); - EXPECT_EQ(0_Hz, overridePair.second); - - overridePair = history().getGameFrameRateOverride(1); - EXPECT_EQ(0_Hz, overridePair.first); - EXPECT_EQ(0_Hz, overridePair.second); -} - -TEST_F(LayerHistoryTest, oneLayerGameFrameRateOverride) { - SET_FLAG_FOR_TEST(flags::game_default_frame_rate, true); - - const uid_t uid = 0; - const Fps gameDefaultFrameRate = Fps::fromValue(30.0f); - const Fps gameModeFrameRate = Fps::fromValue(60.0f); - const auto layer = createLayer("GameFrameRateLayer", uid); - EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true)); - EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate())); - EXPECT_CALL(*layer, getOwnerUid()).WillRepeatedly(Return(uid)); - - EXPECT_EQ(1, layerCount()); - EXPECT_EQ(0, activeLayerCount()); - - // update game default frame rate override - history().updateGameDefaultFrameRateOverride( - FrameRateOverride({uid, gameDefaultFrameRate.getValue()})); - - nsecs_t time = systemTime(); - LayerHistory::Summary summary; - for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { - history().record(layer->getSequence(), layer->getLayerProps(), time, time, - LayerHistory::LayerUpdateType::Buffer); - time += gameDefaultFrameRate.getPeriodNsecs(); - - summary = summarizeLayerHistory(time); - } - - ASSERT_EQ(1, summary.size()); - ASSERT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summary[0].vote); - ASSERT_EQ(30.0_Hz, summary[0].desiredRefreshRate); - - // test against setFrameRate vote - const Fps setFrameRate = Fps::fromValue(120.0f); - EXPECT_CALL(*layer, getFrameRateForLayerTree()) - .WillRepeatedly( - Return(Layer::FrameRate(setFrameRate, Layer::FrameRateCompatibility::Default))); - - for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { - history().record(layer->getSequence(), layer->getLayerProps(), time, time, - LayerHistory::LayerUpdateType::Buffer); - time += setFrameRate.getPeriodNsecs(); - - summary = summarizeLayerHistory(time); - } - - ASSERT_EQ(1, summary.size()); - ASSERT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summary[0].vote); - ASSERT_EQ(120.0_Hz, summary[0].desiredRefreshRate); - - // update game mode frame rate override - history().updateGameModeFrameRateOverride( - FrameRateOverride({uid, gameModeFrameRate.getValue()})); - - for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { - history().record(layer->getSequence(), layer->getLayerProps(), time, time, - LayerHistory::LayerUpdateType::Buffer); - time += gameModeFrameRate.getPeriodNsecs(); - - summary = summarizeLayerHistory(time); - } - - ASSERT_EQ(1, summary.size()); - ASSERT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summary[0].vote); - ASSERT_EQ(60.0_Hz, summary[0].desiredRefreshRate); -} - -TEST_F(LayerHistoryTest, oneInvisibleLayer) { - const auto layer = createLayer(); - EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true)); - EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate())); - - EXPECT_EQ(1, layerCount()); - EXPECT_EQ(0, activeLayerCount()); - - nsecs_t time = systemTime(); - - history().record(layer->getSequence(), layer->getLayerProps(), 0, time, - LayerHistory::LayerUpdateType::Buffer); - auto summary = summarizeLayerHistory(time); - ASSERT_EQ(1, summarizeLayerHistory(time).size()); - // Layer is still considered inactive so we expect to get Min - EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote); - EXPECT_EQ(1, activeLayerCount()); - - EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(false)); - history().record(layer->getSequence(), layer->getLayerProps(), 0, time, - LayerHistory::LayerUpdateType::Buffer); - - summary = summarizeLayerHistory(time); - EXPECT_TRUE(summarizeLayerHistory(time).empty()); - EXPECT_EQ(0, activeLayerCount()); -} - -TEST_F(LayerHistoryTest, explicitTimestamp) { - const auto layer = createLayer(); - EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true)); - EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate())); - - EXPECT_EQ(1, layerCount()); - EXPECT_EQ(0, activeLayerCount()); - - nsecs_t time = systemTime(); - for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { - history().record(layer->getSequence(), layer->getLayerProps(), time, time, - LayerHistory::LayerUpdateType::Buffer); - time += LO_FPS_PERIOD; - } - - ASSERT_EQ(1, summarizeLayerHistory(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summarizeLayerHistory(time)[0].vote); - EXPECT_EQ(LO_FPS, summarizeLayerHistory(time)[0].desiredRefreshRate); - EXPECT_EQ(1, activeLayerCount()); - EXPECT_EQ(1, frequentLayerCount(time)); -} - -TEST_F(LayerHistoryTest, oneLayerNoVote) { - const auto layer = createLayer(); - EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true)); - EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate())); - - setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::NoVote); - - EXPECT_EQ(1, layerCount()); - EXPECT_EQ(0, activeLayerCount()); - - nsecs_t time = systemTime(); - for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { - history().record(layer->getSequence(), layer->getLayerProps(), time, time, - LayerHistory::LayerUpdateType::Buffer); - time += HI_FPS_PERIOD; - } - - ASSERT_TRUE(summarizeLayerHistory(time).empty()); - EXPECT_EQ(1, activeLayerCount()); - EXPECT_EQ(1, frequentLayerCount(time)); - - // layer became inactive - time += MAX_ACTIVE_LAYER_PERIOD_NS.count(); - ASSERT_TRUE(summarizeLayerHistory(time).empty()); - EXPECT_EQ(0, activeLayerCount()); - EXPECT_EQ(0, frequentLayerCount(time)); -} - -TEST_F(LayerHistoryTest, oneLayerMinVote) { - const auto layer = createLayer(); - EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true)); - EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate())); - - setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Min); - - EXPECT_EQ(1, layerCount()); - EXPECT_EQ(0, activeLayerCount()); - - nsecs_t time = systemTime(); - for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { - history().record(layer->getSequence(), layer->getLayerProps(), time, time, - LayerHistory::LayerUpdateType::Buffer); - time += HI_FPS_PERIOD; - } - - ASSERT_EQ(1, summarizeLayerHistory(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote); - EXPECT_EQ(1, activeLayerCount()); - EXPECT_EQ(1, frequentLayerCount(time)); - - // layer became inactive - time += MAX_ACTIVE_LAYER_PERIOD_NS.count(); - ASSERT_TRUE(summarizeLayerHistory(time).empty()); - EXPECT_EQ(0, activeLayerCount()); - EXPECT_EQ(0, frequentLayerCount(time)); -} - -TEST_F(LayerHistoryTest, oneLayerMaxVote) { - const auto layer = createLayer(); - EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true)); - EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate())); - - setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Max); - - EXPECT_EQ(1, layerCount()); - EXPECT_EQ(0, activeLayerCount()); - - nsecs_t time = systemTime(); - for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { - history().record(layer->getSequence(), layer->getLayerProps(), time, time, - LayerHistory::LayerUpdateType::Buffer); - time += LO_FPS_PERIOD; - } - - ASSERT_EQ(1, summarizeLayerHistory(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote); - EXPECT_EQ(1, activeLayerCount()); - EXPECT_EQ(1, frequentLayerCount(time)); - - // layer became inactive - time += MAX_ACTIVE_LAYER_PERIOD_NS.count(); - ASSERT_TRUE(summarizeLayerHistory(time).empty()); - EXPECT_EQ(0, activeLayerCount()); - EXPECT_EQ(0, frequentLayerCount(time)); -} - -TEST_F(LayerHistoryTest, oneLayerExplicitVote) { - auto layer = createLayer(); - EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true)); - EXPECT_CALL(*layer, getFrameRateForLayerTree()) - .WillRepeatedly( - Return(Layer::FrameRate(73.4_Hz, Layer::FrameRateCompatibility::Default))); - - EXPECT_EQ(1, layerCount()); - EXPECT_EQ(0, activeLayerCount()); - - nsecs_t time = systemTime(); - for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { - history().record(layer->getSequence(), layer->getLayerProps(), time, time, - LayerHistory::LayerUpdateType::Buffer); - time += HI_FPS_PERIOD; - } - - ASSERT_EQ(1, summarizeLayerHistory(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summarizeLayerHistory(time)[0].vote); - EXPECT_EQ(73.4_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate); - EXPECT_EQ(1, activeLayerCount()); - EXPECT_EQ(1, frequentLayerCount(time)); - - // layer became infrequent, but the vote stays - setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic); - time += MAX_ACTIVE_LAYER_PERIOD_NS.count(); - ASSERT_EQ(1, summarizeLayerHistory(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summarizeLayerHistory(time)[0].vote); - EXPECT_EQ(73.4_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate); - EXPECT_EQ(1, activeLayerCount()); - EXPECT_EQ(0, frequentLayerCount(time)); -} - -TEST_F(LayerHistoryTest, oneLayerExplicitExactVote) { - auto layer = createLayer(); - EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true)); - EXPECT_CALL(*layer, getFrameRateForLayerTree()) - .WillRepeatedly(Return( - Layer::FrameRate(73.4_Hz, Layer::FrameRateCompatibility::ExactOrMultiple))); - - EXPECT_EQ(1, layerCount()); - EXPECT_EQ(0, activeLayerCount()); - - nsecs_t time = systemTime(); - for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { - history().record(layer->getSequence(), layer->getLayerProps(), time, time, - LayerHistory::LayerUpdateType::Buffer); - time += HI_FPS_PERIOD; - } - - ASSERT_EQ(1, summarizeLayerHistory(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple, - summarizeLayerHistory(time)[0].vote); - EXPECT_EQ(73.4_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate); - EXPECT_EQ(1, activeLayerCount()); - EXPECT_EQ(1, frequentLayerCount(time)); - - // layer became infrequent, but the vote stays - setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic); - time += MAX_ACTIVE_LAYER_PERIOD_NS.count(); - ASSERT_EQ(1, summarizeLayerHistory(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple, - summarizeLayerHistory(time)[0].vote); - EXPECT_EQ(73.4_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate); - EXPECT_EQ(1, activeLayerCount()); - EXPECT_EQ(0, frequentLayerCount(time)); -} - -TEST_F(LayerHistoryTest, oneLayerExplicitGte_vrr) { - // Set the test to be on a vrr mode. - SET_FLAG_FOR_TEST(flags::vrr_config, true); - mSelector->setActiveMode(kVrrModeId, HI_FPS); - - auto layer = createLayer(); - EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true)); - EXPECT_CALL(*layer, getFrameRateForLayerTree()) - .WillRepeatedly(Return(Layer::FrameRate(33_Hz, Layer::FrameRateCompatibility::Gte, - Seamlessness::OnlySeamless, - FrameRateCategory::Default))); - - EXPECT_EQ(1, layerCount()); - EXPECT_EQ(0, activeLayerCount()); - - nsecs_t time = systemTime(); - for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { - history().record(layer->getSequence(), layer->getLayerProps(), time, time, - LayerHistory::LayerUpdateType::Buffer); - time += HI_FPS_PERIOD; - } - - ASSERT_EQ(1, summarizeLayerHistory(time).size()); - EXPECT_EQ(1, activeLayerCount()); - EXPECT_EQ(1, frequentLayerCount(time)); - EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitGte, summarizeLayerHistory(time)[0].vote); - EXPECT_EQ(33_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate); - EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory); - - // layer became inactive, but the vote stays - setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic); - time += MAX_ACTIVE_LAYER_PERIOD_NS.count(); - ASSERT_EQ(1, summarizeLayerHistory(time).size()); - EXPECT_EQ(1, activeLayerCount()); - EXPECT_EQ(0, frequentLayerCount(time)); - EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitGte, summarizeLayerHistory(time)[0].vote); - EXPECT_EQ(33_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate); - EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory); -} - -// Test for MRR device with VRR features enabled. -TEST_F(LayerHistoryTest, oneLayerExplicitGte_nonVrr) { - SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, true); - // The vrr_config flag is explicitly not set false because this test for an MRR device - // should still work in a VRR-capable world. - - auto layer = createLayer(); - EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true)); - EXPECT_CALL(*layer, getFrameRateForLayerTree()) - .WillRepeatedly(Return(Layer::FrameRate(33_Hz, Layer::FrameRateCompatibility::Gte, - Seamlessness::OnlySeamless, - FrameRateCategory::Default))); - - EXPECT_EQ(1, layerCount()); - EXPECT_EQ(0, activeLayerCount()); - - nsecs_t time = systemTime(); - for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { - history().record(layer->getSequence(), layer->getLayerProps(), time, time, - LayerHistory::LayerUpdateType::Buffer); - time += HI_FPS_PERIOD; - } - - ASSERT_EQ(1, summarizeLayerHistory(time).size()); - EXPECT_EQ(1, activeLayerCount()); - EXPECT_EQ(1, frequentLayerCount(time)); - EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote); - EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate); - EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory); - - // layer became infrequent, but the vote stays - setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic); - time += MAX_ACTIVE_LAYER_PERIOD_NS.count(); - ASSERT_EQ(1, summarizeLayerHistory(time).size()); - EXPECT_EQ(1, activeLayerCount()); - EXPECT_EQ(0, frequentLayerCount(time)); - EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote); - EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate); - EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory); -} - -TEST_F(LayerHistoryTest, oneLayerExplicitVoteWithCategory_vrrFeatureOff) { - SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, false); - - auto layer = createLayer(); - EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true)); - EXPECT_CALL(*layer, getFrameRateForLayerTree()) - .WillRepeatedly( - Return(Layer::FrameRate(73.4_Hz, Layer::FrameRateCompatibility::Default, - Seamlessness::OnlySeamless, FrameRateCategory::High))); - - // Set default to Min so it is obvious that the vote reset triggered. - setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Min); - - EXPECT_EQ(1, layerCount()); - EXPECT_EQ(0, activeLayerCount()); - - nsecs_t time = systemTime(); - for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { - history().record(layer->getSequence(), layer->getLayerProps(), time, time, - LayerHistory::LayerUpdateType::Buffer); - time += HI_FPS_PERIOD; - } - - // There is only 1 LayerRequirement due to the disabled flag frame_rate_category_mrr. - ASSERT_EQ(1, summarizeLayerHistory(time).size()); - EXPECT_EQ(1, activeLayerCount()); - EXPECT_EQ(1, frequentLayerCount(time)); - EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote); - EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate); - EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory); -} - -TEST_F(LayerHistoryTest, oneLayerExplicitCategory) { - SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, true); - - auto layer = createLayer(); - EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true)); - EXPECT_CALL(*layer, getFrameRateForLayerTree()) - .WillRepeatedly( - Return(Layer::FrameRate(0_Hz, Layer::FrameRateCompatibility::Default, - Seamlessness::OnlySeamless, FrameRateCategory::High))); - - EXPECT_EQ(1, layerCount()); - EXPECT_EQ(0, activeLayerCount()); - - nsecs_t time = systemTime(); - for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { - history().record(layer->getSequence(), layer->getLayerProps(), time, time, - LayerHistory::LayerUpdateType::Buffer); - time += HI_FPS_PERIOD; - } - - ASSERT_EQ(1, summarizeLayerHistory(time).size()); - EXPECT_EQ(1, activeLayerCount()); - EXPECT_EQ(1, frequentLayerCount(time)); - // First LayerRequirement is the frame rate specification - EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitCategory, summarizeLayerHistory(time)[0].vote); - EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate); - EXPECT_EQ(FrameRateCategory::High, summarizeLayerHistory(time)[0].frameRateCategory); - - // layer became infrequent, but the vote stays - setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic); - time += MAX_ACTIVE_LAYER_PERIOD_NS.count(); - ASSERT_EQ(1, summarizeLayerHistory(time).size()); - EXPECT_EQ(1, activeLayerCount()); - EXPECT_EQ(0, frequentLayerCount(time)); - EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitCategory, summarizeLayerHistory(time)[0].vote); - EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate); - EXPECT_EQ(FrameRateCategory::High, summarizeLayerHistory(time)[0].frameRateCategory); -} - -// This test case should be the same as oneLayerNoVote except instead of layer vote is NoVote, -// the category is NoPreference. -TEST_F(LayerHistoryTest, oneLayerCategoryNoPreference) { - SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, true); - - auto layer = createLayer(); - EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true)); - EXPECT_CALL(*layer, getFrameRateForLayerTree()) - .WillRepeatedly(Return(Layer::FrameRate(0_Hz, Layer::FrameRateCompatibility::Default, - Seamlessness::OnlySeamless, - FrameRateCategory::NoPreference))); - - EXPECT_EQ(1, layerCount()); - EXPECT_EQ(0, activeLayerCount()); - - nsecs_t time = systemTime(); - for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { - history().record(layer->getSequence(), layer->getLayerProps(), time, time, - LayerHistory::LayerUpdateType::Buffer); - time += HI_FPS_PERIOD; - } - - EXPECT_EQ(1, summarizeLayerHistory(time).size()); - EXPECT_EQ(1, activeLayerCount()); - EXPECT_EQ(1, frequentLayerCount(time)); - - // layer became infrequent - time += MAX_ACTIVE_LAYER_PERIOD_NS.count(); - EXPECT_EQ(1, summarizeLayerHistory(time).size()); - EXPECT_EQ(1, activeLayerCount()); - EXPECT_EQ(0, frequentLayerCount(time)); -} - -TEST_F(LayerHistoryTest, oneLayerExplicitVoteWithCategory) { - SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, true); - - auto layer = createLayer(); - EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true)); - EXPECT_CALL(*layer, getFrameRateForLayerTree()) - .WillRepeatedly( - Return(Layer::FrameRate(73.4_Hz, Layer::FrameRateCompatibility::Default, - Seamlessness::OnlySeamless, FrameRateCategory::High))); - - EXPECT_EQ(1, layerCount()); - EXPECT_EQ(0, activeLayerCount()); - - nsecs_t time = systemTime(); - for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { - history().record(layer->getSequence(), layer->getLayerProps(), time, time, - LayerHistory::LayerUpdateType::Buffer); - time += HI_FPS_PERIOD; - } - - // There are 2 LayerRequirement's due to the frame rate category. - ASSERT_EQ(2, summarizeLayerHistory(time).size()); - EXPECT_EQ(1, activeLayerCount()); - EXPECT_EQ(1, frequentLayerCount(time)); - // First LayerRequirement is the layer's category specification - EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitCategory, summarizeLayerHistory(time)[0].vote); - EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate); - EXPECT_EQ(FrameRateCategory::High, summarizeLayerHistory(time)[0].frameRateCategory); - - // Second LayerRequirement is the frame rate specification - EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summarizeLayerHistory(time)[1].vote); - EXPECT_EQ(73.4_Hz, summarizeLayerHistory(time)[1].desiredRefreshRate); - EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[1].frameRateCategory); - - // layer became infrequent, but the vote stays - setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic); - time += MAX_ACTIVE_LAYER_PERIOD_NS.count(); - ASSERT_EQ(2, summarizeLayerHistory(time).size()); - EXPECT_EQ(1, activeLayerCount()); - EXPECT_EQ(0, frequentLayerCount(time)); - EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitCategory, summarizeLayerHistory(time)[0].vote); - EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate); - EXPECT_EQ(FrameRateCategory::High, summarizeLayerHistory(time)[0].frameRateCategory); -} - -TEST_F(LayerHistoryTest, oneLayerExplicitVoteWithCategoryNotVisibleDoesNotVote) { - SET_FLAG_FOR_TEST(flags::misc1, true); - - auto layer = createLayer(); - EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(false)); - EXPECT_CALL(*layer, getFrameRateForLayerTree()) - .WillRepeatedly( - Return(Layer::FrameRate(12.34_Hz, Layer::FrameRateCompatibility::Default, - Seamlessness::OnlySeamless, FrameRateCategory::High))); - - EXPECT_EQ(1, layerCount()); - EXPECT_EQ(0, activeLayerCount()); - - nsecs_t time = systemTime(); - for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { - history().record(layer->getSequence(), layer->getLayerProps(), time, time, - LayerHistory::LayerUpdateType::Buffer); - time += HI_FPS_PERIOD; - } - - // Layer is not visible, so the layer is moved to inactive, infrequent, and it will not have - // votes to consider for refresh rate selection. - ASSERT_EQ(0, summarizeLayerHistory(time).size()); - EXPECT_EQ(0, activeLayerCount()); - EXPECT_EQ(0, frequentLayerCount(time)); -} - -TEST_F(LayerHistoryTest, multipleLayers) { - auto layer1 = createLayer("A"); - auto layer2 = createLayer("B"); - auto layer3 = createLayer("C"); - - EXPECT_CALL(*layer1, isVisible()).WillRepeatedly(Return(true)); - EXPECT_CALL(*layer1, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate())); - - EXPECT_CALL(*layer2, isVisible()).WillRepeatedly(Return(true)); - EXPECT_CALL(*layer2, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate())); - - EXPECT_CALL(*layer3, isVisible()).WillRepeatedly(Return(true)); - EXPECT_CALL(*layer3, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate())); - - nsecs_t time = systemTime(); - - EXPECT_EQ(3, layerCount()); - EXPECT_EQ(0, activeLayerCount()); - EXPECT_EQ(0, frequentLayerCount(time)); - - LayerHistory::Summary summary; - - // layer1 is active but infrequent. - for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { - history().record(layer1->getSequence(), layer1->getLayerProps(), time, time, - LayerHistory::LayerUpdateType::Buffer); - time += MAX_FREQUENT_LAYER_PERIOD_NS.count(); - summary = summarizeLayerHistory(time); - } - - ASSERT_EQ(1, summary.size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Min, summary[0].vote); - EXPECT_EQ(1, activeLayerCount()); - EXPECT_EQ(0, frequentLayerCount(time)); - - // layer2 is frequent and has high refresh rate. - for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { - history().record(layer2->getSequence(), layer2->getLayerProps(), time, time, - LayerHistory::LayerUpdateType::Buffer); - time += HI_FPS_PERIOD; - summary = summarizeLayerHistory(time); - } - - // layer1 is still active but infrequent. - history().record(layer1->getSequence(), layer1->getLayerProps(), time, time, - LayerHistory::LayerUpdateType::Buffer); - - ASSERT_EQ(2, summary.size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Min, summary[0].vote); - ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote); - EXPECT_EQ(HI_FPS, summarizeLayerHistory(time)[1].desiredRefreshRate); - - EXPECT_EQ(2, activeLayerCount()); - EXPECT_EQ(1, frequentLayerCount(time)); - - // layer1 is no longer active. - // layer2 is frequent and has low refresh rate. - for (int i = 0; i < 2 * PRESENT_TIME_HISTORY_SIZE; i++) { - history().record(layer2->getSequence(), layer2->getLayerProps(), time, time, - LayerHistory::LayerUpdateType::Buffer); - time += LO_FPS_PERIOD; - summary = summarizeLayerHistory(time); - } - - ASSERT_EQ(1, summary.size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote); - EXPECT_EQ(LO_FPS, summary[0].desiredRefreshRate); - EXPECT_EQ(1, activeLayerCount()); - EXPECT_EQ(1, frequentLayerCount(time)); - - // layer2 still has low refresh rate. - // layer3 has high refresh rate but not enough history. - constexpr int RATIO = LO_FPS_PERIOD / HI_FPS_PERIOD; - for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) { - if (i % RATIO == 0) { - history().record(layer2->getSequence(), layer2->getLayerProps(), time, time, - LayerHistory::LayerUpdateType::Buffer); - } - - history().record(layer3->getSequence(), layer3->getLayerProps(), time, time, - LayerHistory::LayerUpdateType::Buffer); - time += HI_FPS_PERIOD; - summary = summarizeLayerHistory(time); - } - - ASSERT_EQ(2, summary.size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote); - EXPECT_EQ(LO_FPS, summary[0].desiredRefreshRate); - EXPECT_EQ(LayerHistory::LayerVoteType::Max, summary[1].vote); - EXPECT_EQ(2, activeLayerCount()); - EXPECT_EQ(2, frequentLayerCount(time)); - - // layer3 becomes recently active. - history().record(layer3->getSequence(), layer3->getLayerProps(), time, time, - LayerHistory::LayerUpdateType::Buffer); - summary = summarizeLayerHistory(time); - ASSERT_EQ(2, summary.size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote); - EXPECT_EQ(LO_FPS, summary[0].desiredRefreshRate); - EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote); - EXPECT_EQ(HI_FPS, summary[1].desiredRefreshRate); - EXPECT_EQ(2, activeLayerCount()); - EXPECT_EQ(2, frequentLayerCount(time)); - - // layer1 expires. - layer1.clear(); - summary = summarizeLayerHistory(time); - ASSERT_EQ(2, summary.size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote); - EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote); - EXPECT_EQ(LO_FPS, summary[0].desiredRefreshRate); - EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote); - EXPECT_EQ(HI_FPS, summary[1].desiredRefreshRate); - EXPECT_EQ(2, layerCount()); - EXPECT_EQ(2, activeLayerCount()); - EXPECT_EQ(2, frequentLayerCount(time)); - - // layer2 still has low refresh rate. - // layer3 becomes inactive. - for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { - history().record(layer2->getSequence(), layer2->getLayerProps(), time, time, - LayerHistory::LayerUpdateType::Buffer); - time += LO_FPS_PERIOD; - summary = summarizeLayerHistory(time); - } - - ASSERT_EQ(1, summary.size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote); - EXPECT_EQ(LO_FPS, summary[0].desiredRefreshRate); - EXPECT_EQ(1, activeLayerCount()); - EXPECT_EQ(1, frequentLayerCount(time)); - - // layer2 expires. - layer2.clear(); - summary = summarizeLayerHistory(time); - EXPECT_TRUE(summary.empty()); - EXPECT_EQ(1, layerCount()); - EXPECT_EQ(0, activeLayerCount()); - EXPECT_EQ(0, frequentLayerCount(time)); - - // layer3 becomes active and has high refresh rate. - for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE + FREQUENT_LAYER_WINDOW_SIZE + 1; i++) { - history().record(layer3->getSequence(), layer3->getLayerProps(), time, time, - LayerHistory::LayerUpdateType::Buffer); - time += HI_FPS_PERIOD; - summary = summarizeLayerHistory(time); - } - - ASSERT_EQ(1, summary.size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote); - EXPECT_EQ(HI_FPS, summary[0].desiredRefreshRate); - EXPECT_EQ(1, layerCount()); - EXPECT_EQ(1, activeLayerCount()); - EXPECT_EQ(1, frequentLayerCount(time)); - - // layer3 expires. - layer3.clear(); - summary = summarizeLayerHistory(time); - EXPECT_TRUE(summary.empty()); - EXPECT_EQ(0, layerCount()); - EXPECT_EQ(0, activeLayerCount()); - EXPECT_EQ(0, frequentLayerCount(time)); -} - -TEST_F(LayerHistoryTest, inactiveLayers) { - auto layer = createLayer(); - - EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true)); - EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate())); - - nsecs_t time = systemTime(); - - // the very first updates makes the layer frequent - for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) { - history().record(layer->getSequence(), layer->getLayerProps(), time, time, - LayerHistory::LayerUpdateType::Buffer); - time += MAX_FREQUENT_LAYER_PERIOD_NS.count(); - - EXPECT_EQ(1, layerCount()); - ASSERT_EQ(1, summarizeLayerHistory(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote); - EXPECT_EQ(1, activeLayerCount()); - EXPECT_EQ(1, frequentLayerCount(time)); - } - - // the next update with the MAX_FREQUENT_LAYER_PERIOD_NS will get us to infrequent - history().record(layer->getSequence(), layer->getLayerProps(), time, time, - LayerHistory::LayerUpdateType::Buffer); - time += MAX_FREQUENT_LAYER_PERIOD_NS.count(); - - EXPECT_EQ(1, layerCount()); - ASSERT_EQ(1, summarizeLayerHistory(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote); - EXPECT_EQ(1, activeLayerCount()); - EXPECT_EQ(0, frequentLayerCount(time)); - - // advance the time for the previous frame to be inactive - time += MAX_ACTIVE_LAYER_PERIOD_NS.count(); - - // Now even if we post a quick few frame we should stay infrequent - for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) { - history().record(layer->getSequence(), layer->getLayerProps(), time, time, - LayerHistory::LayerUpdateType::Buffer); - time += HI_FPS_PERIOD; - - EXPECT_EQ(1, layerCount()); - ASSERT_EQ(1, summarizeLayerHistory(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote); - EXPECT_EQ(1, activeLayerCount()); - EXPECT_EQ(0, frequentLayerCount(time)); - } - - // More quick frames will get us to frequent again - history().record(layer->getSequence(), layer->getLayerProps(), time, time, - LayerHistory::LayerUpdateType::Buffer); - time += HI_FPS_PERIOD; - - EXPECT_EQ(1, layerCount()); - ASSERT_EQ(1, summarizeLayerHistory(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote); - EXPECT_EQ(1, activeLayerCount()); - EXPECT_EQ(1, frequentLayerCount(time)); -} - -TEST_F(LayerHistoryTest, invisibleExplicitLayer) { - SET_FLAG_FOR_TEST(flags::misc1, false); - - auto explicitVisiblelayer = createLayer(); - auto explicitInvisiblelayer = createLayer(); - - EXPECT_CALL(*explicitVisiblelayer, isVisible()).WillRepeatedly(Return(true)); - EXPECT_CALL(*explicitVisiblelayer, getFrameRateForLayerTree()) - .WillRepeatedly(Return( - Layer::FrameRate(60_Hz, Layer::FrameRateCompatibility::ExactOrMultiple))); - - EXPECT_CALL(*explicitInvisiblelayer, isVisible()).WillRepeatedly(Return(false)); - EXPECT_CALL(*explicitInvisiblelayer, getFrameRateForLayerTree()) - .WillRepeatedly(Return( - Layer::FrameRate(90_Hz, Layer::FrameRateCompatibility::ExactOrMultiple))); - - nsecs_t time = systemTime(); - - // Post a buffer to the layers to make them active - history().record(explicitVisiblelayer->getSequence(), explicitVisiblelayer->getLayerProps(), - time, time, LayerHistory::LayerUpdateType::Buffer); - history().record(explicitInvisiblelayer->getSequence(), explicitInvisiblelayer->getLayerProps(), - time, time, LayerHistory::LayerUpdateType::Buffer); - - EXPECT_EQ(2, layerCount()); - ASSERT_EQ(1, summarizeLayerHistory(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple, - summarizeLayerHistory(time)[0].vote); - EXPECT_EQ(60_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate); - EXPECT_EQ(2, activeLayerCount()); - EXPECT_EQ(2, frequentLayerCount(time)); -} - -TEST_F(LayerHistoryTest, invisibleExplicitLayerDoesNotVote) { - SET_FLAG_FOR_TEST(flags::misc1, true); - - auto explicitVisiblelayer = createLayer(); - auto explicitInvisiblelayer = createLayer(); - - EXPECT_CALL(*explicitVisiblelayer, isVisible()).WillRepeatedly(Return(true)); - EXPECT_CALL(*explicitVisiblelayer, getFrameRateForLayerTree()) - .WillRepeatedly(Return( - Layer::FrameRate(60_Hz, Layer::FrameRateCompatibility::ExactOrMultiple))); - - EXPECT_CALL(*explicitInvisiblelayer, isVisible()).WillRepeatedly(Return(false)); - EXPECT_CALL(*explicitInvisiblelayer, getFrameRateForLayerTree()) - .WillRepeatedly(Return( - Layer::FrameRate(90_Hz, Layer::FrameRateCompatibility::ExactOrMultiple))); - - nsecs_t time = systemTime(); - - // Post a buffer to the layers to make them active - history().record(explicitVisiblelayer->getSequence(), explicitVisiblelayer->getLayerProps(), - time, time, LayerHistory::LayerUpdateType::Buffer); - history().record(explicitInvisiblelayer->getSequence(), explicitInvisiblelayer->getLayerProps(), - time, time, LayerHistory::LayerUpdateType::Buffer); - - EXPECT_EQ(2, layerCount()); - ASSERT_EQ(1, summarizeLayerHistory(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple, - summarizeLayerHistory(time)[0].vote); - EXPECT_EQ(60_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate); - EXPECT_EQ(1, activeLayerCount()); - EXPECT_EQ(1, frequentLayerCount(time)); -} - -TEST_F(LayerHistoryTest, infrequentAnimatingLayer) { - auto layer = createLayer(); - - EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true)); - EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate())); - - nsecs_t time = systemTime(); - - EXPECT_EQ(1, layerCount()); - EXPECT_EQ(0, activeLayerCount()); - EXPECT_EQ(0, frequentLayerCount(time)); - EXPECT_EQ(0, animatingLayerCount(time)); - - // layer is active but infrequent. - for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { - history().record(layer->getSequence(), layer->getLayerProps(), time, time, - LayerHistory::LayerUpdateType::Buffer); - time += MAX_FREQUENT_LAYER_PERIOD_NS.count(); - } - - ASSERT_EQ(1, summarizeLayerHistory(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote); - EXPECT_EQ(1, activeLayerCount()); - EXPECT_EQ(0, frequentLayerCount(time)); - EXPECT_EQ(0, animatingLayerCount(time)); - - // another update with the same cadence keep in infrequent - history().record(layer->getSequence(), layer->getLayerProps(), time, time, - LayerHistory::LayerUpdateType::Buffer); - time += MAX_FREQUENT_LAYER_PERIOD_NS.count(); - - ASSERT_EQ(1, summarizeLayerHistory(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote); - EXPECT_EQ(1, activeLayerCount()); - EXPECT_EQ(0, frequentLayerCount(time)); - EXPECT_EQ(0, animatingLayerCount(time)); - - // an update as animation will immediately vote for Max - history().record(layer->getSequence(), layer->getLayerProps(), time, time, - LayerHistory::LayerUpdateType::AnimationTX); - time += MAX_FREQUENT_LAYER_PERIOD_NS.count(); - - ASSERT_EQ(1, summarizeLayerHistory(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote); - EXPECT_EQ(1, activeLayerCount()); - EXPECT_EQ(0, frequentLayerCount(time)); - EXPECT_EQ(1, animatingLayerCount(time)); -} - -TEST_F(LayerHistoryTest, frontBufferedLayerVotesMax) { - SET_FLAG_FOR_TEST(flags::vrr_config, true); - auto layer = createLayer(); - - EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true)); - EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate())); - EXPECT_CALL(*layer, isFrontBuffered()).WillRepeatedly(Return(true)); - - nsecs_t time = systemTime(); - - EXPECT_EQ(1, layerCount()); - EXPECT_EQ(0, activeLayerCount()); - EXPECT_EQ(0, frequentLayerCount(time)); - EXPECT_EQ(0, animatingLayerCount(time)); - - // layer is active but infrequent. - for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { - history().record(layer->getSequence(), layer->getLayerProps(), time, time, - LayerHistory::LayerUpdateType::Buffer); - time += MAX_FREQUENT_LAYER_PERIOD_NS.count(); - } - - ASSERT_EQ(1, summarizeLayerHistory(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote); - EXPECT_EQ(1, activeLayerCount()); - EXPECT_EQ(0, frequentLayerCount(time)); - EXPECT_EQ(0, animatingLayerCount(time)); - - // Layer still active due to front buffering, but it's infrequent. - time += MAX_ACTIVE_LAYER_PERIOD_NS.count(); - ASSERT_EQ(1, summarizeLayerHistory(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote); - EXPECT_EQ(1, activeLayerCount()); - EXPECT_EQ(0, frequentLayerCount(time)); - EXPECT_EQ(0, animatingLayerCount(time)); -} - -TEST_F(LayerHistoryTest, frequentLayerBecomingInfrequentAndBack) { - auto layer = createLayer(); - - EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true)); - EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate())); - - nsecs_t time = systemTime(); - - EXPECT_EQ(1, layerCount()); - EXPECT_EQ(0, activeLayerCount()); - EXPECT_EQ(0, frequentLayerCount(time)); - EXPECT_EQ(0, animatingLayerCount(time)); - - // Fill up the window with frequent updates - for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE; i++) { - history().record(layer->getSequence(), layer->getLayerProps(), time, time, - LayerHistory::LayerUpdateType::Buffer); - time += (60_Hz).getPeriodNsecs(); - - EXPECT_EQ(1, layerCount()); - ASSERT_EQ(1, summarizeLayerHistory(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote); - EXPECT_EQ(1, activeLayerCount()); - EXPECT_EQ(1, frequentLayerCount(time)); - } - - // posting a buffer after long inactivity should retain the layer as active - time += std::chrono::nanoseconds(3s).count(); - history().record(layer->getSequence(), layer->getLayerProps(), time, time, - LayerHistory::LayerUpdateType::Buffer); - EXPECT_EQ(0, clearLayerHistoryCount(time)); - ASSERT_EQ(1, summarizeLayerHistory(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summarizeLayerHistory(time)[0].vote); - EXPECT_EQ(60_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate); - EXPECT_EQ(1, activeLayerCount()); - EXPECT_EQ(1, frequentLayerCount(time)); - EXPECT_EQ(0, animatingLayerCount(time)); - - // posting more infrequent buffer should make the layer infrequent - time += (MAX_FREQUENT_LAYER_PERIOD_NS + 1ms).count(); - history().record(layer->getSequence(), layer->getLayerProps(), time, time, - LayerHistory::LayerUpdateType::Buffer); - time += (MAX_FREQUENT_LAYER_PERIOD_NS + 1ms).count(); - history().record(layer->getSequence(), layer->getLayerProps(), time, time, - LayerHistory::LayerUpdateType::Buffer); - EXPECT_EQ(0, clearLayerHistoryCount(time)); - ASSERT_EQ(1, summarizeLayerHistory(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote); - EXPECT_EQ(1, activeLayerCount()); - EXPECT_EQ(0, frequentLayerCount(time)); - EXPECT_EQ(0, animatingLayerCount(time)); - - // posting another buffer should keep the layer infrequent - history().record(layer->getSequence(), layer->getLayerProps(), time, time, - LayerHistory::LayerUpdateType::Buffer); - EXPECT_EQ(0, clearLayerHistoryCount(time)); - ASSERT_EQ(1, summarizeLayerHistory(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote); - EXPECT_EQ(1, activeLayerCount()); - EXPECT_EQ(0, frequentLayerCount(time)); - EXPECT_EQ(0, animatingLayerCount(time)); - - // posting more buffers would mean starting of an animation, so making the layer frequent - history().record(layer->getSequence(), layer->getLayerProps(), time, time, - LayerHistory::LayerUpdateType::Buffer); - history().record(layer->getSequence(), layer->getLayerProps(), time, time, - LayerHistory::LayerUpdateType::Buffer); - EXPECT_EQ(1, clearLayerHistoryCount(time)); - ASSERT_EQ(1, summarizeLayerHistory(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote); - EXPECT_EQ(1, activeLayerCount()); - EXPECT_EQ(1, frequentLayerCount(time)); - EXPECT_EQ(0, animatingLayerCount(time)); - - // posting a buffer after long inactivity should retain the layer as active - time += std::chrono::nanoseconds(3s).count(); - history().record(layer->getSequence(), layer->getLayerProps(), time, time, - LayerHistory::LayerUpdateType::Buffer); - EXPECT_EQ(0, clearLayerHistoryCount(time)); - ASSERT_EQ(1, summarizeLayerHistory(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote); - EXPECT_EQ(1, activeLayerCount()); - EXPECT_EQ(1, frequentLayerCount(time)); - EXPECT_EQ(0, animatingLayerCount(time)); - - // posting another buffer should keep the layer frequent - time += (60_Hz).getPeriodNsecs(); - history().record(layer->getSequence(), layer->getLayerProps(), time, time, - LayerHistory::LayerUpdateType::Buffer); - EXPECT_EQ(0, clearLayerHistoryCount(time)); - ASSERT_EQ(1, summarizeLayerHistory(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote); - EXPECT_EQ(1, activeLayerCount()); - EXPECT_EQ(1, frequentLayerCount(time)); - EXPECT_EQ(0, animatingLayerCount(time)); -} - -TEST_F(LayerHistoryTest, inconclusiveLayerBecomingFrequent) { - auto layer = createLayer(); - - EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true)); - EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate())); - - nsecs_t time = systemTime(); - - EXPECT_EQ(1, layerCount()); - EXPECT_EQ(0, activeLayerCount()); - EXPECT_EQ(0, frequentLayerCount(time)); - EXPECT_EQ(0, animatingLayerCount(time)); - - // Fill up the window with frequent updates - for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE; i++) { - history().record(layer->getSequence(), layer->getLayerProps(), time, time, - LayerHistory::LayerUpdateType::Buffer); - time += (60_Hz).getPeriodNsecs(); - - EXPECT_EQ(1, layerCount()); - ASSERT_EQ(1, summarizeLayerHistory(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote); - EXPECT_EQ(1, activeLayerCount()); - EXPECT_EQ(1, frequentLayerCount(time)); - } - - // posting infrequent buffers after long inactivity should make the layer - // inconclusive but frequent. - time += std::chrono::nanoseconds(3s).count(); - history().record(layer->getSequence(), layer->getLayerProps(), time, time, - LayerHistory::LayerUpdateType::Buffer); - time += (MAX_FREQUENT_LAYER_PERIOD_NS + 1ms).count(); - history().record(layer->getSequence(), layer->getLayerProps(), time, time, - LayerHistory::LayerUpdateType::Buffer); - EXPECT_EQ(0, clearLayerHistoryCount(time)); - ASSERT_EQ(1, summarizeLayerHistory(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summarizeLayerHistory(time)[0].vote); - EXPECT_EQ(1, activeLayerCount()); - EXPECT_EQ(1, frequentLayerCount(time)); - EXPECT_EQ(0, animatingLayerCount(time)); - - // posting more buffers should make the layer frequent and switch the refresh rate to max - // by clearing the history - history().record(layer->getSequence(), layer->getLayerProps(), time, time, - LayerHistory::LayerUpdateType::Buffer); - history().record(layer->getSequence(), layer->getLayerProps(), time, time, - LayerHistory::LayerUpdateType::Buffer); - history().record(layer->getSequence(), layer->getLayerProps(), time, time, - LayerHistory::LayerUpdateType::Buffer); - EXPECT_EQ(1, clearLayerHistoryCount(time)); - ASSERT_EQ(1, summarizeLayerHistory(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote); - EXPECT_EQ(1, activeLayerCount()); - EXPECT_EQ(1, frequentLayerCount(time)); - EXPECT_EQ(0, animatingLayerCount(time)); -} - -TEST_F(LayerHistoryTest, getFramerate) { - auto layer = createLayer(); - - EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true)); - EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate())); - - nsecs_t time = systemTime(); - - EXPECT_EQ(1, layerCount()); - EXPECT_EQ(0, activeLayerCount()); - EXPECT_EQ(0, frequentLayerCount(time)); - EXPECT_EQ(0, animatingLayerCount(time)); - - // layer is active but infrequent. - for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { - history().record(layer->getSequence(), layer->getLayerProps(), time, time, - LayerHistory::LayerUpdateType::Buffer); - time += MAX_FREQUENT_LAYER_PERIOD_NS.count(); - } - - float expectedFramerate = 1e9f / MAX_FREQUENT_LAYER_PERIOD_NS.count(); - EXPECT_FLOAT_EQ(expectedFramerate, history().getLayerFramerate(time, layer->getSequence())); -} - -TEST_F(LayerHistoryTest, heuristicLayer60Hz) { - const auto layer = createLayer(); - EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true)); - EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate())); - - nsecs_t time = systemTime(); - for (float fps = 54.0f; fps < 65.0f; fps += 0.1f) { - recordFramesAndExpect(layer, time, Fps::fromValue(fps), 60_Hz, PRESENT_TIME_HISTORY_SIZE); - } -} - -TEST_F(LayerHistoryTest, heuristicLayer60_30Hz) { - const auto layer = createLayer(); - EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true)); - EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate())); - - nsecs_t time = systemTime(); - recordFramesAndExpect(layer, time, 60_Hz, 60_Hz, PRESENT_TIME_HISTORY_SIZE); - - recordFramesAndExpect(layer, time, 60_Hz, 60_Hz, PRESENT_TIME_HISTORY_SIZE); - recordFramesAndExpect(layer, time, 30_Hz, 60_Hz, PRESENT_TIME_HISTORY_SIZE); - recordFramesAndExpect(layer, time, 30_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE); - recordFramesAndExpect(layer, time, 60_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE); - recordFramesAndExpect(layer, time, 60_Hz, 60_Hz, PRESENT_TIME_HISTORY_SIZE); -} - -TEST_F(LayerHistoryTest, heuristicLayerNotOscillating) { - SET_FLAG_FOR_TEST(flags::use_known_refresh_rate_for_fps_consistency, false); - - const auto layer = createLayer(); - EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true)); - EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate())); - - nsecs_t time = systemTime(); - - recordFramesAndExpect(layer, time, 27.1_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE); - recordFramesAndExpect(layer, time, 26.9_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE); - recordFramesAndExpect(layer, time, 26_Hz, 24_Hz, PRESENT_TIME_HISTORY_SIZE); - recordFramesAndExpect(layer, time, 26.9_Hz, 24_Hz, PRESENT_TIME_HISTORY_SIZE); - recordFramesAndExpect(layer, time, 27.1_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE); -} - -TEST_F(LayerHistoryTest, heuristicLayerNotOscillating_useKnownRefreshRate) { - SET_FLAG_FOR_TEST(flags::use_known_refresh_rate_for_fps_consistency, true); - - const auto layer = createLayer(); - EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true)); - EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate())); - - nsecs_t time = systemTime(); - - recordFramesAndExpect(layer, time, 27.1_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE); - recordFramesAndExpect(layer, time, 26.9_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE); - recordFramesAndExpect(layer, time, 26_Hz, 24_Hz, PRESENT_TIME_HISTORY_SIZE); - recordFramesAndExpect(layer, time, 26.9_Hz, 24_Hz, PRESENT_TIME_HISTORY_SIZE); - recordFramesAndExpect(layer, time, 27.1_Hz, 24_Hz, PRESENT_TIME_HISTORY_SIZE); - recordFramesAndExpect(layer, time, 27.1_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE); -} - -TEST_F(LayerHistoryTest, smallDirtyLayer) { - auto layer = createLayer(); - - EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true)); - EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate())); - - nsecs_t time = systemTime(); - - EXPECT_EQ(1, layerCount()); - EXPECT_EQ(0, activeLayerCount()); - EXPECT_EQ(0, frequentLayerCount(time)); - - LayerHistory::Summary summary; - - // layer is active but infrequent. - for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { - auto props = layer->getLayerProps(); - if (i % 3 == 0) { - props.isSmallDirty = false; - } else { - props.isSmallDirty = true; - } - - history().record(layer->getSequence(), props, time, time, - LayerHistory::LayerUpdateType::Buffer); - time += HI_FPS_PERIOD; - summary = summarizeLayerHistory(time); - } - - ASSERT_EQ(1, summary.size()); - ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote); - EXPECT_GE(HI_FPS, summary[0].desiredRefreshRate); -} - -TEST_F(LayerHistoryTest, smallDirtyInMultiLayer) { - auto layer1 = createLayer("UI"); - auto layer2 = createLayer("Video"); - - EXPECT_CALL(*layer1, isVisible()).WillRepeatedly(Return(true)); - EXPECT_CALL(*layer1, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate())); - - EXPECT_CALL(*layer2, isVisible()).WillRepeatedly(Return(true)); - EXPECT_CALL(*layer2, getFrameRateForLayerTree()) - .WillRepeatedly( - Return(Layer::FrameRate(30_Hz, Layer::FrameRateCompatibility::Default))); - - nsecs_t time = systemTime(); - - EXPECT_EQ(2, layerCount()); - EXPECT_EQ(0, activeLayerCount()); - EXPECT_EQ(0, frequentLayerCount(time)); - - LayerHistory::Summary summary; - - // layer1 is updating small dirty. - for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE + FREQUENT_LAYER_WINDOW_SIZE + 1; i++) { - auto props = layer1->getLayerProps(); - props.isSmallDirty = true; - history().record(layer1->getSequence(), props, 0 /*presentTime*/, time, - LayerHistory::LayerUpdateType::Buffer); - history().record(layer2->getSequence(), layer2->getLayerProps(), time, time, - LayerHistory::LayerUpdateType::Buffer); - time += HI_FPS_PERIOD; - summary = summarizeLayerHistory(time); - } - - ASSERT_EQ(1, summary.size()); - ASSERT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summary[0].vote); - ASSERT_EQ(30_Hz, summary[0].desiredRefreshRate); -} - -class LayerHistoryTestParameterized : public LayerHistoryTest, - public testing::WithParamInterface<std::chrono::nanoseconds> { -}; - -TEST_P(LayerHistoryTestParameterized, HeuristicLayerWithInfrequentLayer) { - std::chrono::nanoseconds infrequentUpdateDelta = GetParam(); - auto heuristicLayer = createLayer("HeuristicLayer"); - - EXPECT_CALL(*heuristicLayer, isVisible()).WillRepeatedly(Return(true)); - EXPECT_CALL(*heuristicLayer, getFrameRateForLayerTree()) - .WillRepeatedly(Return(Layer::FrameRate())); - - auto infrequentLayer = createLayer("InfrequentLayer"); - EXPECT_CALL(*infrequentLayer, isVisible()).WillRepeatedly(Return(true)); - EXPECT_CALL(*infrequentLayer, getFrameRateForLayerTree()) - .WillRepeatedly(Return(Layer::FrameRate())); - - const nsecs_t startTime = systemTime(); - - const std::chrono::nanoseconds heuristicUpdateDelta = 41'666'667ns; - history().record(heuristicLayer->getSequence(), heuristicLayer->getLayerProps(), startTime, - startTime, LayerHistory::LayerUpdateType::Buffer); - history().record(infrequentLayer->getSequence(), heuristicLayer->getLayerProps(), startTime, - startTime, LayerHistory::LayerUpdateType::Buffer); - - nsecs_t time = startTime; - nsecs_t lastInfrequentUpdate = startTime; - const int totalInfrequentLayerUpdates = FREQUENT_LAYER_WINDOW_SIZE * 5; - int infrequentLayerUpdates = 0; - while (infrequentLayerUpdates <= totalInfrequentLayerUpdates) { - time += heuristicUpdateDelta.count(); - history().record(heuristicLayer->getSequence(), heuristicLayer->getLayerProps(), time, time, - LayerHistory::LayerUpdateType::Buffer); - - if (time - lastInfrequentUpdate >= infrequentUpdateDelta.count()) { - ALOGI("submitting infrequent frame [%d/%d]", infrequentLayerUpdates, - totalInfrequentLayerUpdates); - lastInfrequentUpdate = time; - history().record(infrequentLayer->getSequence(), infrequentLayer->getLayerProps(), time, - time, LayerHistory::LayerUpdateType::Buffer); - infrequentLayerUpdates++; - } - - if (time - startTime > PRESENT_TIME_HISTORY_DURATION.count()) { - ASSERT_NE(0, summarizeLayerHistory(time).size()); - ASSERT_GE(2, summarizeLayerHistory(time).size()); - - bool max = false; - bool min = false; - Fps heuristic; - for (const auto& layer : summarizeLayerHistory(time)) { - if (layer.vote == LayerHistory::LayerVoteType::Heuristic) { - heuristic = layer.desiredRefreshRate; - } else if (layer.vote == LayerHistory::LayerVoteType::Max) { - max = true; - } else if (layer.vote == LayerHistory::LayerVoteType::Min) { - min = true; - } - } - - if (infrequentLayerUpdates > FREQUENT_LAYER_WINDOW_SIZE) { - EXPECT_EQ(24_Hz, heuristic); - EXPECT_FALSE(max); - if (summarizeLayerHistory(time).size() == 2) { - EXPECT_TRUE(min); - } - } - } - } -} - -INSTANTIATE_TEST_CASE_P(LeapYearTests, LayerHistoryTestParameterized, - ::testing::Values(1s, 2s, 3s, 4s, 5s)); - -} // namespace -} // namespace android::scheduler - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic pop // ignored "-Wextra" diff --git a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp index bc15dec493..c7cc21ce07 100644 --- a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp @@ -61,18 +61,6 @@ public: class LayerLifecycleManagerTest : public LayerHierarchyTestBase { protected: - std::unique_ptr<RequestedLayerState> rootLayer(uint32_t id) { - return std::make_unique<RequestedLayerState>(createArgs(/*id=*/id, /*canBeRoot=*/true, - /*parent=*/UNASSIGNED_LAYER_ID, - /*mirror=*/UNASSIGNED_LAYER_ID)); - } - - std::unique_ptr<RequestedLayerState> childLayer(uint32_t id, uint32_t parentId) { - return std::make_unique<RequestedLayerState>(createArgs(/*id=*/id, /*canBeRoot=*/false, - parentId, - /*mirror=*/UNASSIGNED_LAYER_ID)); - } - RequestedLayerState* getRequestedLayerState(LayerLifecycleManager& lifecycleManager, uint32_t layerId) { return lifecycleManager.getLayerFromId(layerId); @@ -631,4 +619,14 @@ TEST_F(LayerLifecycleManagerTest, isSimpleBufferUpdate) { } } +TEST_F(LayerLifecycleManagerTest, testInputInfoOfRequestedLayerState) { + // By default the layer has no buffer, so it doesn't need an input info + EXPECT_FALSE(getRequestedLayerState(mLifecycleManager, 111)->needsInputInfo()); + + setBuffer(111); + mLifecycleManager.commitChanges(); + + EXPECT_TRUE(getRequestedLayerState(mLifecycleManager, 111)->needsInputInfo()); +} + } // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp index 8b9ac93310..75d2fa3c7f 100644 --- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp @@ -27,6 +27,7 @@ #include "LayerHierarchyTest.h" #include "ui/GraphicTypes.h" +#include <com_android_graphics_libgui_flags.h> #include <com_android_graphics_surfaceflinger_flags.h> #define UPDATE_AND_VERIFY(BUILDER, ...) \ @@ -56,6 +57,17 @@ using namespace com::android::graphics::surfaceflinger; class LayerSnapshotTest : public LayerSnapshotTestBase { protected: + const Layer::FrameRate FRAME_RATE_VOTE1 = + Layer::FrameRate(67_Hz, scheduler::FrameRateCompatibility::Default); + const Layer::FrameRate FRAME_RATE_VOTE2 = + Layer::FrameRate(14_Hz, scheduler::FrameRateCompatibility::Default); + const Layer::FrameRate FRAME_RATE_VOTE3 = + Layer::FrameRate(99_Hz, scheduler::FrameRateCompatibility::Default); + const Layer::FrameRate FRAME_RATE_TREE = + Layer::FrameRate(Fps(), scheduler::FrameRateCompatibility::NoVote); + const Layer::FrameRate FRAME_RATE_NO_VOTE = + Layer::FrameRate(Fps(), scheduler::FrameRateCompatibility::Default); + LayerSnapshotTest() : LayerSnapshotTestBase() { UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); } @@ -281,21 +293,132 @@ TEST_F(LayerSnapshotTest, FastPathSetsChangeFlagToContent) { EXPECT_EQ(getSnapshot(1)->clientChanges, layer_state_t::eColorChanged); } -TEST_F(LayerSnapshotTest, GameMode) { +TEST_F(LayerSnapshotTest, ChildrenInheritGameMode) { + setGameMode(1, gui::GameMode::Performance); + EXPECT_EQ(mLifecycleManager.getGlobalChanges(), + RequestedLayerState::Changes::GameMode | RequestedLayerState::Changes::Metadata); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_EQ(getSnapshot(1)->clientChanges, layer_state_t::eMetadataChanged); + EXPECT_EQ(getSnapshot(1)->gameMode, gui::GameMode::Performance); + EXPECT_EQ(getSnapshot(11)->gameMode, gui::GameMode::Performance); +} + +TEST_F(LayerSnapshotTest, ChildrenCanOverrideGameMode) { + setGameMode(1, gui::GameMode::Performance); + setGameMode(11, gui::GameMode::Battery); + EXPECT_EQ(mLifecycleManager.getGlobalChanges(), + RequestedLayerState::Changes::GameMode | RequestedLayerState::Changes::Metadata); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_EQ(getSnapshot(1)->clientChanges, layer_state_t::eMetadataChanged); + EXPECT_EQ(getSnapshot(1)->gameMode, gui::GameMode::Performance); + EXPECT_EQ(getSnapshot(11)->gameMode, gui::GameMode::Battery); +} + +TEST_F(LayerSnapshotTest, ReparentingUpdatesGameMode) { + setGameMode(1, gui::GameMode::Performance); + EXPECT_EQ(mLifecycleManager.getGlobalChanges(), + RequestedLayerState::Changes::GameMode | RequestedLayerState::Changes::Metadata); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_EQ(getSnapshot(1)->clientChanges, layer_state_t::eMetadataChanged); + EXPECT_EQ(getSnapshot(1)->gameMode, gui::GameMode::Performance); + EXPECT_EQ(getSnapshot(2)->gameMode, gui::GameMode::Unsupported); + + reparentLayer(2, 1); + setZ(2, 2); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_EQ(getSnapshot(2)->gameMode, gui::GameMode::Performance); +} + +TEST_F(LayerSnapshotTest, UpdateMetadata) { std::vector<TransactionState> transactions; transactions.emplace_back(); transactions.back().states.push_back({}); transactions.back().states.front().state.what = layer_state_t::eMetadataChanged; + // This test focuses on metadata used by ARC++ to ensure LayerMetadata is updated correctly, + // and not using stale data. transactions.back().states.front().state.metadata = LayerMetadata(); - transactions.back().states.front().state.metadata.setInt32(METADATA_GAME_MODE, 42); + transactions.back().states.front().state.metadata.setInt32(METADATA_OWNER_UID, 123); + transactions.back().states.front().state.metadata.setInt32(METADATA_WINDOW_TYPE, 234); + transactions.back().states.front().state.metadata.setInt32(METADATA_TASK_ID, 345); + transactions.back().states.front().state.metadata.setInt32(METADATA_MOUSE_CURSOR, 456); + transactions.back().states.front().state.metadata.setInt32(METADATA_ACCESSIBILITY_ID, 567); + transactions.back().states.front().state.metadata.setInt32(METADATA_OWNER_PID, 678); + transactions.back().states.front().state.metadata.setInt32(METADATA_CALLING_UID, 789); + transactions.back().states.front().layerId = 1; transactions.back().states.front().state.layerId = static_cast<int32_t>(1); + mLifecycleManager.applyTransactions(transactions); - EXPECT_EQ(mLifecycleManager.getGlobalChanges(), RequestedLayerState::Changes::GameMode); - UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_EQ(mLifecycleManager.getGlobalChanges(), RequestedLayerState::Changes::Metadata); + + // Setting includeMetadata=true to ensure metadata update is applied to LayerSnapshot + LayerSnapshotBuilder::Args args{.root = mHierarchyBuilder.getHierarchy(), + .layerLifecycleManager = mLifecycleManager, + .includeMetadata = true, + .displays = mFrontEndDisplayInfos, + .globalShadowSettings = globalShadowSettings, + .supportsBlur = true, + .supportedLayerGenericMetadata = {}, + .genericLayerMetadataKeyMap = {}}; + update(mSnapshotBuilder, args); + EXPECT_EQ(getSnapshot(1)->clientChanges, layer_state_t::eMetadataChanged); - EXPECT_EQ(static_cast<int32_t>(getSnapshot(1)->gameMode), 42); - EXPECT_EQ(static_cast<int32_t>(getSnapshot(11)->gameMode), 42); + EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_OWNER_UID, -1), 123); + EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_WINDOW_TYPE, -1), 234); + EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_TASK_ID, -1), 345); + EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_MOUSE_CURSOR, -1), 456); + EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_ACCESSIBILITY_ID, -1), 567); + EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_OWNER_PID, -1), 678); + EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_CALLING_UID, -1), 789); +} + +TEST_F(LayerSnapshotTest, UpdateMetadataOfHiddenLayers) { + hideLayer(1); + + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + transactions.back().states.front().state.what = layer_state_t::eMetadataChanged; + // This test focuses on metadata used by ARC++ to ensure LayerMetadata is updated correctly, + // and not using stale data. + transactions.back().states.front().state.metadata = LayerMetadata(); + transactions.back().states.front().state.metadata.setInt32(METADATA_OWNER_UID, 123); + transactions.back().states.front().state.metadata.setInt32(METADATA_WINDOW_TYPE, 234); + transactions.back().states.front().state.metadata.setInt32(METADATA_TASK_ID, 345); + transactions.back().states.front().state.metadata.setInt32(METADATA_MOUSE_CURSOR, 456); + transactions.back().states.front().state.metadata.setInt32(METADATA_ACCESSIBILITY_ID, 567); + transactions.back().states.front().state.metadata.setInt32(METADATA_OWNER_PID, 678); + transactions.back().states.front().state.metadata.setInt32(METADATA_CALLING_UID, 789); + + transactions.back().states.front().layerId = 1; + transactions.back().states.front().state.layerId = static_cast<int32_t>(1); + + mLifecycleManager.applyTransactions(transactions); + EXPECT_EQ(mLifecycleManager.getGlobalChanges(), + RequestedLayerState::Changes::Metadata | RequestedLayerState::Changes::Visibility | + RequestedLayerState::Changes::VisibleRegion | + RequestedLayerState::Changes::AffectsChildren); + + // Setting includeMetadata=true to ensure metadata update is applied to LayerSnapshot + LayerSnapshotBuilder::Args args{.root = mHierarchyBuilder.getHierarchy(), + .layerLifecycleManager = mLifecycleManager, + .includeMetadata = true, + .displays = mFrontEndDisplayInfos, + .globalShadowSettings = globalShadowSettings, + .supportsBlur = true, + .supportedLayerGenericMetadata = {}, + .genericLayerMetadataKeyMap = {}}; + update(mSnapshotBuilder, args); + + EXPECT_EQ(static_cast<int64_t>(getSnapshot(1)->clientChanges), + layer_state_t::eMetadataChanged | layer_state_t::eFlagsChanged); + EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_OWNER_UID, -1), 123); + EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_WINDOW_TYPE, -1), 234); + EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_TASK_ID, -1), 345); + EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_MOUSE_CURSOR, -1), 456); + EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_ACCESSIBILITY_ID, -1), 567); + EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_OWNER_PID, -1), 678); + EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_CALLING_UID, -1), 789); } TEST_F(LayerSnapshotTest, NoLayerVoteForParentWithChildVotes) { @@ -676,6 +799,155 @@ TEST_F(LayerSnapshotTest, framerate) { scheduler::FrameRateCompatibility::Default); } +TEST_F(LayerSnapshotTest, frameRateSetAndGet) { + setFrameRate(1, FRAME_RATE_VOTE1.vote.rate.getValue(), 0, 0); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + // verify parent is gets no vote + EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_VOTE1); +} + +TEST_F(LayerSnapshotTest, frameRateSetAndGetParent) { + setFrameRate(111, FRAME_RATE_VOTE1.vote.rate.getValue(), 0, 0); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_TREE); + EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_TREE); + EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_VOTE1); + + setFrameRate(111, FRAME_RATE_NO_VOTE.vote.rate.getValue(), 0, 0); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_NO_VOTE); + EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_NO_VOTE); + EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_NO_VOTE); +} + +TEST_F(LayerSnapshotTest, frameRateSetAndGetParentAllVote) { + setFrameRate(1, FRAME_RATE_VOTE3.vote.rate.getValue(), 0, 0); + setFrameRate(11, FRAME_RATE_VOTE2.vote.rate.getValue(), 0, 0); + setFrameRate(111, FRAME_RATE_VOTE1.vote.rate.getValue(), 0, 0); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + + EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_VOTE3); + EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_VOTE2); + EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_VOTE1); + + setFrameRate(111, FRAME_RATE_NO_VOTE.vote.rate.getValue(), 0, 0); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_VOTE3); + EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_VOTE2); + EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_VOTE2); + + setFrameRate(11, FRAME_RATE_NO_VOTE.vote.rate.getValue(), 0, 0); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_VOTE3); + EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_VOTE3); + EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_VOTE3); + + setFrameRate(1, FRAME_RATE_NO_VOTE.vote.rate.getValue(), 0, 0); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_NO_VOTE); + EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_NO_VOTE); + EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_NO_VOTE); +} + +TEST_F(LayerSnapshotTest, frameRateSetAndGetChild) { + setFrameRate(1, FRAME_RATE_VOTE1.vote.rate.getValue(), 0, 0); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_VOTE1); + EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_VOTE1); + EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_VOTE1); + + setFrameRate(1, FRAME_RATE_NO_VOTE.vote.rate.getValue(), 0, 0); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_NO_VOTE); + EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_NO_VOTE); + EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_NO_VOTE); +} + +TEST_F(LayerSnapshotTest, frameRateSetAndGetChildAllVote) { + setFrameRate(1, FRAME_RATE_VOTE3.vote.rate.getValue(), 0, 0); + setFrameRate(11, FRAME_RATE_VOTE2.vote.rate.getValue(), 0, 0); + setFrameRate(111, FRAME_RATE_VOTE1.vote.rate.getValue(), 0, 0); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_VOTE3); + EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_VOTE2); + EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_VOTE1); + + setFrameRate(1, FRAME_RATE_NO_VOTE.vote.rate.getValue(), 0, 0); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_TREE); + EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_VOTE2); + EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_VOTE1); + + setFrameRate(11, FRAME_RATE_NO_VOTE.vote.rate.getValue(), 0, 0); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_TREE); + EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_TREE); + EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_VOTE1); + + setFrameRate(111, FRAME_RATE_NO_VOTE.vote.rate.getValue(), 0, 0); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_NO_VOTE); + EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_NO_VOTE); + EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_NO_VOTE); +} + +TEST_F(LayerSnapshotTest, frameRateSetAndGetChildAddAfterVote) { + setFrameRate(1, FRAME_RATE_VOTE1.vote.rate.getValue(), 0, 0); + reparentLayer(111, 2); + std::vector<uint32_t> traversalOrder = {1, 11, 12, 121, 122, 1221, 13, 2, 111}; + UPDATE_AND_VERIFY(mSnapshotBuilder, traversalOrder); + EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_VOTE1); + EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_VOTE1); + EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_NO_VOTE); + + reparentLayer(111, 11); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_VOTE1); + EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_VOTE1); + EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_VOTE1); + + setFrameRate(1, FRAME_RATE_NO_VOTE.vote.rate.getValue(), 0, 0); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_NO_VOTE); + EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_NO_VOTE); + EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_NO_VOTE); +} + +TEST_F(LayerSnapshotTest, frameRateSetAndGetChildRemoveAfterVote) { + setFrameRate(1, FRAME_RATE_VOTE1.vote.rate.getValue(), 0, 0); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_VOTE1); + EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_VOTE1); + EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_VOTE1); + + reparentLayer(111, 2); + std::vector<uint32_t> traversalOrder = {1, 11, 12, 121, 122, 1221, 13, 2, 111}; + UPDATE_AND_VERIFY(mSnapshotBuilder, traversalOrder); + EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_VOTE1); + EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_VOTE1); + EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_NO_VOTE); + + setFrameRate(1, FRAME_RATE_NO_VOTE.vote.rate.getValue(), 0, 0); + UPDATE_AND_VERIFY(mSnapshotBuilder, traversalOrder); + EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_NO_VOTE); + EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_NO_VOTE); + EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_NO_VOTE); +} + +TEST_F(LayerSnapshotTest, frameRateAddChildForParentWithTreeVote) { + setFrameRate(11, FRAME_RATE_VOTE1.vote.rate.getValue(), 0, 0); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_TREE); + EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_VOTE1); + EXPECT_EQ(getSnapshot({.id = 12})->frameRate, FRAME_RATE_NO_VOTE); + + setFrameRate(11, FRAME_RATE_NO_VOTE.vote.rate.getValue(), 0, 0); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_NO_VOTE); + EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_NO_VOTE); + EXPECT_EQ(getSnapshot({.id = 12})->frameRate, FRAME_RATE_NO_VOTE); +} + TEST_F(LayerSnapshotTest, translateDataspace) { setDataspace(1, ui::Dataspace::UNKNOWN); UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); @@ -854,7 +1126,7 @@ TEST_F(LayerSnapshotTest, frameRateSelectionStrategy) { EXPECT_FALSE(getSnapshot({.id = 1})->frameRate.vote.rate.isValid()); EXPECT_EQ(getSnapshot({.id = 1})->frameRate.vote.type, scheduler::FrameRateCompatibility::NoVote); - EXPECT_FALSE(getSnapshot({.id = 1})->changes.test(RequestedLayerState::Changes::FrameRate)); + EXPECT_TRUE(getSnapshot({.id = 1})->changes.test(RequestedLayerState::Changes::FrameRate)); // verify layer 12 and all descendants (121, 122, 1221) get the requested vote EXPECT_FALSE(getSnapshot({.id = 12})->frameRate.vote.rate.isValid()); @@ -945,7 +1217,7 @@ TEST_F(LayerSnapshotTest, frameRateSelectionStrategyWithCategory) { EXPECT_EQ(getSnapshot({.id = 1})->frameRate.vote.type, scheduler::FrameRateCompatibility::NoVote); EXPECT_EQ(getSnapshot({.id = 1})->frameRate.category, FrameRateCategory::Default); - EXPECT_FALSE(getSnapshot({.id = 1})->changes.test(RequestedLayerState::Changes::FrameRate)); + EXPECT_TRUE(getSnapshot({.id = 1})->changes.test(RequestedLayerState::Changes::FrameRate)); // verify layer 12 and all descendants (121, 122, 1221) get the requested vote EXPECT_FALSE(getSnapshot({.id = 12})->frameRate.vote.rate.isValid()); @@ -1304,6 +1576,17 @@ TEST_F(LayerSnapshotTest, NonVisibleLayerWithInput) { EXPECT_TRUE(foundInputLayer); } +TEST_F(LayerSnapshotTest, ForEachSnapshotsWithPredicate) { + std::vector<uint32_t> visitedUniqueSequences; + mSnapshotBuilder.forEachSnapshot( + [&](const std::unique_ptr<frontend::LayerSnapshot>& snapshot) { + visitedUniqueSequences.push_back(snapshot->uniqueSequence); + }, + [](const frontend::LayerSnapshot& snapshot) { return snapshot.uniqueSequence == 111; }); + EXPECT_EQ(visitedUniqueSequences.size(), 1u); + EXPECT_EQ(visitedUniqueSequences[0], 111u); +} + TEST_F(LayerSnapshotTest, canOccludePresentation) { setFlags(12, layer_state_t::eCanOccludePresentation, layer_state_t::eCanOccludePresentation); LayerSnapshotBuilder::Args args{.root = mHierarchyBuilder.getHierarchy(), @@ -1435,4 +1718,221 @@ TEST_F(LayerSnapshotTest, doNotOverrideParentTrustedOverlayState) { gui::WindowInfo::InputConfig::TRUSTED_OVERLAY)); } +static constexpr const FloatRect LARGE_FLOAT_RECT{std::numeric_limits<float>::min(), + std::numeric_limits<float>::min(), + std::numeric_limits<float>::max(), + std::numeric_limits<float>::max()}; +TEST_F(LayerSnapshotTest, layerVisibleByDefault) { + DisplayInfo info; + info.info.logicalHeight = 1000000; + info.info.logicalWidth = 1000000; + mFrontEndDisplayInfos.emplace_or_replace(ui::LayerStack::fromValue(1), info); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_FALSE(getSnapshot(1)->isHiddenByPolicy()); +} + +TEST_F(LayerSnapshotTest, hideLayerWithZeroMatrix) { + DisplayInfo info; + info.info.logicalHeight = 1000000; + info.info.logicalWidth = 1000000; + mFrontEndDisplayInfos.emplace_or_replace(ui::LayerStack::fromValue(1), info); + setMatrix(1, 0.f, 0.f, 0.f, 0.f); + UPDATE_AND_VERIFY(mSnapshotBuilder, {2}); + EXPECT_TRUE(getSnapshot(1)->isHiddenByPolicy()); +} + +TEST_F(LayerSnapshotTest, hideLayerWithInfMatrix) { + DisplayInfo info; + info.info.logicalHeight = 1000000; + info.info.logicalWidth = 1000000; + mFrontEndDisplayInfos.emplace_or_replace(ui::LayerStack::fromValue(1), info); + setMatrix(1, std::numeric_limits<float>::infinity(), 0.f, 0.f, + std::numeric_limits<float>::infinity()); + UPDATE_AND_VERIFY(mSnapshotBuilder, {2}); + EXPECT_TRUE(getSnapshot(1)->isHiddenByPolicy()); +} + +TEST_F(LayerSnapshotTest, hideLayerWithNanMatrix) { + DisplayInfo info; + info.info.logicalHeight = 1000000; + info.info.logicalWidth = 1000000; + mFrontEndDisplayInfos.emplace_or_replace(ui::LayerStack::fromValue(1), info); + setMatrix(1, std::numeric_limits<float>::quiet_NaN(), 0.f, 0.f, + std::numeric_limits<float>::quiet_NaN()); + UPDATE_AND_VERIFY(mSnapshotBuilder, {2}); + EXPECT_TRUE(getSnapshot(1)->isHiddenByPolicy()); +} + +TEST_F(LayerSnapshotTest, edgeExtensionPropagatesInHierarchy) { + if (!com::android::graphics::libgui::flags::edge_extension_shader()) { + GTEST_SKIP() << "Skipping test because edge_extension_shader is off"; + } + setCrop(1, Rect(0, 0, 20, 20)); + setBuffer(1221, + std::make_shared<renderengine::mock::FakeExternalTexture>(20 /* width */, + 20 /* height */, + 42ULL /* bufferId */, + HAL_PIXEL_FORMAT_RGBA_8888, + 0 /*usage*/)); + setEdgeExtensionEffect(12, LEFT); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + + EXPECT_TRUE(getSnapshot({.id = 12})->edgeExtensionEffect.extendsEdge(LEFT)); + EXPECT_TRUE(getSnapshot({.id = 121})->edgeExtensionEffect.extendsEdge(LEFT)); + EXPECT_TRUE(getSnapshot({.id = 1221})->edgeExtensionEffect.extendsEdge(LEFT)); + + setEdgeExtensionEffect(12, RIGHT); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + + EXPECT_TRUE(getSnapshot({.id = 12})->edgeExtensionEffect.extendsEdge(RIGHT)); + EXPECT_TRUE(getSnapshot({.id = 121})->edgeExtensionEffect.extendsEdge(RIGHT)); + EXPECT_TRUE(getSnapshot({.id = 1221})->edgeExtensionEffect.extendsEdge(RIGHT)); + + setEdgeExtensionEffect(12, TOP); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + + EXPECT_TRUE(getSnapshot({.id = 12})->edgeExtensionEffect.extendsEdge(TOP)); + EXPECT_TRUE(getSnapshot({.id = 121})->edgeExtensionEffect.extendsEdge(TOP)); + EXPECT_TRUE(getSnapshot({.id = 1221})->edgeExtensionEffect.extendsEdge(TOP)); + + setEdgeExtensionEffect(12, BOTTOM); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + + EXPECT_TRUE(getSnapshot({.id = 12})->edgeExtensionEffect.extendsEdge(BOTTOM)); + EXPECT_TRUE(getSnapshot({.id = 121})->edgeExtensionEffect.extendsEdge(BOTTOM)); + EXPECT_TRUE(getSnapshot({.id = 1221})->edgeExtensionEffect.extendsEdge(BOTTOM)); +} + +TEST_F(LayerSnapshotTest, leftEdgeExtensionIncreaseBoundSizeWithinCrop) { + // The left bound is extended when shifting to the right + if (!com::android::graphics::libgui::flags::edge_extension_shader()) { + GTEST_SKIP() << "Skipping test because edge_extension_shader is off"; + } + setCrop(1, Rect(0, 0, 20, 20)); + const int texSize = 10; + setBuffer(1221, + std::make_shared<renderengine::mock::FakeExternalTexture>(texSize /* width */, + texSize /* height*/, + 42ULL /* bufferId */, + HAL_PIXEL_FORMAT_RGBA_8888, + 0 /*usage*/)); + const float translation = 5.0; + setPosition(12, translation, 0); + setEdgeExtensionEffect(12, LEFT); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_EQ(getSnapshot({.id = 1221})->transformedBounds.right, texSize + translation); + EXPECT_LT(getSnapshot({.id = 1221})->transformedBounds.left, translation); + EXPECT_GE(getSnapshot({.id = 1221})->transformedBounds.left, 0.0); +} + +TEST_F(LayerSnapshotTest, rightEdgeExtensionIncreaseBoundSizeWithinCrop) { + // The right bound is extended when shifting to the left + if (!com::android::graphics::libgui::flags::edge_extension_shader()) { + GTEST_SKIP() << "Skipping test because edge_extension_shader is off"; + } + const int crop = 20; + setCrop(1, Rect(0, 0, crop, crop)); + const int texSize = 10; + setBuffer(1221, + std::make_shared<renderengine::mock::FakeExternalTexture>(texSize /* width */, + texSize /* height*/, + 42ULL /* bufferId */, + HAL_PIXEL_FORMAT_RGBA_8888, + 0 /*usage*/)); + const float translation = -5.0; + setPosition(12, translation, 0); + setEdgeExtensionEffect(12, RIGHT); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_EQ(getSnapshot({.id = 1221})->transformedBounds.left, 0); + EXPECT_GT(getSnapshot({.id = 1221})->transformedBounds.right, texSize + translation); + EXPECT_LE(getSnapshot({.id = 1221})->transformedBounds.right, (float)crop); +} + +TEST_F(LayerSnapshotTest, topEdgeExtensionIncreaseBoundSizeWithinCrop) { + // The top bound is extended when shifting to the bottom + if (!com::android::graphics::libgui::flags::edge_extension_shader()) { + GTEST_SKIP() << "Skipping test because edge_extension_shader is off"; + } + setCrop(1, Rect(0, 0, 20, 20)); + const int texSize = 10; + setBuffer(1221, + std::make_shared<renderengine::mock::FakeExternalTexture>(texSize /* width */, + texSize /* height*/, + 42ULL /* bufferId */, + HAL_PIXEL_FORMAT_RGBA_8888, + 0 /*usage*/)); + const float translation = 5.0; + setPosition(12, 0, translation); + setEdgeExtensionEffect(12, TOP); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_EQ(getSnapshot({.id = 1221})->transformedBounds.bottom, texSize + translation); + EXPECT_LT(getSnapshot({.id = 1221})->transformedBounds.top, translation); + EXPECT_GE(getSnapshot({.id = 1221})->transformedBounds.top, 0.0); +} + +TEST_F(LayerSnapshotTest, bottomEdgeExtensionIncreaseBoundSizeWithinCrop) { + // The bottom bound is extended when shifting to the top + if (!com::android::graphics::libgui::flags::edge_extension_shader()) { + GTEST_SKIP() << "Skipping test because edge_extension_shader is off"; + } + const int crop = 20; + setCrop(1, Rect(0, 0, crop, crop)); + const int texSize = 10; + setBuffer(1221, + std::make_shared<renderengine::mock::FakeExternalTexture>(texSize /* width */, + texSize /* height*/, + 42ULL /* bufferId */, + HAL_PIXEL_FORMAT_RGBA_8888, + 0 /*usage*/)); + const float translation = -5.0; + setPosition(12, 0, translation); + setEdgeExtensionEffect(12, BOTTOM); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_EQ(getSnapshot({.id = 1221})->transformedBounds.top, 0); + EXPECT_GT(getSnapshot({.id = 1221})->transformedBounds.bottom, texSize - translation); + EXPECT_LE(getSnapshot({.id = 1221})->transformedBounds.bottom, (float)crop); +} + +TEST_F(LayerSnapshotTest, multipleEdgeExtensionIncreaseBoundSizeWithinCrop) { + // The left bound is extended when shifting to the right + if (!com::android::graphics::libgui::flags::edge_extension_shader()) { + GTEST_SKIP() << "Skipping test because edge_extension_shader is off"; + } + const int crop = 20; + setCrop(1, Rect(0, 0, crop, crop)); + const int texSize = 10; + setBuffer(1221, + std::make_shared<renderengine::mock::FakeExternalTexture>(texSize /* width */, + texSize /* height*/, + 42ULL /* bufferId */, + HAL_PIXEL_FORMAT_RGBA_8888, + 0 /*usage*/)); + const float translation = 5.0; + setPosition(12, translation, translation); + setEdgeExtensionEffect(12, LEFT | RIGHT | TOP | BOTTOM); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_GT(getSnapshot({.id = 1221})->transformedBounds.right, texSize + translation); + EXPECT_LE(getSnapshot({.id = 1221})->transformedBounds.right, (float)crop); + EXPECT_LT(getSnapshot({.id = 1221})->transformedBounds.left, translation); + EXPECT_GE(getSnapshot({.id = 1221})->transformedBounds.left, 0.0); + EXPECT_GT(getSnapshot({.id = 1221})->transformedBounds.bottom, texSize + translation); + EXPECT_LE(getSnapshot({.id = 1221})->transformedBounds.bottom, (float)crop); + EXPECT_LT(getSnapshot({.id = 1221})->transformedBounds.top, translation); + EXPECT_GE(getSnapshot({.id = 1221})->transformedBounds.top, 0); +} + +TEST_F(LayerSnapshotTest, shouldUpdateInputWhenNoInputInfo) { + // By default the layer has no buffer, so we don't expect it to have an input info + EXPECT_FALSE(getSnapshot(111)->hasInputInfo()); + + setBuffer(111); + + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + + EXPECT_TRUE(getSnapshot(111)->hasInputInfo()); + EXPECT_TRUE(getSnapshot(111)->inputInfo.inputConfig.test( + gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL)); + EXPECT_FALSE(getSnapshot(2)->hasInputInfo()); +} + } // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/tests/unittests/LayerTest.cpp b/services/surfaceflinger/tests/unittests/LayerTest.cpp deleted file mode 100644 index 95e54f655b..0000000000 --- a/services/surfaceflinger/tests/unittests/LayerTest.cpp +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#undef LOG_TAG -#define LOG_TAG "LibSurfaceFlingerUnittests" - -#include <gtest/gtest.h> -#include <ui/FloatRect.h> -#include <ui/Transform.h> -#include <limits> - -#include "LayerTestUtils.h" -#include "TestableSurfaceFlinger.h" - -namespace android { -namespace { - -class LayerTest : public BaseLayerTest { -protected: - static constexpr const float MIN_FLOAT = std::numeric_limits<float>::min(); - static constexpr const float MAX_FLOAT = std::numeric_limits<float>::max(); - static constexpr const FloatRect LARGE_FLOAT_RECT{MIN_FLOAT, MIN_FLOAT, MAX_FLOAT, MAX_FLOAT}; -}; - -INSTANTIATE_TEST_SUITE_P(PerLayerType, LayerTest, - testing::Values(std::make_shared<BufferStateLayerFactory>(), - std::make_shared<EffectLayerFactory>()), - PrintToStringParamName); - -TEST_P(LayerTest, layerVisibleByDefault) { - sp<Layer> layer = GetParam()->createLayer(mFlinger); - layer->updateGeometry(); - layer->computeBounds(LARGE_FLOAT_RECT, ui::Transform(), 0.f); - ASSERT_FALSE(layer->isHiddenByPolicy()); -} - -TEST_P(LayerTest, hideLayerWithZeroMatrix) { - sp<Layer> layer = GetParam()->createLayer(mFlinger); - - layer_state_t::matrix22_t matrix{0, 0, 0, 0}; - layer->setMatrix(matrix); - layer->updateGeometry(); - layer->computeBounds(LARGE_FLOAT_RECT, ui::Transform(), 0.f); - - ASSERT_TRUE(layer->isHiddenByPolicy()); -} - -TEST_P(LayerTest, hideLayerWithInfMatrix) { - sp<Layer> layer = GetParam()->createLayer(mFlinger); - - constexpr const float INF = std::numeric_limits<float>::infinity(); - layer_state_t::matrix22_t matrix{INF, 0, 0, INF}; - layer->setMatrix(matrix); - layer->updateGeometry(); - layer->computeBounds(LARGE_FLOAT_RECT, ui::Transform(), 0.f); - - ASSERT_TRUE(layer->isHiddenByPolicy()); -} - -TEST_P(LayerTest, hideLayerWithNanMatrix) { - sp<Layer> layer = GetParam()->createLayer(mFlinger); - - constexpr const float QUIET_NAN = std::numeric_limits<float>::quiet_NaN(); - layer_state_t::matrix22_t matrix{QUIET_NAN, 0, 0, QUIET_NAN}; - layer->setMatrix(matrix); - layer->updateGeometry(); - layer->computeBounds(LARGE_FLOAT_RECT, ui::Transform(), 0.f); - - ASSERT_TRUE(layer->isHiddenByPolicy()); -} - -} // namespace -} // namespace android diff --git a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp index e74f64305c..c879280e57 100644 --- a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp +++ b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp @@ -706,7 +706,7 @@ TEST_F(PowerAdvisorTest, setGpuFenceTime_UnsingaledGpuFenceFrameUsingPreviousFra testGpuScenario(config, res); EXPECT_EQ(res.gpuDurationNanos, 0L); EXPECT_EQ(res.cpuDurationNanos, 0L); - EXPECT_GE(res.durationNanos, toNanos(30ms + getErrorMargin())); + EXPECT_GE(res.durationNanos, toNanos(29ms + getErrorMargin())); EXPECT_LE(res.durationNanos, toNanos(31ms + getErrorMargin())); } diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp index d64cf2f52b..adbd868a28 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp @@ -1566,7 +1566,7 @@ TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_30_60 // When frame rates get an equal score, the lower is chosen, unless there are Max votes. {0_Hz, FrameRateCategory::High, 90_Hz}, {0_Hz, FrameRateCategory::Normal, 60_Hz}, - {0_Hz, FrameRateCategory::Low, 30_Hz}, + {0_Hz, FrameRateCategory::Low, 60_Hz}, {0_Hz, FrameRateCategory::NoPreference, 60_Hz}, // Cases that have both desired frame rate and frame rate category requirements. @@ -1613,6 +1613,77 @@ TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_30_60 } } +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_120_vrr) { + if (GetParam() != Config::FrameRateOverride::Enabled) { + return; + } + + SET_FLAG_FOR_TEST(flags::vrr_config, true); + // Device with VRR config mode + auto selector = createSelector(kVrrMode_120, kModeId120); + + struct Case { + // Params + Fps desiredFrameRate = 0_Hz; + FrameRateCategory frameRateCategory = FrameRateCategory::Default; + + // Expected result + Fps expectedFrameRate = 0_Hz; + }; + + // Prepare a table with the vote and the expected refresh rate + const std::initializer_list<Case> testCases = { + // Cases that only have frame rate category requirements, but no desired frame rate. + // When frame rates get an equal score, the lower is chosen, unless there are Max votes. + {0_Hz, FrameRateCategory::High, 120_Hz}, + {0_Hz, FrameRateCategory::Normal, 60_Hz}, + {0_Hz, FrameRateCategory::Low, 48_Hz}, + {0_Hz, FrameRateCategory::NoPreference, 120_Hz}, + + // Cases that have both desired frame rate and frame rate category requirements. + {24_Hz, FrameRateCategory::High, 120_Hz}, + {30_Hz, FrameRateCategory::High, 120_Hz}, + {12_Hz, FrameRateCategory::Normal, 60_Hz}, + {24_Hz, FrameRateCategory::Low, 48_Hz}, + {30_Hz, FrameRateCategory::NoPreference, 30_Hz}, + + // Cases that only have desired frame rate. + {30_Hz, FrameRateCategory::Default, 30_Hz}, + }; + + for (auto testCase : testCases) { + std::vector<LayerRequirement> layers; + ALOGI("**** %s: Testing desiredFrameRate=%s, frameRateCategory=%s", __func__, + to_string(testCase.desiredFrameRate).c_str(), + ftl::enum_string(testCase.frameRateCategory).c_str()); + + if (testCase.desiredFrameRate.isValid()) { + std::stringstream ss; + ss << to_string(testCase.desiredFrameRate) << "ExplicitDefault"; + LayerRequirement layer = {.name = ss.str(), + .vote = LayerVoteType::ExplicitDefault, + .desiredRefreshRate = testCase.desiredFrameRate, + .weight = 1.f}; + layers.push_back(layer); + } + + if (testCase.frameRateCategory != FrameRateCategory::Default) { + std::stringstream ss; + ss << "ExplicitCategory (" << ftl::enum_string(testCase.frameRateCategory) << ")"; + LayerRequirement layer = {.name = ss.str(), + .vote = LayerVoteType::ExplicitCategory, + .frameRateCategory = testCase.frameRateCategory, + .weight = 1.f}; + layers.push_back(layer); + } + + EXPECT_EQ(testCase.expectedFrameRate, selector.getBestFrameRateMode(layers).fps) + << "Did not get expected frame rate for frameRate=" + << to_string(testCase.desiredFrameRate) + << " category=" << ftl::enum_string(testCase.frameRateCategory); + } +} + TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategoryMultiLayers_30_60_90_120) { auto selector = createSelector(makeModes(kMode30, kMode60, kMode90, kMode120), kModeId60); @@ -2181,14 +2252,14 @@ TEST_P(RefreshRateSelectorTest, // These layers may switch modes because smoothSwitchOnly=false. {FrameRateCategory::Default, false, 120_Hz, kModeId120}, {FrameRateCategory::NoPreference, false, 120_Hz, kModeId120}, - {FrameRateCategory::Low, false, 30_Hz, kModeId60}, + {FrameRateCategory::Low, false, 60_Hz, kModeId60}, {FrameRateCategory::Normal, false, 60_Hz, kModeId60}, {FrameRateCategory::High, false, 120_Hz, kModeId120}, // These layers cannot change mode due to smoothSwitchOnly, and will definitely use // active mode (120Hz). {FrameRateCategory::NoPreference, true, 120_Hz, kModeId120}, - {FrameRateCategory::Low, true, 40_Hz, kModeId120}, + {FrameRateCategory::Low, true, 120_Hz, kModeId120}, {FrameRateCategory::Normal, true, 120_Hz, kModeId120}, {FrameRateCategory::High, true, 120_Hz, kModeId120}, }; @@ -2248,13 +2319,13 @@ TEST_P(RefreshRateSelectorTest, {FrameRateCategory::Default, false, 120_Hz}, // TODO(b/266481656): Once this bug is fixed, NoPreference should be a lower frame rate. {FrameRateCategory::NoPreference, false, 120_Hz}, - {FrameRateCategory::Low, false, 30_Hz}, + {FrameRateCategory::Low, false, 48_Hz}, {FrameRateCategory::Normal, false, 60_Hz}, {FrameRateCategory::High, false, 120_Hz}, {FrameRateCategory::Default, true, 120_Hz}, // TODO(b/266481656): Once this bug is fixed, NoPreference should be a lower frame rate. {FrameRateCategory::NoPreference, true, 120_Hz}, - {FrameRateCategory::Low, true, 30_Hz}, + {FrameRateCategory::Low, true, 48_Hz}, {FrameRateCategory::Normal, true, 60_Hz}, {FrameRateCategory::High, true, 120_Hz}, }; diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp index fc54a8b74f..ac09cbcea6 100644 --- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp +++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp @@ -34,6 +34,7 @@ #include "mock/MockSchedulerCallback.h" #include <FrontEnd/LayerHierarchy.h> +#include <scheduler/FrameTime.h> #include <com_android_graphics_surfaceflinger_flags.h> #include "FpsOps.h" @@ -77,6 +78,8 @@ protected: SchedulerTest(); + static constexpr RefreshRateSelector::LayerRequirement kLayer = {.weight = 1.f}; + static constexpr PhysicalDisplayId kDisplayId1 = PhysicalDisplayId::fromPort(255u); static inline const ftl::NonNull<DisplayModePtr> kDisplay1Mode60 = ftl::as_non_null(createDisplayMode(kDisplayId1, DisplayModeId(0), 60_Hz)); @@ -84,6 +87,9 @@ protected: ftl::as_non_null(createDisplayMode(kDisplayId1, DisplayModeId(1), 120_Hz)); static inline const DisplayModes kDisplay1Modes = makeModes(kDisplay1Mode60, kDisplay1Mode120); + static inline FrameRateMode kDisplay1Mode60_60{60_Hz, kDisplay1Mode60}; + static inline FrameRateMode kDisplay1Mode120_120{120_Hz, kDisplay1Mode120}; + static constexpr PhysicalDisplayId kDisplayId2 = PhysicalDisplayId::fromPort(254u); static inline const ftl::NonNull<DisplayModePtr> kDisplay2Mode60 = ftl::as_non_null(createDisplayMode(kDisplayId2, DisplayModeId(0), 60_Hz)); @@ -118,10 +124,11 @@ SchedulerTest::SchedulerTest() { // createConnection call to scheduler makes a createEventConnection call to EventThread. Make // sure that call gets executed and returns an EventThread::Connection object. - EXPECT_CALL(*mEventThread, createEventConnection(_, _)) + EXPECT_CALL(*mEventThread, createEventConnection(_)) .WillRepeatedly(Return(mEventThreadConnection)); mScheduler->setEventThread(Cycle::Render, std::move(eventThread)); + mScheduler->setEventThread(Cycle::LastComposite, std::make_unique<MockEventThread>()); mFlinger.resetScheduler(mScheduler); } @@ -161,7 +168,17 @@ TEST_F(SchedulerTest, chooseRefreshRateForContentIsNoopWhenModeSwitchingIsNotSup // recordLayerHistory should be a noop ASSERT_EQ(0u, mScheduler->getNumActiveLayers()); - mScheduler->recordLayerHistory(layer->getSequence(), layer->getLayerProps(), 0, 0, + scheduler::LayerProps layerProps = { + .visible = true, + .bounds = {0, 0, 100, 100}, + .transform = {}, + .setFrameRateVote = {}, + .frameRateSelectionPriority = Layer::PRIORITY_UNSET, + .isSmallDirty = false, + .isFrontBuffered = false, + }; + + mScheduler->recordLayerHistory(layer->getSequence(), layerProps, 0, 0, LayerHistory::LayerUpdateType::Buffer); ASSERT_EQ(0u, mScheduler->getNumActiveLayers()); @@ -187,16 +204,53 @@ TEST_F(SchedulerTest, updateDisplayModes) { kDisplay1Mode60->getId())); ASSERT_EQ(0u, mScheduler->getNumActiveLayers()); - mScheduler->recordLayerHistory(layer->getSequence(), layer->getLayerProps(), 0, 0, + scheduler::LayerProps layerProps = { + .visible = true, + .bounds = {0, 0, 100, 100}, + .transform = {}, + .setFrameRateVote = {}, + .frameRateSelectionPriority = Layer::PRIORITY_UNSET, + .isSmallDirty = false, + .isFrontBuffered = false, + }; + mScheduler->recordLayerHistory(layer->getSequence(), layerProps, 0, 0, LayerHistory::LayerUpdateType::Buffer); ASSERT_EQ(1u, mScheduler->getNumActiveLayers()); } -TEST_F(SchedulerTest, dispatchCachedReportedMode) { - mScheduler->clearCachedReportedMode(); +TEST_F(SchedulerTest, emitModeChangeEvent) { + const auto selectorPtr = + std::make_shared<RefreshRateSelector>(kDisplay1Modes, kDisplay1Mode120->getId()); + mScheduler->registerDisplay(kDisplayId1, selectorPtr); + mScheduler->onDisplayModeChanged(kDisplayId1, kDisplay1Mode120_120); + mScheduler->setContentRequirements({kLayer}); + + // No event is emitted in response to idle. EXPECT_CALL(*mEventThread, onModeChanged(_)).Times(0); - EXPECT_NO_FATAL_FAILURE(mScheduler->dispatchCachedReportedMode()); + + using TimerState = TestableScheduler::TimerState; + + mScheduler->idleTimerCallback(TimerState::Expired); + selectorPtr->setActiveMode(kDisplay1Mode60->getId(), 60_Hz); + + auto layer = kLayer; + layer.vote = RefreshRateSelector::LayerVoteType::ExplicitExact; + layer.desiredRefreshRate = 60_Hz; + mScheduler->setContentRequirements({layer}); + + // An event is emitted implicitly despite choosing the same mode as when idle. + EXPECT_CALL(*mEventThread, onModeChanged(kDisplay1Mode60_60)).Times(1); + + mScheduler->idleTimerCallback(TimerState::Reset); + + mScheduler->setContentRequirements({kLayer}); + + // An event is emitted explicitly for the mode change. + EXPECT_CALL(*mEventThread, onModeChanged(kDisplay1Mode120_120)).Times(1); + + mScheduler->touchTimerCallback(TimerState::Reset); + mScheduler->onDisplayModeChanged(kDisplayId1, kDisplay1Mode120_120); } TEST_F(SchedulerTest, calculateMaxAcquiredBufferCount) { @@ -224,9 +278,16 @@ TEST_F(SchedulerTest, chooseRefreshRateForContentSelectsMaxRefreshRate) { kDisplay1Mode60->getId())); const sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger()); - EXPECT_CALL(*layer, isVisible()).WillOnce(Return(true)); - - mScheduler->recordLayerHistory(layer->getSequence(), layer->getLayerProps(), 0, systemTime(), + scheduler::LayerProps layerProps = { + .visible = true, + .bounds = {0, 0, 0, 0}, + .transform = {}, + .setFrameRateVote = {}, + .frameRateSelectionPriority = Layer::PRIORITY_UNSET, + .isSmallDirty = false, + .isFrontBuffered = false, + }; + mScheduler->recordLayerHistory(layer->getSequence(), layerProps, 0, systemTime(), LayerHistory::LayerUpdateType::Buffer); constexpr hal::PowerMode kPowerModeOn = hal::PowerMode::ON; @@ -245,14 +306,12 @@ TEST_F(SchedulerTest, chooseRefreshRateForContentSelectsMaxRefreshRate) { /*updateAttachedChoreographer*/ false); } -TEST_F(SchedulerTest, chooseDisplayModesSingleDisplay) { +TEST_F(SchedulerTest, chooseDisplayModes) { mScheduler->registerDisplay(kDisplayId1, std::make_shared<RefreshRateSelector>(kDisplay1Modes, kDisplay1Mode60->getId())); - std::vector<RefreshRateSelector::LayerRequirement> layers = - std::vector<RefreshRateSelector::LayerRequirement>({{.weight = 1.f}, {.weight = 1.f}}); - mScheduler->setContentRequirements(layers); + mScheduler->setContentRequirements({kLayer, kLayer}); GlobalSignals globalSignals = {.idle = true}; mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals); @@ -287,15 +346,14 @@ TEST_F(SchedulerTest, chooseDisplayModesSingleDisplay) { EXPECT_EQ(choice->get(), DisplayModeChoice({120_Hz, kDisplay1Mode120}, globalSignals)); } -TEST_F(SchedulerTest, chooseDisplayModesSingleDisplayHighHintTouchSignal) { +TEST_F(SchedulerTest, chooseDisplayModesHighHintTouchSignal) { mScheduler->registerDisplay(kDisplayId1, std::make_shared<RefreshRateSelector>(kDisplay1Modes, kDisplay1Mode60->getId())); using DisplayModeChoice = TestableScheduler::DisplayModeChoice; - std::vector<RefreshRateSelector::LayerRequirement> layers = - std::vector<RefreshRateSelector::LayerRequirement>({{.weight = 1.f}, {.weight = 1.f}}); + std::vector<RefreshRateSelector::LayerRequirement> layers = {kLayer, kLayer}; auto& lr1 = layers[0]; auto& lr2 = layers[1]; @@ -370,9 +428,7 @@ TEST_F(SchedulerTest, chooseDisplayModesMultipleDisplays) { kDisplay2Mode60}, GlobalSignals{}); - std::vector<RefreshRateSelector::LayerRequirement> layers = {{.weight = 1.f}, - {.weight = 1.f}}; - mScheduler->setContentRequirements(layers); + mScheduler->setContentRequirements({kLayer, kLayer}); mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals); const auto actualChoices = mScheduler->chooseDisplayModes(); @@ -607,7 +663,8 @@ TEST_F(SchedulerTest, nextFrameIntervalTest) { TimePoint::fromNs(2000))); // Not crossing the min frame period - vrrTracker->onFrameBegin(TimePoint::fromNs(2000), TimePoint::fromNs(1500)); + vrrTracker->onFrameBegin(TimePoint::fromNs(2000), + {TimePoint::fromNs(1500), TimePoint::fromNs(1500)}); EXPECT_EQ(Fps::fromPeriodNsecs(1000), scheduler.getNextFrameInterval(kMode->getPhysicalDisplayId(), TimePoint::fromNs(2500))); @@ -740,7 +797,7 @@ TEST_F(AttachedChoreographerTest, registerMultipleOnSameLayer) { const auto mockConnection1 = sp<MockEventThreadConnection>::make(mEventThread); const auto mockConnection2 = sp<MockEventThreadConnection>::make(mEventThread); - EXPECT_CALL(*mEventThread, createEventConnection(_, _)) + EXPECT_CALL(*mEventThread, createEventConnection(_)) .WillOnce(Return(mockConnection1)) .WillOnce(Return(mockConnection2)); @@ -827,7 +884,6 @@ TEST_F(AttachedChoreographerTest, removedWhenLayerIsGone) { mScheduler->createDisplayEventConnection(Cycle::Render, {}, layer->getHandle()); layer.clear(); - mFlinger.mutableLayersPendingRemoval().clear(); EXPECT_TRUE(mScheduler->mutableAttachedChoreographers().empty()); } diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp deleted file mode 100644 index 9899d4290b..0000000000 --- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp +++ /dev/null @@ -1,390 +0,0 @@ -/* - * Copyright 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#undef LOG_TAG -#define LOG_TAG "LibSurfaceFlingerUnittests" - -#include <gmock/gmock.h> -#include <gtest/gtest.h> -#include <gui/FrameRateUtils.h> -#include <gui/LayerMetadata.h> - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wconversion" -#include "Layer.h" -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic pop // ignored "-Wconversion" -#include "FpsOps.h" -#include "LayerTestUtils.h" -#include "TestableSurfaceFlinger.h" -#include "mock/DisplayHardware/MockComposer.h" -#include "mock/MockVsyncController.h" - -namespace android { - -using testing::DoAll; -using testing::Mock; -using testing::SetArgPointee; - -using android::Hwc2::IComposer; -using android::Hwc2::IComposerClient; - -using scheduler::LayerHistory; - -using FrameRate = Layer::FrameRate; -using FrameRateCompatibility = Layer::FrameRateCompatibility; - -/** - * This class tests the behaviour of Layer::SetFrameRate and Layer::GetFrameRate - */ -class SetFrameRateTest : public BaseLayerTest { -protected: - const FrameRate FRAME_RATE_VOTE1 = FrameRate(67_Hz, FrameRateCompatibility::Default); - const FrameRate FRAME_RATE_VOTE2 = FrameRate(14_Hz, FrameRateCompatibility::ExactOrMultiple); - const FrameRate FRAME_RATE_VOTE3 = FrameRate(99_Hz, FrameRateCompatibility::NoVote); - const FrameRate FRAME_RATE_TREE = FrameRate(Fps(), FrameRateCompatibility::NoVote); - const FrameRate FRAME_RATE_NO_VOTE = FrameRate(Fps(), FrameRateCompatibility::Default); - - SetFrameRateTest(); - - void addChild(sp<Layer> layer, sp<Layer> child); - void removeChild(sp<Layer> layer, sp<Layer> child); - void commitTransaction(); - - std::vector<sp<Layer>> mLayers; -}; - -SetFrameRateTest::SetFrameRateTest() { - const ::testing::TestInfo* const test_info = - ::testing::UnitTest::GetInstance()->current_test_info(); - ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); - - mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>()); -} - -void SetFrameRateTest::addChild(sp<Layer> layer, sp<Layer> child) { - layer->addChild(child); -} - -void SetFrameRateTest::removeChild(sp<Layer> layer, sp<Layer> child) { - layer->removeChild(child); -} - -void SetFrameRateTest::commitTransaction() { - for (auto layer : mLayers) { - layer->commitTransaction(); - } -} - -namespace { - -TEST_P(SetFrameRateTest, SetAndGet) { - EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); - - const auto& layerFactory = GetParam(); - - auto layer = mLayers.emplace_back(layerFactory->createLayer(mFlinger)); - layer->setFrameRate(FRAME_RATE_VOTE1.vote); - commitTransaction(); - EXPECT_EQ(FRAME_RATE_VOTE1, layer->getFrameRateForLayerTree()); -} - -TEST_P(SetFrameRateTest, SetAndGetParent) { - EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); - - const auto& layerFactory = GetParam(); - - auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger)); - auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger)); - auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger)); - - addChild(parent, child1); - addChild(child1, child2); - - child2->setFrameRate(FRAME_RATE_VOTE1.vote); - commitTransaction(); - EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree()); - EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree()); - EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree()); - - child2->setFrameRate(FRAME_RATE_NO_VOTE.vote); - commitTransaction(); - EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree()); - EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree()); - EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree()); -} - -TEST_P(SetFrameRateTest, SetAndGetParentAllVote) { - EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); - - const auto& layerFactory = GetParam(); - - auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger)); - auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger)); - auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger)); - - addChild(parent, child1); - addChild(child1, child2); - - child2->setFrameRate(FRAME_RATE_VOTE1.vote); - child1->setFrameRate(FRAME_RATE_VOTE2.vote); - parent->setFrameRate(FRAME_RATE_VOTE3.vote); - commitTransaction(); - EXPECT_EQ(FRAME_RATE_VOTE3, parent->getFrameRateForLayerTree()); - EXPECT_EQ(FRAME_RATE_VOTE2, child1->getFrameRateForLayerTree()); - EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree()); - - child2->setFrameRate(FRAME_RATE_NO_VOTE.vote); - commitTransaction(); - EXPECT_EQ(FRAME_RATE_VOTE3, parent->getFrameRateForLayerTree()); - EXPECT_EQ(FRAME_RATE_VOTE2, child1->getFrameRateForLayerTree()); - EXPECT_EQ(FRAME_RATE_VOTE2, child2->getFrameRateForLayerTree()); - - child1->setFrameRate(FRAME_RATE_NO_VOTE.vote); - commitTransaction(); - EXPECT_EQ(FRAME_RATE_VOTE3, parent->getFrameRateForLayerTree()); - EXPECT_EQ(FRAME_RATE_VOTE3, child1->getFrameRateForLayerTree()); - EXPECT_EQ(FRAME_RATE_VOTE3, child2->getFrameRateForLayerTree()); - - parent->setFrameRate(FRAME_RATE_NO_VOTE.vote); - commitTransaction(); - EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree()); - EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree()); - EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree()); -} - -TEST_P(SetFrameRateTest, SetAndGetChild) { - EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); - - const auto& layerFactory = GetParam(); - - auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger)); - auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger)); - auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger)); - - addChild(parent, child1); - addChild(child1, child2); - - parent->setFrameRate(FRAME_RATE_VOTE1.vote); - commitTransaction(); - EXPECT_EQ(FRAME_RATE_VOTE1, parent->getFrameRateForLayerTree()); - EXPECT_EQ(FRAME_RATE_VOTE1, child1->getFrameRateForLayerTree()); - EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree()); - - parent->setFrameRate(FRAME_RATE_NO_VOTE.vote); - commitTransaction(); - EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree()); - EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree()); - EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree()); -} - -TEST_P(SetFrameRateTest, SetAndGetChildAllVote) { - EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); - - const auto& layerFactory = GetParam(); - - auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger)); - auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger)); - auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger)); - - addChild(parent, child1); - addChild(child1, child2); - - child2->setFrameRate(FRAME_RATE_VOTE1.vote); - child1->setFrameRate(FRAME_RATE_VOTE2.vote); - parent->setFrameRate(FRAME_RATE_VOTE3.vote); - commitTransaction(); - EXPECT_EQ(FRAME_RATE_VOTE3, parent->getFrameRateForLayerTree()); - EXPECT_EQ(FRAME_RATE_VOTE2, child1->getFrameRateForLayerTree()); - EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree()); - - parent->setFrameRate(FRAME_RATE_NO_VOTE.vote); - commitTransaction(); - EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree()); - EXPECT_EQ(FRAME_RATE_VOTE2, child1->getFrameRateForLayerTree()); - EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree()); - - child1->setFrameRate(FRAME_RATE_NO_VOTE.vote); - commitTransaction(); - EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree()); - EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree()); - EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree()); - - child2->setFrameRate(FRAME_RATE_NO_VOTE.vote); - commitTransaction(); - EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree()); - EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree()); - EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree()); -} - -TEST_P(SetFrameRateTest, SetAndGetChildAddAfterVote) { - EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); - - const auto& layerFactory = GetParam(); - - auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger)); - auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger)); - auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger)); - - addChild(parent, child1); - - parent->setFrameRate(FRAME_RATE_VOTE1.vote); - commitTransaction(); - EXPECT_EQ(FRAME_RATE_VOTE1, parent->getFrameRateForLayerTree()); - EXPECT_EQ(FRAME_RATE_VOTE1, child1->getFrameRateForLayerTree()); - EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree()); - - addChild(child1, child2); - commitTransaction(); - EXPECT_EQ(FRAME_RATE_VOTE1, parent->getFrameRateForLayerTree()); - EXPECT_EQ(FRAME_RATE_VOTE1, child1->getFrameRateForLayerTree()); - EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree()); - - parent->setFrameRate(FRAME_RATE_NO_VOTE.vote); - commitTransaction(); - EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree()); - EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree()); - EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree()); -} - -TEST_P(SetFrameRateTest, SetAndGetChildRemoveAfterVote) { - EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); - - const auto& layerFactory = GetParam(); - - auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger)); - auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger)); - auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger)); - - addChild(parent, child1); - addChild(child1, child2); - - parent->setFrameRate(FRAME_RATE_VOTE1.vote); - commitTransaction(); - EXPECT_EQ(FRAME_RATE_VOTE1, parent->getFrameRateForLayerTree()); - EXPECT_EQ(FRAME_RATE_VOTE1, child1->getFrameRateForLayerTree()); - EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree()); - - removeChild(child1, child2); - commitTransaction(); - EXPECT_EQ(FRAME_RATE_VOTE1, parent->getFrameRateForLayerTree()); - EXPECT_EQ(FRAME_RATE_VOTE1, child1->getFrameRateForLayerTree()); - EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree()); - - parent->setFrameRate(FRAME_RATE_NO_VOTE.vote); - commitTransaction(); - EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree()); - EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree()); - EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree()); -} - -TEST_P(SetFrameRateTest, SetAndGetParentNotInTree) { - EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); - - const auto& layerFactory = GetParam(); - - auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger)); - auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger)); - auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger)); - auto child2_1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger)); - - addChild(parent, child1); - addChild(child1, child2); - addChild(child1, child2_1); - - child2->setFrameRate(FRAME_RATE_VOTE1.vote); - commitTransaction(); - EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree()); - EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree()); - EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree()); - EXPECT_EQ(FRAME_RATE_NO_VOTE, child2_1->getFrameRateForLayerTree()); - - child2->setFrameRate(FRAME_RATE_NO_VOTE.vote); - commitTransaction(); - EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree()); - EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree()); - EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree()); - EXPECT_EQ(FRAME_RATE_NO_VOTE, child2_1->getFrameRateForLayerTree()); -} - -INSTANTIATE_TEST_SUITE_P(PerLayerType, SetFrameRateTest, - testing::Values(std::make_shared<BufferStateLayerFactory>(), - std::make_shared<EffectLayerFactory>()), - PrintToStringParamName); - -TEST_P(SetFrameRateTest, SetOnParentActivatesTree) { - const auto& layerFactory = GetParam(); - - auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger)); - - auto child = mLayers.emplace_back(layerFactory->createLayer(mFlinger)); - addChild(parent, child); - - parent->setFrameRate(FRAME_RATE_VOTE1.vote); - commitTransaction(); - - auto& history = mFlinger.mutableScheduler().mutableLayerHistory(); - history.record(parent->getSequence(), parent->getLayerProps(), 0, 0, - LayerHistory::LayerUpdateType::Buffer); - history.record(child->getSequence(), child->getLayerProps(), 0, 0, - LayerHistory::LayerUpdateType::Buffer); - - const auto selectorPtr = mFlinger.mutableScheduler().refreshRateSelector(); - const auto summary = history.summarize(*selectorPtr, 0); - - ASSERT_EQ(2u, summary.size()); - EXPECT_EQ(FRAME_RATE_VOTE1.vote.rate, summary[0].desiredRefreshRate); - EXPECT_EQ(FRAME_RATE_VOTE1.vote.rate, summary[1].desiredRefreshRate); -} - -TEST_P(SetFrameRateTest, addChildForParentWithTreeVote) { - EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); - - const auto& layerFactory = GetParam(); - - const auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger)); - const auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger)); - const auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger)); - const auto childOfChild1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger)); - - addChild(parent, child1); - addChild(child1, childOfChild1); - - childOfChild1->setFrameRate(FRAME_RATE_VOTE1.vote); - commitTransaction(); - EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree()); - EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree()); - EXPECT_EQ(FRAME_RATE_VOTE1, childOfChild1->getFrameRateForLayerTree()); - EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree()); - - addChild(parent, child2); - commitTransaction(); - EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree()); - EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree()); - EXPECT_EQ(FRAME_RATE_VOTE1, childOfChild1->getFrameRateForLayerTree()); - EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree()); - - childOfChild1->setFrameRate(FRAME_RATE_NO_VOTE.vote); - commitTransaction(); - EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree()); - EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree()); - EXPECT_EQ(FRAME_RATE_NO_VOTE, childOfChild1->getFrameRateForLayerTree()); - EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree()); -} - -} // namespace -} // namespace android diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_ColorMatrixTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_ColorMatrixTest.cpp index ff7612e064..d638024a73 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_ColorMatrixTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_ColorMatrixTest.cpp @@ -28,7 +28,6 @@ namespace android { class ColorMatrixTest : public CommitAndCompositeTest {}; TEST_F(ColorMatrixTest, colorMatrixChanged) { - mFlinger.enableLayerLifecycleManager(); EXPECT_COLOR_MATRIX_CHANGED(true, true); mFlinger.mutableTransactionFlags() |= eTransactionNeeded; @@ -46,7 +45,6 @@ TEST_F(ColorMatrixTest, colorMatrixChanged) { } TEST_F(ColorMatrixTest, colorMatrixChangedAfterDisplayTransaction) { - mFlinger.enableLayerLifecycleManager(); EXPECT_COLOR_MATRIX_CHANGED(true, true); mFlinger.mutableTransactionFlags() |= eTransactionNeeded; diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp index e5f2a9138d..2d3ebb47bd 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp @@ -97,7 +97,7 @@ TEST_F(CreateDisplayTest, createDisplaySetsCurrentStateForNonsecureDisplay) { // Cleanup conditions // Creating the display commits a display transaction. - EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); + EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1); } TEST_F(CreateDisplayTest, createDisplaySetsCurrentStateForSecureDisplay) { @@ -129,7 +129,7 @@ TEST_F(CreateDisplayTest, createDisplaySetsCurrentStateForSecureDisplay) { // Cleanup conditions // Creating the display commits a display transaction. - EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); + EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1); } TEST_F(CreateDisplayTest, createDisplaySetsCurrentStateForUniqueId) { @@ -159,7 +159,7 @@ TEST_F(CreateDisplayTest, createDisplaySetsCurrentStateForUniqueId) { // Cleanup conditions // Creating the display commits a display transaction. - EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); + EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1); } // Requesting 0 tells SF not to do anything, i.e., default to refresh as physical displays diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp index f8ad8e1e1b..df8f68f2b6 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp @@ -38,7 +38,7 @@ TEST_F(DestroyDisplayTest, destroyDisplayClearsCurrentStateForDisplay) { // Call Expectations // Destroying the display commits a display transaction. - EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); + EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1); // -------------------------------------------------------------------- // Invocation diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp index 0c3e875432..86996214b5 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp @@ -42,6 +42,47 @@ namespace { using android::hardware::graphics::composer::V2_4::Error; using android::hardware::graphics::composer::V2_4::VsyncPeriodChangeTimeline; +MATCHER_P2(ModeSettledTo, dmc, modeId, "") { + const auto displayId = arg->getPhysicalId(); + + if (const auto desiredOpt = dmc->getDesiredMode(displayId)) { + *result_listener << "Unsettled desired mode " + << ftl::to_underlying(desiredOpt->mode.modePtr->getId()); + return false; + } + + if (dmc->getActiveMode(displayId).modePtr->getId() != modeId) { + *result_listener << "Settled to unexpected active mode " << ftl::to_underlying(modeId); + return false; + } + + return true; +} + +MATCHER_P2(ModeSwitchingTo, flinger, modeId, "") { + const auto displayId = arg->getPhysicalId(); + auto& dmc = flinger->mutableDisplayModeController(); + + if (!dmc.getDesiredMode(displayId)) { + *result_listener << "No desired mode"; + return false; + } + + if (dmc.getDesiredMode(displayId)->mode.modePtr->getId() != modeId) { + *result_listener << "Unexpected desired mode " << ftl::to_underlying(modeId); + return false; + } + + // VsyncModulator should react to mode switches on the pacesetter display. + if (displayId == flinger->scheduler()->pacesetterDisplayId() && + !flinger->scheduler()->vsyncModulator().isVsyncConfigEarly()) { + *result_listener << "VsyncModulator did not shift to early phase"; + return false; + } + + return true; +} + class DisplayModeSwitchingTest : public DisplayTransactionTest { public: void SetUp() override { @@ -58,8 +99,7 @@ public: setupScheduler(selectorPtr); - mFlinger.onComposerHalHotplugEvent(PrimaryDisplayVariant::HWC_DISPLAY_ID, - DisplayHotplugEvent::CONNECTED); + mFlinger.onComposerHalHotplugEvent(kInnerDisplayHwcId, DisplayHotplugEvent::CONNECTED); mFlinger.configureAndCommit(); auto vsyncController = std::make_unique<mock::VsyncController>(); @@ -87,8 +127,13 @@ public: static constexpr HWDisplayId kInnerDisplayHwcId = PrimaryDisplayVariant::HWC_DISPLAY_ID; static constexpr HWDisplayId kOuterDisplayHwcId = kInnerDisplayHwcId + 1; + static constexpr PhysicalDisplayId kOuterDisplayId = PhysicalDisplayId::fromPort(254u); + auto injectOuterDisplay() { - constexpr PhysicalDisplayId kOuterDisplayId = PhysicalDisplayId::fromPort(254u); + // For the inner display, this is handled by setupHwcHotplugCallExpectations. + EXPECT_CALL(*mComposer, getDisplayConnectionType(kOuterDisplayHwcId, _)) + .WillOnce(DoAll(SetArgPointee<1>(IComposerClient::DisplayConnectionType::INTERNAL), + Return(hal::V2_4::Error::NONE))); constexpr bool kIsPrimary = false; TestableSurfaceFlinger::FakeHwcDisplayInjector(kOuterDisplayId, hal::DisplayType::PHYSICAL, @@ -142,16 +187,6 @@ void DisplayModeSwitchingTest::setupScheduler( mAppEventThread = eventThread.get(); auto sfEventThread = std::make_unique<mock::EventThread>(); - EXPECT_CALL(*eventThread, registerDisplayEventConnection(_)); - EXPECT_CALL(*eventThread, createEventConnection(_, _)) - .WillOnce(Return(sp<EventThreadConnection>::make(eventThread.get(), - mock::EventThread::kCallingUid))); - - EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_)); - EXPECT_CALL(*sfEventThread, createEventConnection(_, _)) - .WillOnce(Return(sp<EventThreadConnection>::make(sfEventThread.get(), - mock::EventThread::kCallingUid))); - auto vsyncController = std::make_unique<mock::VsyncController>(); auto vsyncTracker = std::make_shared<mock::VSyncTracker>(); @@ -169,129 +204,139 @@ void DisplayModeSwitchingTest::setupScheduler( TestableSurfaceFlinger::SchedulerCallbackImpl::kNoOp); } -TEST_F(DisplayModeSwitchingTest, changeRefreshRateOnActiveDisplayWithRefreshRequired) { - ftl::FakeGuard guard(kMainThreadContext); - - EXPECT_FALSE(dmc().getDesiredMode(mDisplayId)); - EXPECT_EQ(dmc().getActiveMode(mDisplayId).modePtr->getId(), kModeId60); - - mFlinger.onActiveDisplayChanged(nullptr, *mDisplay); +TEST_F(DisplayModeSwitchingTest, changeRefreshRateWithRefreshRequired) { + EXPECT_THAT(mDisplay, ModeSettledTo(&dmc(), kModeId60)); - mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), - mock::createDisplayModeSpecs(kModeId90, false, 0, 120)); + EXPECT_EQ(NO_ERROR, + mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), + mock::createDisplayModeSpecs(kModeId90, 120_Hz))); - ASSERT_TRUE(dmc().getDesiredMode(mDisplayId)); - EXPECT_EQ(dmc().getDesiredMode(mDisplayId)->mode.modePtr->getId(), kModeId90); - EXPECT_EQ(dmc().getActiveMode(mDisplayId).modePtr->getId(), kModeId60); + EXPECT_THAT(mDisplay, ModeSwitchingTo(&mFlinger, kModeId90)); // Verify that next commit will call setActiveConfigWithConstraints in HWC const VsyncPeriodChangeTimeline timeline{.refreshRequired = true}; - EXPECT_SET_ACTIVE_CONFIG(PrimaryDisplayVariant::HWC_DISPLAY_ID, kModeId90); + EXPECT_SET_ACTIVE_CONFIG(kInnerDisplayHwcId, kModeId90); mFlinger.commit(); - Mock::VerifyAndClearExpectations(mComposer); - EXPECT_TRUE(dmc().getDesiredMode(mDisplayId)); - EXPECT_EQ(dmc().getActiveMode(mDisplayId).modePtr->getId(), kModeId60); + EXPECT_THAT(mDisplay, ModeSwitchingTo(&mFlinger, kModeId90)); // Verify that the next commit will complete the mode change and send // a onModeChanged event to the framework. EXPECT_CALL(*mAppEventThread, onModeChanged(scheduler::FrameRateMode{90_Hz, ftl::as_non_null(kMode90)})); + mFlinger.commit(); Mock::VerifyAndClearExpectations(mAppEventThread); - EXPECT_FALSE(dmc().getDesiredMode(mDisplayId)); - EXPECT_EQ(dmc().getActiveMode(mDisplayId).modePtr->getId(), kModeId90); + EXPECT_THAT(mDisplay, ModeSettledTo(&dmc(), kModeId90)); } -TEST_F(DisplayModeSwitchingTest, changeRefreshRateOnActiveDisplayWithoutRefreshRequired) { - ftl::FakeGuard guard(kMainThreadContext); - - EXPECT_FALSE(dmc().getDesiredMode(mDisplayId)); - - mFlinger.onActiveDisplayChanged(nullptr, *mDisplay); +TEST_F(DisplayModeSwitchingTest, changeRefreshRateWithoutRefreshRequired) { + EXPECT_THAT(mDisplay, ModeSettledTo(&dmc(), kModeId60)); - mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), - mock::createDisplayModeSpecs(kModeId90, true, 0, 120)); + constexpr bool kAllowGroupSwitching = true; + EXPECT_EQ(NO_ERROR, + mFlinger.setDesiredDisplayModeSpecs( + mDisplay->getDisplayToken().promote(), + mock::createDisplayModeSpecs(kModeId90, 120_Hz, kAllowGroupSwitching))); - ASSERT_TRUE(dmc().getDesiredMode(mDisplayId)); - EXPECT_EQ(dmc().getDesiredMode(mDisplayId)->mode.modePtr->getId(), kModeId90); - EXPECT_EQ(dmc().getActiveMode(mDisplayId).modePtr->getId(), kModeId60); + EXPECT_THAT(mDisplay, ModeSwitchingTo(&mFlinger, kModeId90)); // Verify that next commit will call setActiveConfigWithConstraints in HWC // and complete the mode change. const VsyncPeriodChangeTimeline timeline{.refreshRequired = false}; - EXPECT_SET_ACTIVE_CONFIG(PrimaryDisplayVariant::HWC_DISPLAY_ID, kModeId90); + EXPECT_SET_ACTIVE_CONFIG(kInnerDisplayHwcId, kModeId90); EXPECT_CALL(*mAppEventThread, onModeChanged(scheduler::FrameRateMode{90_Hz, ftl::as_non_null(kMode90)})); mFlinger.commit(); - EXPECT_FALSE(dmc().getDesiredMode(mDisplayId)); - EXPECT_EQ(dmc().getActiveMode(mDisplayId).modePtr->getId(), kModeId90); + EXPECT_THAT(mDisplay, ModeSettledTo(&dmc(), kModeId90)); } -TEST_F(DisplayModeSwitchingTest, twoConsecutiveSetDesiredDisplayModeSpecs) { - ftl::FakeGuard guard(kMainThreadContext); +TEST_F(DisplayModeSwitchingTest, changeRefreshRateOnTwoDisplaysWithoutRefreshRequired) { + const auto [innerDisplay, outerDisplay] = injectOuterDisplay(); + EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId60)); + EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId120)); + + EXPECT_EQ(NO_ERROR, + mFlinger.setDesiredDisplayModeSpecs(innerDisplay->getDisplayToken().promote(), + mock::createDisplayModeSpecs(kModeId90, 120_Hz, + true))); + EXPECT_EQ(NO_ERROR, + mFlinger.setDesiredDisplayModeSpecs(outerDisplay->getDisplayToken().promote(), + mock::createDisplayModeSpecs(kModeId60, 60_Hz, + true))); + + EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90)); + EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60)); + + // Verify that next commit will call setActiveConfigWithConstraints in HWC + // and complete the mode change. + const VsyncPeriodChangeTimeline timeline{.refreshRequired = false}; + EXPECT_SET_ACTIVE_CONFIG(kInnerDisplayHwcId, kModeId90); + EXPECT_SET_ACTIVE_CONFIG(kOuterDisplayHwcId, kModeId60); + + EXPECT_CALL(*mAppEventThread, onModeChanged(_)).Times(2); + + mFlinger.commit(); + + EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId90)); + EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId60)); +} + +TEST_F(DisplayModeSwitchingTest, twoConsecutiveSetDesiredDisplayModeSpecs) { // Test that if we call setDesiredDisplayModeSpecs while a previous mode change // is still being processed the later call will be respected. - EXPECT_FALSE(dmc().getDesiredMode(mDisplayId)); - EXPECT_EQ(dmc().getActiveMode(mDisplayId).modePtr->getId(), kModeId60); - - mFlinger.onActiveDisplayChanged(nullptr, *mDisplay); + EXPECT_THAT(mDisplay, ModeSettledTo(&dmc(), kModeId60)); - mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), - mock::createDisplayModeSpecs(kModeId90, false, 0, 120)); + EXPECT_EQ(NO_ERROR, + mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), + mock::createDisplayModeSpecs(kModeId90, 120_Hz))); const VsyncPeriodChangeTimeline timeline{.refreshRequired = true}; - EXPECT_SET_ACTIVE_CONFIG(PrimaryDisplayVariant::HWC_DISPLAY_ID, kModeId90); + EXPECT_SET_ACTIVE_CONFIG(kInnerDisplayHwcId, kModeId90); mFlinger.commit(); - mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), - mock::createDisplayModeSpecs(kModeId120, false, 0, 180)); + EXPECT_EQ(NO_ERROR, + mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), + mock::createDisplayModeSpecs(kModeId120, + 180_Hz))); - ASSERT_TRUE(dmc().getDesiredMode(mDisplayId)); - EXPECT_EQ(dmc().getDesiredMode(mDisplayId)->mode.modePtr->getId(), kModeId120); + EXPECT_THAT(mDisplay, ModeSwitchingTo(&mFlinger, kModeId120)); - EXPECT_SET_ACTIVE_CONFIG(PrimaryDisplayVariant::HWC_DISPLAY_ID, kModeId120); + EXPECT_SET_ACTIVE_CONFIG(kInnerDisplayHwcId, kModeId120); mFlinger.commit(); - ASSERT_TRUE(dmc().getDesiredMode(mDisplayId)); - EXPECT_EQ(dmc().getDesiredMode(mDisplayId)->mode.modePtr->getId(), kModeId120); + EXPECT_THAT(mDisplay, ModeSwitchingTo(&mFlinger, kModeId120)); mFlinger.commit(); - EXPECT_FALSE(dmc().getDesiredMode(mDisplayId)); - EXPECT_EQ(dmc().getActiveMode(mDisplayId).modePtr->getId(), kModeId120); + EXPECT_THAT(mDisplay, ModeSettledTo(&dmc(), kModeId120)); } -TEST_F(DisplayModeSwitchingTest, changeResolutionOnActiveDisplayWithoutRefreshRequired) { - ftl::FakeGuard guard(kMainThreadContext); - - EXPECT_FALSE(dmc().getDesiredMode(mDisplayId)); - EXPECT_EQ(dmc().getActiveMode(mDisplayId).modePtr->getId(), kModeId60); - - mFlinger.onActiveDisplayChanged(nullptr, *mDisplay); +TEST_F(DisplayModeSwitchingTest, changeResolutionWithoutRefreshRequired) { + EXPECT_THAT(mDisplay, ModeSettledTo(&dmc(), kModeId60)); - mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), - mock::createDisplayModeSpecs(kModeId90_4K, false, 0, 120)); + EXPECT_EQ(NO_ERROR, + mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), + mock::createDisplayModeSpecs(kModeId90_4K, + 120_Hz))); - ASSERT_TRUE(dmc().getDesiredMode(mDisplayId)); - EXPECT_EQ(dmc().getDesiredMode(mDisplayId)->mode.modePtr->getId(), kModeId90_4K); - EXPECT_EQ(dmc().getActiveMode(mDisplayId).modePtr->getId(), kModeId60); + EXPECT_THAT(mDisplay, ModeSwitchingTo(&mFlinger, kModeId90_4K)); // Verify that next commit will call setActiveConfigWithConstraints in HWC // and complete the mode change. const VsyncPeriodChangeTimeline timeline{.refreshRequired = false}; - EXPECT_SET_ACTIVE_CONFIG(PrimaryDisplayVariant::HWC_DISPLAY_ID, kModeId90_4K); + EXPECT_SET_ACTIVE_CONFIG(kInnerDisplayHwcId, kModeId90_4K); EXPECT_CALL(*mAppEventThread, onHotplugReceived(mDisplayId, true)); @@ -310,61 +355,12 @@ TEST_F(DisplayModeSwitchingTest, changeResolutionOnActiveDisplayWithoutRefreshRe mFlinger.commit(); - EXPECT_FALSE(dmc().getDesiredMode(mDisplayId)); - EXPECT_EQ(dmc().getActiveMode(mDisplayId).modePtr->getId(), kModeId90_4K); -} - -MATCHER_P2(ModeSwitchingTo, flinger, modeId, "") { - const auto displayId = arg->getPhysicalId(); - auto& dmc = flinger->mutableDisplayModeController(); - - if (!dmc.getDesiredMode(displayId)) { - *result_listener << "No desired mode"; - return false; - } - - if (dmc.getDesiredMode(displayId)->mode.modePtr->getId() != modeId) { - *result_listener << "Unexpected desired mode " << ftl::to_underlying(modeId); - return false; - } - - // VsyncModulator should react to mode switches on the pacesetter display. - if (displayId == flinger->scheduler()->pacesetterDisplayId() && - !flinger->scheduler()->vsyncModulator().isVsyncConfigEarly()) { - *result_listener << "VsyncModulator did not shift to early phase"; - return false; - } - - return true; -} - -MATCHER_P2(ModeSettledTo, dmc, modeId, "") { - const auto displayId = arg->getPhysicalId(); - - if (const auto desiredOpt = dmc->getDesiredMode(displayId)) { - *result_listener << "Unsettled desired mode " - << ftl::to_underlying(desiredOpt->mode.modePtr->getId()); - return false; - } - - ftl::FakeGuard guard(kMainThreadContext); - - if (dmc->getActiveMode(displayId).modePtr->getId() != modeId) { - *result_listener << "Settled to unexpected active mode " << ftl::to_underlying(modeId); - return false; - } - - return true; + EXPECT_THAT(mDisplay, ModeSettledTo(&dmc(), kModeId90_4K)); } TEST_F(DisplayModeSwitchingTest, innerXorOuterDisplay) { SET_FLAG_FOR_TEST(flags::connected_display, true); - // For the inner display, this is handled by setupHwcHotplugCallExpectations. - EXPECT_CALL(*mComposer, getDisplayConnectionType(kOuterDisplayHwcId, _)) - .WillOnce(DoAll(SetArgPointee<1>(IComposerClient::DisplayConnectionType::INTERNAL), - Return(hal::V2_4::Error::NONE))); - const auto [innerDisplay, outerDisplay] = injectOuterDisplay(); EXPECT_TRUE(innerDisplay->isPoweredOn()); @@ -381,13 +377,11 @@ TEST_F(DisplayModeSwitchingTest, innerXorOuterDisplay) { EXPECT_EQ(NO_ERROR, mFlinger.setDesiredDisplayModeSpecs(innerDisplay->getDisplayToken().promote(), - mock::createDisplayModeSpecs(kModeId90, false, - 0.f, 120.f))); + mock::createDisplayModeSpecs(kModeId90, 120_Hz))); EXPECT_EQ(NO_ERROR, mFlinger.setDesiredDisplayModeSpecs(outerDisplay->getDisplayToken().promote(), - mock::createDisplayModeSpecs(kModeId60, false, - 0.f, 120.f))); + mock::createDisplayModeSpecs(kModeId60, 120_Hz))); EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90)); EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60)); @@ -414,8 +408,7 @@ TEST_F(DisplayModeSwitchingTest, innerXorOuterDisplay) { EXPECT_EQ(NO_ERROR, mFlinger.setDesiredDisplayModeSpecs(innerDisplay->getDisplayToken().promote(), - mock::createDisplayModeSpecs(kModeId60, false, - 0.f, 120.f))); + mock::createDisplayModeSpecs(kModeId60, 120_Hz))); EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId60)); EXPECT_SET_ACTIVE_CONFIG(kInnerDisplayHwcId, kModeId60); @@ -434,10 +427,6 @@ TEST_F(DisplayModeSwitchingTest, innerXorOuterDisplay) { TEST_F(DisplayModeSwitchingTest, innerAndOuterDisplay) { SET_FLAG_FOR_TEST(flags::connected_display, true); - // For the inner display, this is handled by setupHwcHotplugCallExpectations. - EXPECT_CALL(*mComposer, getDisplayConnectionType(kOuterDisplayHwcId, _)) - .WillOnce(DoAll(SetArgPointee<1>(IComposerClient::DisplayConnectionType::INTERNAL), - Return(hal::V2_4::Error::NONE))); const auto [innerDisplay, outerDisplay] = injectOuterDisplay(); EXPECT_TRUE(innerDisplay->isPoweredOn()); @@ -454,13 +443,11 @@ TEST_F(DisplayModeSwitchingTest, innerAndOuterDisplay) { EXPECT_EQ(NO_ERROR, mFlinger.setDesiredDisplayModeSpecs(innerDisplay->getDisplayToken().promote(), - mock::createDisplayModeSpecs(kModeId90, false, - 0.f, 120.f))); + mock::createDisplayModeSpecs(kModeId90, 120_Hz))); EXPECT_EQ(NO_ERROR, mFlinger.setDesiredDisplayModeSpecs(outerDisplay->getDisplayToken().promote(), - mock::createDisplayModeSpecs(kModeId60, false, - 0.f, 120.f))); + mock::createDisplayModeSpecs(kModeId60, 120_Hz))); EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90)); EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60)); @@ -486,8 +473,7 @@ TEST_F(DisplayModeSwitchingTest, powerOffDuringModeSet) { EXPECT_EQ(NO_ERROR, mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), - mock::createDisplayModeSpecs(kModeId90, false, - 0.f, 120.f))); + mock::createDisplayModeSpecs(kModeId90, 120_Hz))); EXPECT_THAT(mDisplay, ModeSwitchingTo(&mFlinger, kModeId90)); @@ -511,11 +497,6 @@ TEST_F(DisplayModeSwitchingTest, powerOffDuringModeSet) { TEST_F(DisplayModeSwitchingTest, powerOffDuringConcurrentModeSet) { SET_FLAG_FOR_TEST(flags::connected_display, true); - // For the inner display, this is handled by setupHwcHotplugCallExpectations. - EXPECT_CALL(*mComposer, getDisplayConnectionType(kOuterDisplayHwcId, _)) - .WillOnce(DoAll(SetArgPointee<1>(IComposerClient::DisplayConnectionType::INTERNAL), - Return(hal::V2_4::Error::NONE))); - const auto [innerDisplay, outerDisplay] = injectOuterDisplay(); EXPECT_TRUE(innerDisplay->isPoweredOn()); @@ -532,13 +513,11 @@ TEST_F(DisplayModeSwitchingTest, powerOffDuringConcurrentModeSet) { EXPECT_EQ(NO_ERROR, mFlinger.setDesiredDisplayModeSpecs(innerDisplay->getDisplayToken().promote(), - mock::createDisplayModeSpecs(kModeId90, false, - 0.f, 120.f))); + mock::createDisplayModeSpecs(kModeId90, 120_Hz))); EXPECT_EQ(NO_ERROR, mFlinger.setDesiredDisplayModeSpecs(outerDisplay->getDisplayToken().promote(), - mock::createDisplayModeSpecs(kModeId60, false, - 0.f, 120.f))); + mock::createDisplayModeSpecs(kModeId60, 120_Hz))); EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90)); EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60)); @@ -566,8 +545,8 @@ TEST_F(DisplayModeSwitchingTest, powerOffDuringConcurrentModeSet) { EXPECT_EQ(NO_ERROR, mFlinger.setDesiredDisplayModeSpecs(outerDisplay->getDisplayToken().promote(), - mock::createDisplayModeSpecs(kModeId120, false, - 0.f, 120.f))); + mock::createDisplayModeSpecs(kModeId120, + 120_Hz))); EXPECT_SET_ACTIVE_CONFIG(kOuterDisplayHwcId, kModeId120); diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp index b620830357..9bf344c7af 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp @@ -265,6 +265,13 @@ TEST_F(DisplayTransactionCommitTest, processesHotplugConnectExternalDisplay) { processesHotplugConnectCommon<SimpleExternalDisplayCase>(); } +TEST_F(DisplayTransactionCommitTest, processesHotplugConnectNonSecureExternalDisplay) { + // Inject a primary display. + PrimaryDisplayVariant::injectHwcDisplay(this); + + processesHotplugConnectCommon<SimpleExternalDisplayNonSecureCase>(); +} + TEST_F(DisplayTransactionCommitTest, ignoresHotplugConnectIfPrimaryAndExternalAlreadyConnected) { // Inject both a primary and external display. PrimaryDisplayVariant::injectHwcDisplay(this); @@ -273,13 +280,29 @@ TEST_F(DisplayTransactionCommitTest, ignoresHotplugConnectIfPrimaryAndExternalAl // TODO: This is an unnecessary call. EXPECT_CALL(*mComposer, getDisplayIdentificationData(TertiaryDisplayVariant::HWC_DISPLAY_ID, _, _)) - .WillOnce(DoAll(SetArgPointee<1>(TertiaryDisplay::PORT), - SetArgPointee<2>(TertiaryDisplay::GET_IDENTIFICATION_DATA()), + .WillOnce(DoAll(SetArgPointee<1>(TertiaryDisplay<kSecure>::PORT), + SetArgPointee<2>(TertiaryDisplay<kSecure>::GET_IDENTIFICATION_DATA()), Return(Error::NONE))); ignoresHotplugConnectCommon<SimpleTertiaryDisplayCase>(); } +TEST_F(DisplayTransactionCommitTest, + ignoresHotplugConnectNonSecureIfPrimaryAndExternalAlreadyConnected) { + // Inject both a primary and external display. + PrimaryDisplayVariant::injectHwcDisplay(this); + ExternalDisplayVariant::injectHwcDisplay(this); + + // TODO: This is an unnecessary call. + EXPECT_CALL(*mComposer, + getDisplayIdentificationData(TertiaryDisplayVariant::HWC_DISPLAY_ID, _, _)) + .WillOnce(DoAll(SetArgPointee<1>(TertiaryDisplay<kSecure>::PORT), + SetArgPointee<2>(TertiaryDisplay<kSecure>::GET_IDENTIFICATION_DATA()), + Return(Error::NONE))); + + ignoresHotplugConnectCommon<SimpleTertiaryDisplayNonSecureCase>(); +} + TEST_F(DisplayTransactionCommitTest, processesHotplugDisconnectPrimaryDisplay) { EXPECT_EXIT(processesHotplugDisconnectCommon<SimplePrimaryDisplayCase>(), testing::KilledBySignal(SIGABRT), "Primary display cannot be disconnected."); @@ -289,6 +312,10 @@ TEST_F(DisplayTransactionCommitTest, processesHotplugDisconnectExternalDisplay) processesHotplugDisconnectCommon<SimpleExternalDisplayCase>(); } +TEST_F(DisplayTransactionCommitTest, processesHotplugDisconnectNonSecureExternalDisplay) { + processesHotplugDisconnectCommon<SimpleExternalDisplayNonSecureCase>(); +} + TEST_F(DisplayTransactionCommitTest, processesHotplugConnectThenDisconnectPrimary) { EXPECT_EXIT( [this] { diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HdrOutputControlTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HdrOutputControlTest.cpp index db6df229d5..4bc134fae8 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HdrOutputControlTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HdrOutputControlTest.cpp @@ -18,7 +18,7 @@ #define LOG_TAG "LibSurfaceFlingerUnittests" #include <gtest/gtest.h> -#include <gui/AidlStatusUtil.h> +#include <gui/AidlUtil.h> #include <private/gui/ComposerService.h> #include <private/gui/ComposerServiceAIDL.h> diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp index 897f9a0319..aef467ab9d 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp @@ -48,7 +48,7 @@ TEST_F(HotplugTest, schedulesConfigureToProcessHotplugEvents) { TEST_F(HotplugTest, schedulesFrameToCommitDisplayTransaction) { EXPECT_CALL(*mFlinger.scheduler(), scheduleConfigure()).Times(1); - EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); + EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1); constexpr HWDisplayId displayId1 = 456; mFlinger.onComposerHalHotplugEvent(displayId1, DisplayHotplugEvent::DISCONNECTED); @@ -73,7 +73,7 @@ TEST_F(HotplugTest, ignoresDuplicateDisconnection) { .WillOnce(Return(Error::NONE)); // A single commit should be scheduled for both configure calls. - EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); + EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1); ExternalDisplay::injectPendingHotplugEvent(this, Connection::CONNECTED); mFlinger.configure(); @@ -116,7 +116,7 @@ TEST_F(HotplugTest, rejectsHotplugIfFailedToLoadDisplayModes) { setVsyncEnabled(ExternalDisplay::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE)) .WillOnce(Return(Error::NONE)); - EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); + EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1); ExternalDisplay::injectPendingHotplugEvent(this, Connection::CONNECTED); mFlinger.configure(); diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_InitializeDisplaysTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_InitializeDisplaysTest.cpp index eaf468432c..5231965277 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_InitializeDisplaysTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_InitializeDisplaysTest.cpp @@ -28,7 +28,7 @@ struct InitializeDisplaysTest : DualDisplayTransactionTest<hal::PowerMode::OFF, TEST_F(InitializeDisplaysTest, initializesDisplays) { // Scheduled by the display transaction, and by powering on each display. - EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(3); + EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(3); EXPECT_CALL(static_cast<mock::VSyncTracker&>( mFlinger.scheduler()->getVsyncSchedule()->getTracker()), diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp index 83e2f980ce..fed7b2e767 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp @@ -271,7 +271,7 @@ struct DisplayPowerCase { } static void setupRepaintEverythingCallExpectations(DisplayTransactionTest* test) { - EXPECT_CALL(*test->mFlinger.scheduler(), scheduleFrame()).Times(1); + EXPECT_CALL(*test->mFlinger.scheduler(), scheduleFrame(_)).Times(1); } static void setupComposerCallExpectations(DisplayTransactionTest* test, PowerMode mode) { diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp index 933d03dac1..352000ef9a 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp @@ -239,7 +239,7 @@ void SetupNewDisplayDeviceInternalTest::setupNewDisplayDeviceInternalTest() { ASSERT_TRUE(displayId); const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value; ASSERT_TRUE(hwcDisplayId); - mFlinger.getHwComposer().allocatePhysicalDisplay(*hwcDisplayId, *displayId); + mFlinger.getHwComposer().allocatePhysicalDisplay(*hwcDisplayId, *displayId, std::nullopt); DisplayModePtr activeMode = DisplayMode::Builder(Case::Display::HWC_ACTIVE_CONFIG_ID) .setResolution(Case::Display::RESOLUTION) .setVsyncPeriod(DEFAULT_VSYNC_PERIOD) diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp deleted file mode 100644 index 0e5f1ea789..0000000000 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp +++ /dev/null @@ -1,194 +0,0 @@ -#include <gmock/gmock.h> -#include <gtest/gtest.h> -#include <gui/LayerMetadata.h> - -#include "TestableSurfaceFlinger.h" - -namespace android { - -using testing::_; -using testing::Return; - -class SurfaceFlingerUpdateLayerMetadataSnapshotTest : public testing::Test { -public: - SurfaceFlingerUpdateLayerMetadataSnapshotTest() { mFlinger.setupMockScheduler(); } - -protected: - sp<Layer> createLayer(const char* name, LayerMetadata& inOutlayerMetadata) { - LayerCreationArgs args = - LayerCreationArgs{mFlinger.flinger(), nullptr, name, 0, inOutlayerMetadata}; - inOutlayerMetadata = args.metadata; - return sp<Layer>::make(args); - } - - TestableSurfaceFlinger mFlinger; -}; - -class LayerMetadataBuilder { -public: - LayerMetadataBuilder(LayerMetadata layerMetadata = {}) : mLayerMetadata(layerMetadata) {} - - LayerMetadataBuilder& setInt32(uint32_t key, int32_t value) { - mLayerMetadata.setInt32(key, value); - return *this; - } - - LayerMetadata build() { return mLayerMetadata; } - -private: - LayerMetadata mLayerMetadata; -}; - -bool operator==(const LayerMetadata& lhs, const LayerMetadata& rhs) { - return lhs.mMap == rhs.mMap; -} - -std::ostream& operator<<(std::ostream& stream, const LayerMetadata& layerMetadata) { - stream << "LayerMetadata{"; - for (auto it = layerMetadata.mMap.cbegin(); it != layerMetadata.mMap.cend(); it++) { - if (it != layerMetadata.mMap.cbegin()) { - stream << ", "; - } - stream << layerMetadata.itemToString(it->first, ":"); - } - return stream << "}"; -} - -// Test that the snapshot's layer metadata is set. -TEST_F(SurfaceFlingerUpdateLayerMetadataSnapshotTest, updatesSnapshotMetadata) { - auto layerMetadata = LayerMetadataBuilder().setInt32(METADATA_TASK_ID, 1).build(); - auto layer = createLayer("layer", layerMetadata); - mFlinger.mutableDrawingState().layersSortedByZ.add(layer); - - mFlinger.updateLayerMetadataSnapshot(); - - EXPECT_EQ(layer->getLayerSnapshot()->layerMetadata, layerMetadata); -} - -// Test that snapshot layer metadata is set by merging the child's metadata on top of its -// parent's metadata. -TEST_F(SurfaceFlingerUpdateLayerMetadataSnapshotTest, mergesSnapshotMetadata) { - auto layerAMetadata = LayerMetadataBuilder() - .setInt32(METADATA_OWNER_UID, 1) - .setInt32(METADATA_TASK_ID, 2) - .build(); - auto layerA = createLayer("parent", layerAMetadata); - auto layerBMetadata = LayerMetadataBuilder().setInt32(METADATA_TASK_ID, 3).build(); - auto layerB = createLayer("child", layerBMetadata); - layerA->addChild(layerB); - layerA->commitChildList(); - mFlinger.mutableDrawingState().layersSortedByZ.add(layerA); - - mFlinger.updateLayerMetadataSnapshot(); - - EXPECT_EQ(layerA->getLayerSnapshot()->layerMetadata, layerAMetadata); - auto expectedChildMetadata = - LayerMetadataBuilder(layerAMetadata).setInt32(METADATA_TASK_ID, 3).build(); - EXPECT_EQ(layerB->getLayerSnapshot()->layerMetadata, expectedChildMetadata); -} - -// Test that snapshot relative layer metadata is set to the parent's layer metadata merged on top of -// that parent's relative layer metadata. -TEST_F(SurfaceFlingerUpdateLayerMetadataSnapshotTest, updatesRelativeMetadata) { - auto layerAMetadata = LayerMetadataBuilder().setInt32(METADATA_TASK_ID, 1).build(); - auto layerA = createLayer("relative-parent", layerAMetadata); - auto layerAHandle = layerA->getHandle(); - auto layerBMetadata = LayerMetadataBuilder().setInt32(METADATA_TASK_ID, 2).build(); - auto layerB = createLayer("relative-child", layerBMetadata); - layerB->setRelativeLayer(layerAHandle, 1); - mFlinger.mutableDrawingState().layersSortedByZ.add(layerA); - mFlinger.mutableDrawingState().layersSortedByZ.add(layerB); - - mFlinger.updateLayerMetadataSnapshot(); - - EXPECT_EQ(layerA->getLayerSnapshot()->relativeLayerMetadata, LayerMetadata{}); - EXPECT_EQ(layerB->getLayerSnapshot()->relativeLayerMetadata, layerAMetadata); -} - -// Test that snapshot relative layer metadata is set correctly when a layer is interleaved within -// two other layers. -// -// Layer -// A -// / \ -// B D -// / -// C -// -// Z-order Relatives -// B <- D <- C -TEST_F(SurfaceFlingerUpdateLayerMetadataSnapshotTest, updatesRelativeMetadataInterleaved) { - auto layerAMetadata = LayerMetadataBuilder().setInt32(METADATA_OWNER_UID, 1).build(); - auto layerA = createLayer("layer-a", layerAMetadata); - auto layerBMetadata = LayerMetadataBuilder() - .setInt32(METADATA_TASK_ID, 2) - .setInt32(METADATA_OWNER_PID, 3) - .build(); - auto layerB = createLayer("layer-b", layerBMetadata); - auto layerBHandle = layerB->getHandle(); - LayerMetadata layerCMetadata; - auto layerC = createLayer("layer-c", layerCMetadata); - auto layerDMetadata = LayerMetadataBuilder().setInt32(METADATA_TASK_ID, 4).build(); - auto layerD = createLayer("layer-d", layerDMetadata); - auto layerDHandle = layerD->getHandle(); - layerB->addChild(layerC); - layerA->addChild(layerB); - layerA->addChild(layerD); - layerC->setRelativeLayer(layerDHandle, 1); - layerD->setRelativeLayer(layerBHandle, 1); - layerA->commitChildList(); - mFlinger.mutableDrawingState().layersSortedByZ.add(layerA); - - mFlinger.updateLayerMetadataSnapshot(); - - auto expectedLayerDRelativeMetadata = - LayerMetadataBuilder() - // From layer A, parent of relative parent - .setInt32(METADATA_OWNER_UID, 1) - // From layer B, relative parent - .setInt32(METADATA_TASK_ID, 2) - .setInt32(METADATA_OWNER_PID, 3) - // added by layer creation args - .setInt32(gui::METADATA_CALLING_UID, - layerDMetadata.getInt32(gui::METADATA_CALLING_UID, 0)) - .build(); - EXPECT_EQ(layerD->getLayerSnapshot()->relativeLayerMetadata, expectedLayerDRelativeMetadata); - auto expectedLayerCRelativeMetadata = - LayerMetadataBuilder() - // From layer A, parent of relative parent - .setInt32(METADATA_OWNER_UID, 1) - // From layer B, relative parent of relative parent - .setInt32(METADATA_OWNER_PID, 3) - // From layer D, relative parent - .setInt32(METADATA_TASK_ID, 4) - // added by layer creation args - .setInt32(gui::METADATA_CALLING_UID, - layerDMetadata.getInt32(gui::METADATA_CALLING_UID, 0)) - .build(); - EXPECT_EQ(layerC->getLayerSnapshot()->relativeLayerMetadata, expectedLayerCRelativeMetadata); -} - -TEST_F(SurfaceFlingerUpdateLayerMetadataSnapshotTest, - updatesRelativeMetadataMultipleRelativeChildren) { - auto layerAMetadata = LayerMetadataBuilder().setInt32(METADATA_OWNER_UID, 1).build(); - auto layerA = createLayer("layer-a", layerAMetadata); - auto layerAHandle = layerA->getHandle(); - LayerMetadata layerBMetadata; - auto layerB = createLayer("layer-b", layerBMetadata); - LayerMetadata layerCMetadata; - auto layerC = createLayer("layer-c", layerCMetadata); - layerB->setRelativeLayer(layerAHandle, 1); - layerC->setRelativeLayer(layerAHandle, 2); - layerA->commitChildList(); - mFlinger.mutableDrawingState().layersSortedByZ.add(layerA); - mFlinger.mutableDrawingState().layersSortedByZ.add(layerB); - mFlinger.mutableDrawingState().layersSortedByZ.add(layerC); - - mFlinger.updateLayerMetadataSnapshot(); - - EXPECT_EQ(layerA->getLayerSnapshot()->relativeLayerMetadata, LayerMetadata{}); - EXPECT_EQ(layerB->getLayerSnapshot()->relativeLayerMetadata, layerAMetadata); - EXPECT_EQ(layerC->getLayerSnapshot()->relativeLayerMetadata, layerAMetadata); -} - -} // namespace android diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h index 1f7bf5f2a8..9de3346fb0 100644 --- a/services/surfaceflinger/tests/unittests/TestableScheduler.h +++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h @@ -62,7 +62,7 @@ public: } MOCK_METHOD(void, scheduleConfigure, (), (override)); - MOCK_METHOD(void, scheduleFrame, (), (override)); + MOCK_METHOD(void, scheduleFrame, (Duration), (override)); MOCK_METHOD(void, postMessage, (sp<MessageHandler>&&), (override)); void doFrameSignal(ICompositor& compositor, VsyncId vsyncId) { @@ -74,10 +74,8 @@ public: void setEventThread(Cycle cycle, std::unique_ptr<EventThread> eventThreadPtr) { if (cycle == Cycle::Render) { mRenderEventThread = std::move(eventThreadPtr); - mRenderEventConnection = mRenderEventThread->createEventConnection(); } else { mLastCompositeEventThread = std::move(eventThreadPtr); - mLastCompositeEventConnection = mLastCompositeEventThread->createEventConnection(); } } @@ -178,6 +176,11 @@ public: mPolicy.idleTimer = globalSignals.idle ? TimerState::Expired : TimerState::Reset; } + using Scheduler::TimerState; + + using Scheduler::idleTimerCallback; + using Scheduler::touchTimerCallback; + void setContentRequirements(std::vector<RefreshRateSelector::LayerRequirement> layers) { std::lock_guard<std::mutex> lock(mPolicyLock); mPolicy.contentRequirements = std::move(layers); @@ -190,15 +193,7 @@ public: return Scheduler::chooseDisplayModes(); } - void dispatchCachedReportedMode() { - std::lock_guard<std::mutex> lock(mPolicyLock); - Scheduler::dispatchCachedReportedMode(); - } - - void clearCachedReportedMode() { - std::lock_guard<std::mutex> lock(mPolicyLock); - mPolicy.cachedModeChangedParams.reset(); - } + using Scheduler::onDisplayModeChanged; void setInitialHwVsyncEnabled(PhysicalDisplayId id, bool enabled) { auto schedule = getVsyncSchedule(id); diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index 0d13dc563d..c043b880ec 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -16,7 +16,6 @@ #pragma once -#include <algorithm> #include <chrono> #include <memory> #include <variant> @@ -44,7 +43,6 @@ #include "Layer.h" #include "NativeWindowSurface.h" #include "RenderArea.h" -#include "Scheduler/MessageQueue.h" #include "Scheduler/RefreshRateSelector.h" #include "SurfaceFlinger.h" #include "TestableScheduler.h" @@ -60,7 +58,6 @@ #include "Scheduler/VSyncTracker.h" #include "Scheduler/VsyncController.h" -#include "mock/MockVSyncDispatch.h" #include "mock/MockVSyncTracker.h" #include "mock/MockVsyncController.h" @@ -88,9 +85,7 @@ class Factory final : public surfaceflinger::Factory { public: ~Factory() = default; - std::unique_ptr<HWComposer> createHWComposer(const std::string&) override { - return nullptr; - } + std::unique_ptr<HWComposer> createHWComposer(const std::string&) override { return nullptr; } std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration( Fps /*currentRefreshRate*/) override { @@ -276,17 +271,6 @@ public: auto eventThread = makeMock<mock::EventThread>(options.useNiceMock); auto sfEventThread = makeMock<mock::EventThread>(options.useNiceMock); - - EXPECT_CALL(*eventThread, registerDisplayEventConnection(_)); - EXPECT_CALL(*eventThread, createEventConnection(_, _)) - .WillOnce(Return(sp<EventThreadConnection>::make(eventThread.get(), - mock::EventThread::kCallingUid))); - - EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_)); - EXPECT_CALL(*sfEventThread, createEventConnection(_, _)) - .WillOnce(Return(sp<EventThreadConnection>::make(sfEventThread.get(), - mock::EventThread::kCallingUid))); - auto vsyncController = makeMock<mock::VsyncController>(options.useNiceMock); auto vsyncTracker = makeSharedMock<mock::VSyncTracker>(options.useNiceMock); @@ -326,35 +310,25 @@ public: auto& mutableStateLock() { return mFlinger->mStateLock; } - static auto findOutputLayerForDisplay(const sp<Layer>& layer, - const sp<const DisplayDevice>& display) { - return layer->findOutputLayerForDisplay(display.get()); - } - - static void setLayerSidebandStream(const sp<Layer>& layer, - const sp<NativeHandle>& sidebandStream) { - layer->mDrawingState.sidebandStream = sidebandStream; - layer->mSidebandStream = sidebandStream; - layer->editLayerSnapshot()->sidebandStream = sidebandStream; + compositionengine::OutputLayer* findOutputLayerForDisplay( + uint32_t layerId, const sp<const DisplayDevice>& display) { + ftl::FakeGuard guard(kMainThreadContext); + if (mFlinger->mLegacyLayers.find(layerId) == mFlinger->mLegacyLayers.end()) { + return nullptr; + } + return mFlinger->mLegacyLayers[layerId]->findOutputLayerForDisplay(display.get()); } void setLayerCompositionType(const sp<Layer>& layer, aidl::android::hardware::graphics::composer3::Composition type) { - auto outputLayer = findOutputLayerForDisplay(layer, mFlinger->getDefaultDisplayDevice()); + auto outputLayer = findOutputLayerForDisplay(static_cast<uint32_t>(layer->sequence), + mFlinger->getDefaultDisplayDevice()); LOG_ALWAYS_FATAL_IF(!outputLayer); auto& state = outputLayer->editState(); LOG_ALWAYS_FATAL_IF(!outputLayer->getState().hwc); (*state.hwc).hwcCompositionType = type; } - static void setLayerPotentialCursor(const sp<Layer>& layer, bool potentialCursor) { - layer->mPotentialCursor = potentialCursor; - } - - static void setLayerDrawingParent(const sp<Layer>& layer, const sp<Layer>& drawingParent) { - layer->mDrawingParent = drawingParent; - } - /* ------------------------------------------------------------------------ * Forwarding for functions being tested */ @@ -395,7 +369,7 @@ public: targets.try_emplace(id, &frameTargeter.target()); targeters.try_emplace(id, &frameTargeter); } - + mFlinger->setTransactionFlags(eTransactionFlushNeeded); mFlinger->commit(displayId, targets); if (composite) { @@ -500,19 +474,19 @@ public: auto layers = getLayerSnapshotsFn(); auto layerFEs = mFlinger->extractLayerFEs(layers); - return mFlinger->renderScreenImpl(std::move(renderArea), buffer, regionSampling, + return mFlinger->renderScreenImpl(renderArea.get(), buffer, regionSampling, false /* grayscale */, false /* isProtected */, - captureResults, displayState, layers, layerFEs); + false /* attachGainmap */, captureResults, displayState, + layers, layerFEs); } - auto traverseLayersInLayerStack(ui::LayerStack layerStack, int32_t uid, - std::unordered_set<uint32_t> excludeLayerIds, - const LayerVector::Visitor& visitor) { - return mFlinger->traverseLayersInLayerStack(layerStack, uid, excludeLayerIds, visitor); + auto getLayerSnapshotsForScreenshotsFn(ui::LayerStack layerStack, uint32_t uid) { + return mFlinger->getLayerSnapshotsForScreenshots(layerStack, uid, + std::unordered_set<uint32_t>{}); } auto getDisplayNativePrimaries(const sp<IBinder>& displayToken, - ui::DisplayPrimaries &primaries) { + ui::DisplayPrimaries& primaries) { return mFlinger->SurfaceFlinger::getDisplayNativePrimaries(displayToken, primaries); } @@ -530,7 +504,7 @@ public: auto setTransactionState( const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& states, - const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken, + 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, @@ -586,8 +560,6 @@ public: return mFlinger->mirrorLayer(args, mirrorFromHandle, outResult); } - void updateLayerMetadataSnapshot() { mFlinger->updateLayerMetadataSnapshot(); } - void getDynamicDisplayInfoFromToken(const sp<IBinder>& displayToken, ui::DynamicDisplayInfo* dynamicDisplayInfo) { mFlinger->getDynamicDisplayInfoFromToken(displayToken, dynamicDisplayInfo); @@ -622,6 +594,18 @@ public: mFlinger->mNewLayers.emplace_back(std::move(layer)); } + // Used to add a layer before updateLayerSnapshots is called. + // Must have transactionsFlushed enabled for the new layer to be updated. + void addLayer(uint32_t layerId) { + std::scoped_lock<std::mutex> lock(mFlinger->mCreatedLayersLock); + LayerCreationArgs args(std::make_optional(layerId)); + args.flinger = this->mFlinger.get(); + auto layer = std::make_unique<frontend::RequestedLayerState>(args); + auto legacyLayer = sp<Layer>::make(args); + injectLegacyLayer(legacyLayer); + mFlinger->mNewLayers.emplace_back(std::move(layer)); + } + /* ------------------------------------------------------------------------ * Read-only access to private data to assert post-conditions. */ @@ -639,12 +623,24 @@ public: void injectLegacyLayer(sp<Layer> layer) { FTL_FAKE_GUARD(kMainThreadContext, mFlinger->mLegacyLayers[static_cast<uint32_t>(layer->sequence)] = layer); - }; + } void releaseLegacyLayer(uint32_t sequence) { FTL_FAKE_GUARD(kMainThreadContext, mFlinger->mLegacyLayers.erase(sequence)); + } + + auto getLegacyLayer(uint32_t layerId) { + ftl::FakeGuard guard(kMainThreadContext); + return mFlinger->mLegacyLayers[layerId]; }; + void destroyAllLayerHandles() { + ftl::FakeGuard guard(kMainThreadContext); + for (auto [layerId, legacyLayer] : mFlinger->mLegacyLayers) { + mFlinger->onHandleDestroyed(nullptr, legacyLayer, layerId); + } + } + auto setLayerHistoryDisplayArea(uint32_t displayArea) { return mFlinger->mScheduler->onActiveDisplayAreaChanged(displayArea); }; @@ -707,7 +703,6 @@ public: } auto& mutableMinAcquiredBuffers() { return SurfaceFlinger::minAcquiredBuffers; } - auto& mutableLayersPendingRemoval() { return mFlinger->mLayersPendingRemoval; } auto& mutableLayerSnapshotBuilder() NO_THREAD_SAFETY_ANALYSIS { return mFlinger->mLayerSnapshotBuilder; } @@ -719,10 +714,6 @@ public: return mFlinger->initTransactionTraceWriter(); } - // Needed since mLayerLifecycleManagerEnabled is false by default and must - // be enabled for tests to go through the new front end path. - void enableLayerLifecycleManager() { mFlinger->mLayerLifecycleManagerEnabled = true; } - void notifyExpectedPresentIfRequired(PhysicalDisplayId displayId, Period vsyncPeriod, TimePoint expectedPresentTime, Fps frameInterval, std::optional<Period> timeoutOpt) { @@ -781,7 +772,6 @@ public: mutableDisplays().clear(); mutableCurrentState().displays.clear(); mutableDrawingState().displays.clear(); - mFlinger->mLayersPendingRemoval.clear(); mFlinger->mScheduler.reset(); mFlinger->mCompositionEngine->setHwComposer(std::unique_ptr<HWComposer>()); mFlinger->mRenderEngine = std::unique_ptr<renderengine::RenderEngine>(); diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp index 7fb9247ee0..fab1f6d451 100644 --- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp @@ -17,6 +17,7 @@ #undef LOG_TAG #define LOG_TAG "TransactionApplicationTest" +#include <binder/Binder.h> #include <common/test/FlagUtils.h> #include <compositionengine/Display.h> #include <compositionengine/mock/DisplaySurface.h> @@ -26,10 +27,10 @@ #include <gui/SurfaceComposerClient.h> #include <gui/fake/BufferData.h> #include <log/log.h> +#include <renderengine/mock/RenderEngine.h> #include <ui/MockFence.h> #include <utils/String8.h> #include <vector> -#include <binder/Binder.h> #include "FrontEnd/TransactionHandler.h" #include "TestableSurfaceFlinger.h" @@ -55,6 +56,7 @@ public: mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>()); mFlinger.setupMockScheduler(); + mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine)); mFlinger.flinger()->addTransactionReadyFilters(); } @@ -65,6 +67,7 @@ public: } TestableSurfaceFlinger mFlinger; + renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine(); struct TransactionInfo { Vector<ComposerState> states; @@ -102,7 +105,7 @@ public: void NotPlacedOnTransactionQueue(uint32_t flags) { ASSERT_TRUE(mFlinger.getTransactionQueue().isEmpty()); - EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); + EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1); TransactionInfo transaction; setupSingle(transaction, flags, /*desiredPresentTime*/ systemTime(), /*isAutoTimestamp*/ true, @@ -126,7 +129,7 @@ public: void PlaceOnTransactionQueue(uint32_t flags) { ASSERT_TRUE(mFlinger.getTransactionQueue().isEmpty()); - EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); + EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1); // first check will see desired present time has not passed, // but afterwards it will look like the desired present time has passed @@ -152,7 +155,7 @@ public: void BlockedByPriorTransaction(uint32_t flags) { ASSERT_TRUE(mFlinger.getTransactionQueue().isEmpty()); nsecs_t time = systemTime(); - EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(2); + EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(2); // transaction that should go on the pending thread TransactionInfo transactionA; @@ -214,7 +217,7 @@ public: TEST_F(TransactionApplicationTest, AddToPendingQueue) { ASSERT_TRUE(mFlinger.getTransactionQueue().isEmpty()); - EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); + EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1); TransactionInfo transactionA; // transaction to go on pending queue setupSingle(transactionA, /*flags*/ 0, /*desiredPresentTime*/ s2ns(1), false, @@ -235,7 +238,7 @@ TEST_F(TransactionApplicationTest, AddToPendingQueue) { TEST_F(TransactionApplicationTest, Flush_RemovesFromQueue) { ASSERT_TRUE(mFlinger.getTransactionQueue().isEmpty()); - EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); + EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1); TransactionInfo transactionA; // transaction to go on pending queue setupSingle(transactionA, /*flags*/ 0, /*desiredPresentTime*/ s2ns(1), false, @@ -323,15 +326,17 @@ TEST_F(TransactionApplicationTest, ApplyTokensUseDifferentQueues) { transaction1.states[0].state.bufferData = std::make_shared<fake::BufferData>(/* bufferId */ 1, /* width */ 1, /* height */ 1, /* pixelFormat */ 0, /* outUsage */ 0); + mFlinger.addLayer(1); + bool out; + mFlinger.updateLayerSnapshots(VsyncId{1}, 0, /* transactionsFlushed */ true, out); transaction1.states[0].externalTexture = std::make_shared<FakeExternalTexture>(*transaction1.states[0].state.bufferData); - transaction1.states[0].state.surface = - sp<Layer>::make(LayerCreationArgs(mFlinger.flinger(), nullptr, "TestLayer", 0, {})) - ->getHandle(); + transaction1.states[0].state.surface = mFlinger.getLegacyLayer(1)->getHandle(); auto fence = sp<mock::MockFence>::make(); EXPECT_CALL(*fence, getStatus()).WillRepeatedly(Return(Fence::Status::Unsignaled)); transaction1.states[0].state.bufferData->acquireFence = std::move(fence); transaction1.states[0].state.bufferData->flags = BufferData::BufferDataChange::fenceChanged; + transaction1.states[0].layerId = 1; transaction1.isAutoTimestamp = true; // Transaction 2 should be ready to be applied. @@ -361,8 +366,7 @@ public: } mFlinger.getPendingTransactionQueue().clear(); mFlinger.commitTransactionsLocked(eTransactionMask); - mFlinger.mutableCurrentState().layersSortedByZ.clear(); - mFlinger.mutableDrawingState().layersSortedByZ.clear(); + mFlinger.destroyAllLayerHandles(); } static sp<Fence> fence(Fence::Status status) { @@ -371,8 +375,7 @@ public: return fence; } - ComposerState createComposerState(int layerId, sp<Fence> fence, uint64_t what, - std::optional<sp<IBinder>> layerHandle = std::nullopt) { + ComposerState createComposerState(int layerId, sp<Fence> fence, uint64_t what) { ComposerState state; state.state.bufferData = std::make_shared<fake::BufferData>(/* bufferId */ 123L, /* width */ 1, @@ -380,9 +383,6 @@ public: /* outUsage */ 0); state.state.bufferData->acquireFence = std::move(fence); state.state.layerId = layerId; - state.state.surface = layerHandle.value_or( - sp<Layer>::make(LayerCreationArgs(mFlinger.flinger(), nullptr, "TestLayer", 0, {})) - ->getHandle()); state.state.bufferData->flags = BufferData::BufferDataChange::fenceChanged; state.state.what = what; @@ -418,6 +418,19 @@ public: size_t expectedTransactionsPending) { EXPECT_TRUE(mFlinger.getTransactionQueue().isEmpty()); EXPECT_EQ(0u, mFlinger.getPendingTransactionQueue().size()); + std::unordered_set<uint32_t> createdLayers; + for (auto transaction : transactions) { + for (auto& state : transaction.states) { + auto layerId = static_cast<uint32_t>(state.state.layerId); + if (createdLayers.find(layerId) == createdLayers.end()) { + mFlinger.addLayer(layerId); + createdLayers.insert(layerId); + } + } + } + bool unused; + bool mustComposite = mFlinger.updateLayerSnapshots(VsyncId{1}, /*frameTimeNs=*/0, + /*transactionsFlushed=*/true, unused); for (auto transaction : transactions) { std::vector<ResolvedComposerState> resolvedStates; @@ -427,6 +440,9 @@ public: resolvedState.state = std::move(state.state); resolvedState.externalTexture = std::make_shared<FakeExternalTexture>(*resolvedState.state.bufferData); + resolvedState.layerId = static_cast<uint32_t>(state.state.layerId); + resolvedState.state.surface = + mFlinger.getLegacyLayer(resolvedState.layerId)->getHandle(); resolvedStates.emplace_back(resolvedState); } @@ -458,9 +474,8 @@ public: TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_RemovesSingleSignaledFromTheQueue) { const sp<IBinder> kApplyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance()); - const auto kLayerId = 1; + const auto kLayerId = 10; const auto kExpectedTransactionsPending = 0u; - const auto signaledTransaction = createTransactionInfo(kApplyToken, {createComposerState(kLayerId, fence(Fence::Status::Signaled), @@ -773,7 +788,7 @@ public: TEST_F(LatchUnsignaledDisabledTest, Flush_RemovesSignaledFromTheQueue) { const sp<IBinder> kApplyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance()); - const auto kLayerId = 1; + const auto kLayerId = 10; const auto kExpectedTransactionsPending = 0u; const auto signaledTransaction = diff --git a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp index 85b61f8fb9..abfab9ad95 100644 --- a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp @@ -94,7 +94,7 @@ public: HAL_PIXEL_FORMAT_RGBA_8888, 0ULL /*usage*/); layer->setBuffer(externalTexture, bufferData, postTime, /*desiredPresentTime*/ 30, false, - FrameTimelineInfo{}); + FrameTimelineInfo{}, gui::GameMode::Unsupported); commitTransaction(layer.get()); nsecs_t latchTime = 25; @@ -112,7 +112,8 @@ public: EXPECT_CALL(*mFlinger.getFrameTracer(), traceFence(layerId, bufferId, frameNumber, presentFence, FrameTracer::FrameEvent::PRESENT_FENCE, /*startTime*/ 0)); - layer->onCompositionPresented(nullptr, glDoneFence, presentFence, compositorTiming); + layer->onCompositionPresented(nullptr, glDoneFence, presentFence, compositorTiming, + gui::GameMode::Unsupported); } }; diff --git a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp index 46733b9a83..9a68d75d55 100644 --- a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp @@ -72,7 +72,8 @@ public: FrameTimelineInfo ftInfo; ftInfo.vsyncId = 1; ftInfo.inputEventId = 0; - layer->setFrameTimelineVsyncForBufferlessTransaction(ftInfo, 10); + layer->setFrameTimelineVsyncForBufferlessTransaction(ftInfo, 10, + gui::GameMode::Unsupported); EXPECT_EQ(1u, layer->mDrawingState.bufferlessSurfaceFramesTX.size()); ASSERT_TRUE(layer->mDrawingState.bufferSurfaceFrameTX == nullptr); const auto surfaceFrame = layer->mDrawingState.bufferlessSurfaceFramesTX.at(/*token*/ 1); @@ -99,7 +100,8 @@ public: FrameTimelineInfo ftInfo; ftInfo.vsyncId = 1; ftInfo.inputEventId = 0; - layer->setBuffer(externalTexture, bufferData, 10, 20, false, ftInfo); + layer->setBuffer(externalTexture, bufferData, 10, 20, false, ftInfo, + gui::GameMode::Unsupported); acquireFence->signalForTest(12); commitTransaction(layer.get()); @@ -134,7 +136,8 @@ public: FrameTimelineInfo ftInfo; ftInfo.vsyncId = 1; ftInfo.inputEventId = 0; - layer->setBuffer(externalTexture1, bufferData, 10, 20, false, ftInfo); + layer->setBuffer(externalTexture1, bufferData, 10, 20, false, ftInfo, + gui::GameMode::Unsupported); EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size()); ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); const auto droppedSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX; @@ -151,7 +154,8 @@ public: 2ULL /* bufferId */, HAL_PIXEL_FORMAT_RGBA_8888, 0ULL /*usage*/); - layer->setBuffer(externalTexture2, bufferData, 10, 20, false, ftInfo); + layer->setBuffer(externalTexture2, bufferData, 10, 20, false, ftInfo, + gui::GameMode::Unsupported); nsecs_t end = systemTime(); acquireFence2->signalForTest(12); @@ -180,7 +184,8 @@ public: ftInfo.vsyncId = 1; ftInfo.inputEventId = 0; - layer->setFrameTimelineVsyncForBufferlessTransaction(ftInfo, 10); + layer->setFrameTimelineVsyncForBufferlessTransaction(ftInfo, 10, + gui::GameMode::Unsupported); EXPECT_EQ(1u, layer->mDrawingState.bufferlessSurfaceFramesTX.size()); ASSERT_EQ(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); @@ -197,7 +202,8 @@ public: 1ULL /* bufferId */, HAL_PIXEL_FORMAT_RGBA_8888, 0ULL /*usage*/); - layer->setBuffer(externalTexture, bufferData, 10, 20, false, ftInfo); + layer->setBuffer(externalTexture, bufferData, 10, 20, false, ftInfo, + gui::GameMode::Unsupported); acquireFence->signalForTest(12); EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size()); @@ -232,11 +238,13 @@ public: FrameTimelineInfo ftInfo; ftInfo.vsyncId = 1; ftInfo.inputEventId = 0; - layer->setBuffer(externalTexture, bufferData, 10, 20, false, ftInfo); + layer->setBuffer(externalTexture, bufferData, 10, 20, false, ftInfo, + gui::GameMode::Unsupported); EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size()); ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); - layer->setFrameTimelineVsyncForBufferlessTransaction(ftInfo, 10); + layer->setFrameTimelineVsyncForBufferlessTransaction(ftInfo, 10, + gui::GameMode::Unsupported); EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size()); ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); } @@ -246,7 +254,8 @@ public: FrameTimelineInfo ftInfo; ftInfo.vsyncId = 1; ftInfo.inputEventId = 0; - layer->setFrameTimelineVsyncForBufferlessTransaction(ftInfo, 10); + layer->setFrameTimelineVsyncForBufferlessTransaction(ftInfo, 10, + gui::GameMode::Unsupported); EXPECT_EQ(1u, layer->mDrawingState.bufferlessSurfaceFramesTX.size()); ASSERT_EQ(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); const auto bufferlessSurfaceFrame1 = @@ -255,7 +264,8 @@ public: FrameTimelineInfo ftInfo2; ftInfo2.vsyncId = 4; ftInfo2.inputEventId = 0; - layer->setFrameTimelineVsyncForBufferlessTransaction(ftInfo2, 10); + layer->setFrameTimelineVsyncForBufferlessTransaction(ftInfo2, 10, + gui::GameMode::Unsupported); EXPECT_EQ(2u, layer->mDrawingState.bufferlessSurfaceFramesTX.size()); ASSERT_EQ(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); const auto bufferlessSurfaceFrame2 = layer->mDrawingState.bufferlessSurfaceFramesTX[4]; @@ -275,7 +285,8 @@ public: FrameTimelineInfo ftInfo3; ftInfo3.vsyncId = 3; ftInfo3.inputEventId = 0; - layer->setBuffer(externalTexture, bufferData, 10, 20, false, ftInfo3); + layer->setBuffer(externalTexture, bufferData, 10, 20, false, ftInfo3, + gui::GameMode::Unsupported); EXPECT_EQ(2u, layer->mDrawingState.bufferlessSurfaceFramesTX.size()); ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); const auto bufferSurfaceFrameTX = layer->mDrawingState.bufferSurfaceFrameTX; @@ -302,58 +313,6 @@ public: EXPECT_EQ(PresentState::Presented, bufferSurfaceFrameTX->getPresentState()); } - void PendingSurfaceFramesRemovedAfterClassification() { - sp<Layer> layer = createLayer(); - - sp<Fence> fence1(sp<Fence>::make()); - auto acquireFence1 = fenceFactory.createFenceTimeForTest(fence1); - BufferData bufferData; - bufferData.acquireFence = fence1; - bufferData.frameNumber = 1; - bufferData.flags |= BufferData::BufferDataChange::fenceChanged; - bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged; - std::shared_ptr<renderengine::ExternalTexture> externalTexture1 = std::make_shared< - renderengine::mock::FakeExternalTexture>(1U /*width*/, 1U /*height*/, - 1ULL /* bufferId */, - HAL_PIXEL_FORMAT_RGBA_8888, - 0ULL /*usage*/); - FrameTimelineInfo ftInfo; - ftInfo.vsyncId = 1; - ftInfo.inputEventId = 0; - layer->setBuffer(externalTexture1, bufferData, 10, 20, false, ftInfo); - ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); - const auto droppedSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX; - - sp<Fence> fence2(sp<Fence>::make()); - auto acquireFence2 = fenceFactory.createFenceTimeForTest(fence2); - bufferData.acquireFence = fence2; - bufferData.frameNumber = 1; - bufferData.flags |= BufferData::BufferDataChange::fenceChanged; - bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged; - std::shared_ptr<renderengine::ExternalTexture> externalTexture2 = std::make_shared< - renderengine::mock::FakeExternalTexture>(1U /*width*/, 1U /*height*/, - 1ULL /* bufferId */, - HAL_PIXEL_FORMAT_RGBA_8888, - 0ULL /*usage*/); - layer->setBuffer(externalTexture2, bufferData, 10, 20, false, ftInfo); - acquireFence2->signalForTest(12); - - ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); - auto presentedSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX; - - commitTransaction(layer.get()); - layer->updateTexImage(15); - - // Both the droppedSurfaceFrame and presentedSurfaceFrame should be in - // pendingJankClassifications. - EXPECT_EQ(2u, layer->mPendingJankClassifications.size()); - presentedSurfaceFrame->onPresent(20, JankType::None, 90_Hz, 90_Hz, - /*displayDeadlineDelta*/ 0, /*displayPresentDelta*/ 0); - layer->releasePendingBuffer(25); - - EXPECT_EQ(0u, layer->mPendingJankClassifications.size()); - } - void BufferSurfaceFrame_ReplaceValidTokenBufferWithInvalidTokenBuffer() { sp<Layer> layer = createLayer(); @@ -372,7 +331,8 @@ public: FrameTimelineInfo ftInfo; ftInfo.vsyncId = 1; ftInfo.inputEventId = 0; - layer->setBuffer(externalTexture1, bufferData, 10, 20, false, ftInfo); + layer->setBuffer(externalTexture1, bufferData, 10, 20, false, ftInfo, + gui::GameMode::Unsupported); EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size()); ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); const auto droppedSurfaceFrame1 = layer->mDrawingState.bufferSurfaceFrameTX; @@ -392,7 +352,8 @@ public: FrameTimelineInfo ftInfoInv; ftInfoInv.vsyncId = FrameTimelineInfo::INVALID_VSYNC_ID; ftInfoInv.inputEventId = 0; - layer->setBuffer(externalTexture2, bufferData, 10, 20, false, ftInfoInv); + layer->setBuffer(externalTexture2, bufferData, 10, 20, false, ftInfoInv, + gui::GameMode::Unsupported); auto dropEndTime1 = systemTime(); EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size()); ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); @@ -413,7 +374,8 @@ public: FrameTimelineInfo ftInfo2; ftInfo2.vsyncId = 2; ftInfo2.inputEventId = 0; - layer->setBuffer(externalTexture3, bufferData, 10, 20, false, ftInfo2); + layer->setBuffer(externalTexture3, bufferData, 10, 20, false, ftInfo2, + gui::GameMode::Unsupported); auto dropEndTime2 = systemTime(); acquireFence3->signalForTest(12); @@ -445,8 +407,7 @@ public: void MultipleCommitsBeforeLatch() { sp<Layer> layer = createLayer(); - uint32_t surfaceFramesPendingClassification = 0; - std::vector<std::shared_ptr<frametimeline::SurfaceFrame>> bufferlessSurfaceFrames; + std::vector<std::shared_ptr<frametimeline::SurfaceFrame>> surfaceFrames; for (int i = 0; i < 10; i += 2) { sp<Fence> fence(sp<Fence>::make()); BufferData bufferData; @@ -462,58 +423,52 @@ public: FrameTimelineInfo ftInfo; ftInfo.vsyncId = 1; ftInfo.inputEventId = 0; - layer->setBuffer(externalTexture, bufferData, 10, 20, false, ftInfo); + layer->setBuffer(externalTexture, bufferData, 10, 20, false, ftInfo, + gui::GameMode::Unsupported); FrameTimelineInfo ftInfo2; ftInfo2.vsyncId = 2; ftInfo2.inputEventId = 0; - layer->setFrameTimelineVsyncForBufferlessTransaction(ftInfo2, 10); + layer->setFrameTimelineVsyncForBufferlessTransaction(ftInfo2, 10, + gui::GameMode::Unsupported); ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); EXPECT_EQ(1u, layer->mDrawingState.bufferlessSurfaceFramesTX.size()); - auto& bufferlessSurfaceFrame = - layer->mDrawingState.bufferlessSurfaceFramesTX.at(/*vsyncId*/ 2); - bufferlessSurfaceFrames.push_back(bufferlessSurfaceFrame); + + surfaceFrames.push_back(layer->mDrawingState.bufferSurfaceFrameTX); + surfaceFrames.push_back( + layer->mDrawingState.bufferlessSurfaceFramesTX.at(/*vsyncId*/ 2)); commitTransaction(layer.get()); - surfaceFramesPendingClassification += 2; - EXPECT_EQ(surfaceFramesPendingClassification, - layer->mPendingJankClassifications.size()); } auto presentedBufferSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX; layer->updateTexImage(15); // BufferlessSurfaceFrames are immediately set to presented and added to the DisplayFrame. // Since we don't have access to DisplayFrame here, trigger an onPresent directly. - for (auto& surfaceFrame : bufferlessSurfaceFrames) { - surfaceFrame->onPresent(20, JankType::None, 90_Hz, 90_Hz, - /*displayDeadlineDelta*/ 0, /*displayPresentDelta*/ 0); + // The odd indices are the bufferless frames. + for (uint32_t i = 1; i < 10; i += 2) { + surfaceFrames[i]->onPresent(20, JankType::None, 90_Hz, 90_Hz, + /*displayDeadlineDelta*/ 0, /*displayPresentDelta*/ 0); } presentedBufferSurfaceFrame->onPresent(20, JankType::None, 90_Hz, 90_Hz, /*displayDeadlineDelta*/ 0, /*displayPresentDelta*/ 0); - // There should be 10 bufferlessSurfaceFrames and 1 bufferSurfaceFrame - ASSERT_EQ(10u, surfaceFramesPendingClassification); - ASSERT_EQ(surfaceFramesPendingClassification, layer->mPendingJankClassifications.size()); - // For the frames upto 8, the bufferSurfaceFrame should have been dropped while the // bufferlessSurfaceFrame presented for (uint32_t i = 0; i < 8; i += 2) { - auto& bufferSurfaceFrame = layer->mPendingJankClassifications[i]; - auto& bufferlessSurfaceFrame = layer->mPendingJankClassifications[i + 1]; + auto bufferSurfaceFrame = surfaceFrames[i]; + auto bufferlessSurfaceFrame = surfaceFrames[i + 1]; EXPECT_EQ(bufferSurfaceFrame->getPresentState(), PresentState::Dropped); EXPECT_EQ(bufferlessSurfaceFrame->getPresentState(), PresentState::Presented); } { - auto& bufferSurfaceFrame = layer->mPendingJankClassifications[8u]; - auto& bufferlessSurfaceFrame = layer->mPendingJankClassifications[9u]; + auto bufferSurfaceFrame = surfaceFrames[8]; + auto bufferlessSurfaceFrame = surfaceFrames[9]; EXPECT_EQ(bufferSurfaceFrame->getPresentState(), PresentState::Presented); EXPECT_EQ(bufferlessSurfaceFrame->getPresentState(), PresentState::Presented); } layer->releasePendingBuffer(25); - - // There shouldn't be any pending classifications. Everything should have been cleared. - EXPECT_EQ(0u, layer->mPendingJankClassifications.size()); } }; @@ -541,10 +496,6 @@ TEST_F(TransactionSurfaceFrameTest, MultipleSurfaceFramesPresentedTogether) { MultipleSurfaceFramesPresentedTogether(); } -TEST_F(TransactionSurfaceFrameTest, PendingSurfaceFramesRemovedAfterClassification) { - PendingSurfaceFramesRemovedAfterClassification(); -} - TEST_F(TransactionSurfaceFrameTest, BufferSurfaceFrame_ReplaceValidTokenBufferWithInvalidTokenBuffer) { BufferSurfaceFrame_ReplaceValidTokenBufferWithInvalidTokenBuffer(); diff --git a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp index 7bf167498b..f8f08c78fd 100644 --- a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp @@ -20,6 +20,7 @@ #include <gui/SurfaceComposerClient.h> #include <cstdint> #include "Client.h" +#include "Layer.h" #include <layerproto/LayerProtoHeader.h> #include "FrontEnd/LayerCreationArgs.h" diff --git a/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp b/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp index 1cf14ae519..e27af0e62c 100644 --- a/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp +++ b/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp @@ -127,13 +127,11 @@ TEST_F(TunnelModeEnabledReporterTest, callsNewListenerWithFreshInformation) { sp<NativeHandle> stream = NativeHandle::create(reinterpret_cast<native_handle_t*>(DEFAULT_SIDEBAND_STREAM), false); - layer->setSidebandStream(stream, FrameTimelineInfo{}, 20); - mFlinger.mutableCurrentState().layersSortedByZ.add(layer); + layer->setSidebandStream(stream, FrameTimelineInfo{}, 20, gui::GameMode::Unsupported); mTunnelModeEnabledReporter->updateTunnelModeStatus(); mTunnelModeEnabledReporter->addListener(mTunnelModeEnabledListener); EXPECT_EQ(true, mTunnelModeEnabledListener->mTunnelModeEnabled); mTunnelModeEnabledReporter->removeListener(mTunnelModeEnabledListener); - mFlinger.mutableCurrentState().layersSortedByZ.remove(layer); layer = nullptr; mTunnelModeEnabledReporter->updateTunnelModeStatus(); @@ -151,14 +149,12 @@ TEST_F(TunnelModeEnabledReporterTest, layerWithSidebandStreamTriggersUpdate) { sp<NativeHandle> stream = NativeHandle::create(reinterpret_cast<native_handle_t*>(DEFAULT_SIDEBAND_STREAM), false); - layerWithSidebandStream->setSidebandStream(stream, FrameTimelineInfo{}, 20); + layerWithSidebandStream->setSidebandStream(stream, FrameTimelineInfo{}, 20, + gui::GameMode::Unsupported); - mFlinger.mutableCurrentState().layersSortedByZ.add(simpleLayer); - mFlinger.mutableCurrentState().layersSortedByZ.add(layerWithSidebandStream); mTunnelModeEnabledReporter->updateTunnelModeStatus(); EXPECT_EQ(true, mTunnelModeEnabledListener->mTunnelModeEnabled); - mFlinger.mutableCurrentState().layersSortedByZ.remove(layerWithSidebandStream); layerWithSidebandStream = nullptr; mTunnelModeEnabledReporter->updateTunnelModeStatus(); EXPECT_EQ(false, mTunnelModeEnabledListener->mTunnelModeEnabled); diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp index 3b095545e9..b63f29990e 100644 --- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp @@ -19,6 +19,7 @@ #include <gmock/gmock.h> #include <gtest/gtest.h> +#include <scheduler/FrameTime.h> #include <scheduler/Timer.h> #include "Scheduler/VSyncDispatchTimerQueue.h" @@ -51,7 +52,7 @@ public: bool isVSyncInPhase(nsecs_t, Fps) final { return false; } void setDisplayModePtr(ftl::NonNull<DisplayModePtr>) final {} void setRenderRate(Fps, bool) final {} - void onFrameBegin(TimePoint, TimePoint) final {} + void onFrameBegin(TimePoint, scheduler::FrameTime) final {} void onFrameMissed(TimePoint) final {} void dump(std::string&) const final {} bool isCurrentMode(const ftl::NonNull<DisplayModePtr>&) const final { return false; }; diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp index 5109ea6793..918107d270 100644 --- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp @@ -673,6 +673,36 @@ TEST_F(VSyncPredictorTest, setRenderRateIsIgnoredIfNotDivisor) { EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 5100), Eq(mNow + 6 * mPeriod)); } +TEST_F(VSyncPredictorTest, setRenderRateWhenRenderRateGoesDown) { + SET_FLAG_FOR_TEST(flags::vrr_config, true); + SET_FLAG_FOR_TEST(flags::vrr_bugfix_24q4, true); + + const int32_t kGroup = 0; + const auto kResolution = ui::Size(1920, 1080); + const auto vsyncRate = Fps::fromPeriodNsecs(500); + const auto minFrameRate = Fps::fromPeriodNsecs(1000); + hal::VrrConfig vrrConfig; + vrrConfig.minFrameIntervalNs = minFrameRate.getPeriodNsecs(); + const ftl::NonNull<DisplayModePtr> kMode = + ftl::as_non_null(createDisplayModeBuilder(DisplayModeId(0), vsyncRate, kGroup, + kResolution, DEFAULT_DISPLAY_ID) + .setVrrConfig(std::move(vrrConfig)) + .build()); + + VSyncPredictor vrrTracker{std::make_unique<ClockWrapper>(mClock), kMode, kHistorySize, + kMinimumSamplesForPrediction, kOutlierTolerancePercent}; + + Fps frameRate = Fps::fromPeriodNsecs(1000); + vrrTracker.setRenderRate(frameRate, /*applyImmediately*/ false); + vrrTracker.addVsyncTimestamp(0); + EXPECT_EQ(1000, vrrTracker.nextAnticipatedVSyncTimeFrom(700)); + EXPECT_EQ(2000, vrrTracker.nextAnticipatedVSyncTimeFrom(1000, 1000)); + + frameRate = Fps::fromPeriodNsecs(3000); + vrrTracker.setRenderRate(frameRate, /*applyImmediately*/ false); + EXPECT_TRUE(vrrTracker.isVSyncInPhase(2000, frameRate)); +} + TEST_F(VSyncPredictorTest, setRenderRateHighIsAppliedImmediately) { SET_FLAG_FOR_TEST(flags::vrr_config, true); @@ -871,18 +901,22 @@ TEST_F(VSyncPredictorTest, adjustsVrrTimeline) { EXPECT_EQ(1000, vrrTracker.nextAnticipatedVSyncTimeFrom(700)); EXPECT_EQ(2000, vrrTracker.nextAnticipatedVSyncTimeFrom(1000)); - vrrTracker.onFrameBegin(TimePoint::fromNs(2000), TimePoint::fromNs(1500)); + vrrTracker.onFrameBegin(TimePoint::fromNs(2000), + {TimePoint::fromNs(1500), TimePoint::fromNs(1500)}); EXPECT_EQ(3500, vrrTracker.nextAnticipatedVSyncTimeFrom(2000, 2000)); EXPECT_EQ(4500, vrrTracker.nextAnticipatedVSyncTimeFrom(3500, 3500)); // Miss when starting 4500 and expect the next vsync will be at 5000 (next one) - vrrTracker.onFrameBegin(TimePoint::fromNs(3500), TimePoint::fromNs(2500)); + vrrTracker.onFrameBegin(TimePoint::fromNs(3500), + {TimePoint::fromNs(2500), TimePoint::fromNs(2500)}); vrrTracker.onFrameMissed(TimePoint::fromNs(4500)); EXPECT_EQ(5000, vrrTracker.nextAnticipatedVSyncTimeFrom(4500, 4500)); EXPECT_EQ(6000, vrrTracker.nextAnticipatedVSyncTimeFrom(5000, 5000)); - vrrTracker.onFrameBegin(TimePoint::fromNs(7000), TimePoint::fromNs(6500)); - EXPECT_EQ(10500, vrrTracker.nextAnticipatedVSyncTimeFrom(9000, 7000)); + vrrTracker.onFrameBegin(TimePoint::fromNs(7000), + {TimePoint::fromNs(6500), TimePoint::fromNs(6500)}); + EXPECT_EQ(8500, vrrTracker.nextAnticipatedVSyncTimeFrom(8000, 7000)); + EXPECT_EQ(9500, vrrTracker.nextAnticipatedVSyncTimeFrom(9000, 7000)); } TEST_F(VSyncPredictorTest, adjustsVrrTimelineTwoClients) { @@ -913,7 +947,7 @@ TEST_F(VSyncPredictorTest, adjustsVrrTimelineTwoClients) { // SF starts to catch up EXPECT_EQ(3000, vrrTracker.nextAnticipatedVSyncTimeFrom(2700)); - vrrTracker.onFrameBegin(TimePoint::fromNs(3000), TimePoint::fromNs(0)); + vrrTracker.onFrameBegin(TimePoint::fromNs(3000), {TimePoint::fromNs(0), TimePoint::fromNs(0)}); // SF misses last frame (3000) and observes that when committing (4000) EXPECT_EQ(6000, vrrTracker.nextAnticipatedVSyncTimeFrom(5000, 5000)); @@ -922,17 +956,20 @@ TEST_F(VSyncPredictorTest, adjustsVrrTimelineTwoClients) { // SF wakes up again instead of the (4000) missed frame EXPECT_EQ(4500, vrrTracker.nextAnticipatedVSyncTimeFrom(4000, 4000)); - vrrTracker.onFrameBegin(TimePoint::fromNs(4500), TimePoint::fromNs(4500)); + vrrTracker.onFrameBegin(TimePoint::fromNs(4500), + {TimePoint::fromNs(4500), TimePoint::fromNs(4500)}); // Timeline shifted. The app needs to get the next frame at (7500) as its last frame (6500) will // be presented at (7500) EXPECT_EQ(7500, vrrTracker.nextAnticipatedVSyncTimeFrom(6000, 6000)); EXPECT_EQ(5500, vrrTracker.nextAnticipatedVSyncTimeFrom(4500, 4500)); - vrrTracker.onFrameBegin(TimePoint::fromNs(5500), TimePoint::fromNs(4500)); + vrrTracker.onFrameBegin(TimePoint::fromNs(5500), + {TimePoint::fromNs(4500), TimePoint::fromNs(4500)}); EXPECT_EQ(8500, vrrTracker.nextAnticipatedVSyncTimeFrom(7500, 7500)); EXPECT_EQ(6500, vrrTracker.nextAnticipatedVSyncTimeFrom(5500, 5500)); - vrrTracker.onFrameBegin(TimePoint::fromNs(6500), TimePoint::fromNs(5500)); + vrrTracker.onFrameBegin(TimePoint::fromNs(6500), + {TimePoint::fromNs(5500), TimePoint::fromNs(5500)}); } TEST_F(VSyncPredictorTest, renderRateIsPreservedForCommittedVsyncs) { @@ -990,6 +1027,65 @@ TEST_F(VSyncPredictorTest, renderRateChangeAfterAppliedImmediately) { EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(9001), Eq(13000)); } +TEST_F(VSyncPredictorTest, timelineNotAdjustedForEarlyPresent) { + SET_FLAG_FOR_TEST(flags::vrr_config, true); + + const int32_t kGroup = 0; + const auto kResolution = ui::Size(1920, 1080); + const auto refreshRate = Fps::fromPeriodNsecs(500); + const auto minFrameRate = Fps::fromPeriodNsecs(1000); + hal::VrrConfig vrrConfig; + vrrConfig.minFrameIntervalNs = minFrameRate.getPeriodNsecs(); + const ftl::NonNull<DisplayModePtr> kMode = + ftl::as_non_null(createDisplayModeBuilder(DisplayModeId(0), refreshRate, kGroup, + kResolution, DEFAULT_DISPLAY_ID) + .setVrrConfig(std::move(vrrConfig)) + .build()); + + VSyncPredictor vrrTracker{std::make_unique<ClockWrapper>(mClock), kMode, kHistorySize, + kMinimumSamplesForPrediction, kOutlierTolerancePercent}; + + vrrTracker.setRenderRate(minFrameRate, /*applyImmediately*/ false); + vrrTracker.addVsyncTimestamp(0); + EXPECT_EQ(1000, vrrTracker.nextAnticipatedVSyncTimeFrom(700)); + + constexpr auto kLastConfirmedExpectedPresentTime = TimePoint::fromNs(1000); + constexpr auto kLastActualSignalTime = TimePoint::fromNs(700); // presented early + vrrTracker.onFrameBegin(TimePoint::fromNs(1400), + {kLastActualSignalTime, kLastConfirmedExpectedPresentTime}); + EXPECT_EQ(2000, vrrTracker.nextAnticipatedVSyncTimeFrom(1400, 1000)); + EXPECT_EQ(3000, vrrTracker.nextAnticipatedVSyncTimeFrom(2000, 1000)); +} + +TEST_F(VSyncPredictorTest, adjustsOnlyMinFrameViolatingVrrTimeline) { + const auto refreshRate = Fps::fromPeriodNsecs(500); + auto minFrameRate = Fps::fromPeriodNsecs(1000); + hal::VrrConfig vrrConfig{.minFrameIntervalNs = + static_cast<int32_t>(minFrameRate.getPeriodNsecs())}; + ftl::NonNull<DisplayModePtr> mode = + ftl::as_non_null(createVrrDisplayMode(DisplayModeId(0), refreshRate, vrrConfig)); + VSyncPredictor vrrTracker{std::make_unique<ClockWrapper>(mClock), mode, kHistorySize, + kMinimumSamplesForPrediction, kOutlierTolerancePercent}; + vrrTracker.setRenderRate(minFrameRate, /*applyImmediately*/ false); + vrrTracker.addVsyncTimestamp(0); + + EXPECT_EQ(1000, vrrTracker.nextAnticipatedVSyncTimeFrom(700)); + EXPECT_EQ(2000, vrrTracker.nextAnticipatedVSyncTimeFrom(1000)); + auto lastConfirmedSignalTime = TimePoint::fromNs(1500); + auto lastConfirmedExpectedPresentTime = TimePoint::fromNs(1000); + vrrTracker.onFrameBegin(TimePoint::fromNs(2000), + {lastConfirmedSignalTime, lastConfirmedExpectedPresentTime}); + EXPECT_EQ(3500, vrrTracker.nextAnticipatedVSyncTimeFrom(3000, 1500)); + + minFrameRate = Fps::fromPeriodNsecs(2000); + vrrTracker.setRenderRate(minFrameRate, /*applyImmediately*/ false); + lastConfirmedSignalTime = TimePoint::fromNs(2500); + lastConfirmedExpectedPresentTime = TimePoint::fromNs(2500); + vrrTracker.onFrameBegin(TimePoint::fromNs(3000), + {lastConfirmedSignalTime, lastConfirmedExpectedPresentTime}); + // Enough time without adjusting vsync to present with new rate on time, no need of adjustment + EXPECT_EQ(5500, vrrTracker.nextAnticipatedVSyncTimeFrom(4000, 3500)); +} } // namespace android::scheduler // TODO(b/129481165): remove the #pragma below and fix conversion issues diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h index 184dada32e..f472d8fefe 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h @@ -180,6 +180,12 @@ public: MOCK_METHOD1(onHotplugDisconnect, void(Display)); MOCK_METHOD(Error, setRefreshRateChangedCallbackDebugEnabled, (Display, bool)); MOCK_METHOD(Error, notifyExpectedPresent, (Display, nsecs_t, int32_t)); + MOCK_METHOD( + Error, getRequestedLuts, + (Display, + std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*)); + MOCK_METHOD(Error, setLayerLuts, + (Display, Layer, std::vector<aidl::android::hardware::graphics::composer3::Lut>&)); }; } // namespace Hwc2::mock diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h index 602bdfc152..5edd2cd9e3 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h @@ -35,6 +35,7 @@ public: (const, override)); MOCK_METHOD(bool, isVsyncPeriodSwitchSupported, (), (const, override)); MOCK_METHOD(void, onLayerDestroyed, (hal::HWLayerId), (override)); + MOCK_METHOD(std::optional<ui::Size>, getPhysicalSizeInMm, (), (const override)); MOCK_METHOD(hal::Error, acceptChanges, (), (override)); MOCK_METHOD((base::expected<std::shared_ptr<HWC2::Layer>, hal::Error>), createLayer, (), @@ -109,6 +110,9 @@ public: MOCK_METHOD(hal::Error, getOverlaySupport, (aidl::android::hardware::graphics::composer3::OverlayProperties *), (const override)); + MOCK_METHOD(hal::Error, getRequestedLuts, + (std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*), + (override)); }; class Layer : public HWC2::Layer { @@ -143,6 +147,8 @@ public: (const std::string &, bool, const std::vector<uint8_t> &), (override)); MOCK_METHOD(hal::Error, setBrightness, (float), (override)); MOCK_METHOD(hal::Error, setBlockingRegion, (const android::Region &), (override)); + MOCK_METHOD(hal::Error, setLuts, + (std::vector<aidl::android::hardware::graphics::composer3::Lut>&), (override)); }; } // namespace android::HWC2::mock diff --git a/services/surfaceflinger/tests/unittests/mock/MockDisplayModeSpecs.h b/services/surfaceflinger/tests/unittests/mock/MockDisplayModeSpecs.h index 7b18a82283..4d35d4dcc6 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockDisplayModeSpecs.h +++ b/services/surfaceflinger/tests/unittests/mock/MockDisplayModeSpecs.h @@ -22,14 +22,13 @@ namespace android::mock { -inline gui::DisplayModeSpecs createDisplayModeSpecs(DisplayModeId defaultMode, - bool allowGroupSwitching, float minFps, - float maxFps) { +inline gui::DisplayModeSpecs createDisplayModeSpecs(DisplayModeId defaultMode, Fps maxFps, + bool allowGroupSwitching = false) { gui::DisplayModeSpecs specs; specs.defaultMode = ftl::to_underlying(defaultMode); specs.allowGroupSwitching = allowGroupSwitching; - specs.primaryRanges.physical.min = minFps; - specs.primaryRanges.physical.max = maxFps; + specs.primaryRanges.physical.min = 0.f; + specs.primaryRanges.physical.max = maxFps.getValue(); specs.primaryRanges.render = specs.primaryRanges.physical; specs.appRequestRanges = specs.primaryRanges; return specs; diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h index 8dd1a34ec4..7398cbebe3 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h +++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h @@ -24,21 +24,11 @@ namespace android::mock { class EventThread : public android::EventThread { public: - static constexpr auto kCallingUid = static_cast<uid_t>(0); - EventThread(); ~EventThread() override; - // TODO(b/302035909): workaround otherwise gtest complains about - // error: no viable conversion from - // 'tuple<android::ftl::Flags<android::gui::ISurfaceComposer::EventRegistration> &&>' to 'const - // tuple<android::ftl::Flags<android::gui::ISurfaceComposer::EventRegistration>>' - sp<EventThreadConnection> createEventConnection(EventRegistrationFlags flags) const override { - return createEventConnection(false, flags); - } - MOCK_METHOD(sp<EventThreadConnection>, createEventConnection, (bool, EventRegistrationFlags), - (const)); - + MOCK_METHOD(sp<EventThreadConnection>, createEventConnection, (EventRegistrationFlags), + (const, override)); MOCK_METHOD(void, enableSyntheticVsync, (bool), (override)); MOCK_METHOD(void, onHotplugReceived, (PhysicalDisplayId, bool), (override)); MOCK_METHOD(void, onHotplugConnectionError, (int32_t), (override)); diff --git a/services/surfaceflinger/tests/unittests/mock/MockLayer.h b/services/surfaceflinger/tests/unittests/mock/MockLayer.h index 002fa9fb55..45f86fa1a8 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockLayer.h +++ b/services/surfaceflinger/tests/unittests/mock/MockLayer.h @@ -24,28 +24,18 @@ namespace android::mock { class MockLayer : public Layer { public: MockLayer(SurfaceFlinger* flinger, std::string name) - : Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 0, {})) { - EXPECT_CALL(*this, getDefaultFrameRateCompatibility()) - .WillOnce(testing::Return(scheduler::FrameRateCompatibility::Default)); - } + : Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 0, {})) {} MockLayer(SurfaceFlinger* flinger, std::string name, std::optional<uint32_t> uid) - : Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 0, {}, uid)) { - EXPECT_CALL(*this, getDefaultFrameRateCompatibility()) - .WillOnce(testing::Return(scheduler::FrameRateCompatibility::Default)); - } + : Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 0, {}, uid)) {} explicit MockLayer(SurfaceFlinger* flinger) : MockLayer(flinger, "TestLayer") {} - MOCK_CONST_METHOD0(getType, const char*()); MOCK_METHOD0(getFrameSelectionPriority, int32_t()); - MOCK_CONST_METHOD0(isVisible, bool()); MOCK_METHOD0(createClone, sp<Layer>()); MOCK_CONST_METHOD0(getFrameRateForLayerTree, FrameRate()); MOCK_CONST_METHOD0(getDefaultFrameRateCompatibility, scheduler::FrameRateCompatibility()); MOCK_CONST_METHOD0(getOwnerUid, uid_t()); - MOCK_CONST_METHOD0(getDataSpace, ui::Dataspace()); - MOCK_METHOD(bool, isFrontBuffered, (), (const, override)); }; } // namespace android::mock diff --git a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h index 8f21cdbaa6..d45cc66945 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h +++ b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h @@ -26,7 +26,6 @@ struct SchedulerCallback final : ISchedulerCallback { MOCK_METHOD(void, requestHardwareVsync, (PhysicalDisplayId, bool), (override)); MOCK_METHOD(void, requestDisplayModes, (std::vector<display::DisplayModeRequest>), (override)); MOCK_METHOD(void, kernelTimerChanged, (bool), (override)); - MOCK_METHOD(void, triggerOnFrameRateOverridesChanged, (), (override)); MOCK_METHOD(void, onChoreographerAttached, (), (override)); MOCK_METHOD(void, onExpectedPresentTimePosted, (TimePoint, ftl::NonNull<DisplayModePtr>, Fps), (override)); @@ -38,7 +37,6 @@ struct NoOpSchedulerCallback final : ISchedulerCallback { void requestHardwareVsync(PhysicalDisplayId, bool) override {} void requestDisplayModes(std::vector<display::DisplayModeRequest>) override {} void kernelTimerChanged(bool) override {} - void triggerOnFrameRateOverridesChanged() override {} void onChoreographerAttached() override {} void onExpectedPresentTimePosted(TimePoint, ftl::NonNull<DisplayModePtr>, Fps) override {} void onCommitNotComposited(PhysicalDisplayId) override {} diff --git a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h index 4f44d1b4fc..8d6d1d3cdc 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h +++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h @@ -18,6 +18,8 @@ #include <gmock/gmock.h> +#include <scheduler/FrameTime.h> + #include "Scheduler/VSyncTracker.h" namespace android::mock { @@ -37,7 +39,7 @@ public: MOCK_METHOD(bool, isVSyncInPhase, (nsecs_t, Fps), (override)); MOCK_METHOD(void, setDisplayModePtr, (ftl::NonNull<DisplayModePtr>), (override)); MOCK_METHOD(void, setRenderRate, (Fps, bool), (override)); - MOCK_METHOD(void, onFrameBegin, (TimePoint, TimePoint), (override)); + MOCK_METHOD(void, onFrameBegin, (TimePoint, scheduler::FrameTime), (override)); MOCK_METHOD(void, onFrameMissed, (TimePoint), (override)); MOCK_METHOD(void, dump, (std::string&), (const, override)); MOCK_METHOD(bool, isCurrentMode, (const ftl::NonNull<DisplayModePtr>&), (const, override)); diff --git a/services/surfaceflinger/tests/utils/ColorUtils.h b/services/surfaceflinger/tests/utils/ColorUtils.h index 07916b60a7..253bad7a59 100644 --- a/services/surfaceflinger/tests/utils/ColorUtils.h +++ b/services/surfaceflinger/tests/utils/ColorUtils.h @@ -74,8 +74,8 @@ public: static void applyMatrix(half3& color, const mat3& mat) { half3 ret = half3(0); - for (int i = 0; i < 3; i++) { - for (int j = 0; j < 3; j++) { + for (size_t i = 0; i < 3; i++) { + for (size_t j = 0; j < 3; j++) { ret[i] = ret[i] + color[j] * mat[j][i]; } } diff --git a/services/surfaceflinger/tests/utils/ScreenshotUtils.h b/services/surfaceflinger/tests/utils/ScreenshotUtils.h index 1675584f5a..0bedcd174e 100644 --- a/services/surfaceflinger/tests/utils/ScreenshotUtils.h +++ b/services/surfaceflinger/tests/utils/ScreenshotUtils.h @@ -15,7 +15,7 @@ */ #pragma once -#include <gui/AidlStatusUtil.h> +#include <gui/AidlUtil.h> #include <gui/SyncScreenCaptureListener.h> #include <private/gui/ComposerServiceAIDL.h> #include <ui/FenceResult.h> @@ -39,7 +39,7 @@ public: const auto sf = ComposerServiceAIDL::getComposerService(); SurfaceComposerClient::Transaction().apply(true); - captureArgs.dataspace = ui::Dataspace::V0_SRGB; + captureArgs.captureArgs.dataspace = static_cast<int32_t>(ui::Dataspace::V0_SRGB); const sp<SyncScreenCaptureListener> captureListener = sp<SyncScreenCaptureListener>::make(); binder::Status status = sf->captureDisplay(captureArgs, captureListener); status_t err = statusTFromBinderStatus(status); @@ -77,7 +77,7 @@ public: const auto sf = ComposerServiceAIDL::getComposerService(); SurfaceComposerClient::Transaction().apply(true); - captureArgs.dataspace = ui::Dataspace::V0_SRGB; + captureArgs.captureArgs.dataspace = static_cast<int32_t>(ui::Dataspace::V0_SRGB); const sp<SyncScreenCaptureListener> captureListener = sp<SyncScreenCaptureListener>::make(); binder::Status status = sf->captureLayers(captureArgs, captureListener); status_t err = statusTFromBinderStatus(status); diff --git a/services/vibratorservice/Android.bp b/services/vibratorservice/Android.bp index 2002bdf628..4735ae5897 100644 --- a/services/vibratorservice/Android.bp +++ b/services/vibratorservice/Android.bp @@ -33,19 +33,19 @@ cc_library_shared { ], aidl: { - local_include_dirs: ["include"], - include_dirs: [ - "hardware/interfaces/vibrator/aidl/android/hardware/vibrator", - ], - export_aidl_headers: true + local_include_dirs: ["include"], + include_dirs: [ + "hardware/interfaces/vibrator/aidl/android/hardware/vibrator", + ], + export_aidl_headers: true, }, shared_libs: [ - "libbinder", + "libbinder_ndk", "libhidlbase", "liblog", "libutils", - "android.hardware.vibrator-V2-cpp", + "android.hardware.vibrator-V3-ndk", "android.hardware.vibrator@1.0", "android.hardware.vibrator@1.1", "android.hardware.vibrator@1.2", diff --git a/services/vibratorservice/VibratorHalController.cpp b/services/vibratorservice/VibratorHalController.cpp index c1795f5c32..283a5f0301 100644 --- a/services/vibratorservice/VibratorHalController.cpp +++ b/services/vibratorservice/VibratorHalController.cpp @@ -16,9 +16,9 @@ #define LOG_TAG "VibratorHalController" +#include <aidl/android/hardware/vibrator/IVibrator.h> +#include <android/binder_manager.h> #include <android/hardware/vibrator/1.3/IVibrator.h> -#include <android/hardware/vibrator/IVibrator.h> -#include <binder/IServiceManager.h> #include <hardware/vibrator.h> #include <utils/Log.h> @@ -27,10 +27,10 @@ #include <vibratorservice/VibratorHalController.h> #include <vibratorservice/VibratorHalWrapper.h> -using android::hardware::vibrator::CompositeEffect; -using android::hardware::vibrator::CompositePrimitive; -using android::hardware::vibrator::Effect; -using android::hardware::vibrator::EffectStrength; +using aidl::android::hardware::vibrator::CompositeEffect; +using aidl::android::hardware::vibrator::CompositePrimitive; +using aidl::android::hardware::vibrator::Effect; +using aidl::android::hardware::vibrator::EffectStrength; using std::chrono::milliseconds; @@ -38,7 +38,7 @@ namespace V1_0 = android::hardware::vibrator::V1_0; namespace V1_1 = android::hardware::vibrator::V1_1; namespace V1_2 = android::hardware::vibrator::V1_2; namespace V1_3 = android::hardware::vibrator::V1_3; -namespace Aidl = android::hardware::vibrator; +namespace Aidl = aidl::android::hardware::vibrator; namespace android { @@ -53,10 +53,14 @@ std::shared_ptr<HalWrapper> connectHal(std::shared_ptr<CallbackScheduler> schedu return nullptr; } - sp<Aidl::IVibrator> aidlHal = waitForVintfService<Aidl::IVibrator>(); - if (aidlHal) { - ALOGV("Successfully connected to Vibrator HAL AIDL service."); - return std::make_shared<AidlHalWrapper>(std::move(scheduler), aidlHal); + auto serviceName = std::string(Aidl::IVibrator::descriptor) + "/default"; + if (AServiceManager_isDeclared(serviceName.c_str())) { + std::shared_ptr<Aidl::IVibrator> hal = Aidl::IVibrator::fromBinder( + ndk::SpAIBinder(AServiceManager_waitForService(serviceName.c_str()))); + if (hal) { + ALOGV("Successfully connected to Vibrator HAL AIDL service."); + return std::make_shared<AidlHalWrapper>(std::move(scheduler), std::move(hal)); + } } sp<V1_0::IVibrator> halV1_0 = V1_0::IVibrator::getService(); diff --git a/services/vibratorservice/VibratorHalWrapper.cpp b/services/vibratorservice/VibratorHalWrapper.cpp index f10ba44d74..4ac16188fa 100644 --- a/services/vibratorservice/VibratorHalWrapper.cpp +++ b/services/vibratorservice/VibratorHalWrapper.cpp @@ -16,8 +16,8 @@ #define LOG_TAG "VibratorHalWrapper" +#include <aidl/android/hardware/vibrator/IVibrator.h> #include <android/hardware/vibrator/1.3/IVibrator.h> -#include <android/hardware/vibrator/IVibrator.h> #include <hardware/vibrator.h> #include <cmath> @@ -26,12 +26,15 @@ #include <vibratorservice/VibratorCallbackScheduler.h> #include <vibratorservice/VibratorHalWrapper.h> -using android::hardware::vibrator::Braking; -using android::hardware::vibrator::CompositeEffect; -using android::hardware::vibrator::CompositePrimitive; -using android::hardware::vibrator::Effect; -using android::hardware::vibrator::EffectStrength; -using android::hardware::vibrator::PrimitivePwle; +using aidl::android::hardware::vibrator::Braking; +using aidl::android::hardware::vibrator::CompositeEffect; +using aidl::android::hardware::vibrator::CompositePrimitive; +using aidl::android::hardware::vibrator::Effect; +using aidl::android::hardware::vibrator::EffectStrength; +using aidl::android::hardware::vibrator::PrimitivePwle; +using aidl::android::hardware::vibrator::PwleV2OutputMapEntry; +using aidl::android::hardware::vibrator::PwleV2Primitive; +using aidl::android::hardware::vibrator::VendorEffect; using std::chrono::milliseconds; @@ -39,7 +42,7 @@ namespace V1_0 = android::hardware::vibrator::V1_0; namespace V1_1 = android::hardware::vibrator::V1_1; namespace V1_2 = android::hardware::vibrator::V1_2; namespace V1_3 = android::hardware::vibrator::V1_3; -namespace Aidl = android::hardware::vibrator; +namespace Aidl = aidl::android::hardware::vibrator; namespace android { @@ -93,9 +96,25 @@ Info HalWrapper::getInfo() { if (mInfoCache.mMaxAmplitudes.isFailed()) { mInfoCache.mMaxAmplitudes = getMaxAmplitudesInternal(); } + if (mInfoCache.mMaxEnvelopeEffectSize.isFailed()) { + mInfoCache.mMaxEnvelopeEffectSize = getMaxEnvelopeEffectSizeInternal(); + } + if (mInfoCache.mMinEnvelopeEffectControlPointDuration.isFailed()) { + mInfoCache.mMinEnvelopeEffectControlPointDuration = + getMinEnvelopeEffectControlPointDurationInternal(); + } + if (mInfoCache.mMaxEnvelopeEffectControlPointDuration.isFailed()) { + mInfoCache.mMaxEnvelopeEffectControlPointDuration = + getMaxEnvelopeEffectControlPointDurationInternal(); + } return mInfoCache.get(); } +HalResult<void> HalWrapper::performVendorEffect(const VendorEffect&, const std::function<void()>&) { + ALOGV("Skipped performVendorEffect because it's not available in Vibrator HAL"); + return HalResult<void>::unsupported(); +} + HalResult<milliseconds> HalWrapper::performComposedEffect(const std::vector<CompositeEffect>&, const std::function<void()>&) { ALOGV("Skipped performComposedEffect because it's not available in Vibrator HAL"); @@ -108,6 +127,12 @@ HalResult<void> HalWrapper::performPwleEffect(const std::vector<PrimitivePwle>&, return HalResult<void>::unsupported(); } +HalResult<void> HalWrapper::composePwleV2(const std::vector<PwleV2Primitive>&, + const std::function<void()>&) { + ALOGV("Skipped composePwleV2 because it's not available in Vibrator HAL"); + return HalResult<void>::unsupported(); +} + HalResult<Capabilities> HalWrapper::getCapabilities() { std::lock_guard<std::mutex> lock(mInfoMutex); if (mInfoCache.mCapabilities.isFailed()) { @@ -196,11 +221,28 @@ HalResult<std::vector<float>> HalWrapper::getMaxAmplitudesInternal() { ALOGV("Skipped getMaxAmplitudes because it's not available in Vibrator HAL"); return HalResult<std::vector<float>>::unsupported(); } +HalResult<int32_t> HalWrapper::getMaxEnvelopeEffectSizeInternal() { + ALOGV("Skipped getMaxEnvelopeEffectSizeInternal because it's not available " + "in Vibrator HAL"); + return HalResult<int32_t>::unsupported(); +} + +HalResult<milliseconds> HalWrapper::getMinEnvelopeEffectControlPointDurationInternal() { + ALOGV("Skipped getMinEnvelopeEffectControlPointDurationInternal because it's not " + "available in Vibrator HAL"); + return HalResult<milliseconds>::unsupported(); +} + +HalResult<milliseconds> HalWrapper::getMaxEnvelopeEffectControlPointDurationInternal() { + ALOGV("Skipped getMaxEnvelopeEffectControlPointDurationInternal because it's not " + "available in Vibrator HAL"); + return HalResult<milliseconds>::unsupported(); +} // ------------------------------------------------------------------------------------------------- HalResult<void> AidlHalWrapper::ping() { - return HalResultFactory::fromStatus(IInterface::asBinder(getHal())->pingBinder()); + return HalResultFactory::fromStatus(AIBinder_ping(getHal()->asBinder().get())); } void AidlHalWrapper::tryReconnect() { @@ -208,7 +250,7 @@ void AidlHalWrapper::tryReconnect() { if (!result.isOk()) { return; } - sp<Aidl::IVibrator> newHandle = result.value(); + std::shared_ptr<Aidl::IVibrator> newHandle = result.value(); if (newHandle) { std::lock_guard<std::mutex> lock(mHandleMutex); mHandle = std::move(newHandle); @@ -220,7 +262,8 @@ HalResult<void> AidlHalWrapper::on(milliseconds timeout, HalResult<Capabilities> capabilities = getCapabilities(); bool supportsCallback = capabilities.isOk() && static_cast<int32_t>(capabilities.value() & Capabilities::ON_CALLBACK); - auto cb = supportsCallback ? new HalCallbackWrapper(completionCallback) : nullptr; + auto cb = supportsCallback ? ndk::SharedRefBase::make<HalCallbackWrapper>(completionCallback) + : nullptr; auto ret = HalResultFactory::fromStatus(getHal()->on(timeout.count(), cb)); if (!supportsCallback && ret.isOk()) { @@ -255,13 +298,14 @@ HalResult<milliseconds> AidlHalWrapper::performEffect( HalResult<Capabilities> capabilities = getCapabilities(); bool supportsCallback = capabilities.isOk() && static_cast<int32_t>(capabilities.value() & Capabilities::PERFORM_CALLBACK); - auto cb = supportsCallback ? new HalCallbackWrapper(completionCallback) : nullptr; + auto cb = supportsCallback ? ndk::SharedRefBase::make<HalCallbackWrapper>(completionCallback) + : nullptr; int32_t lengthMs; - auto result = getHal()->perform(effect, strength, cb, &lengthMs); + auto status = getHal()->perform(effect, strength, cb, &lengthMs); milliseconds length = milliseconds(lengthMs); - auto ret = HalResultFactory::fromStatus<milliseconds>(result, length); + auto ret = HalResultFactory::fromStatus<milliseconds>(std::move(status), length); if (!supportsCallback && ret.isOk()) { mCallbackScheduler->schedule(completionCallback, length); } @@ -269,11 +313,18 @@ HalResult<milliseconds> AidlHalWrapper::performEffect( return ret; } +HalResult<void> AidlHalWrapper::performVendorEffect( + const VendorEffect& effect, const std::function<void()>& completionCallback) { + // This method should always support callbacks, so no need to double check. + auto cb = ndk::SharedRefBase::make<HalCallbackWrapper>(completionCallback); + return HalResultFactory::fromStatus(getHal()->performVendorEffect(effect, cb)); +} + HalResult<milliseconds> AidlHalWrapper::performComposedEffect( const std::vector<CompositeEffect>& primitives, const std::function<void()>& completionCallback) { // This method should always support callbacks, so no need to double check. - auto cb = new HalCallbackWrapper(completionCallback); + auto cb = ndk::SharedRefBase::make<HalCallbackWrapper>(completionCallback); auto durations = getPrimitiveDurations().valueOr({}); milliseconds duration(0); @@ -294,40 +345,47 @@ HalResult<milliseconds> AidlHalWrapper::performComposedEffect( HalResult<void> AidlHalWrapper::performPwleEffect(const std::vector<PrimitivePwle>& primitives, const std::function<void()>& completionCallback) { // This method should always support callbacks, so no need to double check. - auto cb = new HalCallbackWrapper(completionCallback); + auto cb = ndk::SharedRefBase::make<HalCallbackWrapper>(completionCallback); return HalResultFactory::fromStatus(getHal()->composePwle(primitives, cb)); } +HalResult<void> AidlHalWrapper::composePwleV2(const std::vector<PwleV2Primitive>& composite, + const std::function<void()>& completionCallback) { + // This method should always support callbacks, so no need to double check. + auto cb = ndk::SharedRefBase::make<HalCallbackWrapper>(completionCallback); + return HalResultFactory::fromStatus(getHal()->composePwleV2(composite, cb)); +} + HalResult<Capabilities> AidlHalWrapper::getCapabilitiesInternal() { - int32_t capabilities = 0; - auto result = getHal()->getCapabilities(&capabilities); - return HalResultFactory::fromStatus<Capabilities>(result, - static_cast<Capabilities>(capabilities)); + int32_t cap = 0; + auto status = getHal()->getCapabilities(&cap); + auto capabilities = static_cast<Capabilities>(cap); + return HalResultFactory::fromStatus<Capabilities>(std::move(status), capabilities); } HalResult<std::vector<Effect>> AidlHalWrapper::getSupportedEffectsInternal() { std::vector<Effect> supportedEffects; - auto result = getHal()->getSupportedEffects(&supportedEffects); - return HalResultFactory::fromStatus<std::vector<Effect>>(result, supportedEffects); + auto status = getHal()->getSupportedEffects(&supportedEffects); + return HalResultFactory::fromStatus<std::vector<Effect>>(std::move(status), supportedEffects); } HalResult<std::vector<Braking>> AidlHalWrapper::getSupportedBrakingInternal() { std::vector<Braking> supportedBraking; - auto result = getHal()->getSupportedBraking(&supportedBraking); - return HalResultFactory::fromStatus<std::vector<Braking>>(result, supportedBraking); + auto status = getHal()->getSupportedBraking(&supportedBraking); + return HalResultFactory::fromStatus<std::vector<Braking>>(std::move(status), supportedBraking); } HalResult<std::vector<CompositePrimitive>> AidlHalWrapper::getSupportedPrimitivesInternal() { std::vector<CompositePrimitive> supportedPrimitives; - auto result = getHal()->getSupportedPrimitives(&supportedPrimitives); - return HalResultFactory::fromStatus<std::vector<CompositePrimitive>>(result, + auto status = getHal()->getSupportedPrimitives(&supportedPrimitives); + return HalResultFactory::fromStatus<std::vector<CompositePrimitive>>(std::move(status), supportedPrimitives); } HalResult<std::vector<milliseconds>> AidlHalWrapper::getPrimitiveDurationsInternal( const std::vector<CompositePrimitive>& supportedPrimitives) { std::vector<milliseconds> durations; - constexpr auto primitiveRange = enum_range<CompositePrimitive>(); + constexpr auto primitiveRange = ndk::enum_range<CompositePrimitive>(); constexpr auto primitiveCount = std::distance(primitiveRange.begin(), primitiveRange.end()); durations.resize(primitiveCount); @@ -340,8 +398,8 @@ HalResult<std::vector<milliseconds>> AidlHalWrapper::getPrimitiveDurationsIntern continue; } int32_t duration = 0; - auto result = getHal()->getPrimitiveDuration(primitive, &duration); - auto halResult = HalResultFactory::fromStatus<int32_t>(result, duration); + auto status = getHal()->getPrimitiveDuration(primitive, &duration); + auto halResult = HalResultFactory::fromStatus<int32_t>(std::move(status), duration); if (halResult.isUnsupported()) { // Should not happen, supported primitives should always support requesting duration. ALOGE("Supported primitive %zu returned unsupported for getPrimitiveDuration", @@ -349,7 +407,7 @@ HalResult<std::vector<milliseconds>> AidlHalWrapper::getPrimitiveDurationsIntern } if (halResult.isFailed()) { // Fail entire request if one request has failed. - return HalResult<std::vector<milliseconds>>::failed(result.toString8().c_str()); + return HalResult<std::vector<milliseconds>>::failed(halResult.errorMessage()); } durations[primitiveIdx] = milliseconds(duration); } @@ -359,59 +417,77 @@ HalResult<std::vector<milliseconds>> AidlHalWrapper::getPrimitiveDurationsIntern HalResult<milliseconds> AidlHalWrapper::getPrimitiveDelayMaxInternal() { int32_t delay = 0; - auto result = getHal()->getCompositionDelayMax(&delay); - return HalResultFactory::fromStatus<milliseconds>(result, milliseconds(delay)); + auto status = getHal()->getCompositionDelayMax(&delay); + return HalResultFactory::fromStatus<milliseconds>(std::move(status), milliseconds(delay)); } HalResult<milliseconds> AidlHalWrapper::getPrimitiveDurationMaxInternal() { int32_t delay = 0; - auto result = getHal()->getPwlePrimitiveDurationMax(&delay); - return HalResultFactory::fromStatus<milliseconds>(result, milliseconds(delay)); + auto status = getHal()->getPwlePrimitiveDurationMax(&delay); + return HalResultFactory::fromStatus<milliseconds>(std::move(status), milliseconds(delay)); } HalResult<int32_t> AidlHalWrapper::getCompositionSizeMaxInternal() { int32_t size = 0; - auto result = getHal()->getCompositionSizeMax(&size); - return HalResultFactory::fromStatus<int32_t>(result, size); + auto status = getHal()->getCompositionSizeMax(&size); + return HalResultFactory::fromStatus<int32_t>(std::move(status), size); } HalResult<int32_t> AidlHalWrapper::getPwleSizeMaxInternal() { int32_t size = 0; - auto result = getHal()->getPwleCompositionSizeMax(&size); - return HalResultFactory::fromStatus<int32_t>(result, size); + auto status = getHal()->getPwleCompositionSizeMax(&size); + return HalResultFactory::fromStatus<int32_t>(std::move(status), size); } HalResult<float> AidlHalWrapper::getMinFrequencyInternal() { float minFrequency = 0; - auto result = getHal()->getFrequencyMinimum(&minFrequency); - return HalResultFactory::fromStatus<float>(result, minFrequency); + auto status = getHal()->getFrequencyMinimum(&minFrequency); + return HalResultFactory::fromStatus<float>(std::move(status), minFrequency); } HalResult<float> AidlHalWrapper::getResonantFrequencyInternal() { float f0 = 0; - auto result = getHal()->getResonantFrequency(&f0); - return HalResultFactory::fromStatus<float>(result, f0); + auto status = getHal()->getResonantFrequency(&f0); + return HalResultFactory::fromStatus<float>(std::move(status), f0); } HalResult<float> AidlHalWrapper::getFrequencyResolutionInternal() { float frequencyResolution = 0; - auto result = getHal()->getFrequencyResolution(&frequencyResolution); - return HalResultFactory::fromStatus<float>(result, frequencyResolution); + auto status = getHal()->getFrequencyResolution(&frequencyResolution); + return HalResultFactory::fromStatus<float>(std::move(status), frequencyResolution); } HalResult<float> AidlHalWrapper::getQFactorInternal() { float qFactor = 0; - auto result = getHal()->getQFactor(&qFactor); - return HalResultFactory::fromStatus<float>(result, qFactor); + auto status = getHal()->getQFactor(&qFactor); + return HalResultFactory::fromStatus<float>(std::move(status), qFactor); } HalResult<std::vector<float>> AidlHalWrapper::getMaxAmplitudesInternal() { std::vector<float> amplitudes; - auto result = getHal()->getBandwidthAmplitudeMap(&litudes); - return HalResultFactory::fromStatus<std::vector<float>>(result, amplitudes); + auto status = getHal()->getBandwidthAmplitudeMap(&litudes); + return HalResultFactory::fromStatus<std::vector<float>>(std::move(status), amplitudes); +} + +HalResult<int32_t> AidlHalWrapper::getMaxEnvelopeEffectSizeInternal() { + int32_t size = 0; + auto status = getHal()->getPwleV2CompositionSizeMax(&size); + return HalResultFactory::fromStatus<int32_t>(std::move(status), size); +} + +HalResult<milliseconds> AidlHalWrapper::getMinEnvelopeEffectControlPointDurationInternal() { + int32_t durationMs = 0; + auto status = getHal()->getPwleV2PrimitiveDurationMinMillis(&durationMs); + return HalResultFactory::fromStatus<milliseconds>(std::move(status), milliseconds(durationMs)); } -sp<Aidl::IVibrator> AidlHalWrapper::getHal() { +HalResult<milliseconds> AidlHalWrapper::getMaxEnvelopeEffectControlPointDurationInternal() { + int32_t durationMs = 0; + auto status = getHal()->getPwleV2PrimitiveDurationMaxMillis(&durationMs); + return HalResultFactory::fromStatus<milliseconds>(std::move(status), milliseconds(durationMs)); +} + +std::shared_ptr<Aidl::IVibrator> AidlHalWrapper::getHal() { std::lock_guard<std::mutex> lock(mHandleMutex); return mHandle; } @@ -420,8 +496,7 @@ sp<Aidl::IVibrator> AidlHalWrapper::getHal() { template <typename I> HalResult<void> HidlHalWrapper<I>::ping() { - auto result = getHal()->ping(); - return HalResultFactory::fromReturn(result); + return HalResultFactory::fromReturn(getHal()->ping()); } template <typename I> @@ -436,8 +511,8 @@ void HidlHalWrapper<I>::tryReconnect() { template <typename I> HalResult<void> HidlHalWrapper<I>::on(milliseconds timeout, const std::function<void()>& completionCallback) { - auto result = getHal()->on(timeout.count()); - auto ret = HalResultFactory::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR)); + auto status = getHal()->on(timeout.count()); + auto ret = HalResultFactory::fromStatus(status.withDefault(V1_0::Status::UNKNOWN_ERROR)); if (ret.isOk()) { mCallbackScheduler->schedule(completionCallback, timeout); } @@ -446,15 +521,15 @@ HalResult<void> HidlHalWrapper<I>::on(milliseconds timeout, template <typename I> HalResult<void> HidlHalWrapper<I>::off() { - auto result = getHal()->off(); - return HalResultFactory::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR)); + auto status = getHal()->off(); + return HalResultFactory::fromStatus(status.withDefault(V1_0::Status::UNKNOWN_ERROR)); } template <typename I> HalResult<void> HidlHalWrapper<I>::setAmplitude(float amplitude) { uint8_t amp = static_cast<uint8_t>(amplitude * std::numeric_limits<uint8_t>::max()); - auto result = getHal()->setAmplitude(amp); - return HalResultFactory::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR)); + auto status = getHal()->setAmplitude(amp); + return HalResultFactory::fromStatus(status.withDefault(V1_0::Status::UNKNOWN_ERROR)); } template <typename I> @@ -480,7 +555,7 @@ HalResult<Capabilities> HidlHalWrapper<I>::getCapabilitiesInternal() { hardware::Return<bool> result = getHal()->supportsAmplitudeControl(); Capabilities capabilities = result.withDefault(false) ? Capabilities::AMPLITUDE_CONTROL : Capabilities::NONE; - return HalResultFactory::fromReturn<Capabilities>(result, capabilities); + return HalResultFactory::fromReturn<Capabilities>(std::move(result), capabilities); } template <typename I> @@ -499,7 +574,7 @@ HalResult<milliseconds> HidlHalWrapper<I>::performInternal( auto result = std::invoke(performFn, handle, effect, effectStrength, effectCallback); milliseconds length = milliseconds(lengthMs); - auto ret = HalResultFactory::fromReturn<milliseconds>(result, status, length); + auto ret = HalResultFactory::fromReturn<milliseconds>(std::move(result), status, length); if (ret.isOk()) { mCallbackScheduler->schedule(completionCallback, length); } @@ -604,7 +679,7 @@ HalResult<Capabilities> HidlHalWrapperV1_3::getCapabilitiesInternal() { sp<V1_3::IVibrator> hal = getHal(); auto amplitudeResult = hal->supportsAmplitudeControl(); if (!amplitudeResult.isOk()) { - return HalResultFactory::fromReturn<Capabilities>(amplitudeResult, capabilities); + return HalResultFactory::fromReturn<Capabilities>(std::move(amplitudeResult), capabilities); } auto externalControlResult = hal->supportsExternalControl(); @@ -619,7 +694,8 @@ HalResult<Capabilities> HidlHalWrapperV1_3::getCapabilitiesInternal() { } } - return HalResultFactory::fromReturn<Capabilities>(externalControlResult, capabilities); + return HalResultFactory::fromReturn<Capabilities>(std::move(externalControlResult), + capabilities); } // ------------------------------------------------------------------------------------------------- diff --git a/services/vibratorservice/VibratorManagerHalController.cpp b/services/vibratorservice/VibratorManagerHalController.cpp index aa5b7fc86f..ba35d15bf2 100644 --- a/services/vibratorservice/VibratorManagerHalController.cpp +++ b/services/vibratorservice/VibratorManagerHalController.cpp @@ -20,7 +20,7 @@ #include <vibratorservice/VibratorManagerHalController.h> -namespace Aidl = android::hardware::vibrator; +namespace Aidl = aidl::android::hardware::vibrator; namespace android { @@ -29,10 +29,15 @@ namespace vibrator { std::shared_ptr<ManagerHalWrapper> connectManagerHal(std::shared_ptr<CallbackScheduler> scheduler) { static bool gHalExists = true; if (gHalExists) { - sp<Aidl::IVibratorManager> hal = waitForVintfService<Aidl::IVibratorManager>(); - if (hal) { - ALOGV("Successfully connected to VibratorManager HAL AIDL service."); - return std::make_shared<AidlManagerHalWrapper>(std::move(scheduler), hal); + auto serviceName = std::string(Aidl::IVibratorManager::descriptor) + "/default"; + if (AServiceManager_isDeclared(serviceName.c_str())) { + std::shared_ptr<Aidl::IVibratorManager> hal = Aidl::IVibratorManager::fromBinder( + ndk::SpAIBinder(AServiceManager_checkService(serviceName.c_str()))); + if (hal) { + ALOGV("Successfully connected to VibratorManager HAL AIDL service."); + return std::make_shared<AidlManagerHalWrapper>(std::move(scheduler), + std::move(hal)); + } } } diff --git a/services/vibratorservice/VibratorManagerHalWrapper.cpp b/services/vibratorservice/VibratorManagerHalWrapper.cpp index 13412667e0..93ec781b21 100644 --- a/services/vibratorservice/VibratorManagerHalWrapper.cpp +++ b/services/vibratorservice/VibratorManagerHalWrapper.cpp @@ -20,7 +20,7 @@ #include <vibratorservice/VibratorManagerHalWrapper.h> -namespace Aidl = android::hardware::vibrator; +namespace Aidl = aidl::android::hardware::vibrator; namespace android { @@ -75,10 +75,11 @@ HalResult<void> LegacyManagerHalWrapper::cancelSynced() { std::shared_ptr<HalWrapper> AidlManagerHalWrapper::connectToVibrator( int32_t vibratorId, std::shared_ptr<CallbackScheduler> callbackScheduler) { - std::function<HalResult<sp<Aidl::IVibrator>>()> reconnectFn = [=, this]() { - sp<Aidl::IVibrator> vibrator; - auto result = this->getHal()->getVibrator(vibratorId, &vibrator); - return HalResultFactory::fromStatus<sp<Aidl::IVibrator>>(result, vibrator); + std::function<HalResult<std::shared_ptr<Aidl::IVibrator>>()> reconnectFn = [=, this]() { + std::shared_ptr<Aidl::IVibrator> vibrator; + auto status = this->getHal()->getVibrator(vibratorId, &vibrator); + return HalResultFactory::fromStatus<std::shared_ptr<Aidl::IVibrator>>(std::move(status), + vibrator); }; auto result = reconnectFn(); if (!result.isOk()) { @@ -93,11 +94,13 @@ std::shared_ptr<HalWrapper> AidlManagerHalWrapper::connectToVibrator( } HalResult<void> AidlManagerHalWrapper::ping() { - return HalResultFactory::fromStatus(IInterface::asBinder(getHal())->pingBinder()); + return HalResultFactory::fromStatus(AIBinder_ping(getHal()->asBinder().get())); } void AidlManagerHalWrapper::tryReconnect() { - sp<Aidl::IVibratorManager> newHandle = checkVintfService<Aidl::IVibratorManager>(); + auto aidlServiceName = std::string(Aidl::IVibratorManager::descriptor) + "/default"; + std::shared_ptr<Aidl::IVibratorManager> newHandle = Aidl::IVibratorManager::fromBinder( + ndk::SpAIBinder(AServiceManager_checkService(aidlServiceName.c_str()))); if (newHandle) { std::lock_guard<std::mutex> lock(mHandleMutex); mHandle = std::move(newHandle); @@ -111,9 +114,9 @@ HalResult<ManagerCapabilities> AidlManagerHalWrapper::getCapabilities() { return HalResult<ManagerCapabilities>::ok(*mCapabilities); } int32_t cap = 0; - auto result = getHal()->getCapabilities(&cap); + auto status = getHal()->getCapabilities(&cap); auto capabilities = static_cast<ManagerCapabilities>(cap); - auto ret = HalResultFactory::fromStatus<ManagerCapabilities>(result, capabilities); + auto ret = HalResultFactory::fromStatus<ManagerCapabilities>(std::move(status), capabilities); if (ret.isOk()) { // Cache copy of returned value. mCapabilities.emplace(ret.value()); @@ -128,8 +131,8 @@ HalResult<std::vector<int32_t>> AidlManagerHalWrapper::getVibratorIds() { return HalResult<std::vector<int32_t>>::ok(*mVibratorIds); } std::vector<int32_t> ids; - auto result = getHal()->getVibratorIds(&ids); - auto ret = HalResultFactory::fromStatus<std::vector<int32_t>>(result, ids); + auto status = getHal()->getVibratorIds(&ids); + auto ret = HalResultFactory::fromStatus<std::vector<int32_t>>(std::move(status), ids); if (ret.isOk()) { // Cache copy of returned value and the individual controllers. mVibratorIds.emplace(ret.value()); @@ -178,7 +181,8 @@ HalResult<void> AidlManagerHalWrapper::triggerSynced( HalResult<ManagerCapabilities> capabilities = getCapabilities(); bool supportsCallback = capabilities.isOk() && static_cast<int32_t>(capabilities.value() & ManagerCapabilities::TRIGGER_CALLBACK); - auto cb = supportsCallback ? new HalCallbackWrapper(completionCallback) : nullptr; + auto cb = supportsCallback ? ndk::SharedRefBase::make<HalCallbackWrapper>(completionCallback) + : nullptr; return HalResultFactory::fromStatus(getHal()->triggerSynced(cb)); } @@ -196,7 +200,7 @@ HalResult<void> AidlManagerHalWrapper::cancelSynced() { return ret; } -sp<Aidl::IVibratorManager> AidlManagerHalWrapper::getHal() { +std::shared_ptr<Aidl::IVibratorManager> AidlManagerHalWrapper::getHal() { std::lock_guard<std::mutex> lock(mHandleMutex); return mHandle; } diff --git a/services/vibratorservice/benchmarks/Android.bp b/services/vibratorservice/benchmarks/Android.bp index 5437995899..915d6c7bc3 100644 --- a/services/vibratorservice/benchmarks/Android.bp +++ b/services/vibratorservice/benchmarks/Android.bp @@ -28,12 +28,12 @@ cc_benchmark { "VibratorHalControllerBenchmarks.cpp", ], shared_libs: [ - "libbinder", + "libbinder_ndk", "libhidlbase", "liblog", "libutils", "libvibratorservice", - "android.hardware.vibrator-V2-cpp", + "android.hardware.vibrator-V3-ndk", "android.hardware.vibrator@1.0", "android.hardware.vibrator@1.1", "android.hardware.vibrator@1.2", diff --git a/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp b/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp index 9b30337885..5c7c9f46c1 100644 --- a/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp +++ b/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp @@ -16,16 +16,15 @@ #define LOG_TAG "VibratorHalControllerBenchmarks" +#include <android/binder_process.h> #include <benchmark/benchmark.h> -#include <binder/ProcessState.h> #include <vibratorservice/VibratorHalController.h> #include <future> -using ::android::enum_range; -using ::android::hardware::vibrator::CompositeEffect; -using ::android::hardware::vibrator::CompositePrimitive; -using ::android::hardware::vibrator::Effect; -using ::android::hardware::vibrator::EffectStrength; +using ::aidl::android::hardware::vibrator::CompositeEffect; +using ::aidl::android::hardware::vibrator::CompositePrimitive; +using ::aidl::android::hardware::vibrator::Effect; +using ::aidl::android::hardware::vibrator::EffectStrength; using ::benchmark::Counter; using ::benchmark::Fixture; using ::benchmark::kMicrosecond; @@ -115,8 +114,8 @@ private: class VibratorBench : public Fixture { public: void SetUp(State& /*state*/) override { - android::ProcessState::self()->setThreadPoolMaxThreadCount(1); - android::ProcessState::self()->startThreadPool(); + ABinderProcess_setThreadPoolMaxThreadCount(1); + ABinderProcess_startThreadPool(); mController.init(); } @@ -388,11 +387,11 @@ public: return; } - for (const auto& effect : enum_range<Effect>()) { + for (const auto& effect : ndk::enum_range<Effect>()) { if (std::find(supported.begin(), supported.end(), effect) == supported.end()) { continue; } - for (const auto& strength : enum_range<EffectStrength>()) { + for (const auto& strength : ndk::enum_range<EffectStrength>()) { b->Args({static_cast<long>(effect), static_cast<long>(strength)}); } } @@ -533,7 +532,7 @@ public: return; } - for (const auto& primitive : enum_range<CompositePrimitive>()) { + for (const auto& primitive : ndk::enum_range<CompositePrimitive>()) { if (std::find(supported.begin(), supported.end(), primitive) == supported.end()) { continue; } diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalController.h b/services/vibratorservice/include/vibratorservice/VibratorHalController.h index f97442ddee..a1cb3fad35 100644 --- a/services/vibratorservice/include/vibratorservice/VibratorHalController.h +++ b/services/vibratorservice/include/vibratorservice/VibratorHalController.h @@ -17,8 +17,8 @@ #ifndef ANDROID_OS_VIBRATORHALCONTROLLER_H #define ANDROID_OS_VIBRATORHALCONTROLLER_H +#include <aidl/android/hardware/vibrator/IVibrator.h> #include <android-base/thread_annotations.h> -#include <android/hardware/vibrator/IVibrator.h> #include <vibratorservice/VibratorCallbackScheduler.h> #include <vibratorservice/VibratorHalWrapper.h> diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h index 39c4eb441e..4938b156e7 100644 --- a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h +++ b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h @@ -17,10 +17,12 @@ #ifndef ANDROID_OS_VIBRATORHALWRAPPER_H #define ANDROID_OS_VIBRATORHALWRAPPER_H +#include <aidl/android/hardware/vibrator/BnVibratorCallback.h> +#include <aidl/android/hardware/vibrator/IVibrator.h> + #include <android-base/thread_annotations.h> +#include <android/binder_manager.h> #include <android/hardware/vibrator/1.3/IVibrator.h> -#include <android/hardware/vibrator/BnVibratorCallback.h> -#include <android/hardware/vibrator/IVibrator.h> #include <binder/IServiceManager.h> #include <vibratorservice/VibratorCallbackScheduler.h> @@ -98,43 +100,49 @@ private: class HalResultFactory { public: template <typename T> - static HalResult<T> fromStatus(binder::Status status, T data) { - return status.isOk() ? HalResult<T>::ok(data) : fromFailedStatus<T>(status); + static HalResult<T> fromStatus(ndk::ScopedAStatus&& status, T data) { + return status.isOk() ? HalResult<T>::ok(std::move(data)) + : fromFailedStatus<T>(std::move(status)); } template <typename T> - static HalResult<T> fromStatus(hardware::vibrator::V1_0::Status status, T data) { - return (status == hardware::vibrator::V1_0::Status::OK) ? HalResult<T>::ok(data) - : fromFailedStatus<T>(status); + static HalResult<T> fromStatus(hardware::vibrator::V1_0::Status&& status, T data) { + return (status == hardware::vibrator::V1_0::Status::OK) + ? HalResult<T>::ok(std::move(data)) + : fromFailedStatus<T>(std::move(status)); } template <typename T, typename R> - static HalResult<T> fromReturn(hardware::Return<R>& ret, T data) { - return ret.isOk() ? HalResult<T>::ok(data) : fromFailedReturn<T, R>(ret); + static HalResult<T> fromReturn(hardware::Return<R>&& ret, T data) { + return ret.isOk() ? HalResult<T>::ok(std::move(data)) + : fromFailedReturn<T, R>(std::move(ret)); } template <typename T, typename R> - static HalResult<T> fromReturn(hardware::Return<R>& ret, + static HalResult<T> fromReturn(hardware::Return<R>&& ret, hardware::vibrator::V1_0::Status status, T data) { - return ret.isOk() ? fromStatus<T>(status, data) : fromFailedReturn<T, R>(ret); + return ret.isOk() ? fromStatus<T>(std::move(status), std::move(data)) + : fromFailedReturn<T, R>(std::move(ret)); } static HalResult<void> fromStatus(status_t status) { - return (status == android::OK) ? HalResult<void>::ok() : fromFailedStatus<void>(status); + return (status == android::OK) ? HalResult<void>::ok() + : fromFailedStatus<void>(std::move(status)); } - static HalResult<void> fromStatus(binder::Status status) { - return status.isOk() ? HalResult<void>::ok() : fromFailedStatus<void>(status); + static HalResult<void> fromStatus(ndk::ScopedAStatus&& status) { + return status.isOk() ? HalResult<void>::ok() : fromFailedStatus<void>(std::move(status)); } - static HalResult<void> fromStatus(hardware::vibrator::V1_0::Status status) { - return (status == hardware::vibrator::V1_0::Status::OK) ? HalResult<void>::ok() - : fromFailedStatus<void>(status); + static HalResult<void> fromStatus(hardware::vibrator::V1_0::Status&& status) { + return (status == hardware::vibrator::V1_0::Status::OK) + ? HalResult<void>::ok() + : fromFailedStatus<void>(std::move(status)); } template <typename R> - static HalResult<void> fromReturn(hardware::Return<R>& ret) { - return ret.isOk() ? HalResult<void>::ok() : fromFailedReturn<void, R>(ret); + static HalResult<void> fromReturn(hardware::Return<R>&& ret) { + return ret.isOk() ? HalResult<void>::ok() : fromFailedReturn<void, R>(std::move(ret)); } private: @@ -146,21 +154,21 @@ private: } template <typename T> - static HalResult<T> fromFailedStatus(binder::Status status) { - if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION || - status.transactionError() == android::UNKNOWN_TRANSACTION) { - // UNKNOWN_TRANSACTION means the HAL implementation is an older version, so this is - // the same as the operation being unsupported by this HAL. Should not retry. + static HalResult<T> fromFailedStatus(ndk::ScopedAStatus&& status) { + if (status.getExceptionCode() == EX_UNSUPPORTED_OPERATION || + status.getStatus() == STATUS_UNKNOWN_TRANSACTION) { + // STATUS_UNKNOWN_TRANSACTION means the HAL implementation is an older version, so this + // is the same as the operation being unsupported by this HAL. Should not retry. return HalResult<T>::unsupported(); } - if (status.exceptionCode() == binder::Status::EX_TRANSACTION_FAILED) { - return HalResult<T>::transactionFailed(status.toString8().c_str()); + if (status.getExceptionCode() == EX_TRANSACTION_FAILED) { + return HalResult<T>::transactionFailed(status.getMessage()); } - return HalResult<T>::failed(status.toString8().c_str()); + return HalResult<T>::failed(status.getMessage()); } template <typename T> - static HalResult<T> fromFailedStatus(hardware::vibrator::V1_0::Status status) { + static HalResult<T> fromFailedStatus(hardware::vibrator::V1_0::Status&& status) { switch (status) { case hardware::vibrator::V1_0::Status::UNSUPPORTED_OPERATION: return HalResult<T>::unsupported(); @@ -171,7 +179,7 @@ private: } template <typename T, typename R> - static HalResult<T> fromFailedReturn(hardware::Return<R>& ret) { + static HalResult<T> fromFailedReturn(hardware::Return<R>&& ret) { return ret.isDeadObject() ? HalResult<T>::transactionFailed(ret.description().c_str()) : HalResult<T>::failed(ret.description().c_str()); } @@ -179,14 +187,14 @@ private: // ------------------------------------------------------------------------------------------------- -class HalCallbackWrapper : public hardware::vibrator::BnVibratorCallback { +class HalCallbackWrapper : public aidl::android::hardware::vibrator::BnVibratorCallback { public: HalCallbackWrapper(std::function<void()> completionCallback) : mCompletionCallback(completionCallback) {} - binder::Status onComplete() override { + ndk::ScopedAStatus onComplete() override { mCompletionCallback(); - return binder::Status::ok(); + return ndk::ScopedAStatus::ok(); } private: @@ -198,14 +206,15 @@ private: // Vibrator HAL capabilities. enum class Capabilities : int32_t { NONE = 0, - ON_CALLBACK = hardware::vibrator::IVibrator::CAP_ON_CALLBACK, - PERFORM_CALLBACK = hardware::vibrator::IVibrator::CAP_PERFORM_CALLBACK, - AMPLITUDE_CONTROL = hardware::vibrator::IVibrator::CAP_AMPLITUDE_CONTROL, - EXTERNAL_CONTROL = hardware::vibrator::IVibrator::CAP_EXTERNAL_CONTROL, - EXTERNAL_AMPLITUDE_CONTROL = hardware::vibrator::IVibrator::CAP_EXTERNAL_AMPLITUDE_CONTROL, - COMPOSE_EFFECTS = hardware::vibrator::IVibrator::CAP_COMPOSE_EFFECTS, - COMPOSE_PWLE_EFFECTS = hardware::vibrator::IVibrator::CAP_COMPOSE_PWLE_EFFECTS, - ALWAYS_ON_CONTROL = hardware::vibrator::IVibrator::CAP_ALWAYS_ON_CONTROL, + ON_CALLBACK = aidl::android::hardware::vibrator::IVibrator::CAP_ON_CALLBACK, + PERFORM_CALLBACK = aidl::android::hardware::vibrator::IVibrator::CAP_PERFORM_CALLBACK, + AMPLITUDE_CONTROL = aidl::android::hardware::vibrator::IVibrator::CAP_AMPLITUDE_CONTROL, + EXTERNAL_CONTROL = aidl::android::hardware::vibrator::IVibrator::CAP_EXTERNAL_CONTROL, + EXTERNAL_AMPLITUDE_CONTROL = + aidl::android::hardware::vibrator::IVibrator::CAP_EXTERNAL_AMPLITUDE_CONTROL, + COMPOSE_EFFECTS = aidl::android::hardware::vibrator::IVibrator::CAP_COMPOSE_EFFECTS, + COMPOSE_PWLE_EFFECTS = aidl::android::hardware::vibrator::IVibrator::CAP_COMPOSE_PWLE_EFFECTS, + ALWAYS_ON_CONTROL = aidl::android::hardware::vibrator::IVibrator::CAP_ALWAYS_ON_CONTROL, }; inline Capabilities operator|(Capabilities lhs, Capabilities rhs) { @@ -230,10 +239,15 @@ inline Capabilities& operator&=(Capabilities& lhs, Capabilities rhs) { class Info { public: + using Effect = aidl::android::hardware::vibrator::Effect; + using EffectStrength = aidl::android::hardware::vibrator::EffectStrength; + using CompositePrimitive = aidl::android::hardware::vibrator::CompositePrimitive; + using Braking = aidl::android::hardware::vibrator::Braking; + const HalResult<Capabilities> capabilities; - const HalResult<std::vector<hardware::vibrator::Effect>> supportedEffects; - const HalResult<std::vector<hardware::vibrator::Braking>> supportedBraking; - const HalResult<std::vector<hardware::vibrator::CompositePrimitive>> supportedPrimitives; + const HalResult<std::vector<Effect>> supportedEffects; + const HalResult<std::vector<Braking>> supportedBraking; + const HalResult<std::vector<CompositePrimitive>> supportedPrimitives; const HalResult<std::vector<std::chrono::milliseconds>> primitiveDurations; const HalResult<std::chrono::milliseconds> primitiveDelayMax; const HalResult<std::chrono::milliseconds> pwlePrimitiveDurationMax; @@ -244,15 +258,15 @@ public: const HalResult<float> frequencyResolution; const HalResult<float> qFactor; const HalResult<std::vector<float>> maxAmplitudes; + const HalResult<int32_t> maxEnvelopeEffectSize; + const HalResult<std::chrono::milliseconds> minEnvelopeEffectControlPointDuration; + const HalResult<std::chrono::milliseconds> maxEnvelopeEffectControlPointDuration; void logFailures() const { logFailure<Capabilities>(capabilities, "getCapabilities"); - logFailure<std::vector<hardware::vibrator::Effect>>(supportedEffects, - "getSupportedEffects"); - logFailure<std::vector<hardware::vibrator::Braking>>(supportedBraking, - "getSupportedBraking"); - logFailure<std::vector<hardware::vibrator::CompositePrimitive>>(supportedPrimitives, - "getSupportedPrimitives"); + logFailure<std::vector<Effect>>(supportedEffects, "getSupportedEffects"); + logFailure<std::vector<Braking>>(supportedBraking, "getSupportedBraking"); + logFailure<std::vector<CompositePrimitive>>(supportedPrimitives, "getSupportedPrimitives"); logFailure<std::vector<std::chrono::milliseconds>>(primitiveDurations, "getPrimitiveDuration"); logFailure<std::chrono::milliseconds>(primitiveDelayMax, "getPrimitiveDelayMax"); @@ -265,6 +279,11 @@ public: logFailure<float>(frequencyResolution, "getFrequencyResolution"); logFailure<float>(qFactor, "getQFactor"); logFailure<std::vector<float>>(maxAmplitudes, "getMaxAmplitudes"); + logFailure<int32_t>(maxEnvelopeEffectSize, "getMaxEnvelopeEffectSize"); + logFailure<std::chrono::milliseconds>(minEnvelopeEffectControlPointDuration, + "getMinEnvelopeEffectControlPointDuration"); + logFailure<std::chrono::milliseconds>(maxEnvelopeEffectControlPointDuration, + "getMaxEnvelopeEffectControlPointDuration"); } bool shouldRetry() const { @@ -274,7 +293,10 @@ public: pwlePrimitiveDurationMax.shouldRetry() || compositionSizeMax.shouldRetry() || pwleSizeMax.shouldRetry() || minFrequency.shouldRetry() || resonantFrequency.shouldRetry() || frequencyResolution.shouldRetry() || - qFactor.shouldRetry() || maxAmplitudes.shouldRetry(); + qFactor.shouldRetry() || maxAmplitudes.shouldRetry() || + maxEnvelopeEffectSize.shouldRetry() || + minEnvelopeEffectControlPointDuration.shouldRetry() || + maxEnvelopeEffectControlPointDuration.shouldRetry(); } private: @@ -302,19 +324,22 @@ public: mResonantFrequency, mFrequencyResolution, mQFactor, - mMaxAmplitudes}; + mMaxAmplitudes, + mMaxEnvelopeEffectSize, + mMinEnvelopeEffectControlPointDuration, + mMaxEnvelopeEffectControlPointDuration}; } private: // Create a transaction failed results as default so we can retry on the first time we get them. static const constexpr char* MSG = "never loaded"; HalResult<Capabilities> mCapabilities = HalResult<Capabilities>::transactionFailed(MSG); - HalResult<std::vector<hardware::vibrator::Effect>> mSupportedEffects = - HalResult<std::vector<hardware::vibrator::Effect>>::transactionFailed(MSG); - HalResult<std::vector<hardware::vibrator::Braking>> mSupportedBraking = - HalResult<std::vector<hardware::vibrator::Braking>>::transactionFailed(MSG); - HalResult<std::vector<hardware::vibrator::CompositePrimitive>> mSupportedPrimitives = - HalResult<std::vector<hardware::vibrator::CompositePrimitive>>::transactionFailed(MSG); + HalResult<std::vector<Info::Effect>> mSupportedEffects = + HalResult<std::vector<Info::Effect>>::transactionFailed(MSG); + HalResult<std::vector<Info::Braking>> mSupportedBraking = + HalResult<std::vector<Info::Braking>>::transactionFailed(MSG); + HalResult<std::vector<Info::CompositePrimitive>> mSupportedPrimitives = + HalResult<std::vector<Info::CompositePrimitive>>::transactionFailed(MSG); HalResult<std::vector<std::chrono::milliseconds>> mPrimitiveDurations = HalResult<std::vector<std::chrono::milliseconds>>::transactionFailed(MSG); HalResult<std::chrono::milliseconds> mPrimitiveDelayMax = @@ -329,6 +354,11 @@ private: HalResult<float> mQFactor = HalResult<float>::transactionFailed(MSG); HalResult<std::vector<float>> mMaxAmplitudes = HalResult<std::vector<float>>::transactionFailed(MSG); + HalResult<int32_t> mMaxEnvelopeEffectSize = HalResult<int>::transactionFailed(MSG); + HalResult<std::chrono::milliseconds> mMinEnvelopeEffectControlPointDuration = + HalResult<std::chrono::milliseconds>::transactionFailed(MSG); + HalResult<std::chrono::milliseconds> mMaxEnvelopeEffectControlPointDuration = + HalResult<std::chrono::milliseconds>::transactionFailed(MSG); friend class HalWrapper; }; @@ -336,6 +366,16 @@ private: // Wrapper for Vibrator HAL handlers. class HalWrapper { public: + using Effect = aidl::android::hardware::vibrator::Effect; + using EffectStrength = aidl::android::hardware::vibrator::EffectStrength; + using VendorEffect = aidl::android::hardware::vibrator::VendorEffect; + using CompositePrimitive = aidl::android::hardware::vibrator::CompositePrimitive; + using CompositeEffect = aidl::android::hardware::vibrator::CompositeEffect; + using Braking = aidl::android::hardware::vibrator::Braking; + using PrimitivePwle = aidl::android::hardware::vibrator::PrimitivePwle; + using PwleV2Primitive = aidl::android::hardware::vibrator::PwleV2Primitive; + using PwleV2OutputMapEntry = aidl::android::hardware::vibrator::PwleV2OutputMapEntry; + explicit HalWrapper(std::shared_ptr<CallbackScheduler> scheduler) : mCallbackScheduler(std::move(scheduler)) {} virtual ~HalWrapper() = default; @@ -355,21 +395,25 @@ public: virtual HalResult<void> setAmplitude(float amplitude) = 0; virtual HalResult<void> setExternalControl(bool enabled) = 0; - virtual HalResult<void> alwaysOnEnable(int32_t id, hardware::vibrator::Effect effect, - hardware::vibrator::EffectStrength strength) = 0; + virtual HalResult<void> alwaysOnEnable(int32_t id, Effect effect, EffectStrength strength) = 0; virtual HalResult<void> alwaysOnDisable(int32_t id) = 0; virtual HalResult<std::chrono::milliseconds> performEffect( - hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength, + Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) = 0; + virtual HalResult<void> performVendorEffect(const VendorEffect& effect, + const std::function<void()>& completionCallback); + virtual HalResult<std::chrono::milliseconds> performComposedEffect( - const std::vector<hardware::vibrator::CompositeEffect>& primitives, + const std::vector<CompositeEffect>& primitives, const std::function<void()>& completionCallback); - virtual HalResult<void> performPwleEffect( - const std::vector<hardware::vibrator::PrimitivePwle>& primitives, - const std::function<void()>& completionCallback); + virtual HalResult<void> performPwleEffect(const std::vector<PrimitivePwle>& primitives, + const std::function<void()>& completionCallback); + + virtual HalResult<void> composePwleV2(const std::vector<PwleV2Primitive>& composite, + const std::function<void()>& completionCallback); protected: // Shared pointer to allow CallbackScheduler to outlive this wrapper. @@ -381,12 +425,11 @@ protected: // Request vibrator info to HAL skipping cache. virtual HalResult<Capabilities> getCapabilitiesInternal() = 0; - virtual HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffectsInternal(); - virtual HalResult<std::vector<hardware::vibrator::Braking>> getSupportedBrakingInternal(); - virtual HalResult<std::vector<hardware::vibrator::CompositePrimitive>> - getSupportedPrimitivesInternal(); + virtual HalResult<std::vector<Effect>> getSupportedEffectsInternal(); + virtual HalResult<std::vector<Braking>> getSupportedBrakingInternal(); + virtual HalResult<std::vector<CompositePrimitive>> getSupportedPrimitivesInternal(); virtual HalResult<std::vector<std::chrono::milliseconds>> getPrimitiveDurationsInternal( - const std::vector<hardware::vibrator::CompositePrimitive>& supportedPrimitives); + const std::vector<CompositePrimitive>& supportedPrimitives); virtual HalResult<std::chrono::milliseconds> getPrimitiveDelayMaxInternal(); virtual HalResult<std::chrono::milliseconds> getPrimitiveDurationMaxInternal(); virtual HalResult<int32_t> getCompositionSizeMaxInternal(); @@ -396,6 +439,9 @@ protected: virtual HalResult<float> getFrequencyResolutionInternal(); virtual HalResult<float> getQFactorInternal(); virtual HalResult<std::vector<float>> getMaxAmplitudesInternal(); + virtual HalResult<int32_t> getMaxEnvelopeEffectSizeInternal(); + virtual HalResult<std::chrono::milliseconds> getMinEnvelopeEffectControlPointDurationInternal(); + virtual HalResult<std::chrono::milliseconds> getMaxEnvelopeEffectControlPointDurationInternal(); private: std::mutex mInfoMutex; @@ -405,12 +451,17 @@ private: // Wrapper for the AIDL Vibrator HAL. class AidlHalWrapper : public HalWrapper { public: + using IVibrator = aidl::android::hardware::vibrator::IVibrator; + using reconnect_fn = std::function<HalResult<std::shared_ptr<IVibrator>>()>; + AidlHalWrapper( - std::shared_ptr<CallbackScheduler> scheduler, sp<hardware::vibrator::IVibrator> handle, - std::function<HalResult<sp<hardware::vibrator::IVibrator>>()> reconnectFn = + std::shared_ptr<CallbackScheduler> scheduler, std::shared_ptr<IVibrator> handle, + reconnect_fn reconnectFn = []() { - return HalResult<sp<hardware::vibrator::IVibrator>>::ok( - checkVintfService<hardware::vibrator::IVibrator>()); + auto serviceName = std::string(IVibrator::descriptor) + "/default"; + auto hal = IVibrator::fromBinder( + ndk::SpAIBinder(AServiceManager_checkService(serviceName.c_str()))); + return HalResult<std::shared_ptr<IVibrator>>::ok(std::move(hal)); }) : HalWrapper(std::move(scheduler)), mReconnectFn(reconnectFn), @@ -427,32 +478,36 @@ public: HalResult<void> setAmplitude(float amplitude) override final; HalResult<void> setExternalControl(bool enabled) override final; - HalResult<void> alwaysOnEnable(int32_t id, hardware::vibrator::Effect effect, - hardware::vibrator::EffectStrength strength) override final; + HalResult<void> alwaysOnEnable(int32_t id, Effect effect, + EffectStrength strength) override final; HalResult<void> alwaysOnDisable(int32_t id) override final; HalResult<std::chrono::milliseconds> performEffect( - hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength, + Effect effect, EffectStrength strength, + const std::function<void()>& completionCallback) override final; + + HalResult<void> performVendorEffect( + const VendorEffect& effect, const std::function<void()>& completionCallback) override final; HalResult<std::chrono::milliseconds> performComposedEffect( - const std::vector<hardware::vibrator::CompositeEffect>& primitives, + const std::vector<CompositeEffect>& primitives, const std::function<void()>& completionCallback) override final; HalResult<void> performPwleEffect( - const std::vector<hardware::vibrator::PrimitivePwle>& primitives, + const std::vector<PrimitivePwle>& primitives, const std::function<void()>& completionCallback) override final; + HalResult<void> composePwleV2(const std::vector<PwleV2Primitive>& composite, + const std::function<void()>& completionCallback) override final; + protected: HalResult<Capabilities> getCapabilitiesInternal() override final; - HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffectsInternal() override final; - HalResult<std::vector<hardware::vibrator::Braking>> getSupportedBrakingInternal() - override final; - HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitivesInternal() - override final; + HalResult<std::vector<Effect>> getSupportedEffectsInternal() override final; + HalResult<std::vector<Braking>> getSupportedBrakingInternal() override final; + HalResult<std::vector<CompositePrimitive>> getSupportedPrimitivesInternal() override final; HalResult<std::vector<std::chrono::milliseconds>> getPrimitiveDurationsInternal( - const std::vector<hardware::vibrator::CompositePrimitive>& supportedPrimitives) - override final; + const std::vector<CompositePrimitive>& supportedPrimitives) override final; HalResult<std::chrono::milliseconds> getPrimitiveDelayMaxInternal() override final; HalResult<std::chrono::milliseconds> getPrimitiveDurationMaxInternal() override final; HalResult<int32_t> getCompositionSizeMaxInternal() override final; @@ -462,13 +517,20 @@ protected: HalResult<float> getFrequencyResolutionInternal() override final; HalResult<float> getQFactorInternal() override final; HalResult<std::vector<float>> getMaxAmplitudesInternal() override final; + HalResult<int32_t> getMaxEnvelopeEffectSizeInternal() override final; + + HalResult<std::chrono::milliseconds> getMinEnvelopeEffectControlPointDurationInternal() + override final; + + HalResult<std::chrono::milliseconds> getMaxEnvelopeEffectControlPointDurationInternal() + override final; private: - const std::function<HalResult<sp<hardware::vibrator::IVibrator>>()> mReconnectFn; + const reconnect_fn mReconnectFn; std::mutex mHandleMutex; - sp<hardware::vibrator::IVibrator> mHandle GUARDED_BY(mHandleMutex); + std::shared_ptr<IVibrator> mHandle GUARDED_BY(mHandleMutex); - sp<hardware::vibrator::IVibrator> getHal(); + std::shared_ptr<IVibrator> getHal(); }; // Wrapper for the HDIL Vibrator HALs. @@ -489,8 +551,8 @@ public: HalResult<void> setAmplitude(float amplitude) override final; virtual HalResult<void> setExternalControl(bool enabled) override; - HalResult<void> alwaysOnEnable(int32_t id, hardware::vibrator::Effect effect, - hardware::vibrator::EffectStrength strength) override final; + HalResult<void> alwaysOnEnable(int32_t id, HalWrapper::Effect effect, + HalWrapper::EffectStrength strength) override final; HalResult<void> alwaysOnDisable(int32_t id) override final; protected: @@ -506,8 +568,7 @@ protected: template <class T> HalResult<std::chrono::milliseconds> performInternal( - perform_fn<T> performFn, sp<I> handle, T effect, - hardware::vibrator::EffectStrength strength, + perform_fn<T> performFn, sp<I> handle, T effect, HalWrapper::EffectStrength strength, const std::function<void()>& completionCallback); sp<I> getHal(); @@ -523,7 +584,7 @@ public: virtual ~HidlHalWrapperV1_0() = default; HalResult<std::chrono::milliseconds> performEffect( - hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength, + HalWrapper::Effect effect, HalWrapper::EffectStrength strength, const std::function<void()>& completionCallback) override final; }; @@ -537,7 +598,7 @@ public: virtual ~HidlHalWrapperV1_1() = default; HalResult<std::chrono::milliseconds> performEffect( - hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength, + HalWrapper::Effect effect, HalWrapper::EffectStrength strength, const std::function<void()>& completionCallback) override final; }; @@ -551,7 +612,7 @@ public: virtual ~HidlHalWrapperV1_2() = default; HalResult<std::chrono::milliseconds> performEffect( - hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength, + HalWrapper::Effect effect, HalWrapper::EffectStrength strength, const std::function<void()>& completionCallback) override final; }; @@ -567,7 +628,7 @@ public: HalResult<void> setExternalControl(bool enabled) override final; HalResult<std::chrono::milliseconds> performEffect( - hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength, + HalWrapper::Effect effect, HalWrapper::EffectStrength strength, const std::function<void()>& completionCallback) override final; protected: diff --git a/services/vibratorservice/include/vibratorservice/VibratorManagerHalController.h b/services/vibratorservice/include/vibratorservice/VibratorManagerHalController.h index 9168565ca0..70c846b4ae 100644 --- a/services/vibratorservice/include/vibratorservice/VibratorManagerHalController.h +++ b/services/vibratorservice/include/vibratorservice/VibratorManagerHalController.h @@ -17,7 +17,7 @@ #ifndef ANDROID_OS_VIBRATOR_MANAGER_HAL_CONTROLLER_H #define ANDROID_OS_VIBRATOR_MANAGER_HAL_CONTROLLER_H -#include <android/hardware/vibrator/IVibratorManager.h> +#include <aidl/android/hardware/vibrator/IVibratorManager.h> #include <vibratorservice/VibratorHalController.h> #include <vibratorservice/VibratorManagerHalWrapper.h> #include <unordered_map> diff --git a/services/vibratorservice/include/vibratorservice/VibratorManagerHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorManagerHalWrapper.h index 563f55e9f3..9e3f221fa7 100644 --- a/services/vibratorservice/include/vibratorservice/VibratorManagerHalWrapper.h +++ b/services/vibratorservice/include/vibratorservice/VibratorManagerHalWrapper.h @@ -17,7 +17,7 @@ #ifndef ANDROID_OS_VIBRATOR_MANAGER_HAL_WRAPPER_H #define ANDROID_OS_VIBRATOR_MANAGER_HAL_WRAPPER_H -#include <android/hardware/vibrator/IVibratorManager.h> +#include <aidl/android/hardware/vibrator/IVibratorManager.h> #include <vibratorservice/VibratorHalController.h> #include <unordered_map> @@ -28,14 +28,17 @@ namespace vibrator { // VibratorManager HAL capabilities. enum class ManagerCapabilities : int32_t { NONE = 0, - SYNC = hardware::vibrator::IVibratorManager::CAP_SYNC, - PREPARE_ON = hardware::vibrator::IVibratorManager::CAP_PREPARE_ON, - PREPARE_PERFORM = hardware::vibrator::IVibratorManager::CAP_PREPARE_PERFORM, - PREPARE_COMPOSE = hardware::vibrator::IVibratorManager::CAP_PREPARE_COMPOSE, - MIXED_TRIGGER_ON = hardware::vibrator::IVibratorManager::IVibratorManager::CAP_MIXED_TRIGGER_ON, - MIXED_TRIGGER_PERFORM = hardware::vibrator::IVibratorManager::CAP_MIXED_TRIGGER_PERFORM, - MIXED_TRIGGER_COMPOSE = hardware::vibrator::IVibratorManager::CAP_MIXED_TRIGGER_COMPOSE, - TRIGGER_CALLBACK = hardware::vibrator::IVibratorManager::CAP_TRIGGER_CALLBACK + SYNC = aidl::android::hardware::vibrator::IVibratorManager::CAP_SYNC, + PREPARE_ON = aidl::android::hardware::vibrator::IVibratorManager::CAP_PREPARE_ON, + PREPARE_PERFORM = aidl::android::hardware::vibrator::IVibratorManager::CAP_PREPARE_PERFORM, + PREPARE_COMPOSE = aidl::android::hardware::vibrator::IVibratorManager::CAP_PREPARE_COMPOSE, + MIXED_TRIGGER_ON = aidl::android::hardware::vibrator::IVibratorManager::IVibratorManager:: + CAP_MIXED_TRIGGER_ON, + MIXED_TRIGGER_PERFORM = + aidl::android::hardware::vibrator::IVibratorManager::CAP_MIXED_TRIGGER_PERFORM, + MIXED_TRIGGER_COMPOSE = + aidl::android::hardware::vibrator::IVibratorManager::CAP_MIXED_TRIGGER_COMPOSE, + TRIGGER_CALLBACK = aidl::android::hardware::vibrator::IVibratorManager::CAP_TRIGGER_CALLBACK }; inline ManagerCapabilities operator|(ManagerCapabilities lhs, ManagerCapabilities rhs) { @@ -106,8 +109,10 @@ private: // Wrapper for the AIDL VibratorManager HAL. class AidlManagerHalWrapper : public ManagerHalWrapper { public: + using VibratorManager = aidl::android::hardware::vibrator::IVibratorManager; + explicit AidlManagerHalWrapper(std::shared_ptr<CallbackScheduler> callbackScheduler, - sp<hardware::vibrator::IVibratorManager> handle) + std::shared_ptr<VibratorManager> handle) : mHandle(std::move(handle)), mCallbackScheduler(callbackScheduler) {} virtual ~AidlManagerHalWrapper() = default; @@ -126,14 +131,14 @@ private: std::mutex mHandleMutex; std::mutex mCapabilitiesMutex; std::mutex mVibratorsMutex; - sp<hardware::vibrator::IVibratorManager> mHandle GUARDED_BY(mHandleMutex); + std::shared_ptr<VibratorManager> mHandle GUARDED_BY(mHandleMutex); std::optional<ManagerCapabilities> mCapabilities GUARDED_BY(mCapabilitiesMutex); std::optional<std::vector<int32_t>> mVibratorIds GUARDED_BY(mVibratorsMutex); std::unordered_map<int32_t, std::shared_ptr<HalController>> mVibrators GUARDED_BY(mVibratorsMutex); std::shared_ptr<CallbackScheduler> mCallbackScheduler; - sp<hardware::vibrator::IVibratorManager> getHal(); + std::shared_ptr<VibratorManager> getHal(); std::shared_ptr<HalWrapper> connectToVibrator(int32_t vibratorId, std::shared_ptr<CallbackScheduler> scheduler); }; diff --git a/services/vibratorservice/test/Android.bp b/services/vibratorservice/test/Android.bp index be71dc2ce3..92527eb5cf 100644 --- a/services/vibratorservice/test/Android.bp +++ b/services/vibratorservice/test/Android.bp @@ -44,12 +44,12 @@ cc_test { ], shared_libs: [ "libbase", - "libbinder", + "libbinder_ndk", "libhidlbase", "liblog", "libvibratorservice", "libutils", - "android.hardware.vibrator-V2-cpp", + "android.hardware.vibrator-V3-ndk", "android.hardware.vibrator@1.0", "android.hardware.vibrator@1.1", "android.hardware.vibrator@1.2", diff --git a/services/vibratorservice/test/VibratorHalControllerTest.cpp b/services/vibratorservice/test/VibratorHalControllerTest.cpp index 15fde914f6..f4c28981aa 100644 --- a/services/vibratorservice/test/VibratorHalControllerTest.cpp +++ b/services/vibratorservice/test/VibratorHalControllerTest.cpp @@ -16,7 +16,7 @@ #define LOG_TAG "VibratorHalControllerTest" -#include <android/hardware/vibrator/IVibrator.h> +#include <aidl/android/hardware/vibrator/IVibrator.h> #include <cutils/atomic.h> #include <gmock/gmock.h> @@ -29,10 +29,11 @@ #include <vibratorservice/VibratorHalController.h> #include <vibratorservice/VibratorHalWrapper.h> +#include "test_mocks.h" #include "test_utils.h" -using android::hardware::vibrator::Effect; -using android::hardware::vibrator::EffectStrength; +using aidl::android::hardware::vibrator::Effect; +using aidl::android::hardware::vibrator::EffectStrength; using std::chrono::milliseconds; @@ -46,41 +47,12 @@ static const auto PING_FN = [](vibrator::HalWrapper* hal) { return hal->ping(); // ------------------------------------------------------------------------------------------------- -class MockHalWrapper : public vibrator::HalWrapper { -public: - MockHalWrapper(std::shared_ptr<vibrator::CallbackScheduler> scheduler) - : HalWrapper(scheduler) {} - virtual ~MockHalWrapper() = default; - - MOCK_METHOD(vibrator::HalResult<void>, ping, (), (override)); - MOCK_METHOD(void, tryReconnect, (), (override)); - MOCK_METHOD(vibrator::HalResult<void>, on, - (milliseconds timeout, const std::function<void()>& completionCallback), - (override)); - MOCK_METHOD(vibrator::HalResult<void>, off, (), (override)); - MOCK_METHOD(vibrator::HalResult<void>, setAmplitude, (float amplitude), (override)); - MOCK_METHOD(vibrator::HalResult<void>, setExternalControl, (bool enabled), (override)); - MOCK_METHOD(vibrator::HalResult<void>, alwaysOnEnable, - (int32_t id, Effect effect, EffectStrength strength), (override)); - MOCK_METHOD(vibrator::HalResult<void>, alwaysOnDisable, (int32_t id), (override)); - MOCK_METHOD(vibrator::HalResult<milliseconds>, performEffect, - (Effect effect, EffectStrength strength, - const std::function<void()>& completionCallback), - (override)); - MOCK_METHOD(vibrator::HalResult<vibrator::Capabilities>, getCapabilitiesInternal, (), - (override)); - - vibrator::CallbackScheduler* getCallbackScheduler() { return mCallbackScheduler.get(); } -}; - -// ------------------------------------------------------------------------------------------------- - class VibratorHalControllerTest : public Test { public: void SetUp() override { mConnectCounter = 0; auto callbackScheduler = std::make_shared<vibrator::CallbackScheduler>(); - mMockHal = std::make_shared<StrictMock<MockHalWrapper>>(callbackScheduler); + mMockHal = std::make_shared<StrictMock<vibrator::MockHalWrapper>>(callbackScheduler); mController = std::make_unique< vibrator::HalController>(std::move(callbackScheduler), [&](std::shared_ptr<vibrator::CallbackScheduler>) { @@ -92,7 +64,7 @@ public: protected: int32_t mConnectCounter; - std::shared_ptr<MockHalWrapper> mMockHal; + std::shared_ptr<vibrator::MockHalWrapper> mMockHal; std::unique_ptr<vibrator::HalController> mController; }; diff --git a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp index 03c9e77f3e..17f384d320 100644 --- a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp +++ b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp @@ -16,7 +16,8 @@ #define LOG_TAG "VibratorHalWrapperAidlTest" -#include <android/hardware/vibrator/IVibrator.h> +#include <aidl/android/hardware/vibrator/IVibrator.h> +#include <android/persistable_bundle_aidl.h> #include <gmock/gmock.h> #include <gtest/gtest.h> @@ -27,18 +28,20 @@ #include <vibratorservice/VibratorCallbackScheduler.h> #include <vibratorservice/VibratorHalWrapper.h> +#include "test_mocks.h" #include "test_utils.h" -using android::binder::Status; - -using android::hardware::vibrator::Braking; -using android::hardware::vibrator::CompositeEffect; -using android::hardware::vibrator::CompositePrimitive; -using android::hardware::vibrator::Effect; -using android::hardware::vibrator::EffectStrength; -using android::hardware::vibrator::IVibrator; -using android::hardware::vibrator::IVibratorCallback; -using android::hardware::vibrator::PrimitivePwle; +using aidl::android::hardware::vibrator::Braking; +using aidl::android::hardware::vibrator::CompositeEffect; +using aidl::android::hardware::vibrator::CompositePrimitive; +using aidl::android::hardware::vibrator::Effect; +using aidl::android::hardware::vibrator::EffectStrength; +using aidl::android::hardware::vibrator::IVibrator; +using aidl::android::hardware::vibrator::IVibratorCallback; +using aidl::android::hardware::vibrator::PrimitivePwle; +using aidl::android::hardware::vibrator::PwleV2Primitive; +using aidl::android::hardware::vibrator::VendorEffect; +using aidl::android::os::PersistableBundle; using namespace android; using namespace std::chrono_literals; @@ -46,61 +49,10 @@ using namespace testing; // ------------------------------------------------------------------------------------------------- -class MockBinder : public BBinder { -public: - MOCK_METHOD(status_t, linkToDeath, - (const sp<DeathRecipient>& recipient, void* cookie, uint32_t flags), (override)); - MOCK_METHOD(status_t, unlinkToDeath, - (const wp<DeathRecipient>& recipient, void* cookie, uint32_t flags, - wp<DeathRecipient>* outRecipient), - (override)); - MOCK_METHOD(status_t, pingBinder, (), (override)); -}; - -class MockIVibrator : public IVibrator { -public: - MOCK_METHOD(Status, getCapabilities, (int32_t * ret), (override)); - MOCK_METHOD(Status, off, (), (override)); - MOCK_METHOD(Status, on, (int32_t timeout, const sp<IVibratorCallback>& cb), (override)); - MOCK_METHOD(Status, perform, - (Effect e, EffectStrength s, const sp<IVibratorCallback>& cb, int32_t* ret), - (override)); - MOCK_METHOD(Status, getSupportedEffects, (std::vector<Effect> * ret), (override)); - MOCK_METHOD(Status, setAmplitude, (float amplitude), (override)); - MOCK_METHOD(Status, setExternalControl, (bool enabled), (override)); - MOCK_METHOD(Status, getCompositionDelayMax, (int32_t * ret), (override)); - MOCK_METHOD(Status, getCompositionSizeMax, (int32_t * ret), (override)); - MOCK_METHOD(Status, getSupportedPrimitives, (std::vector<CompositePrimitive> * ret), - (override)); - MOCK_METHOD(Status, getPrimitiveDuration, (CompositePrimitive p, int32_t* ret), (override)); - MOCK_METHOD(Status, compose, - (const std::vector<CompositeEffect>& e, const sp<IVibratorCallback>& cb), - (override)); - MOCK_METHOD(Status, composePwle, - (const std::vector<PrimitivePwle>& e, const sp<IVibratorCallback>& cb), (override)); - MOCK_METHOD(Status, getSupportedAlwaysOnEffects, (std::vector<Effect> * ret), (override)); - MOCK_METHOD(Status, alwaysOnEnable, (int32_t id, Effect e, EffectStrength s), (override)); - MOCK_METHOD(Status, alwaysOnDisable, (int32_t id), (override)); - MOCK_METHOD(Status, getQFactor, (float * ret), (override)); - MOCK_METHOD(Status, getResonantFrequency, (float * ret), (override)); - MOCK_METHOD(Status, getFrequencyResolution, (float* ret), (override)); - MOCK_METHOD(Status, getFrequencyMinimum, (float* ret), (override)); - MOCK_METHOD(Status, getBandwidthAmplitudeMap, (std::vector<float> * ret), (override)); - MOCK_METHOD(Status, getPwlePrimitiveDurationMax, (int32_t * ret), (override)); - MOCK_METHOD(Status, getPwleCompositionSizeMax, (int32_t * ret), (override)); - MOCK_METHOD(Status, getSupportedBraking, (std::vector<Braking> * ret), (override)); - MOCK_METHOD(int32_t, getInterfaceVersion, (), (override)); - MOCK_METHOD(std::string, getInterfaceHash, (), (override)); - MOCK_METHOD(IBinder*, onAsBinder, (), (override)); -}; - -// ------------------------------------------------------------------------------------------------- - class VibratorHalWrapperAidlTest : public Test { public: void SetUp() override { - mMockBinder = new StrictMock<MockBinder>(); - mMockHal = new StrictMock<MockIVibrator>(); + mMockHal = ndk::SharedRefBase::make<StrictMock<vibrator::MockIVibrator>>(); mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>(); mWrapper = std::make_unique<vibrator::AidlHalWrapper>(mMockScheduler, mMockHal); ASSERT_NE(mWrapper, nullptr); @@ -109,54 +61,28 @@ public: protected: std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr; std::unique_ptr<vibrator::HalWrapper> mWrapper = nullptr; - sp<StrictMock<MockIVibrator>> mMockHal = nullptr; - sp<StrictMock<MockBinder>> mMockBinder = nullptr; + std::shared_ptr<StrictMock<vibrator::MockIVibrator>> mMockHal = nullptr; }; // ------------------------------------------------------------------------------------------------- -ACTION(TriggerCallbackInArg1) { - if (arg1 != nullptr) { - arg1->onComplete(); - } -} - -ACTION(TriggerCallbackInArg2) { - if (arg2 != nullptr) { - arg2->onComplete(); - } -} - -TEST_F(VibratorHalWrapperAidlTest, TestPing) { - EXPECT_CALL(*mMockHal.get(), onAsBinder()) - .Times(Exactly(2)) - .WillRepeatedly(Return(mMockBinder.get())); - EXPECT_CALL(*mMockBinder.get(), pingBinder()) - .Times(Exactly(2)) - .WillOnce(Return(android::OK)) - .WillRepeatedly(Return(android::DEAD_OBJECT)); - - ASSERT_TRUE(mWrapper->ping().isOk()); - ASSERT_TRUE(mWrapper->ping().isFailed()); -} - TEST_F(VibratorHalWrapperAidlTest, TestOnWithCallbackSupport) { { InSequence seq; EXPECT_CALL(*mMockHal.get(), getCapabilities(_)) .Times(Exactly(1)) - .WillRepeatedly( - DoAll(SetArgPointee<0>(IVibrator::CAP_ON_CALLBACK), Return(Status()))); + .WillOnce(DoAll(SetArgPointee<0>(IVibrator::CAP_ON_CALLBACK), + Return(ndk::ScopedAStatus::ok()))); EXPECT_CALL(*mMockHal.get(), on(Eq(10), _)) .Times(Exactly(1)) - .WillRepeatedly(DoAll(TriggerCallbackInArg1(), Return(Status()))); + .WillOnce(DoAll(WithArg<1>(vibrator::TriggerCallback()), + Return(ndk::ScopedAStatus::ok()))); EXPECT_CALL(*mMockHal.get(), on(Eq(100), _)) .Times(Exactly(1)) - .WillRepeatedly(Return( - Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION))); + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION))); EXPECT_CALL(*mMockHal.get(), on(Eq(1000), _)) .Times(Exactly(1)) - .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))); + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY))); } std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); @@ -179,20 +105,20 @@ TEST_F(VibratorHalWrapperAidlTest, TestOnWithoutCallbackSupport) { InSequence seq; EXPECT_CALL(*mMockHal.get(), getCapabilities(_)) .Times(Exactly(1)) - .WillRepeatedly( - DoAll(SetArgPointee<0>(IVibrator::CAP_COMPOSE_EFFECTS), Return(Status()))); + .WillOnce(DoAll(SetArgPointee<0>(IVibrator::CAP_COMPOSE_EFFECTS), + Return(ndk::ScopedAStatus::ok()))); EXPECT_CALL(*mMockHal.get(), on(Eq(10), _)) .Times(Exactly(1)) - .WillRepeatedly(Return(Status())); + .WillOnce(Return(ndk::ScopedAStatus::ok())); EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms))) .Times(Exactly(1)) - .WillRepeatedly(vibrator::TriggerSchedulerCallback()); + .WillOnce(vibrator::TriggerSchedulerCallback()); EXPECT_CALL(*mMockHal.get(), on(Eq(11), _)) .Times(Exactly(1)) - .WillRepeatedly(Return(Status::fromStatusT(UNKNOWN_TRANSACTION))); + .WillOnce(Return(ndk::ScopedAStatus::fromStatus(STATUS_UNKNOWN_TRANSACTION))); EXPECT_CALL(*mMockHal.get(), on(Eq(12), _)) .Times(Exactly(1)) - .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))); + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY))); } std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); @@ -211,10 +137,9 @@ TEST_F(VibratorHalWrapperAidlTest, TestOnWithoutCallbackSupport) { TEST_F(VibratorHalWrapperAidlTest, TestOff) { EXPECT_CALL(*mMockHal.get(), off()) .Times(Exactly(3)) - .WillOnce(Return(Status())) - .WillOnce( - Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION))) - .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))); + .WillOnce(Return(ndk::ScopedAStatus::ok())) + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION))) + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY))); ASSERT_TRUE(mWrapper->off().isOk()); ASSERT_TRUE(mWrapper->off().isUnsupported()); @@ -224,13 +149,15 @@ TEST_F(VibratorHalWrapperAidlTest, TestOff) { TEST_F(VibratorHalWrapperAidlTest, TestSetAmplitude) { { InSequence seq; - EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(0.1f))).Times(Exactly(1)); + EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(0.1f))) + .Times(Exactly(1)) + .WillOnce(Return(ndk::ScopedAStatus::ok())); EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(0.2f))) .Times(Exactly(1)) - .WillRepeatedly(Return(Status::fromStatusT(UNKNOWN_TRANSACTION))); + .WillOnce(Return(ndk::ScopedAStatus::fromStatus(STATUS_UNKNOWN_TRANSACTION))); EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(0.5f))) .Times(Exactly(1)) - .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))); + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY))); } ASSERT_TRUE(mWrapper->setAmplitude(0.1f).isOk()); @@ -241,12 +168,13 @@ TEST_F(VibratorHalWrapperAidlTest, TestSetAmplitude) { TEST_F(VibratorHalWrapperAidlTest, TestSetExternalControl) { { InSequence seq; - EXPECT_CALL(*mMockHal.get(), setExternalControl(Eq(true))).Times(Exactly(1)); + EXPECT_CALL(*mMockHal.get(), setExternalControl(Eq(true))) + .Times(Exactly(1)) + .WillOnce(Return(ndk::ScopedAStatus::ok())); EXPECT_CALL(*mMockHal.get(), setExternalControl(Eq(false))) .Times(Exactly(2)) - .WillOnce(Return( - Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION))) - .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))); + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION))) + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY))); } ASSERT_TRUE(mWrapper->setExternalControl(true).isOk()); @@ -259,15 +187,16 @@ TEST_F(VibratorHalWrapperAidlTest, TestAlwaysOnEnable) { InSequence seq; EXPECT_CALL(*mMockHal.get(), alwaysOnEnable(Eq(1), Eq(Effect::CLICK), Eq(EffectStrength::LIGHT))) - .Times(Exactly(1)); + .Times(Exactly(1)) + .WillOnce(Return(ndk::ScopedAStatus::ok())); EXPECT_CALL(*mMockHal.get(), alwaysOnEnable(Eq(2), Eq(Effect::TICK), Eq(EffectStrength::MEDIUM))) .Times(Exactly(1)) - .WillRepeatedly(Return(Status::fromStatusT(UNKNOWN_TRANSACTION))); + .WillOnce(Return(ndk::ScopedAStatus::fromStatus(STATUS_UNKNOWN_TRANSACTION))); EXPECT_CALL(*mMockHal.get(), alwaysOnEnable(Eq(3), Eq(Effect::POP), Eq(EffectStrength::STRONG))) .Times(Exactly(1)) - .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))); + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY))); } auto result = mWrapper->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT); @@ -281,14 +210,15 @@ TEST_F(VibratorHalWrapperAidlTest, TestAlwaysOnEnable) { TEST_F(VibratorHalWrapperAidlTest, TestAlwaysOnDisable) { { InSequence seq; - EXPECT_CALL(*mMockHal.get(), alwaysOnDisable(Eq(1))).Times(Exactly(1)); + EXPECT_CALL(*mMockHal.get(), alwaysOnDisable(Eq(1))) + .Times(Exactly(1)) + .WillOnce(Return(ndk::ScopedAStatus::ok())); EXPECT_CALL(*mMockHal.get(), alwaysOnDisable(Eq(2))) .Times(Exactly(1)) - .WillRepeatedly(Return( - Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION))); + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION))); EXPECT_CALL(*mMockHal.get(), alwaysOnDisable(Eq(3))) .Times(Exactly(1)) - .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))); + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY))); } ASSERT_TRUE(mWrapper->alwaysOnDisable(1).isOk()); @@ -305,72 +235,94 @@ TEST_F(VibratorHalWrapperAidlTest, TestGetInfoDoesNotCacheFailedResult) { constexpr int32_t PWLE_SIZE_MAX = 20; constexpr int32_t PRIMITIVE_DELAY_MAX = 100; constexpr int32_t PWLE_DURATION_MAX = 200; + constexpr int32_t PWLE_V2_COMPOSITION_SIZE_MAX = 16; + constexpr int32_t PWLE_V2_MAX_ALLOWED_PRIMITIVE_MIN_DURATION_MS = 20; + constexpr int32_t PWLE_V2_MIN_REQUIRED_PRIMITIVE_MAX_DURATION_MS = 1000; std::vector<Effect> supportedEffects = {Effect::CLICK, Effect::TICK}; std::vector<CompositePrimitive> supportedPrimitives = {CompositePrimitive::CLICK}; std::vector<Braking> supportedBraking = {Braking::CLAB}; std::vector<float> amplitudes = {0.f, 1.f, 0.f}; std::vector<std::chrono::milliseconds> primitiveDurations; - constexpr auto primitiveRange = enum_range<CompositePrimitive>(); + constexpr auto primitiveRange = ndk::enum_range<CompositePrimitive>(); constexpr auto primitiveCount = std::distance(primitiveRange.begin(), primitiveRange.end()); primitiveDurations.resize(primitiveCount); primitiveDurations[static_cast<size_t>(CompositePrimitive::CLICK)] = 10ms; EXPECT_CALL(*mMockHal.get(), getCapabilities(_)) .Times(Exactly(2)) - .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))) - .WillRepeatedly(DoAll(SetArgPointee<0>(IVibrator::CAP_ON_CALLBACK), Return(Status()))); + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY))) + .WillOnce(DoAll(SetArgPointee<0>(IVibrator::CAP_ON_CALLBACK), + Return(ndk::ScopedAStatus::ok()))); EXPECT_CALL(*mMockHal.get(), getSupportedEffects(_)) .Times(Exactly(2)) - .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))) - .WillRepeatedly(DoAll(SetArgPointee<0>(supportedEffects), Return(Status()))); + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY))) + .WillOnce(DoAll(SetArgPointee<0>(supportedEffects), Return(ndk::ScopedAStatus::ok()))); EXPECT_CALL(*mMockHal.get(), getSupportedBraking(_)) .Times(Exactly(2)) - .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))) - .WillRepeatedly(DoAll(SetArgPointee<0>(supportedBraking), Return(Status()))); + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY))) + .WillOnce(DoAll(SetArgPointee<0>(supportedBraking), Return(ndk::ScopedAStatus::ok()))); EXPECT_CALL(*mMockHal.get(), getSupportedPrimitives(_)) .Times(Exactly(2)) - .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))) - .WillRepeatedly(DoAll(SetArgPointee<0>(supportedPrimitives), Return(Status()))); + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY))) + .WillOnce( + DoAll(SetArgPointee<0>(supportedPrimitives), Return(ndk::ScopedAStatus::ok()))); EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::CLICK), _)) .Times(Exactly(1)) - .WillRepeatedly(DoAll(SetArgPointee<1>(10), Return(Status()))); + .WillOnce(DoAll(SetArgPointee<1>(10), Return(ndk::ScopedAStatus::ok()))); EXPECT_CALL(*mMockHal.get(), getCompositionSizeMax(_)) .Times(Exactly(2)) - .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))) - .WillRepeatedly(DoAll(SetArgPointee<0>(COMPOSITION_SIZE_MAX), Return(Status()))); + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY))) + .WillOnce(DoAll(SetArgPointee<0>(COMPOSITION_SIZE_MAX), + Return(ndk::ScopedAStatus::ok()))); EXPECT_CALL(*mMockHal.get(), getCompositionDelayMax(_)) .Times(Exactly(2)) - .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))) - .WillRepeatedly(DoAll(SetArgPointee<0>(PRIMITIVE_DELAY_MAX), Return(Status()))); + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY))) + .WillOnce( + DoAll(SetArgPointee<0>(PRIMITIVE_DELAY_MAX), Return(ndk::ScopedAStatus::ok()))); EXPECT_CALL(*mMockHal.get(), getPwlePrimitiveDurationMax(_)) .Times(Exactly(2)) - .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))) - .WillRepeatedly(DoAll(SetArgPointee<0>(PWLE_DURATION_MAX), Return(Status()))); + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY))) + .WillOnce(DoAll(SetArgPointee<0>(PWLE_DURATION_MAX), Return(ndk::ScopedAStatus::ok()))); EXPECT_CALL(*mMockHal.get(), getPwleCompositionSizeMax(_)) .Times(Exactly(2)) - .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))) - .WillRepeatedly(DoAll(SetArgPointee<0>(PWLE_SIZE_MAX), Return(Status()))); + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY))) + .WillOnce(DoAll(SetArgPointee<0>(PWLE_SIZE_MAX), Return(ndk::ScopedAStatus::ok()))); EXPECT_CALL(*mMockHal.get(), getFrequencyMinimum(_)) .Times(Exactly(2)) - .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))) - .WillRepeatedly(DoAll(SetArgPointee<0>(F_MIN), Return(Status()))); + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY))) + .WillOnce(DoAll(SetArgPointee<0>(F_MIN), Return(ndk::ScopedAStatus::ok()))); EXPECT_CALL(*mMockHal.get(), getResonantFrequency(_)) .Times(Exactly(2)) - .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))) - .WillRepeatedly(DoAll(SetArgPointee<0>(F0), Return(Status()))); + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY))) + .WillOnce(DoAll(SetArgPointee<0>(F0), Return(ndk::ScopedAStatus::ok()))); EXPECT_CALL(*mMockHal.get(), getFrequencyResolution(_)) .Times(Exactly(2)) - .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))) - .WillRepeatedly(DoAll(SetArgPointee<0>(F_RESOLUTION), Return(Status()))); + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY))) + .WillOnce(DoAll(SetArgPointee<0>(F_RESOLUTION), Return(ndk::ScopedAStatus::ok()))); EXPECT_CALL(*mMockHal.get(), getQFactor(_)) .Times(Exactly(2)) - .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))) - .WillRepeatedly(DoAll(SetArgPointee<0>(Q_FACTOR), Return(Status()))); + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY))) + .WillOnce(DoAll(SetArgPointee<0>(Q_FACTOR), Return(ndk::ScopedAStatus::ok()))); EXPECT_CALL(*mMockHal.get(), getBandwidthAmplitudeMap(_)) .Times(Exactly(2)) - .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))) - .WillRepeatedly(DoAll(SetArgPointee<0>(amplitudes), Return(Status()))); + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY))) + .WillOnce(DoAll(SetArgPointee<0>(amplitudes), Return(ndk::ScopedAStatus::ok()))); + EXPECT_CALL(*mMockHal.get(), getPwleV2CompositionSizeMax(_)) + .Times(Exactly(2)) + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY))) + .WillOnce(DoAll(SetArgPointee<0>(PWLE_V2_COMPOSITION_SIZE_MAX), + Return(ndk::ScopedAStatus::ok()))); + EXPECT_CALL(*mMockHal.get(), getPwleV2PrimitiveDurationMinMillis(_)) + .Times(Exactly(2)) + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY))) + .WillOnce(DoAll(SetArgPointee<0>(PWLE_V2_MAX_ALLOWED_PRIMITIVE_MIN_DURATION_MS), + Return(ndk::ScopedAStatus::ok()))); + EXPECT_CALL(*mMockHal.get(), getPwleV2PrimitiveDurationMaxMillis(_)) + .Times(Exactly(2)) + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY))) + .WillOnce(DoAll(SetArgPointee<0>(PWLE_V2_MIN_REQUIRED_PRIMITIVE_MAX_DURATION_MS), + Return(ndk::ScopedAStatus::ok()))); vibrator::Info failed = mWrapper->getInfo(); ASSERT_TRUE(failed.capabilities.isFailed()); @@ -387,6 +339,9 @@ TEST_F(VibratorHalWrapperAidlTest, TestGetInfoDoesNotCacheFailedResult) { ASSERT_TRUE(failed.frequencyResolution.isFailed()); ASSERT_TRUE(failed.qFactor.isFailed()); ASSERT_TRUE(failed.maxAmplitudes.isFailed()); + ASSERT_TRUE(failed.maxEnvelopeEffectSize.isFailed()); + ASSERT_TRUE(failed.minEnvelopeEffectControlPointDuration.isFailed()); + ASSERT_TRUE(failed.maxEnvelopeEffectControlPointDuration.isFailed()); vibrator::Info successful = mWrapper->getInfo(); ASSERT_EQ(vibrator::Capabilities::ON_CALLBACK, successful.capabilities.value()); @@ -404,6 +359,11 @@ TEST_F(VibratorHalWrapperAidlTest, TestGetInfoDoesNotCacheFailedResult) { ASSERT_EQ(F_RESOLUTION, successful.frequencyResolution.value()); ASSERT_EQ(Q_FACTOR, successful.qFactor.value()); ASSERT_EQ(amplitudes, successful.maxAmplitudes.value()); + ASSERT_EQ(PWLE_V2_COMPOSITION_SIZE_MAX, successful.maxEnvelopeEffectSize.value()); + ASSERT_EQ(std::chrono::milliseconds(PWLE_V2_MAX_ALLOWED_PRIMITIVE_MIN_DURATION_MS), + successful.minEnvelopeEffectControlPointDuration.value()); + ASSERT_EQ(std::chrono::milliseconds(PWLE_V2_MIN_REQUIRED_PRIMITIVE_MAX_DURATION_MS), + successful.maxEnvelopeEffectControlPointDuration.value()); } TEST_F(VibratorHalWrapperAidlTest, TestGetInfoCachesResult) { @@ -413,50 +373,65 @@ TEST_F(VibratorHalWrapperAidlTest, TestGetInfoCachesResult) { constexpr int32_t PWLE_SIZE_MAX = 20; constexpr int32_t PRIMITIVE_DELAY_MAX = 100; constexpr int32_t PWLE_DURATION_MAX = 200; + constexpr int32_t PWLE_V2_COMPOSITION_SIZE_MAX = 16; + constexpr int32_t PWLE_V2_MAX_ALLOWED_PRIMITIVE_MIN_DURATION_MS = 20; + constexpr int32_t PWLE_V2_MIN_REQUIRED_PRIMITIVE_MAX_DURATION_MS = 1000; std::vector<Effect> supportedEffects = {Effect::CLICK, Effect::TICK}; EXPECT_CALL(*mMockHal.get(), getCapabilities(_)) .Times(Exactly(1)) - .WillRepeatedly(DoAll(SetArgPointee<0>(IVibrator::CAP_ON_CALLBACK), Return(Status()))); + .WillOnce(DoAll(SetArgPointee<0>(IVibrator::CAP_ON_CALLBACK), + Return(ndk::ScopedAStatus::ok()))); EXPECT_CALL(*mMockHal.get(), getSupportedEffects(_)) .Times(Exactly(1)) - .WillRepeatedly(DoAll(SetArgPointee<0>(supportedEffects), Return(Status()))); + .WillOnce(DoAll(SetArgPointee<0>(supportedEffects), Return(ndk::ScopedAStatus::ok()))); EXPECT_CALL(*mMockHal.get(), getQFactor(_)) .Times(Exactly(1)) - .WillRepeatedly( - Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION))); + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION))); EXPECT_CALL(*mMockHal.get(), getSupportedPrimitives(_)) .Times(Exactly(1)) - .WillRepeatedly(Return(Status::fromStatusT(UNKNOWN_TRANSACTION))); + .WillOnce(Return(ndk::ScopedAStatus::fromStatus(STATUS_UNKNOWN_TRANSACTION))); EXPECT_CALL(*mMockHal.get(), getCompositionSizeMax(_)) .Times(Exactly(1)) - .WillRepeatedly(DoAll(SetArgPointee<0>(COMPOSITION_SIZE_MAX), Return(Status()))); + .WillOnce(DoAll(SetArgPointee<0>(COMPOSITION_SIZE_MAX), + Return(ndk::ScopedAStatus::ok()))); EXPECT_CALL(*mMockHal.get(), getCompositionDelayMax(_)) .Times(Exactly(1)) - .WillRepeatedly(DoAll(SetArgPointee<0>(PRIMITIVE_DELAY_MAX), Return(Status()))); + .WillOnce( + DoAll(SetArgPointee<0>(PRIMITIVE_DELAY_MAX), Return(ndk::ScopedAStatus::ok()))); EXPECT_CALL(*mMockHal.get(), getPwlePrimitiveDurationMax(_)) .Times(Exactly(1)) - .WillRepeatedly(DoAll(SetArgPointee<0>(PWLE_DURATION_MAX), Return(Status()))); + .WillOnce(DoAll(SetArgPointee<0>(PWLE_DURATION_MAX), Return(ndk::ScopedAStatus::ok()))); EXPECT_CALL(*mMockHal.get(), getPwleCompositionSizeMax(_)) .Times(Exactly(1)) - .WillRepeatedly(DoAll(SetArgPointee<0>(PWLE_SIZE_MAX), Return(Status()))); + .WillOnce(DoAll(SetArgPointee<0>(PWLE_SIZE_MAX), Return(ndk::ScopedAStatus::ok()))); EXPECT_CALL(*mMockHal.get(), getFrequencyMinimum(_)) .Times(Exactly(1)) - .WillRepeatedly(DoAll(SetArgPointee<0>(F_MIN), Return(Status()))); + .WillOnce(DoAll(SetArgPointee<0>(F_MIN), Return(ndk::ScopedAStatus::ok()))); EXPECT_CALL(*mMockHal.get(), getResonantFrequency(_)) .Times(Exactly(1)) - .WillRepeatedly(DoAll(SetArgPointee<0>(F0), Return(Status()))); + .WillOnce(DoAll(SetArgPointee<0>(F0), Return(ndk::ScopedAStatus::ok()))); EXPECT_CALL(*mMockHal.get(), getFrequencyResolution(_)) .Times(Exactly(1)) - .WillRepeatedly( - Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION))); + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION))); EXPECT_CALL(*mMockHal.get(), getBandwidthAmplitudeMap(_)) .Times(Exactly(1)) - .WillRepeatedly(Return(Status::fromStatusT(UNKNOWN_TRANSACTION))); + .WillOnce(Return(ndk::ScopedAStatus::fromStatus(STATUS_UNKNOWN_TRANSACTION))); EXPECT_CALL(*mMockHal.get(), getSupportedBraking(_)) .Times(Exactly(1)) - .WillRepeatedly( - Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION))); + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION))); + EXPECT_CALL(*mMockHal.get(), getPwleV2CompositionSizeMax(_)) + .Times(Exactly(1)) + .WillOnce(DoAll(SetArgPointee<0>(PWLE_V2_COMPOSITION_SIZE_MAX), + Return(ndk::ScopedAStatus::ok()))); + EXPECT_CALL(*mMockHal.get(), getPwleV2PrimitiveDurationMinMillis(_)) + .Times(Exactly(1)) + .WillOnce(DoAll(SetArgPointee<0>(PWLE_V2_MAX_ALLOWED_PRIMITIVE_MIN_DURATION_MS), + Return(ndk::ScopedAStatus::ok()))); + EXPECT_CALL(*mMockHal.get(), getPwleV2PrimitiveDurationMaxMillis(_)) + .Times(Exactly(1)) + .WillOnce(DoAll(SetArgPointee<0>(PWLE_V2_MIN_REQUIRED_PRIMITIVE_MAX_DURATION_MS), + Return(ndk::ScopedAStatus::ok()))); std::vector<std::thread> threads; for (int i = 0; i < 10; i++) { @@ -480,6 +455,11 @@ TEST_F(VibratorHalWrapperAidlTest, TestGetInfoCachesResult) { ASSERT_TRUE(info.frequencyResolution.isUnsupported()); ASSERT_TRUE(info.qFactor.isUnsupported()); ASSERT_TRUE(info.maxAmplitudes.isUnsupported()); + ASSERT_EQ(PWLE_V2_COMPOSITION_SIZE_MAX, info.maxEnvelopeEffectSize.value()); + ASSERT_EQ(std::chrono::milliseconds(PWLE_V2_MAX_ALLOWED_PRIMITIVE_MIN_DURATION_MS), + info.minEnvelopeEffectControlPointDuration.value()); + ASSERT_EQ(std::chrono::milliseconds(PWLE_V2_MIN_REQUIRED_PRIMITIVE_MAX_DURATION_MS), + info.maxEnvelopeEffectControlPointDuration.value()); } TEST_F(VibratorHalWrapperAidlTest, TestPerformEffectWithCallbackSupport) { @@ -487,18 +467,18 @@ TEST_F(VibratorHalWrapperAidlTest, TestPerformEffectWithCallbackSupport) { InSequence seq; EXPECT_CALL(*mMockHal.get(), getCapabilities(_)) .Times(Exactly(1)) - .WillRepeatedly( - DoAll(SetArgPointee<0>(IVibrator::CAP_PERFORM_CALLBACK), Return(Status()))); + .WillOnce(DoAll(SetArgPointee<0>(IVibrator::CAP_PERFORM_CALLBACK), + Return(ndk::ScopedAStatus::ok()))); EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::CLICK), Eq(EffectStrength::LIGHT), _, _)) .Times(Exactly(1)) - .WillRepeatedly( - DoAll(SetArgPointee<3>(1000), TriggerCallbackInArg2(), Return(Status()))); + .WillOnce(DoAll(SetArgPointee<3>(1000), WithArg<2>(vibrator::TriggerCallback()), + Return(ndk::ScopedAStatus::ok()))); EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::POP), Eq(EffectStrength::MEDIUM), _, _)) .Times(Exactly(1)) - .WillRepeatedly(Return(Status::fromStatusT(UNKNOWN_TRANSACTION))); + .WillOnce(Return(ndk::ScopedAStatus::fromStatus(STATUS_UNKNOWN_TRANSACTION))); EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::THUD), Eq(EffectStrength::STRONG), _, _)) .Times(Exactly(1)) - .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))); + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY))); } std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); @@ -525,21 +505,20 @@ TEST_F(VibratorHalWrapperAidlTest, TestPerformEffectWithoutCallbackSupport) { InSequence seq; EXPECT_CALL(*mMockHal.get(), getCapabilities(_)) .Times(Exactly(1)) - .WillRepeatedly( - DoAll(SetArgPointee<0>(IVibrator::CAP_ON_CALLBACK), Return(Status()))); + .WillOnce(DoAll(SetArgPointee<0>(IVibrator::CAP_ON_CALLBACK), + Return(ndk::ScopedAStatus::ok()))); EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::CLICK), Eq(EffectStrength::LIGHT), _, _)) .Times(Exactly(1)) - .WillRepeatedly(DoAll(SetArgPointee<3>(10), Return(Status()))); + .WillOnce(DoAll(SetArgPointee<3>(10), Return(ndk::ScopedAStatus::ok()))); EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms))) .Times(Exactly(1)) - .WillRepeatedly(vibrator::TriggerSchedulerCallback()); + .WillOnce(vibrator::TriggerSchedulerCallback()); EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::POP), Eq(EffectStrength::MEDIUM), _, _)) .Times(Exactly(1)) - .WillRepeatedly(Return( - Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION))); + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION))); EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::THUD), Eq(EffectStrength::STRONG), _, _)) .Times(Exactly(1)) - .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))); + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY))); } std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); @@ -560,6 +539,42 @@ TEST_F(VibratorHalWrapperAidlTest, TestPerformEffectWithoutCallbackSupport) { ASSERT_EQ(1, *callbackCounter.get()); } +TEST_F(VibratorHalWrapperAidlTest, TestPerformVendorEffect) { + PersistableBundle vendorData; + vendorData.putInt("key", 1); + VendorEffect vendorEffect; + vendorEffect.vendorData = vendorData; + vendorEffect.strength = EffectStrength::MEDIUM; + vendorEffect.scale = 0.5f; + + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), performVendorEffect(_, _)) + .Times(Exactly(3)) + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION))) + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY))) + .WillOnce(DoAll(WithArg<1>(vibrator::TriggerCallback()), + Return(ndk::ScopedAStatus::ok()))); + } + + std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); + auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); + + auto result = mWrapper->performVendorEffect(vendorEffect, callback); + ASSERT_TRUE(result.isUnsupported()); + // Callback not triggered on failure + ASSERT_EQ(0, *callbackCounter.get()); + + result = mWrapper->performVendorEffect(vendorEffect, callback); + ASSERT_TRUE(result.isFailed()); + // Callback not triggered for unsupported + ASSERT_EQ(0, *callbackCounter.get()); + + result = mWrapper->performVendorEffect(vendorEffect, callback); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(1, *callbackCounter.get()); +} + TEST_F(VibratorHalWrapperAidlTest, TestPerformComposedEffect) { std::vector<CompositePrimitive> supportedPrimitives = {CompositePrimitive::CLICK, CompositePrimitive::SPIN, @@ -576,26 +591,28 @@ TEST_F(VibratorHalWrapperAidlTest, TestPerformComposedEffect) { InSequence seq; EXPECT_CALL(*mMockHal.get(), getSupportedPrimitives(_)) .Times(Exactly(1)) - .WillRepeatedly(DoAll(SetArgPointee<0>(supportedPrimitives), Return(Status()))); + .WillOnce(DoAll(SetArgPointee<0>(supportedPrimitives), + Return(ndk::ScopedAStatus::ok()))); EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::CLICK), _)) .Times(Exactly(1)) - .WillRepeatedly(DoAll(SetArgPointee<1>(1), Return(Status()))); + .WillOnce(DoAll(SetArgPointee<1>(1), Return(ndk::ScopedAStatus::ok()))); EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::SPIN), _)) .Times(Exactly(1)) - .WillRepeatedly(DoAll(SetArgPointee<1>(2), Return(Status()))); + .WillOnce(DoAll(SetArgPointee<1>(2), Return(ndk::ScopedAStatus::ok()))); EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::THUD), _)) .Times(Exactly(1)) - .WillRepeatedly(DoAll(SetArgPointee<1>(3), Return(Status()))); + .WillOnce(DoAll(SetArgPointee<1>(3), Return(ndk::ScopedAStatus::ok()))); EXPECT_CALL(*mMockHal.get(), compose(Eq(emptyEffects), _)) .Times(Exactly(1)) - .WillRepeatedly(DoAll(TriggerCallbackInArg1(), Return(Status()))); + .WillOnce(DoAll(WithArg<1>(vibrator::TriggerCallback()), + Return(ndk::ScopedAStatus::ok()))); EXPECT_CALL(*mMockHal.get(), compose(Eq(singleEffect), _)) .Times(Exactly(1)) - .WillRepeatedly(Return(Status::fromStatusT(UNKNOWN_TRANSACTION))); + .WillOnce(Return(ndk::ScopedAStatus::fromStatus(STATUS_UNKNOWN_TRANSACTION))); EXPECT_CALL(*mMockHal.get(), compose(Eq(multipleEffects), _)) .Times(Exactly(1)) - .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))); + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY))); } std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); @@ -630,26 +647,32 @@ TEST_F(VibratorHalWrapperAidlTest, TestPerformComposedCachesPrimitiveDurationsAn InSequence seq; EXPECT_CALL(*mMockHal.get(), getSupportedPrimitives(_)) .Times(Exactly(1)) - .WillRepeatedly(DoAll(SetArgPointee<0>(supportedPrimitives), Return(Status()))); + .WillOnce(DoAll(SetArgPointee<0>(supportedPrimitives), + Return(ndk::ScopedAStatus::ok()))); EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::SPIN), _)) .Times(Exactly(1)) - .WillRepeatedly(DoAll(SetArgPointee<1>(2), Return(Status()))); + .WillOnce(DoAll(SetArgPointee<1>(2), Return(ndk::ScopedAStatus::ok()))); EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::THUD), _)) .Times(Exactly(1)) - .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))); + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY))); EXPECT_CALL(*mMockHal.get(), compose(Eq(multipleEffects), _)) .Times(Exactly(1)) - .WillRepeatedly(DoAll(TriggerCallbackInArg1(), Return(Status()))); + .WillOnce(DoAll(WithArg<1>(vibrator::TriggerCallback()), + Return(ndk::ScopedAStatus::ok()))); EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::SPIN), _)) .Times(Exactly(1)) - .WillRepeatedly(DoAll(SetArgPointee<1>(2), Return(Status()))); + .WillOnce(DoAll(SetArgPointee<1>(2), Return(ndk::ScopedAStatus::ok()))); EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::THUD), _)) .Times(Exactly(1)) - .WillRepeatedly(DoAll(SetArgPointee<1>(2), Return(Status()))); + .WillOnce(DoAll(SetArgPointee<1>(2), Return(ndk::ScopedAStatus::ok()))); EXPECT_CALL(*mMockHal.get(), compose(Eq(multipleEffects), _)) .Times(Exactly(2)) - .WillRepeatedly(DoAll(TriggerCallbackInArg1(), Return(Status()))); + // ndk::ScopedAStatus::ok() cannot be copy-constructed so can't use WillRepeatedly + .WillOnce(DoAll(WithArg<1>(vibrator::TriggerCallback()), + Return(ndk::ScopedAStatus::ok()))) + .WillOnce(DoAll(WithArg<1>(vibrator::TriggerCallback()), + Return(ndk::ScopedAStatus::ok()))); } std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); @@ -680,12 +703,12 @@ TEST_F(VibratorHalWrapperAidlTest, TestPerformPwleEffect) { InSequence seq; EXPECT_CALL(*mMockHal.get(), composePwle(Eq(emptyPrimitives), _)) .Times(Exactly(1)) - .WillRepeatedly(Return( - Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION))); + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION))); EXPECT_CALL(*mMockHal.get(), composePwle(Eq(multiplePrimitives), _)) .Times(Exactly(2)) - .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))) - .WillRepeatedly(DoAll(TriggerCallbackInArg1(), Return(Status()))); + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY))) + .WillOnce(DoAll(WithArg<1>(vibrator::TriggerCallback()), + Return(ndk::ScopedAStatus::ok()))); } std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); @@ -705,3 +728,38 @@ TEST_F(VibratorHalWrapperAidlTest, TestPerformPwleEffect) { ASSERT_TRUE(result.isOk()); ASSERT_EQ(1, *callbackCounter.get()); } + +TEST_F(VibratorHalWrapperAidlTest, TestComposePwleV2) { + auto pwleEffect = { + PwleV2Primitive(/*amplitude=*/0.2, /*frequency=*/50, /*time=*/100), + PwleV2Primitive(/*amplitude=*/0.5, /*frequency=*/150, /*time=*/100), + PwleV2Primitive(/*amplitude=*/0.8, /*frequency=*/250, /*time=*/100), + }; + + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), composePwleV2(_, _)) + .Times(Exactly(3)) + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION))) + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY))) + .WillOnce(DoAll(WithArg<1>(vibrator::TriggerCallback()), + Return(ndk::ScopedAStatus::ok()))); + } + + std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); + auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); + + auto result = mWrapper->composePwleV2(pwleEffect, callback); + ASSERT_TRUE(result.isUnsupported()); + // Callback not triggered on failure + ASSERT_EQ(0, *callbackCounter.get()); + + result = mWrapper->composePwleV2(pwleEffect, callback); + ASSERT_TRUE(result.isFailed()); + // Callback not triggered for unsupported + ASSERT_EQ(0, *callbackCounter.get()); + + result = mWrapper->composePwleV2(pwleEffect, callback); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(1, *callbackCounter.get()); +} diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp index 0c27fc73b1..a09ddecf91 100644 --- a/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp +++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp @@ -16,7 +16,8 @@ #define LOG_TAG "VibratorHalWrapperHidlV1_0Test" -#include <android/hardware/vibrator/IVibrator.h> +#include <aidl/android/hardware/vibrator/IVibrator.h> +#include <android/persistable_bundle_aidl.h> #include <gmock/gmock.h> #include <gtest/gtest.h> @@ -27,17 +28,21 @@ #include <vibratorservice/VibratorCallbackScheduler.h> #include <vibratorservice/VibratorHalWrapper.h> +#include "test_mocks.h" #include "test_utils.h" namespace V1_0 = android::hardware::vibrator::V1_0; -using android::hardware::vibrator::Braking; -using android::hardware::vibrator::CompositeEffect; -using android::hardware::vibrator::CompositePrimitive; -using android::hardware::vibrator::Effect; -using android::hardware::vibrator::EffectStrength; -using android::hardware::vibrator::IVibrator; -using android::hardware::vibrator::PrimitivePwle; +using aidl::android::hardware::vibrator::Braking; +using aidl::android::hardware::vibrator::CompositeEffect; +using aidl::android::hardware::vibrator::CompositePrimitive; +using aidl::android::hardware::vibrator::Effect; +using aidl::android::hardware::vibrator::EffectStrength; +using aidl::android::hardware::vibrator::IVibrator; +using aidl::android::hardware::vibrator::PrimitivePwle; +using aidl::android::hardware::vibrator::PwleV2Primitive; +using aidl::android::hardware::vibrator::VendorEffect; +using aidl::android::os::PersistableBundle; using namespace android; using namespace std::chrono_literals; @@ -215,6 +220,9 @@ TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetInfoDoesNotCacheFailedResult) { ASSERT_TRUE(info.frequencyResolution.isUnsupported()); ASSERT_TRUE(info.qFactor.isUnsupported()); ASSERT_TRUE(info.maxAmplitudes.isUnsupported()); + ASSERT_TRUE(info.maxEnvelopeEffectSize.isUnsupported()); + ASSERT_TRUE(info.minEnvelopeEffectControlPointDuration.isUnsupported()); + ASSERT_TRUE(info.maxEnvelopeEffectControlPointDuration.isUnsupported()); } TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetInfoWithoutAmplitudeControl) { @@ -248,6 +256,9 @@ TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetInfoCachesResult) { ASSERT_TRUE(info.frequencyResolution.isUnsupported()); ASSERT_TRUE(info.qFactor.isUnsupported()); ASSERT_TRUE(info.maxAmplitudes.isUnsupported()); + ASSERT_TRUE(info.maxEnvelopeEffectSize.isUnsupported()); + ASSERT_TRUE(info.minEnvelopeEffectControlPointDuration.isUnsupported()); + ASSERT_TRUE(info.maxEnvelopeEffectControlPointDuration.isUnsupported()); } TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformEffect) { @@ -316,6 +327,22 @@ TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformEffectUnsupported) { ASSERT_EQ(0, *callbackCounter.get()); } +TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformVendorEffectUnsupported) { + PersistableBundle vendorData; // empty + VendorEffect vendorEffect; + vendorEffect.vendorData = vendorData; + vendorEffect.strength = EffectStrength::LIGHT; + vendorEffect.scale = 1.0f; + + std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); + auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); + + ASSERT_TRUE(mWrapper->performVendorEffect(vendorEffect, callback).isUnsupported()); + + // No callback is triggered. + ASSERT_EQ(0, *callbackCounter.get()); +} + TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformComposedEffectUnsupported) { std::vector<CompositeEffect> emptyEffects, singleEffect, multipleEffects; singleEffect.push_back( @@ -349,3 +376,19 @@ TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformPwleEffectUnsupported) { // No callback is triggered. ASSERT_EQ(0, *callbackCounter.get()); } + +TEST_F(VibratorHalWrapperHidlV1_0Test, TestComposePwleV2Unsupported) { + auto pwleEffect = { + PwleV2Primitive(/*amplitude=*/0.2, /*frequency=*/50, /*time=*/100), + PwleV2Primitive(/*amplitude=*/0.5, /*frequency=*/150, /*time=*/100), + PwleV2Primitive(/*amplitude=*/0.8, /*frequency=*/250, /*time=*/100), + }; + + std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); + auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); + + ASSERT_TRUE(mWrapper->composePwleV2(pwleEffect, callback).isUnsupported()); + + // No callback is triggered. + ASSERT_EQ(0, *callbackCounter.get()); +} diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_1Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_1Test.cpp index d887efce80..b0a653769e 100644 --- a/services/vibratorservice/test/VibratorHalWrapperHidlV1_1Test.cpp +++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_1Test.cpp @@ -16,7 +16,7 @@ #define LOG_TAG "VibratorHalWrapperHidlV1_1Test" -#include <android/hardware/vibrator/IVibrator.h> +#include <aidl/android/hardware/vibrator/IVibrator.h> #include <gmock/gmock.h> #include <gtest/gtest.h> @@ -26,13 +26,14 @@ #include <vibratorservice/VibratorCallbackScheduler.h> #include <vibratorservice/VibratorHalWrapper.h> +#include "test_mocks.h" #include "test_utils.h" namespace V1_0 = android::hardware::vibrator::V1_0; namespace V1_1 = android::hardware::vibrator::V1_1; -using android::hardware::vibrator::Effect; -using android::hardware::vibrator::EffectStrength; +using aidl::android::hardware::vibrator::Effect; +using aidl::android::hardware::vibrator::EffectStrength; using namespace android; using namespace std::chrono_literals; diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_2Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_2Test.cpp index 26d93503c6..dfe3fa0e68 100644 --- a/services/vibratorservice/test/VibratorHalWrapperHidlV1_2Test.cpp +++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_2Test.cpp @@ -16,7 +16,7 @@ #define LOG_TAG "VibratorHalWrapperHidlV1_2Test" -#include <android/hardware/vibrator/IVibrator.h> +#include <aidl/android/hardware/vibrator/IVibrator.h> #include <gmock/gmock.h> #include <gtest/gtest.h> @@ -26,14 +26,15 @@ #include <vibratorservice/VibratorCallbackScheduler.h> #include <vibratorservice/VibratorHalWrapper.h> +#include "test_mocks.h" #include "test_utils.h" namespace V1_0 = android::hardware::vibrator::V1_0; namespace V1_1 = android::hardware::vibrator::V1_1; namespace V1_2 = android::hardware::vibrator::V1_2; -using android::hardware::vibrator::Effect; -using android::hardware::vibrator::EffectStrength; +using aidl::android::hardware::vibrator::Effect; +using aidl::android::hardware::vibrator::EffectStrength; using namespace android; using namespace std::chrono_literals; diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp index a6f1a74931..86243326ac 100644 --- a/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp +++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp @@ -16,7 +16,7 @@ #define LOG_TAG "VibratorHalWrapperHidlV1_3Test" -#include <android/hardware/vibrator/IVibrator.h> +#include <aidl/android/hardware/vibrator/IVibrator.h> #include <gmock/gmock.h> #include <gtest/gtest.h> @@ -27,6 +27,7 @@ #include <vibratorservice/VibratorCallbackScheduler.h> #include <vibratorservice/VibratorHalWrapper.h> +#include "test_mocks.h" #include "test_utils.h" namespace V1_0 = android::hardware::vibrator::V1_0; @@ -34,9 +35,9 @@ namespace V1_1 = android::hardware::vibrator::V1_1; namespace V1_2 = android::hardware::vibrator::V1_2; namespace V1_3 = android::hardware::vibrator::V1_3; -using android::hardware::vibrator::Effect; -using android::hardware::vibrator::EffectStrength; -using android::hardware::vibrator::IVibrator; +using aidl::android::hardware::vibrator::Effect; +using aidl::android::hardware::vibrator::EffectStrength; +using aidl::android::hardware::vibrator::IVibrator; using namespace android; using namespace std::chrono_literals; diff --git a/services/vibratorservice/test/VibratorManagerHalControllerTest.cpp b/services/vibratorservice/test/VibratorManagerHalControllerTest.cpp index 11a8b66968..c7214e054e 100644 --- a/services/vibratorservice/test/VibratorManagerHalControllerTest.cpp +++ b/services/vibratorservice/test/VibratorManagerHalControllerTest.cpp @@ -24,6 +24,7 @@ #include <vibratorservice/VibratorManagerHalController.h> +#include "test_mocks.h" #include "test_utils.h" using android::vibrator::HalController; @@ -35,6 +36,8 @@ static constexpr int MAX_ATTEMPTS = 2; static const std::vector<int32_t> VIBRATOR_IDS = {1, 2}; static constexpr int VIBRATOR_ID = 1; +// ------------------------------------------------------------------------------------------------- + class MockManagerHalWrapper : public vibrator::ManagerHalWrapper { public: MOCK_METHOD(void, tryReconnect, (), (override)); @@ -51,6 +54,8 @@ public: MOCK_METHOD(vibrator::HalResult<void>, cancelSynced, (), (override)); }; +// ------------------------------------------------------------------------------------------------- + class VibratorManagerHalControllerTest : public Test { public: void SetUp() override { @@ -106,6 +111,8 @@ protected: } }; +// ------------------------------------------------------------------------------------------------- + TEST_F(VibratorManagerHalControllerTest, TestInit) { mController->init(); ASSERT_EQ(1, mConnectCounter); diff --git a/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp b/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp index dffc281fa1..764d9bea4f 100644 --- a/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp +++ b/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp @@ -23,84 +23,42 @@ #include <vibratorservice/VibratorManagerHalWrapper.h> +#include "test_mocks.h" #include "test_utils.h" -using android::binder::Status; - -using android::hardware::vibrator::Braking; -using android::hardware::vibrator::CompositeEffect; -using android::hardware::vibrator::CompositePrimitive; -using android::hardware::vibrator::Effect; -using android::hardware::vibrator::EffectStrength; -using android::hardware::vibrator::IVibrator; -using android::hardware::vibrator::IVibratorCallback; -using android::hardware::vibrator::IVibratorManager; -using android::hardware::vibrator::PrimitivePwle; +using aidl::android::hardware::vibrator::Braking; +using aidl::android::hardware::vibrator::CompositeEffect; +using aidl::android::hardware::vibrator::CompositePrimitive; +using aidl::android::hardware::vibrator::Effect; +using aidl::android::hardware::vibrator::EffectStrength; +using aidl::android::hardware::vibrator::IVibrator; +using aidl::android::hardware::vibrator::IVibratorCallback; +using aidl::android::hardware::vibrator::IVibratorManager; +using aidl::android::hardware::vibrator::PrimitivePwle; using namespace android; using namespace testing; static const auto OFF_FN = [](vibrator::HalWrapper* hal) { return hal->off(); }; -class MockBinder : public BBinder { -public: - MOCK_METHOD(status_t, linkToDeath, - (const sp<DeathRecipient>& recipient, void* cookie, uint32_t flags), (override)); - MOCK_METHOD(status_t, unlinkToDeath, - (const wp<DeathRecipient>& recipient, void* cookie, uint32_t flags, - wp<DeathRecipient>* outRecipient), - (override)); - MOCK_METHOD(status_t, pingBinder, (), (override)); -}; +// ------------------------------------------------------------------------------------------------- -class MockIVibrator : public IVibrator { +class MockIVibratorManager : public IVibratorManager { public: - MOCK_METHOD(Status, getCapabilities, (int32_t * ret), (override)); - MOCK_METHOD(Status, off, (), (override)); - MOCK_METHOD(Status, on, (int32_t timeout, const sp<IVibratorCallback>& cb), (override)); - MOCK_METHOD(Status, perform, - (Effect e, EffectStrength s, const sp<IVibratorCallback>& cb, int32_t* ret), - (override)); - MOCK_METHOD(Status, getSupportedEffects, (std::vector<Effect> * ret), (override)); - MOCK_METHOD(Status, setAmplitude, (float amplitude), (override)); - MOCK_METHOD(Status, setExternalControl, (bool enabled), (override)); - MOCK_METHOD(Status, getCompositionDelayMax, (int32_t * ret), (override)); - MOCK_METHOD(Status, getCompositionSizeMax, (int32_t * ret), (override)); - MOCK_METHOD(Status, getSupportedPrimitives, (std::vector<CompositePrimitive> * ret), + MockIVibratorManager() = default; + + MOCK_METHOD(ndk::ScopedAStatus, getCapabilities, (int32_t * ret), (override)); + MOCK_METHOD(ndk::ScopedAStatus, getVibratorIds, (std::vector<int32_t> * ret), (override)); + MOCK_METHOD(ndk::ScopedAStatus, getVibrator, (int32_t id, std::shared_ptr<IVibrator>* ret), (override)); - MOCK_METHOD(Status, getPrimitiveDuration, (CompositePrimitive p, int32_t* ret), (override)); - MOCK_METHOD(Status, compose, - (const std::vector<CompositeEffect>& e, const sp<IVibratorCallback>& cb), + MOCK_METHOD(ndk::ScopedAStatus, prepareSynced, (const std::vector<int32_t>& ids), (override)); + MOCK_METHOD(ndk::ScopedAStatus, triggerSynced, (const std::shared_ptr<IVibratorCallback>& cb), (override)); - MOCK_METHOD(Status, composePwle, - (const std::vector<PrimitivePwle>& e, const sp<IVibratorCallback>& cb), (override)); - MOCK_METHOD(Status, getSupportedAlwaysOnEffects, (std::vector<Effect> * ret), (override)); - MOCK_METHOD(Status, alwaysOnEnable, (int32_t id, Effect e, EffectStrength s), (override)); - MOCK_METHOD(Status, alwaysOnDisable, (int32_t id), (override)); - MOCK_METHOD(Status, getQFactor, (float * ret), (override)); - MOCK_METHOD(Status, getResonantFrequency, (float * ret), (override)); - MOCK_METHOD(Status, getFrequencyResolution, (float* ret), (override)); - MOCK_METHOD(Status, getFrequencyMinimum, (float* ret), (override)); - MOCK_METHOD(Status, getBandwidthAmplitudeMap, (std::vector<float> * ret), (override)); - MOCK_METHOD(Status, getPwlePrimitiveDurationMax, (int32_t * ret), (override)); - MOCK_METHOD(Status, getPwleCompositionSizeMax, (int32_t * ret), (override)); - MOCK_METHOD(Status, getSupportedBraking, (std::vector<Braking> * ret), (override)); - MOCK_METHOD(int32_t, getInterfaceVersion, (), (override)); - MOCK_METHOD(std::string, getInterfaceHash, (), (override)); - MOCK_METHOD(IBinder*, onAsBinder, (), (override)); -}; - -class MockIVibratorManager : public IVibratorManager { -public: - MOCK_METHOD(Status, getCapabilities, (int32_t * ret), (override)); - MOCK_METHOD(Status, getVibratorIds, (std::vector<int32_t> * ret), (override)); - MOCK_METHOD(Status, getVibrator, (int32_t id, sp<IVibrator>* ret), (override)); - MOCK_METHOD(Status, prepareSynced, (const std::vector<int32_t>& ids), (override)); - MOCK_METHOD(Status, triggerSynced, (const sp<IVibratorCallback>& cb), (override)); - MOCK_METHOD(Status, cancelSynced, (), (override)); - MOCK_METHOD(int32_t, getInterfaceVersion, (), (override)); - MOCK_METHOD(std::string, getInterfaceHash, (), (override)); - MOCK_METHOD(IBinder*, onAsBinder, (), (override)); + MOCK_METHOD(ndk::ScopedAStatus, cancelSynced, (), (override)); + MOCK_METHOD(ndk::ScopedAStatus, getInterfaceVersion, (int32_t*), (override)); + MOCK_METHOD(ndk::ScopedAStatus, getInterfaceHash, (std::string*), (override)); + MOCK_METHOD(ndk::SpAIBinder, asBinder, (), (override)); + MOCK_METHOD(bool, isRemote, (), (override)); }; // ------------------------------------------------------------------------------------------------- @@ -108,9 +66,8 @@ public: class VibratorManagerHalWrapperAidlTest : public Test { public: void SetUp() override { - mMockBinder = new StrictMock<MockBinder>(); - mMockVibrator = new StrictMock<MockIVibrator>(); - mMockHal = new StrictMock<MockIVibratorManager>(); + mMockVibrator = ndk::SharedRefBase::make<StrictMock<vibrator::MockIVibrator>>(); + mMockHal = ndk::SharedRefBase::make<StrictMock<MockIVibratorManager>>(); mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>(); mWrapper = std::make_unique<vibrator::AidlManagerHalWrapper>(mMockScheduler, mMockHal); ASSERT_NE(mWrapper, nullptr); @@ -119,9 +76,8 @@ public: protected: std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr; std::unique_ptr<vibrator::ManagerHalWrapper> mWrapper = nullptr; - sp<StrictMock<MockIVibratorManager>> mMockHal = nullptr; - sp<StrictMock<MockIVibrator>> mMockVibrator = nullptr; - sp<StrictMock<MockBinder>> mMockBinder = nullptr; + std::shared_ptr<StrictMock<MockIVibratorManager>> mMockHal = nullptr; + std::shared_ptr<StrictMock<vibrator::MockIVibrator>> mMockVibrator = nullptr; }; // ------------------------------------------------------------------------------------------------- @@ -129,32 +85,13 @@ protected: static const std::vector<int32_t> kVibratorIds = {1, 2}; static constexpr int kVibratorId = 1; -ACTION(TriggerCallback) { - if (arg0 != nullptr) { - arg0->onComplete(); - } -} - -TEST_F(VibratorManagerHalWrapperAidlTest, TestPing) { - EXPECT_CALL(*mMockHal.get(), onAsBinder()) - .Times(Exactly(2)) - .WillRepeatedly(Return(mMockBinder.get())); - EXPECT_CALL(*mMockBinder.get(), pingBinder()) - .Times(Exactly(2)) - .WillOnce(Return(android::OK)) - .WillRepeatedly(Return(android::DEAD_OBJECT)); - - ASSERT_TRUE(mWrapper->ping().isOk()); - ASSERT_TRUE(mWrapper->ping().isFailed()); -} - TEST_F(VibratorManagerHalWrapperAidlTest, TestGetCapabilitiesDoesNotCacheFailedResult) { EXPECT_CALL(*mMockHal.get(), getCapabilities(_)) .Times(Exactly(3)) - .WillOnce( - Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION))) - .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))) - .WillRepeatedly(DoAll(SetArgPointee<0>(IVibratorManager::CAP_SYNC), Return(Status()))); + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION))) + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY))) + .WillOnce(DoAll(SetArgPointee<0>(IVibratorManager::CAP_SYNC), + Return(ndk::ScopedAStatus::ok()))); ASSERT_TRUE(mWrapper->getCapabilities().isUnsupported()); ASSERT_TRUE(mWrapper->getCapabilities().isFailed()); @@ -167,7 +104,8 @@ TEST_F(VibratorManagerHalWrapperAidlTest, TestGetCapabilitiesDoesNotCacheFailedR TEST_F(VibratorManagerHalWrapperAidlTest, TestGetCapabilitiesCachesResult) { EXPECT_CALL(*mMockHal.get(), getCapabilities(_)) .Times(Exactly(1)) - .WillRepeatedly(DoAll(SetArgPointee<0>(IVibratorManager::CAP_SYNC), Return(Status()))); + .WillOnce(DoAll(SetArgPointee<0>(IVibratorManager::CAP_SYNC), + Return(ndk::ScopedAStatus::ok()))); std::vector<std::thread> threads; for (int i = 0; i < 10; i++) { @@ -187,10 +125,9 @@ TEST_F(VibratorManagerHalWrapperAidlTest, TestGetCapabilitiesCachesResult) { TEST_F(VibratorManagerHalWrapperAidlTest, TestGetVibratorIdsDoesNotCacheFailedResult) { EXPECT_CALL(*mMockHal.get(), getVibratorIds(_)) .Times(Exactly(3)) - .WillOnce( - Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION))) - .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))) - .WillRepeatedly(DoAll(SetArgPointee<0>(kVibratorIds), Return(Status()))); + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION))) + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY))) + .WillOnce(DoAll(SetArgPointee<0>(kVibratorIds), Return(ndk::ScopedAStatus::ok()))); ASSERT_TRUE(mWrapper->getVibratorIds().isUnsupported()); ASSERT_TRUE(mWrapper->getVibratorIds().isFailed()); @@ -203,7 +140,7 @@ TEST_F(VibratorManagerHalWrapperAidlTest, TestGetVibratorIdsDoesNotCacheFailedRe TEST_F(VibratorManagerHalWrapperAidlTest, TestGetVibratorIdsCachesResult) { EXPECT_CALL(*mMockHal.get(), getVibratorIds(_)) .Times(Exactly(1)) - .WillRepeatedly(DoAll(SetArgPointee<0>(kVibratorIds), Return(Status()))); + .WillOnce(DoAll(SetArgPointee<0>(kVibratorIds), Return(ndk::ScopedAStatus::ok()))); std::vector<std::thread> threads; for (int i = 0; i < 10; i++) { @@ -225,11 +162,11 @@ TEST_F(VibratorManagerHalWrapperAidlTest, TestGetVibratorWithValidIdReturnsContr InSequence seq; EXPECT_CALL(*mMockHal.get(), getVibratorIds(_)) .Times(Exactly(1)) - .WillRepeatedly(DoAll(SetArgPointee<0>(kVibratorIds), Return(Status()))); + .WillOnce(DoAll(SetArgPointee<0>(kVibratorIds), Return(ndk::ScopedAStatus::ok()))); EXPECT_CALL(*mMockHal.get(), getVibrator(Eq(kVibratorId), _)) .Times(Exactly(1)) - .WillRepeatedly(DoAll(SetArgPointee<1>(mMockVibrator), Return(Status()))); + .WillOnce(DoAll(SetArgPointee<1>(mMockVibrator), Return(ndk::ScopedAStatus::ok()))); } auto result = mWrapper->getVibrator(kVibratorId); @@ -241,7 +178,7 @@ TEST_F(VibratorManagerHalWrapperAidlTest, TestGetVibratorWithValidIdReturnsContr TEST_F(VibratorManagerHalWrapperAidlTest, TestGetVibratorWithInvalidIdFails) { EXPECT_CALL(*mMockHal.get(), getVibratorIds(_)) .Times(Exactly(1)) - .WillRepeatedly(DoAll(SetArgPointee<0>(kVibratorIds), Return(Status()))); + .WillOnce(DoAll(SetArgPointee<0>(kVibratorIds), Return(ndk::ScopedAStatus::ok()))); ASSERT_TRUE(mWrapper->getVibrator(0).isFailed()); } @@ -249,20 +186,21 @@ TEST_F(VibratorManagerHalWrapperAidlTest, TestGetVibratorWithInvalidIdFails) { TEST_F(VibratorManagerHalWrapperAidlTest, TestGetVibratorRecoversVibratorPointer) { EXPECT_CALL(*mMockHal.get(), getVibratorIds(_)) .Times(Exactly(1)) - .WillRepeatedly(DoAll(SetArgPointee<0>(kVibratorIds), Return(Status()))); + .WillOnce(DoAll(SetArgPointee<0>(kVibratorIds), Return(ndk::ScopedAStatus::ok()))); EXPECT_CALL(*mMockHal.get(), getVibrator(Eq(kVibratorId), _)) .Times(Exactly(3)) .WillOnce(DoAll(SetArgPointee<1>(nullptr), - Return(Status::fromExceptionCode( - Status::Exception::EX_TRANSACTION_FAILED)))) - .WillRepeatedly(DoAll(SetArgPointee<1>(mMockVibrator), Return(Status()))); + Return(ndk::ScopedAStatus::fromExceptionCode(EX_TRANSACTION_FAILED)))) + // ndk::ScopedAStatus::ok() cannot be copy-constructed so can't use WillRepeatedly + .WillOnce(DoAll(SetArgPointee<1>(mMockVibrator), Return(ndk::ScopedAStatus::ok()))) + .WillOnce(DoAll(SetArgPointee<1>(mMockVibrator), Return(ndk::ScopedAStatus::ok()))); EXPECT_CALL(*mMockVibrator.get(), off()) .Times(Exactly(3)) - .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_TRANSACTION_FAILED))) - .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_TRANSACTION_FAILED))) - .WillRepeatedly(Return(Status())); + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_TRANSACTION_FAILED))) + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_TRANSACTION_FAILED))) + .WillOnce(Return(ndk::ScopedAStatus::ok())); // Get vibrator controller is successful even if first getVibrator. auto result = mWrapper->getVibrator(kVibratorId); @@ -281,18 +219,19 @@ TEST_F(VibratorManagerHalWrapperAidlTest, TestGetVibratorRecoversVibratorPointer TEST_F(VibratorManagerHalWrapperAidlTest, TestPrepareSynced) { EXPECT_CALL(*mMockHal.get(), getVibratorIds(_)) .Times(Exactly(1)) - .WillRepeatedly(DoAll(SetArgPointee<0>(kVibratorIds), Return(Status()))); + .WillOnce(DoAll(SetArgPointee<0>(kVibratorIds), Return(ndk::ScopedAStatus::ok()))); EXPECT_CALL(*mMockHal.get(), getVibrator(_, _)) .Times(Exactly(2)) - .WillRepeatedly(DoAll(SetArgPointee<1>(mMockVibrator), Return(Status()))); + // ndk::ScopedAStatus::ok() cannot be copy-constructed so can't use WillRepeatedly + .WillOnce(DoAll(SetArgPointee<1>(mMockVibrator), Return(ndk::ScopedAStatus::ok()))) + .WillOnce(DoAll(SetArgPointee<1>(mMockVibrator), Return(ndk::ScopedAStatus::ok()))); EXPECT_CALL(*mMockHal.get(), prepareSynced(Eq(kVibratorIds))) .Times(Exactly(3)) - .WillOnce( - Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION))) - .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))) - .WillRepeatedly(Return(Status())); + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION))) + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY))) + .WillOnce(Return(ndk::ScopedAStatus::ok())); ASSERT_TRUE(mWrapper->getVibratorIds().isOk()); ASSERT_TRUE(mWrapper->prepareSynced(kVibratorIds).isUnsupported()); @@ -305,13 +244,13 @@ TEST_F(VibratorManagerHalWrapperAidlTest, TestTriggerSyncedWithCallbackSupport) InSequence seq; EXPECT_CALL(*mMockHal.get(), getCapabilities(_)) .Times(Exactly(1)) - .WillRepeatedly(DoAll(SetArgPointee<0>(IVibratorManager::CAP_TRIGGER_CALLBACK), - Return(Status()))); + .WillOnce(DoAll(SetArgPointee<0>(IVibratorManager::CAP_TRIGGER_CALLBACK), + Return(ndk::ScopedAStatus::ok()))); EXPECT_CALL(*mMockHal.get(), triggerSynced(_)) .Times(Exactly(3)) - .WillOnce(Return(Status::fromStatusT(UNKNOWN_TRANSACTION))) - .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))) - .WillRepeatedly(DoAll(TriggerCallback(), Return(Status()))); + .WillOnce(Return(ndk::ScopedAStatus::fromStatus(STATUS_UNKNOWN_TRANSACTION))) + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY))) + .WillOnce(DoAll(vibrator::TriggerCallback(), Return(ndk::ScopedAStatus::ok()))); } std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); @@ -328,11 +267,11 @@ TEST_F(VibratorManagerHalWrapperAidlTest, TestTriggerSyncedWithoutCallbackSuppor InSequence seq; EXPECT_CALL(*mMockHal.get(), getCapabilities(_)) .Times(Exactly(1)) - .WillRepeatedly( - DoAll(SetArgPointee<0>(IVibratorManager::CAP_SYNC), Return(Status()))); + .WillOnce(DoAll(SetArgPointee<0>(IVibratorManager::CAP_SYNC), + Return(ndk::ScopedAStatus::ok()))); EXPECT_CALL(*mMockHal.get(), triggerSynced(Eq(nullptr))) .Times(Exactly(1)) - .WillRepeatedly(Return(Status())); + .WillOnce(Return(ndk::ScopedAStatus::ok())); } std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); @@ -345,9 +284,9 @@ TEST_F(VibratorManagerHalWrapperAidlTest, TestTriggerSyncedWithoutCallbackSuppor TEST_F(VibratorManagerHalWrapperAidlTest, TestCancelSynced) { EXPECT_CALL(*mMockHal.get(), cancelSynced()) .Times(Exactly(3)) - .WillOnce(Return(Status::fromStatusT(UNKNOWN_TRANSACTION))) - .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))) - .WillRepeatedly(Return(Status())); + .WillOnce(Return(ndk::ScopedAStatus::fromStatus(STATUS_UNKNOWN_TRANSACTION))) + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY))) + .WillOnce(Return(ndk::ScopedAStatus::ok())); ASSERT_TRUE(mWrapper->cancelSynced().isUnsupported()); ASSERT_TRUE(mWrapper->cancelSynced().isFailed()); @@ -357,13 +296,17 @@ TEST_F(VibratorManagerHalWrapperAidlTest, TestCancelSynced) { TEST_F(VibratorManagerHalWrapperAidlTest, TestCancelSyncedReloadsAllControllers) { EXPECT_CALL(*mMockHal.get(), getVibratorIds(_)) .Times(Exactly(1)) - .WillRepeatedly(DoAll(SetArgPointee<0>(kVibratorIds), Return(Status()))); + .WillOnce(DoAll(SetArgPointee<0>(kVibratorIds), Return(ndk::ScopedAStatus::ok()))); EXPECT_CALL(*mMockHal.get(), getVibrator(_, _)) .Times(Exactly(2)) - .WillRepeatedly(DoAll(SetArgPointee<1>(mMockVibrator), Return(Status()))); + // ndk::ScopedAStatus::ok() cannot be copy-constructed so can't use WillRepeatedly + .WillOnce(DoAll(SetArgPointee<1>(mMockVibrator), Return(ndk::ScopedAStatus::ok()))) + .WillOnce(DoAll(SetArgPointee<1>(mMockVibrator), Return(ndk::ScopedAStatus::ok()))); - EXPECT_CALL(*mMockHal.get(), cancelSynced()).Times(Exactly(1)).WillRepeatedly(Return(Status())); + EXPECT_CALL(*mMockHal.get(), cancelSynced()) + .Times(Exactly(1)) + .WillOnce(Return(ndk::ScopedAStatus::ok())); ASSERT_TRUE(mWrapper->getVibratorIds().isOk()); ASSERT_TRUE(mWrapper->cancelSynced().isOk()); diff --git a/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp b/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp index 0850ef3b8c..78772369bf 100644 --- a/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp +++ b/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp @@ -23,10 +23,12 @@ #include <vibratorservice/VibratorManagerHalWrapper.h> -using android::hardware::vibrator::CompositeEffect; -using android::hardware::vibrator::CompositePrimitive; -using android::hardware::vibrator::Effect; -using android::hardware::vibrator::EffectStrength; +#include "test_mocks.h" + +using aidl::android::hardware::vibrator::CompositeEffect; +using aidl::android::hardware::vibrator::CompositePrimitive; +using aidl::android::hardware::vibrator::Effect; +using aidl::android::hardware::vibrator::EffectStrength; using std::chrono::milliseconds; @@ -35,27 +37,16 @@ using namespace testing; // ------------------------------------------------------------------------------------------------- -class MockHalController : public vibrator::HalController { -public: - MockHalController() = default; - virtual ~MockHalController() = default; - - MOCK_METHOD(bool, init, (), (override)); - MOCK_METHOD(void, tryReconnect, (), (override)); -}; - -// ------------------------------------------------------------------------------------------------- - class VibratorManagerHalWrapperLegacyTest : public Test { public: void SetUp() override { - mMockController = std::make_shared<StrictMock<MockHalController>>(); + mMockController = std::make_shared<StrictMock<vibrator::MockHalController>>(); mWrapper = std::make_unique<vibrator::LegacyManagerHalWrapper>(mMockController); ASSERT_NE(mWrapper, nullptr); } protected: - std::shared_ptr<StrictMock<MockHalController>> mMockController = nullptr; + std::shared_ptr<StrictMock<vibrator::MockHalController>> mMockController = nullptr; std::unique_ptr<vibrator::ManagerHalWrapper> mWrapper = nullptr; }; diff --git a/services/vibratorservice/test/test_mocks.h b/services/vibratorservice/test/test_mocks.h new file mode 100644 index 0000000000..5e090849af --- /dev/null +++ b/services/vibratorservice/test/test_mocks.h @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef VIBRATORSERVICE_UNITTEST_MOCKS_H_ +#define VIBRATORSERVICE_UNITTEST_MOCKS_H_ + +#include <gmock/gmock.h> + +#include <aidl/android/hardware/vibrator/IVibrator.h> + +#include <vibratorservice/VibratorCallbackScheduler.h> +#include <vibratorservice/VibratorHalController.h> +#include <vibratorservice/VibratorHalWrapper.h> + +namespace android { + +namespace vibrator { + +using std::chrono::milliseconds; + +using namespace testing; + +using aidl::android::hardware::vibrator::Braking; +using aidl::android::hardware::vibrator::CompositeEffect; +using aidl::android::hardware::vibrator::CompositePrimitive; +using aidl::android::hardware::vibrator::Effect; +using aidl::android::hardware::vibrator::EffectStrength; +using aidl::android::hardware::vibrator::IVibrator; +using aidl::android::hardware::vibrator::IVibratorCallback; +using aidl::android::hardware::vibrator::PrimitivePwle; +using aidl::android::hardware::vibrator::PwleV2OutputMapEntry; +using aidl::android::hardware::vibrator::PwleV2Primitive; +using aidl::android::hardware::vibrator::VendorEffect; + +// ------------------------------------------------------------------------------------------------- + +class MockIVibrator : public IVibrator { +public: + MockIVibrator() = default; + + MOCK_METHOD(ndk::ScopedAStatus, getCapabilities, (int32_t * ret), (override)); + MOCK_METHOD(ndk::ScopedAStatus, off, (), (override)); + MOCK_METHOD(ndk::ScopedAStatus, on, + (int32_t timeout, const std::shared_ptr<IVibratorCallback>& cb), (override)); + MOCK_METHOD(ndk::ScopedAStatus, perform, + (Effect e, EffectStrength s, const std::shared_ptr<IVibratorCallback>& cb, + int32_t* ret), + (override)); + MOCK_METHOD(ndk::ScopedAStatus, performVendorEffect, + (const VendorEffect& e, const std::shared_ptr<IVibratorCallback>& cb), (override)); + MOCK_METHOD(ndk::ScopedAStatus, getSupportedEffects, (std::vector<Effect> * ret), (override)); + MOCK_METHOD(ndk::ScopedAStatus, setAmplitude, (float amplitude), (override)); + MOCK_METHOD(ndk::ScopedAStatus, setExternalControl, (bool enabled), (override)); + MOCK_METHOD(ndk::ScopedAStatus, getCompositionDelayMax, (int32_t * ret), (override)); + MOCK_METHOD(ndk::ScopedAStatus, getCompositionSizeMax, (int32_t * ret), (override)); + MOCK_METHOD(ndk::ScopedAStatus, getSupportedPrimitives, (std::vector<CompositePrimitive> * ret), + (override)); + MOCK_METHOD(ndk::ScopedAStatus, getPrimitiveDuration, (CompositePrimitive p, int32_t* ret), + (override)); + MOCK_METHOD(ndk::ScopedAStatus, compose, + (const std::vector<CompositeEffect>& e, + const std::shared_ptr<IVibratorCallback>& cb), + (override)); + MOCK_METHOD(ndk::ScopedAStatus, composePwle, + (const std::vector<PrimitivePwle>& e, const std::shared_ptr<IVibratorCallback>& cb), + (override)); + MOCK_METHOD(ndk::ScopedAStatus, getSupportedAlwaysOnEffects, (std::vector<Effect> * ret), + (override)); + MOCK_METHOD(ndk::ScopedAStatus, alwaysOnEnable, (int32_t id, Effect e, EffectStrength s), + (override)); + MOCK_METHOD(ndk::ScopedAStatus, alwaysOnDisable, (int32_t id), (override)); + MOCK_METHOD(ndk::ScopedAStatus, getQFactor, (float* ret), (override)); + MOCK_METHOD(ndk::ScopedAStatus, getResonantFrequency, (float* ret), (override)); + MOCK_METHOD(ndk::ScopedAStatus, getFrequencyResolution, (float* ret), (override)); + MOCK_METHOD(ndk::ScopedAStatus, getFrequencyMinimum, (float* ret), (override)); + MOCK_METHOD(ndk::ScopedAStatus, getBandwidthAmplitudeMap, (std::vector<float> * ret), + (override)); + MOCK_METHOD(ndk::ScopedAStatus, getPwlePrimitiveDurationMax, (int32_t * ret), (override)); + MOCK_METHOD(ndk::ScopedAStatus, getPwleCompositionSizeMax, (int32_t * ret), (override)); + MOCK_METHOD(ndk::ScopedAStatus, getSupportedBraking, (std::vector<Braking> * ret), (override)); + MOCK_METHOD(ndk::ScopedAStatus, getPwleV2FrequencyToOutputAccelerationMap, + (std::vector<PwleV2OutputMapEntry> * ret), (override)); + MOCK_METHOD(ndk::ScopedAStatus, getPwleV2PrimitiveDurationMaxMillis, (int32_t* ret), + (override)); + MOCK_METHOD(ndk::ScopedAStatus, getPwleV2PrimitiveDurationMinMillis, (int32_t* ret), + (override)); + MOCK_METHOD(ndk::ScopedAStatus, getPwleV2CompositionSizeMax, (int32_t* ret), (override)); + MOCK_METHOD(ndk::ScopedAStatus, composePwleV2, + (const std::vector<PwleV2Primitive>& e, + const std::shared_ptr<IVibratorCallback>& cb), + (override)); + MOCK_METHOD(ndk::ScopedAStatus, getInterfaceVersion, (int32_t*), (override)); + MOCK_METHOD(ndk::ScopedAStatus, getInterfaceHash, (std::string*), (override)); + MOCK_METHOD(ndk::SpAIBinder, asBinder, (), (override)); + MOCK_METHOD(bool, isRemote, (), (override)); +}; + +// gmock requirement to provide a WithArg<0>(TriggerCallback()) matcher +typedef void TriggerCallbackFunction(const std::shared_ptr<IVibratorCallback>&); + +class TriggerCallbackAction : public ActionInterface<TriggerCallbackFunction> { +public: + explicit TriggerCallbackAction() {} + + virtual Result Perform(const ArgumentTuple& args) { + const std::shared_ptr<IVibratorCallback>& callback = get<0>(args); + if (callback) { + callback->onComplete(); + } + } +}; + +inline Action<TriggerCallbackFunction> TriggerCallback() { + return MakeAction(new TriggerCallbackAction()); +} + +// ------------------------------------------------------------------------------------------------- + +class MockCallbackScheduler : public CallbackScheduler { +public: + MOCK_METHOD(void, schedule, (std::function<void()> callback, std::chrono::milliseconds delay), + (override)); +}; + +ACTION(TriggerSchedulerCallback) { + arg0(); +} + +// ------------------------------------------------------------------------------------------------- + +class MockHalWrapper : public HalWrapper { +public: + MockHalWrapper(std::shared_ptr<CallbackScheduler> scheduler) : HalWrapper(scheduler) {} + virtual ~MockHalWrapper() = default; + + MOCK_METHOD(vibrator::HalResult<void>, ping, (), (override)); + MOCK_METHOD(void, tryReconnect, (), (override)); + MOCK_METHOD(vibrator::HalResult<void>, on, + (milliseconds timeout, const std::function<void()>& completionCallback), + (override)); + MOCK_METHOD(vibrator::HalResult<void>, off, (), (override)); + MOCK_METHOD(vibrator::HalResult<void>, setAmplitude, (float amplitude), (override)); + MOCK_METHOD(vibrator::HalResult<void>, setExternalControl, (bool enabled), (override)); + MOCK_METHOD(vibrator::HalResult<void>, alwaysOnEnable, + (int32_t id, Effect effect, EffectStrength strength), (override)); + MOCK_METHOD(vibrator::HalResult<void>, alwaysOnDisable, (int32_t id), (override)); + MOCK_METHOD(vibrator::HalResult<milliseconds>, performEffect, + (Effect effect, EffectStrength strength, + const std::function<void()>& completionCallback), + (override)); + MOCK_METHOD(vibrator::HalResult<vibrator::Capabilities>, getCapabilitiesInternal, (), + (override)); + + CallbackScheduler* getCallbackScheduler() { return mCallbackScheduler.get(); } +}; + +class MockHalController : public vibrator::HalController { +public: + MockHalController() = default; + virtual ~MockHalController() = default; + + MOCK_METHOD(bool, init, (), (override)); + MOCK_METHOD(void, tryReconnect, (), (override)); +}; + +// ------------------------------------------------------------------------------------------------- + +} // namespace vibrator + +} // namespace android + +#endif // VIBRATORSERVICE_UNITTEST_MOCKS_H_ diff --git a/services/vibratorservice/test/test_utils.h b/services/vibratorservice/test/test_utils.h index 715c2215c4..e99965c877 100644 --- a/services/vibratorservice/test/test_utils.h +++ b/services/vibratorservice/test/test_utils.h @@ -17,7 +17,7 @@ #ifndef VIBRATORSERVICE_UNITTEST_UTIL_H_ #define VIBRATORSERVICE_UNITTEST_UTIL_H_ -#include <android/hardware/vibrator/IVibrator.h> +#include <aidl/android/hardware/vibrator/IVibrator.h> #include <vibratorservice/VibratorHalWrapper.h> @@ -25,24 +25,12 @@ namespace android { namespace vibrator { -using ::android::hardware::vibrator::ActivePwle; -using ::android::hardware::vibrator::Braking; -using ::android::hardware::vibrator::BrakingPwle; -using ::android::hardware::vibrator::CompositeEffect; -using ::android::hardware::vibrator::CompositePrimitive; -using ::android::hardware::vibrator::PrimitivePwle; - -// ------------------------------------------------------------------------------------------------- - -class MockCallbackScheduler : public vibrator::CallbackScheduler { -public: - MOCK_METHOD(void, schedule, (std::function<void()> callback, std::chrono::milliseconds delay), - (override)); -}; - -ACTION(TriggerSchedulerCallback) { - arg0(); -} +using aidl::android::hardware::vibrator::ActivePwle; +using aidl::android::hardware::vibrator::Braking; +using aidl::android::hardware::vibrator::BrakingPwle; +using aidl::android::hardware::vibrator::CompositeEffect; +using aidl::android::hardware::vibrator::CompositePrimitive; +using aidl::android::hardware::vibrator::PrimitivePwle; // ------------------------------------------------------------------------------------------------- diff --git a/vulkan/libvulkan/Android.bp b/vulkan/libvulkan/Android.bp index c8ff76a822..879d2d0fa7 100644 --- a/vulkan/libvulkan/Android.bp +++ b/vulkan/libvulkan/Android.bp @@ -29,6 +29,18 @@ ndk_library { unversioned_until: "current", } +aconfig_declarations { + name: "libvulkan_flags", + package: "com.android.graphics.libvulkan.flags", + container: "system", + srcs: ["libvulkan_flags.aconfig"], +} + +cc_aconfig_library { + name: "libvulkanflags", + aconfig_declarations: "libvulkan_flags", +} + cc_library_shared { name: "libvulkan", llndk: { @@ -110,5 +122,8 @@ cc_library_shared { "android.hardware.graphics.common@1.0", "libSurfaceFlingerProp", ], - static_libs: ["libgrallocusage"], + static_libs: [ + "libgrallocusage", + "libvulkanflags", + ], } diff --git a/vulkan/libvulkan/api_gen.cpp b/vulkan/libvulkan/api_gen.cpp index a9706bc871..9ff0b46c0f 100644 --- a/vulkan/libvulkan/api_gen.cpp +++ b/vulkan/libvulkan/api_gen.cpp @@ -25,6 +25,9 @@ #undef VK_NO_PROTOTYPES #include "api.h" +/* + * This file is autogenerated by api_generator.py. Do not edit directly. + */ namespace vulkan { namespace api { @@ -178,7 +181,7 @@ bool InitDispatchTable( INIT_PROC(false, instance, GetPhysicalDeviceExternalSemaphoreProperties); INIT_PROC(false, instance, GetPhysicalDeviceExternalFenceProperties); INIT_PROC(false, instance, EnumeratePhysicalDeviceGroups); - INIT_PROC_EXT(KHR_swapchain, false, instance, GetPhysicalDevicePresentRectanglesKHR); + INIT_PROC_EXT(KHR_swapchain, true, instance, GetPhysicalDevicePresentRectanglesKHR); INIT_PROC(false, instance, GetPhysicalDeviceToolProperties); // clang-format on @@ -325,9 +328,9 @@ bool InitDispatchTable( INIT_PROC(false, dev, BindBufferMemory2); INIT_PROC(false, dev, BindImageMemory2); INIT_PROC(false, dev, CmdSetDeviceMask); - INIT_PROC_EXT(KHR_swapchain, false, dev, GetDeviceGroupPresentCapabilitiesKHR); - INIT_PROC_EXT(KHR_swapchain, false, dev, GetDeviceGroupSurfacePresentModesKHR); - INIT_PROC_EXT(KHR_swapchain, false, dev, AcquireNextImage2KHR); + INIT_PROC_EXT(KHR_swapchain, true, dev, GetDeviceGroupPresentCapabilitiesKHR); + INIT_PROC_EXT(KHR_swapchain, true, dev, GetDeviceGroupSurfacePresentModesKHR); + INIT_PROC_EXT(KHR_swapchain, true, dev, AcquireNextImage2KHR); INIT_PROC(false, dev, CmdDispatchBase); INIT_PROC(false, dev, CreateDescriptorUpdateTemplate); INIT_PROC(false, dev, DestroyDescriptorUpdateTemplate); @@ -659,6 +662,8 @@ VKAPI_ATTR PFN_vkVoidFunction GetDeviceProcAddr(VkDevice device, const char* pNa "vkGetDrmDisplayEXT", "vkGetInstanceProcAddr", "vkGetPhysicalDeviceCalibrateableTimeDomainsEXT", + "vkGetPhysicalDeviceCalibrateableTimeDomainsKHR", + "vkGetPhysicalDeviceCooperativeMatrixPropertiesKHR", "vkGetPhysicalDeviceDisplayPlaneProperties2KHR", "vkGetPhysicalDeviceDisplayProperties2KHR", "vkGetPhysicalDeviceExternalBufferProperties", @@ -703,6 +708,7 @@ VKAPI_ATTR PFN_vkVoidFunction GetDeviceProcAddr(VkDevice device, const char* pNa "vkGetPhysicalDeviceToolProperties", "vkGetPhysicalDeviceToolPropertiesEXT", "vkGetPhysicalDeviceVideoCapabilitiesKHR", + "vkGetPhysicalDeviceVideoEncodeQualityLevelPropertiesKHR", "vkGetPhysicalDeviceVideoFormatPropertiesKHR", "vkSubmitDebugUtilsMessageEXT", }; diff --git a/vulkan/libvulkan/api_gen.h b/vulkan/libvulkan/api_gen.h index 4998018882..b468a8911b 100644 --- a/vulkan/libvulkan/api_gen.h +++ b/vulkan/libvulkan/api_gen.h @@ -25,6 +25,9 @@ #include "driver_gen.h" +/* + * This file is autogenerated by api_generator.py. Do not edit directly. + */ namespace vulkan { namespace api { diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp index ef213f0c7a..01436db4ae 100644 --- a/vulkan/libvulkan/driver.cpp +++ b/vulkan/libvulkan/driver.cpp @@ -41,10 +41,12 @@ #include <new> #include <vector> +#include <com_android_graphics_libvulkan_flags.h> #include "stubhal.h" using namespace android::hardware::configstore; using namespace android::hardware::configstore::V1_0; +using namespace com::android::graphics::libvulkan; extern "C" android_namespace_t* android_get_exported_namespace(const char*); @@ -688,6 +690,7 @@ void CreateInfoWrapper::FilterExtension(const char* name) { case ProcHook::KHR_incremental_present: case ProcHook::KHR_shared_presentable_image: case ProcHook::KHR_swapchain: + case ProcHook::KHR_swapchain_mutable_format: case ProcHook::EXT_hdr_metadata: case ProcHook::EXT_swapchain_maintenance1: case ProcHook::ANDROID_external_memory_android_hardware_buffer: @@ -740,6 +743,7 @@ void CreateInfoWrapper::FilterExtension(const char* name) { break; case ProcHook::ANDROID_external_memory_android_hardware_buffer: case ProcHook::KHR_external_fence_fd: + case ProcHook::KHR_swapchain_mutable_format: case ProcHook::EXTENSION_UNKNOWN: // Extensions we don't need to do anything about at this level break; @@ -1251,6 +1255,15 @@ VkResult EnumerateDeviceExtensionProperties( VK_EXT_SWAPCHAIN_MAINTENANCE_1_SPEC_VERSION}); } + VkPhysicalDeviceProperties pDeviceProperties; + data.driver.GetPhysicalDeviceProperties(physicalDevice, &pDeviceProperties); + if (flags::swapchain_mutable_format_ext() && + pDeviceProperties.apiVersion >= VK_API_VERSION_1_2) { + loader_extensions.push_back( + {VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_EXTENSION_NAME, + VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_SPEC_VERSION}); + } + // enumerate our extensions first if (!pLayerName && pProperties) { uint32_t count = std::min( diff --git a/vulkan/libvulkan/driver_gen.cpp b/vulkan/libvulkan/driver_gen.cpp index 8f090083f8..f741977d50 100644 --- a/vulkan/libvulkan/driver_gen.cpp +++ b/vulkan/libvulkan/driver_gen.cpp @@ -26,6 +26,9 @@ namespace vulkan { namespace driver { +/* + * This file is autogenerated by driver_generator.py. Do not edit directly. + */ namespace { // clang-format off @@ -613,6 +616,7 @@ ProcHook::Extension GetProcHookExtension(const char* name) { if (strcmp(name, "VK_KHR_external_semaphore_capabilities") == 0) return ProcHook::KHR_external_semaphore_capabilities; if (strcmp(name, "VK_KHR_external_fence_capabilities") == 0) return ProcHook::KHR_external_fence_capabilities; if (strcmp(name, "VK_KHR_external_fence_fd") == 0) return ProcHook::KHR_external_fence_fd; + if (strcmp(name, "VK_KHR_swapchain_mutable_format") == 0) return ProcHook::KHR_swapchain_mutable_format; // clang-format on return ProcHook::EXTENSION_UNKNOWN; } diff --git a/vulkan/libvulkan/driver_gen.h b/vulkan/libvulkan/driver_gen.h index 4527214c3f..649c0f1a17 100644 --- a/vulkan/libvulkan/driver_gen.h +++ b/vulkan/libvulkan/driver_gen.h @@ -26,6 +26,9 @@ #include <optional> #include <vector> +/* + * This file is autogenerated by driver_generator.py. Do not edit directly. + */ namespace vulkan { namespace driver { @@ -59,6 +62,7 @@ struct ProcHook { KHR_external_semaphore_capabilities, KHR_external_fence_capabilities, KHR_external_fence_fd, + KHR_swapchain_mutable_format, EXTENSION_CORE_1_0, EXTENSION_CORE_1_1, diff --git a/vulkan/libvulkan/libvulkan_flags.aconfig b/vulkan/libvulkan/libvulkan_flags.aconfig new file mode 100644 index 0000000000..891bc0261b --- /dev/null +++ b/vulkan/libvulkan/libvulkan_flags.aconfig @@ -0,0 +1,10 @@ +package: "com.android.graphics.libvulkan.flags" +container: "system" + +flag { + name: "swapchain_mutable_format_ext" + namespace: "core_graphics" + description: "Enable the VK_KHR_swapchain_mutable_format vulkan extension" + bug: "341978292" + is_fixed_read_only: true +} diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp index f01d1d98b6..09b0a145af 100644 --- a/vulkan/libvulkan/swapchain.cpp +++ b/vulkan/libvulkan/swapchain.cpp @@ -1472,6 +1472,12 @@ static VkResult getProducerUsage(const VkDevice& device, .flags = create_protected_swapchain ? VK_IMAGE_CREATE_PROTECTED_BIT : 0u, }; + // If supporting mutable format swapchain add the mutable format flag + if (create_info->flags & VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR) { + image_format_info.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT; + image_format_info.flags |= VK_IMAGE_CREATE_EXTENDED_USAGE_BIT_KHR; + } + VkAndroidHardwareBufferUsageANDROID ahb_usage; ahb_usage.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_USAGE_ANDROID; ahb_usage.pNext = nullptr; @@ -1480,23 +1486,14 @@ static VkResult getProducerUsage(const VkDevice& device, image_format_properties.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2; image_format_properties.pNext = &ahb_usage; - if (instance_dispatch.GetPhysicalDeviceImageFormatProperties2) { - VkResult result = instance_dispatch.GetPhysicalDeviceImageFormatProperties2( - pdev, &image_format_info, &image_format_properties); - if (result != VK_SUCCESS) { - ALOGE("VkGetPhysicalDeviceImageFormatProperties2 for AHB usage failed: %d", result); - return VK_ERROR_SURFACE_LOST_KHR; - } - } - else { - VkResult result = instance_dispatch.GetPhysicalDeviceImageFormatProperties2KHR( - pdev, &image_format_info, - &image_format_properties); - if (result != VK_SUCCESS) { - ALOGE("VkGetPhysicalDeviceImageFormatProperties2KHR for AHB usage failed: %d", - result); - return VK_ERROR_SURFACE_LOST_KHR; - } + VkResult result = GetPhysicalDeviceImageFormatProperties2( + pdev, &image_format_info, &image_format_properties); + if (result != VK_SUCCESS) { + ALOGE( + "VkGetPhysicalDeviceImageFormatProperties2 for AHB usage " + "failed: %d", + result); + return VK_ERROR_SURFACE_LOST_KHR; } // Determine if USAGE_FRONT_BUFFER is needed. @@ -1899,6 +1896,11 @@ VkResult CreateSwapchainKHR(VkDevice device, num_images = 1; } + VkImageFormatListCreateInfo extra_mutable_formats = { + .sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO_KHR, + }; + VkImageFormatListCreateInfo* extra_mutable_formats_ptr; + // Look through the create_info pNext chain passed to createSwapchainKHR // for an image compression control struct. // if one is found AND the appropriate extensions are enabled, create a @@ -1917,7 +1919,29 @@ VkResult CreateSwapchainKHR(VkDevice device, image_compression.pNext = nullptr; usage_info_pNext = &image_compression; } break; - + case VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO: { + const VkImageFormatListCreateInfo* format_list = + reinterpret_cast<const VkImageFormatListCreateInfo*>( + create_infos); + if (create_info->flags & + VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR) { + if (format_list && format_list->viewFormatCount > 0 && + format_list->pViewFormats) { + extra_mutable_formats.viewFormatCount = + format_list->viewFormatCount; + extra_mutable_formats.pViewFormats = + format_list->pViewFormats; + extra_mutable_formats_ptr = &extra_mutable_formats; + } else { + ALOGE( + "vk_swapchain_create_mutable_format_bit_khr was " + "set during swapchain creation but no valid " + "vkimageformatlistcreateinfo was found in the " + "pnext chain"); + return VK_ERROR_INITIALIZATION_FAILED; + } + } + } break; default: // Ignore all other info structs break; @@ -2013,6 +2037,11 @@ VkResult CreateSwapchainKHR(VkDevice device, .pQueueFamilyIndices = create_info->pQueueFamilyIndices, }; + if (create_info->flags & VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR) { + image_create.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT; + image_create.flags |= VK_IMAGE_CREATE_EXTENDED_USAGE_BIT_KHR; + } + // Note: don't do deferred allocation for shared present modes. There's only one buffer // involved so very little benefit. if ((create_info->flags & VK_SWAPCHAIN_CREATE_DEFERRED_MEMORY_ALLOCATION_BIT_EXT) && @@ -2022,7 +2051,7 @@ VkResult CreateSwapchainKHR(VkDevice device, // AcquireNextImage. VkImageSwapchainCreateInfoKHR image_swapchain_create = { .sType = VK_STRUCTURE_TYPE_IMAGE_SWAPCHAIN_CREATE_INFO_KHR, - .pNext = nullptr, + .pNext = extra_mutable_formats_ptr, .swapchain = HandleFromSwapchain(swapchain), }; image_create.pNext = &image_swapchain_create; @@ -2074,6 +2103,11 @@ VkResult CreateSwapchainKHR(VkDevice device, ANativeWindowBuffer_getHardwareBuffer(img.buffer.get()); image_create.pNext = &image_native_buffer; + if (extra_mutable_formats_ptr) { + extra_mutable_formats_ptr->pNext = image_create.pNext; + image_create.pNext = extra_mutable_formats_ptr; + } + ATRACE_BEGIN("CreateImage"); result = dispatch.CreateImage(device, &image_create, nullptr, &img.image); diff --git a/vulkan/nulldrv/null_driver_gen.cpp b/vulkan/nulldrv/null_driver_gen.cpp index d34851e536..40a45af94e 100644 --- a/vulkan/nulldrv/null_driver_gen.cpp +++ b/vulkan/nulldrv/null_driver_gen.cpp @@ -24,6 +24,9 @@ using namespace null_driver; +/* + * This file is autogenerated by null_generator.py. Do not edit directly. + */ namespace { struct NameProc { diff --git a/vulkan/nulldrv/null_driver_gen.h b/vulkan/nulldrv/null_driver_gen.h index fb3bd05e07..0d1e223226 100644 --- a/vulkan/nulldrv/null_driver_gen.h +++ b/vulkan/nulldrv/null_driver_gen.h @@ -22,6 +22,9 @@ #include <vulkan/vk_android_native_buffer.h> #include <vulkan/vulkan.h> +/* + * This file is autogenerated by null_generator.py. Do not edit directly. + */ namespace null_driver { PFN_vkVoidFunction GetGlobalProcAddr(const char* name); diff --git a/vulkan/scripts/api_generator.py b/vulkan/scripts/api_generator.py index be24172eed..001af208a8 100644 --- a/vulkan/scripts/api_generator.py +++ b/vulkan/scripts/api_generator.py @@ -61,6 +61,9 @@ def gen_h(): #include "driver_gen.h" +/* + * This file is autogenerated by api_generator.py. Do not edit directly. + */ namespace vulkan { namespace api { @@ -283,6 +286,9 @@ def gen_cpp(): #undef VK_NO_PROTOTYPES #include "api.h" +/* + * This file is autogenerated by api_generator.py. Do not edit directly. + */ namespace vulkan { namespace api { diff --git a/vulkan/scripts/driver_generator.py b/vulkan/scripts/driver_generator.py index 48c0ae9304..61595993f1 100644 --- a/vulkan/scripts/driver_generator.py +++ b/vulkan/scripts/driver_generator.py @@ -49,6 +49,7 @@ _KNOWN_EXTENSIONS = _INTERCEPTED_EXTENSIONS + [ 'VK_KHR_external_semaphore_capabilities', 'VK_KHR_external_fence_capabilities', 'VK_KHR_external_fence_fd', + 'VK_KHR_swapchain_mutable_format', ] # Functions needed at vulkan::driver level. @@ -224,6 +225,9 @@ def gen_h(): #include <optional> #include <vector> +/* + * This file is autogenerated by driver_generator.py. Do not edit directly. + */ namespace vulkan { namespace driver { @@ -503,6 +507,9 @@ def gen_cpp(): namespace vulkan { namespace driver { +/* + * This file is autogenerated by driver_generator.py. Do not edit directly. + */ namespace { // clang-format off\n\n""") diff --git a/vulkan/scripts/generator_common.py b/vulkan/scripts/generator_common.py index 866c1b7b75..6b4cbad2e2 100644 --- a/vulkan/scripts/generator_common.py +++ b/vulkan/scripts/generator_common.py @@ -351,9 +351,50 @@ def parse_vulkan_registry(): 'external', 'vulkan-headers', 'registry', 'vk.xml') tree = element_tree.parse(registry) root = tree.getroot() + + for exts in root.iter('extensions'): + for extension in exts.iter('extension'): + if 'vulkan' not in extension.get('supported').split(','): + # ANDROID_native_buffer is a weird special case -- it's declared in vk.xml as + # disabled but we _do_ want to generate plumbing for it in the Android loader. + if extension.get('name') != 'VK_ANDROID_native_buffer': + print('skip extension disabled or not for vulkan: ' + extension.get('name')) + continue + + apiversion = 'VK_VERSION_1_0' + if extension.tag == 'extension': + extname = extension.get('name') + if (extension.get('type') == 'instance' and + extension.get('promotedto') is not None): + promoted_inst_ext_dict[extname] = \ + version_2_api_version(extension.get('promotedto')) + for req in extension.iter('require'): + if req.get('feature') is not None: + apiversion = req.get('feature') + for commands in req: + if commands.tag == 'command': + cmd_name = commands.get('name') + if cmd_name not in extension_dict: + extension_dict[cmd_name] = extname + version_dict[cmd_name] = apiversion + + for feature in root.iter('feature'): + if 'vulkan' not in feature.get('api').split(','): + continue + + apiversion = feature.get('name') + for req in feature.iter('require'): + for command in req: + if command.tag == 'command': + cmd_name = command.get('name') + version_dict[cmd_name] = apiversion + for commands in root.iter('commands'): for command in commands: if command.tag == 'command': + if command.get('api') == 'vulkansc': + continue + parameter_list = [] protoset = False cmd_name = '' @@ -361,12 +402,18 @@ def parse_vulkan_registry(): if command.get('alias') is not None: alias = command.get('alias') cmd_name = command.get('name') - alias_dict[cmd_name] = alias - command_list.append(cmd_name) - param_dict[cmd_name] = param_dict[alias].copy() - return_type_dict[cmd_name] = return_type_dict[alias] + # At this stage all valid commands have been added to the version + # dict so we can use it to filter valid commands + if cmd_name in version_dict: + alias_dict[cmd_name] = alias + command_list.append(cmd_name) + param_dict[cmd_name] = param_dict[alias].copy() + return_type_dict[cmd_name] = return_type_dict[alias] for params in command: if params.tag == 'param': + if params.get('api') == 'vulkansc': + # skip SC-only param variant + continue param_type = '' if params.text is not None and params.text.strip(): param_type = params.text.strip() + ' ' @@ -387,39 +434,13 @@ def parse_vulkan_registry(): cmd_type = c.text if c.tag == 'name': cmd_name = c.text - protoset = True - command_list.append(cmd_name) - return_type_dict[cmd_name] = cmd_type + if cmd_name in version_dict: + protoset = True + command_list.append(cmd_name) + return_type_dict[cmd_name] = cmd_type if protoset: param_dict[cmd_name] = parameter_list.copy() - for exts in root.iter('extensions'): - for extension in exts: - apiversion = 'VK_VERSION_1_0' - if extension.tag == 'extension': - extname = extension.get('name') - if (extension.get('type') == 'instance' and - extension.get('promotedto') is not None): - promoted_inst_ext_dict[extname] = \ - version_2_api_version(extension.get('promotedto')) - for req in extension: - if req.get('feature') is not None: - apiversion = req.get('feature') - for commands in req: - if commands.tag == 'command': - cmd_name = commands.get('name') - if cmd_name not in extension_dict: - extension_dict[cmd_name] = extname - version_dict[cmd_name] = apiversion - - for feature in root.iter('feature'): - apiversion = feature.get('name') - for req in feature: - for command in req: - if command.tag == 'command': - cmd_name = command.get('name') - if cmd_name in command_list: - version_dict[cmd_name] = apiversion version_code_set = set() for version in version_dict.values(): diff --git a/vulkan/scripts/null_generator.py b/vulkan/scripts/null_generator.py index e9faef663b..5c5bea316b 100644 --- a/vulkan/scripts/null_generator.py +++ b/vulkan/scripts/null_generator.py @@ -55,6 +55,9 @@ def gen_h(): #include <vulkan/vk_android_native_buffer.h> #include <vulkan/vulkan.h> +/* + * This file is autogenerated by null_generator.py. Do not edit directly. + */ namespace null_driver { PFN_vkVoidFunction GetGlobalProcAddr(const char* name); @@ -89,12 +92,17 @@ def gen_cpp(): f.write(gencom.copyright_and_warning(2015)) f.write("""\ +#include <android/hardware_buffer.h> + #include <algorithm> #include "null_driver_gen.h" using namespace null_driver; +/* + * This file is autogenerated by null_generator.py. Do not edit directly. + */ namespace { struct NameProc { |