diff options
476 files changed, 17692 insertions, 11095 deletions
diff --git a/Android.bp b/Android.bp index 2520a71968..72311f0263 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 6576ffdc54..220fef6f8d 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -1559,6 +1559,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 87366958dc..3f32a5abb3 100644 --- a/include/android/performance_hint.h +++ b/include/android/performance_hint.h @@ -85,7 +85,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/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..1a3cb6a884 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 { @@ -890,9 +900,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..65c2914b3c 100644 --- a/include/input/InputConsumerNoResampling.h +++ b/include/input/InputConsumerNoResampling.h @@ -16,15 +16,16 @@ #pragma once +#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: @@ -47,13 +48,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 +65,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(); /** @@ -99,6 +110,7 @@ private: std::shared_ptr<InputChannel> mChannel; sp<Looper> mLooper; InputConsumerCallbacks& mCallbacks; + std::unique_ptr<Resampler> mResampler; // Looper-related infrastructure /** @@ -182,6 +194,16 @@ private: */ std::map<DeviceId, std::queue<InputMessage>> mBatches; /** + * Creates a MotionEvent by consuming samples from the provided queue. If one message has + * eventTime > frameTime, 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 requested frameTime will be consumed. + * @param frameTime 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 frameTime, std::queue<InputMessage>& messages); + /** * 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/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..7d11f76c85 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -275,7 +275,7 @@ 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); + android::base::Result<InputMessage> receiveMessage(); /* Tells whether there is a message in the channel available to be received. * @@ -363,7 +363,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/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..2892137ae7 --- /dev/null +++ b/include/input/Resampler.h @@ -0,0 +1,146 @@ +/** + * 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 resampleTime. The provided resampleTime must be greater than + * the latest sample time of motionEvent. It is not guaranteed that resampling occurs at + * resampleTime. 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 resampleTime, + MotionEvent& motionEvent, + const InputMessage* futureSample) = 0; +}; + +class LegacyResampler final : public Resampler { +public: + /** + * Tries to resample `motionEvent` at `resampleTime` 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 resampleTime, MotionEvent& motionEvent, + const InputMessage* futureSample) 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/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 739c3c2a41..f8e3fd0a5c 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -20,6 +20,7 @@ #define ATRACE_TAG ATRACE_TAG_GRAPHICS //#define LOG_NDEBUG 0 +#include <com_android_graphics_libgui_flags.h> #include <cutils/atomic.h> #include <gui/BLASTBufferQueue.h> #include <gui/BufferItemConsumer.h> @@ -39,7 +40,6 @@ #include <private/gui/ComposerServiceAIDL.h> #include <android-base/thread_annotations.h> -#include <chrono> #include <com_android_graphics_libgui_flags.h> @@ -175,16 +175,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); @@ -236,6 +241,11 @@ BLASTBufferQueue::~BLASTBufferQueue() { } } +void BLASTBufferQueue::onFirstRef() { + // safe default, most producers are expected to override this + mProducer->setMaxDequeuedBufferCount(2); +} + 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"); @@ -296,14 +306,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 +344,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*/, @@ -403,8 +408,6 @@ void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence BQA_LOGE("No matching SurfaceControls found: mSurfaceControlsWithPendingCallback was " "empty."); } - - decStrong((void*)transactionCallbackThunk); } } @@ -412,15 +415,17 @@ void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence // 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()); - } +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::flushShadowQueue() { @@ -499,7 +504,13 @@ void BLASTBufferQueue::releaseBuffer(const ReleaseCallbackId& callbackId, callbackId.to_string().c_str()); return; } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) + if (!it->second.disconnectedAfterAcquired) { + mNumAcquired--; + } +#else mNumAcquired--; +#endif BBQ_TRACE("frame=%" PRIu64, callbackId.framenumber); BQA_LOGV("released %s", callbackId.to_string().c_str()); mBufferItemConsumer->releaseBuffer(it->second, releaseFence); @@ -550,7 +561,7 @@ status_t BLASTBufferQueue::acquireNextBufferLocked( applyTransaction = false; } - BufferItem bufferItem; + BLASTBufferItem bufferItem; status_t status = mBufferItemConsumer->acquireBuffer(&bufferItem, 0 /* expectedPresent */, false); @@ -594,9 +605,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 +617,7 @@ 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); + auto releaseBufferCallback = makeReleaseBufferCallbackThunk(); sp<Fence> fence = bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE; nsecs_t dequeueTime = -1; @@ -629,7 +635,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); @@ -761,6 +767,9 @@ void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) { } // add to shadow queue +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) + mNumDequeued--; +#endif mNumFrameAvailable++; if (waitForTransactionCallback && mNumFrameAvailable >= 2) { acquireAndReleaseBuffer(); @@ -786,9 +795,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; @@ -815,9 +824,18 @@ void BLASTBufferQueue::onFrameDequeued(const uint64_t bufferId) { }; void BLASTBufferQueue::onFrameCancelled(const uint64_t bufferId) { - std::lock_guard _lock{mTimestampMutex}; - mDequeueTimestamps.erase(bufferId); -}; + { + std::lock_guard _lock{mTimestampMutex}; + mDequeueTimestamps.erase(bufferId); + } + +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) + { + std::lock_guard lock{mMutex}; + mNumDequeued--; + } +#endif +} bool BLASTBufferQueue::syncNextTransaction( std::function<void(SurfaceComposerClient::Transaction*)> callback, @@ -1102,9 +1120,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 { @@ -1116,30 +1134,152 @@ public: producerControlledByApp, output); } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) + status_t disconnect(int api, DisconnectMode mode) override { + sp<BLASTBufferQueue> bbq = mBLASTBufferQueue.promote(); + if (!bbq) { + return BufferQueueProducer::disconnect(api, mode); + } + + std::lock_guard lock{bbq->mMutex}; + if (status_t status = BufferQueueProducer::disconnect(api, mode); status != OK) { + return status; + } + + // We need to reset dequeued and acquired counts because BufferQueueProducer::disconnect + // calls BufferQueueCore::freeAllBuffersLocked which frees all dequeued and acquired + // buffers. We don't reset mNumFrameAvailable because these buffers are still available + // in BufferItemConsumer. + bbq->mNumDequeued = 0; + bbq->mNumAcquired = 0; + // SurfaceFlinger sends release callbacks for buffers that have been acquired after a + // disconnect. We set disconnectedAfterAcquired to true so that we can ignore any stale + // releases that come in after the producer is disconnected. Otherwise, releaseBuffer will + // decrement mNumAcquired for a buffer that was acquired before we reset mNumAcquired to + // zero. + for (auto& [releaseId, bufferItem] : bbq->mSubmitted) { + bufferItem.disconnectedAfterAcquired = true; + } + + return OK; + } + + status_t setAsyncMode(bool asyncMode) override { + if (status_t status = BufferQueueProducer::setAsyncMode(asyncMode); status != OK) { + return status; + } + + sp<BLASTBufferQueue> bbq = mBLASTBufferQueue.promote(); + if (!bbq) { + return OK; + } + + { + std::lock_guard lock{bbq->mMutex}; + bbq->mAsyncMode = asyncMode; + } + + return OK; + } + + status_t setSharedBufferMode(bool sharedBufferMode) override { + if (status_t status = BufferQueueProducer::setSharedBufferMode(sharedBufferMode); + status != OK) { + return status; + } + + sp<BLASTBufferQueue> bbq = mBLASTBufferQueue.promote(); + if (!bbq) { + return OK; + } + + { + std::lock_guard lock{bbq->mMutex}; + bbq->mSharedBufferMode = sharedBufferMode; + } + + return OK; + } + + status_t detachBuffer(int slot) override { + if (status_t status = BufferQueueProducer::detachBuffer(slot); status != OK) { + return status; + } + + sp<BLASTBufferQueue> bbq = mBLASTBufferQueue.promote(); + if (!bbq) { + return OK; + } + + { + std::lock_guard lock{bbq->mMutex}; + bbq->mNumDequeued--; + } + + return OK; + } + + status_t dequeueBuffer(int* outSlot, sp<Fence>* outFence, uint32_t width, uint32_t height, + PixelFormat format, uint64_t usage, uint64_t* outBufferAge, + FrameEventHistoryDelta* outTimestamps) override { + sp<BLASTBufferQueue> bbq = mBLASTBufferQueue.promote(); + if (!bbq) { + return BufferQueueProducer::dequeueBuffer(outSlot, outFence, width, height, format, + usage, outBufferAge, outTimestamps); + } + + { + std::lock_guard lock{bbq->mMutex}; + bbq->mNumDequeued++; + } + + status_t status = + BufferQueueProducer::dequeueBuffer(outSlot, outFence, width, height, format, usage, + outBufferAge, outTimestamps); + if (status < 0) { + std::lock_guard lock{bbq->mMutex}; + bbq->mNumDequeued--; + } + return status; + } +#endif + // 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; + +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) + { + std::lock_guard lock{bbq->mMutex}; + bbq->mMaxDequeuedBuffers = maxDequeuedBufferCount; + } +#endif + + 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); } 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/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/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/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..2bbed2b9d6 100644 --- a/libs/gui/aidl/android/gui/CaptureArgs.aidl +++ b/libs/gui/aidl/android/gui/CaptureArgs.aidl @@ -16,4 +16,58 @@ 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; +} + 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/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..729d46a387 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(); @@ -131,6 +145,8 @@ public: virtual ~BLASTBufferQueue(); + void onFirstRef() override; + private: friend class BLASTBufferQueueHelper; friend class BBQBufferQueueProducer; @@ -170,8 +186,16 @@ private: // BufferQueue internally allows 1 more than // the max to be acquired - int32_t mMaxAcquiredBuffers = 1; + int32_t mMaxAcquiredBuffers GUARDED_BY(mMutex) = 1; +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) + int32_t mMaxDequeuedBuffers GUARDED_BY(mMutex) = 1; + static constexpr int32_t kMaxBufferCount = BufferQueueDefs::NUM_BUFFER_SLOTS; + + bool mAsyncMode GUARDED_BY(mMutex) = false; + bool mSharedBufferMode GUARDED_BY(mMutex) = false; + int32_t mNumDequeued GUARDED_BY(mMutex) = 0; +#endif int32_t mNumFrameAvailable GUARDED_BY(mMutex) = 0; int32_t mNumAcquired GUARDED_BY(mMutex) = 0; @@ -180,9 +204,16 @@ private: // latch stale buffers and that we don't wait on barriers from an old producer. uint32_t mProducerId = 0; + class BLASTBufferItem : public BufferItem { + public: + // True if BBQBufferQueueProducer is disconnected after the buffer is acquried but + // before it is released. + bool disconnectedAfterAcquired{false}; + }; + // Keep a reference to the submitted buffers so we can release when surfaceflinger drops the // buffer or the buffer has been presented and a new buffer is ready to be presented. - std::unordered_map<ReleaseCallbackId, BufferItem, ReleaseBufferCallbackIdHash> mSubmitted + std::unordered_map<ReleaseCallbackId, BLASTBufferItem, ReleaseBufferCallbackIdHash> mSubmitted GUARDED_BY(mMutex); // Keep a queue of the released buffers instead of immediately releasing 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/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 8fca9460aa..3aac457a09 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/Surface.h b/libs/gui/include/gui/Surface.h index bdcaaf2866..e74f9ad1dc 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> @@ -33,8 +34,12 @@ #include <shared_mutex> #include <unordered_set> +#include <com_android_graphics_libgui_flags.h> + namespace android { +class GraphicBuffer; + namespace gui { class ISurfaceComposer; } // namespace gui @@ -56,8 +61,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 +192,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 +213,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 +372,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 +386,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 +406,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 +429,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 +458,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 +484,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 +547,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..7468401b7a 100644 --- a/libs/gui/libgui_flags.aconfig +++ b/libs/gui/libgui_flags.aconfig @@ -43,3 +43,59 @@ 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
\ No newline at end of file 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..eb2a61d151 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> @@ -229,7 +229,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(); } @@ -1269,6 +1270,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 +1342,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 6880678050..845a1caf21 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 { @@ -42,16 +44,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, kFormat, kMaxLockedBuffers, true); + mBIC = new BufferItemConsumer(kFormat, 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 +83,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 +139,7 @@ class BufferItemConsumerTest : public ::testing::Test { std::mutex mMutex; int mFreedBufferCount{0}; + std::vector<int> mDetachedBufferSlots = {}; sp<BufferItemConsumer> mBIC; sp<BufferFreedListener> mBFL; @@ -203,4 +223,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/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/include/gui/LayerCaptureArgs.h b/libs/gui/tests/testserver/TestServer.h index fae2bcc787..4226f1bb09 100644 --- a/libs/gui/include/gui/LayerCaptureArgs.h +++ b/libs/gui/tests/testserver/TestServer.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 The Android Open Source Project + * 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. @@ -16,19 +16,16 @@ #pragma once -#include <stdint.h> -#include <sys/types.h> +namespace android { -#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; -}; +/* + * 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::gui +} // 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..99ffa683dd 100644 --- a/libs/input/InputConsumerNoResampling.cpp +++ b/libs/input/InputConsumerNoResampling.cpp @@ -17,6 +17,8 @@ #define LOG_TAG "InputTransport" #define ATRACE_TAG ATRACE_TAG_INPUT +#include <chrono> + #include <inttypes.h> #include <android-base/logging.h> @@ -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,6 +170,10 @@ 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; @@ -177,8 +183,13 @@ using android::base::StringPrintf; 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, @@ -362,36 +373,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,6 +456,36 @@ void InputConsumerNoResampling::handleMessage(const InputMessage& msg) const { } } +std::pair<std::unique_ptr<MotionEvent>, std::optional<uint32_t>> +InputConsumerNoResampling::createBatchedMotionEvent(const nsecs_t frameTime, + std::queue<InputMessage>& messages) { + std::unique_ptr<MotionEvent> motionEvent; + std::optional<uint32_t> firstSeqForBatch; + while (!messages.empty() && !(messages.front().body.motion.eventTime > frameTime)) { + 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(static_cast<std::chrono::nanoseconds>(frameTime), + *motionEvent, futureSample); + } + return std::make_pair(std::move(motionEvent), firstSeqForBatch); +} + bool InputConsumerNoResampling::consumeBatchedInputEvents( std::optional<nsecs_t> requestedFrameTime) { ensureCalledOnLooperThread(__func__); @@ -452,28 +493,8 @@ bool InputConsumerNoResampling::consumeBatchedInputEvents( // infinite frameTime. const nsecs_t frameTime = 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& [_, messages] : mBatches) { + auto [motion, firstSeqForBatch] = createBatchedMotionEvent(frameTime, messages); if (motion != nullptr) { LOG_ALWAYS_FATAL_IF(!firstSeqForBatch.has_value()); mCallbacks.onMotionEvent(std::move(motion), *firstSeqForBatch); 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/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..c663649091 --- /dev/null +++ b/libs/input/Resampler.cpp @@ -0,0 +1,260 @@ +/** + * 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()); +} + +void LegacyResampler::resampleMotionEvent(nanoseconds resampleTime, MotionEvent& motionEvent, + const InputMessage* futureSample) { + if (mPreviousDeviceId && *mPreviousDeviceId != motionEvent.getDeviceId()) { + mLatestSamples.clear(); + } + mPreviousDeviceId = motionEvent.getDeviceId(); + + 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..ab117b875e 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,17 @@ 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" +} 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 5010475334..008f675485 100644 --- a/libs/input/rust/lib.rs +++ b/libs/input/rust/lib.rs @@ -16,10 +16,13 @@ //! 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, }; @@ -148,7 +151,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 e9d799ed3f..132866bd99 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -23,6 +23,7 @@ cc_test { "InputVerifier_test.cpp", "MotionPredictor_test.cpp", "MotionPredictorMetricsManager_test.cpp", + "Resampler_test.cpp", "RingBuffer_test.cpp", "TfLiteMotionPredictor_test.cpp", "TouchResampling_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/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..467c3b46ce 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); @@ -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..7ae9a28664 --- /dev/null +++ b/libs/input/tests/Resampler_test.cpp @@ -0,0 +1,854 @@ +/** + * 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 + +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(11ms, 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(11ms, 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); +} + +// Increments of 16 ms for display refresh rate +// Increments of 6 ms for input frequency +// Resampling latency is known to be 5 ms +// Therefore, first resampling time will be 11 ms + +/** + * Timeline + * ----+----------------------+---------+---------+---------+---------- + * 0ms 10ms 11ms 15ms 16ms + * DOWN MOVE | MSG | + * resample frame + * Resampling occurs at 11ms. It is possible to interpolate because there is a sample available + * after the resample time. It is assumed that the InputMessage frequency is 100Hz, and the frame + * frequency is 60Hz. This means the time between InputMessage samples is 10ms, and the time between + * frames is ~16ms. Resample time is frameTime - RESAMPLE_LATENCY. The resampled sample must be the + * last one in the batch to consume. + */ +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(11ms, 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(11ms, secondMotionEvent, nullptr); + + assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, secondMotionEvent, + {Pointer{.id = 0, + .x = 2.2f, + .y = 4.4f, + .isResampled = true}}); + // Integrity of the whole motionEvent + // History size should increment by 1 + // Check if the resampled value is the last one + // Check if the resampleTime is correct + // Check if the PointerCoords are consistent with the other computations +} + +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(11ms, 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(11ms, 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(11ms, 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(27ms, 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(43ms, 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(11ms, 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(11ms, 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(11ms, 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(11ms, 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(11ms, 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(27ms, 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(11ms, 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(11ms, 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(11ms, 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(11ms, 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(11ms, 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(11ms, 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(11ms, 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(11ms, 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(11ms, 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(11ms, 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(11ms, motionEvent, /*futureSample=*/nullptr); + + assertMotionEventIsNotResampled(originalMotionEvent, motionEvent); +} +} // namespace android 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..7639fab3a5 100644 --- a/libs/renderengine/Android.bp +++ b/libs/renderengine/Android.bp @@ -101,10 +101,12 @@ filegroup { "skia/debug/SkiaMemoryReporter.cpp", "skia/filters/BlurFilter.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/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/RenderEngineBench.cpp b/libs/renderengine/benchmark/RenderEngineBench.cpp index 05a2063423..326d1cec1b 100644 --- a/libs/renderengine/benchmark/RenderEngineBench.cpp +++ b/libs/renderengine/benchmark/RenderEngineBench.cpp @@ -64,14 +64,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) @@ -180,7 +181,8 @@ template <class... Args> void BM_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))); + static_cast<RenderEngine::GraphicsApi>(std::get<1>(args_tuple)), + static_cast<RenderEngine::BlurAlgorithm>(std::get<2>(args_tuple))); // Initially use cpu access so we can decode into it with AImageDecoder. auto [width, height] = getDisplaySize(); @@ -224,5 +226,11 @@ void BM_blur(benchmark::State& benchState, Args&&... args) { benchDrawLayers(*re, layers, benchState, "blurred"); } -BENCHMARK_CAPTURE(BM_blur, SkiaGLThreaded, RenderEngine::Threaded::YES, - RenderEngine::GraphicsApi::GL); +BENCHMARK_CAPTURE(BM_blur, gaussian, RenderEngine::Threaded::YES, RenderEngine::GraphicsApi::GL, + RenderEngine::BlurAlgorithm::GAUSSIAN); + +BENCHMARK_CAPTURE(BM_blur, kawase, RenderEngine::Threaded::YES, RenderEngine::GraphicsApi::GL, + RenderEngine::BlurAlgorithm::KAWASE); + +BENCHMARK_CAPTURE(BM_blur, kawase_dual_filter, RenderEngine::Threaded::YES, + RenderEngine::GraphicsApi::GL, RenderEngine::BlurAlgorithm::KAWASE_DUAL_FILTER); 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..9bc2c48a8a 100644 --- a/libs/renderengine/include/renderengine/RenderEngine.h +++ b/libs/renderengine/include/renderengine/RenderEngine.h @@ -131,6 +131,7 @@ public: NONE, GAUSSIAN, KAWASE, + KAWASE_DUAL_FILTER, }; static std::unique_ptr<RenderEngine> create(const RenderEngineCreationArgs& args); 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/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..af24600ade 100644 --- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp @@ -28,12 +28,11 @@ #include <GrContextOptions.h> #include <GrTypes.h> #include <android-base/stringprintf.h> +#include <common/trace.h> #include <gl/GrGLInterface.h> #include <include/gpu/ganesh/gl/GrGLDirectContext.h> -#include <gui/TraceUtils.h> #include <sync/sync.h> #include <ui/DebugUtils.h> -#include <utils/Trace.h> #include <cmath> #include <cstdint> @@ -332,7 +331,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 +340,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 +399,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/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp index e62640eb85..d58f303e70 100644 --- a/libs/renderengine/skia/SkiaRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaRenderEngine.cpp @@ -54,8 +54,8 @@ #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/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> @@ -77,6 +76,7 @@ #include "compat/SkiaGpuContext.h" #include "filters/BlurFilter.h" #include "filters/GaussianBlurFilter.h" +#include "filters/KawaseBlurDualFilter.h" #include "filters/KawaseBlurFilter.h" #include "filters/LinearEffect.h" #include "filters/MouriMap.h" @@ -261,7 +261,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 +286,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 +421,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 +446,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 +503,7 @@ bool SkiaRenderEngine::canSkipPostRenderCleanup() const { } void SkiaRenderEngine::cleanupPostRender() { - ATRACE_CALL(); + SFTRACE_CALL(); std::lock_guard<std::mutex> lock(mRenderingMutex); mTextureCleanupMgr.cleanup(); } @@ -507,16 +512,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 +538,26 @@ 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); + + if (usingLocalTonemap) { + static MouriMap kMapper; + const float ratio = + hdrType == HdrRenderType::GENERIC_HDR ? 1.0f : parameters.layerDimmingRatio; + shader = kMapper.mouriMap(getActiveContext(), shader, ratio); } + // 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 +573,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 +701,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 +793,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 +887,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 +900,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 +983,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 +1050,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 +1116,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 +1131,8 @@ void SkiaRenderEngine::drawLayersInternal( .requiresLinearEffect = requiresLinearEffect, .layerDimmingRatio = layerDimmingRatio, .outputDataSpace = display.outputDataspace, - .fakeOutputDataspace = fakeDataspace})); + .fakeOutputDataspace = fakeDataspace, + .imageBounds = SkRect::MakeEmpty()})); } if (layer.disableBlending) { @@ -1152,7 +1173,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 @@ -1167,7 +1188,7 @@ void SkiaRenderEngine::drawLayersInternal( LOG_ALWAYS_FATAL_IF(activeSurface != dstSurface); auto drawFence = sp<Fence>::make(flushAndSubmit(context, dstSurface)); - if (ATRACE_ENABLED()) { + if (SFTRACE_ENABLED()) { static gui::FenceMonitor sMonitor("RE Completion"); sMonitor.queueFence(drawFence); } @@ -1185,7 +1206,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..224a1cad92 100644 --- a/libs/renderengine/skia/SkiaRenderEngine.h +++ b/libs/renderengine/skia/SkiaRenderEngine.h @@ -38,6 +38,7 @@ #include "compat/SkiaGpuContext.h" #include "debug/SkiaCapture.h" #include "filters/BlurFilter.h" +#include "filters/EdgeExtensionShaderFactory.h" #include "filters/LinearEffect.h" #include "filters/StretchShaderFactory.h" @@ -156,6 +157,7 @@ private: float layerDimmingRatio; const ui::Dataspace outputDataSpace; const ui::Dataspace fakeOutputDataspace; + const SkRect& imageBounds; }; sk_sp<SkShader> createRuntimeEffectShader(const RuntimeEffectShaderParameters&); @@ -175,6 +177,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..d89e818cdc 100644 --- a/libs/renderengine/skia/SkiaVkRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaVkRenderEngine.cpp @@ -29,13 +29,11 @@ #include <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 <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..3fbc6ca22e 100644 --- a/libs/renderengine/skia/compat/GaneshBackendTexture.cpp +++ b/libs/renderengine/skia/compat/GaneshBackendTexture.cpp @@ -32,15 +32,15 @@ #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/GaneshGpuContext.cpp b/libs/renderengine/skia/compat/GaneshGpuContext.cpp index b2eae009ed..b121fe83eb 100644 --- a/libs/renderengine/skia/compat/GaneshGpuContext.cpp +++ b/libs/renderengine/skia/compat/GaneshGpuContext.cpp @@ -25,7 +25,7 @@ #include <include/gpu/ganesh/gl/GrGLDirectContext.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/SkiaGpuContext.h b/libs/renderengine/skia/compat/SkiaGpuContext.h index 282dfe7abe..9fa6fb8521 100644 --- a/libs/renderengine/skia/compat/SkiaGpuContext.h +++ b/libs/renderengine/skia/compat/SkiaGpuContext.h @@ -23,7 +23,6 @@ #include <include/gpu/GrDirectContext.h> #include <include/gpu/gl/GrGLInterface.h> #include <include/gpu/graphite/Context.h> -#include <include/gpu/vk/GrVkBackendContext.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/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..b45893919c 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); @@ -84,13 +84,13 @@ const SkString kTonemap(R"( float3 linear = toLinearSrgb(rgba.rgb) * hdrSdrRatio; if (localMax <= 1.0) { - return float4(fromLinearSrgb(linear), 1.0); + 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); + return float4(fromLinearSrgb(linear * gain), rgba.a); } )"); 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..f5a90fdcfd 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; @@ -252,7 +252,7 @@ void RenderEngineThreaded::drawLayersInternal( 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,7 +261,7 @@ ftl::Future<FenceResult> RenderEngineThreaded::drawLayers( mNeedsPostRenderCleanup = true; mFunctionCalls.push( [resultPromise, display, layers, buffer, fd](renderengine::RenderEngine& instance) { - ATRACE_NAME("REThreaded::drawLayers"); + SFTRACE_NAME("REThreaded::drawLayers"); instance.updateProtectedContext(layers, buffer); instance.drawLayersInternal(std::move(resultPromise), display, layers, buffer, base::unique_fd(fd)); @@ -277,7 +277,7 @@ int RenderEngineThreaded::getContextPriority() { { 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 +297,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 +324,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/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/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/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/vibrator/Android.bp b/libs/vibrator/Android.bp index 2af51a722c..d3b3a734f9 100644 --- a/libs/vibrator/Android.bp +++ b/libs/vibrator/Android.bp @@ -24,6 +24,10 @@ package { cc_defaults { name: "libvibrator_defaults", + defaults: [ + "aconfig_lib_cc_shared_link.defaults", + ], + cflags: [ "-Wall", "-Werror", @@ -50,9 +54,11 @@ cc_library { "libbinder", "liblog", "libutils", + "server_configurable_flags", ], whole_static_libs: [ + "android.os.vibrator.flags-aconfig-cc", "libvibratorutils", ], @@ -79,8 +85,14 @@ cc_library { vendor_available: true, double_loadable: true, + static_libs: [ + "android.os.vibrator.flags-aconfig-cc", + ], + shared_libs: [ + "liblog", "libutils", + "server_configurable_flags", ], srcs: [ @@ -89,6 +101,7 @@ cc_library { visibility: [ "//frameworks/native/libs/vibrator", + "//frameworks/native/libs/vibrator/tests", "//frameworks/av/media/libeffects/hapticgenerator", ], } 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 761ac1bf3b..ca13afcbaf 100644 --- a/libs/vibrator/ExternalVibrationUtils.cpp +++ b/libs/vibrator/ExternalVibrationUtils.cpp @@ -13,10 +13,16 @@ * 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> + +#include <algorithm> #include <math.h> +#include <log/log.h> #include <vibrator/ExternalVibrationUtils.h> namespace android::os { @@ -25,8 +31,10 @@ namespace { static constexpr float HAPTIC_SCALE_VERY_LOW_RATIO = 2.0f / 3.0f; static constexpr float HAPTIC_SCALE_LOW_RATIO = 3.0f / 4.0f; static constexpr float HAPTIC_MAX_AMPLITUDE_FLOAT = 1.0f; +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 getHapticScaleGamma(HapticLevel level) { +float getOldHapticScaleGamma(HapticLevel level) { switch (level) { case HapticLevel::VERY_LOW: return 2.0f; @@ -41,7 +49,7 @@ float getHapticScaleGamma(HapticLevel level) { } } -float getHapticMaxAmplitudeRatio(HapticLevel level) { +float getOldHapticMaxAmplitudeRatio(HapticLevel level) { switch (level) { case HapticLevel::VERY_LOW: return HAPTIC_SCALE_VERY_LOW_RATIO; @@ -56,6 +64,85 @@ float getHapticMaxAmplitudeRatio(HapticLevel 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: + return 0.8f; + case HapticLevel::HIGH: + return 1.2f; + case HapticLevel::VERY_HIGH: + return 1.4f; + default: + return 1.0f; + } +} + +float applyOldHapticScale(float value, float gamma, float maxAmplitudeRatio) { + float sign = value >= 0 ? 1.0 : -1.0; + return powf(fabsf(value / HAPTIC_MAX_AMPLITUDE_FLOAT), gamma) + * maxAmplitudeRatio * HAPTIC_MAX_AMPLITUDE_FLOAT * sign; +} + +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. + // Scale up requires a different curve to ensure the intensity will not become > 1. + return value * scale; + } + + float sign = value >= 0 ? 1.0f : -1.0f; + float extraScale = powf(scaleFactor, 4.0f - scaleFactor); + float x = fabsf(value) * scale * extraScale; + float maxX = scale * extraScale; // scaled x for intensity == 1 + + float expX = expf(x); + float expMaxX = expf(maxX); + + // Using f = tanh as the scale up function so the max value will converge. + // a = 1/f(maxX), used to scale f so that a*f(maxX) = 1 (the value will converge to 1). + float a = (expMaxX + 1.0f) / (expMaxX - 1.0f); + float fx = (expX - 1.0f) / (expX + 1.0f); + + return sign * std::clamp(a * fx, 0.0f, 1.0f); +} + void applyHapticScale(float* buffer, size_t length, HapticScale scale) { if (scale.isScaleMute()) { memset(buffer, 0, length * sizeof(float)); @@ -65,18 +152,22 @@ void applyHapticScale(float* buffer, size_t length, HapticScale scale) { return; } HapticLevel hapticLevel = scale.getLevel(); + float scaleFactor = getHapticScaleFactor(scale); float adaptiveScaleFactor = scale.getAdaptiveScaleFactor(); - float gamma = getHapticScaleGamma(hapticLevel); - float maxAmplitudeRatio = getHapticMaxAmplitudeRatio(hapticLevel); + float oldGamma = getOldHapticScaleGamma(hapticLevel); + float oldMaxAmplitudeRatio = getOldHapticMaxAmplitudeRatio(hapticLevel); for (size_t i = 0; i < length; i++) { if (hapticLevel != HapticLevel::NONE) { - float sign = buffer[i] >= 0 ? 1.0 : -1.0; - buffer[i] = powf(fabsf(buffer[i] / HAPTIC_MAX_AMPLITUDE_FLOAT), gamma) - * maxAmplitudeRatio * HAPTIC_MAX_AMPLITUDE_FLOAT * sign; + 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/TEST_MAPPING b/libs/vibrator/TEST_MAPPING new file mode 100644 index 0000000000..d782b43b57 --- /dev/null +++ b/libs/vibrator/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "postsubmit": [ + { + "name": "libvibrator_test" + } + ] +} 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/Android.bp b/libs/vibrator/tests/Android.bp new file mode 100644 index 0000000000..2921a6275b --- /dev/null +++ b/libs/vibrator/tests/Android.bp @@ -0,0 +1,53 @@ +// 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. +package { + default_team: "trendy_team_haptics_framework", + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + +cc_test { + name: "libvibrator_test", + test_suites: ["general-tests"], + defaults: [ + "aconfig_lib_cc_shared_link.defaults", + ], + srcs: [ + "ExternalVibrationTest.cpp", + "ExternalVibrationUtilsTest.cpp", + ], + cflags: [ + "-Wall", + "-Werror", + "-Wextra", + ], + static_libs: [ + "android.os.vibrator.flags-aconfig-cc", + "libflagtest", + "libgtest", + "liblog", + "libvibrator", + "libvibratorutils", + ], + shared_libs: [ + "libbase", + "libbinder", + "libutils", + "server_configurable_flags", + ], +} diff --git a/libs/vibrator/tests/ExternalVibrationTest.cpp b/libs/vibrator/tests/ExternalVibrationTest.cpp new file mode 100644 index 0000000000..4133836785 --- /dev/null +++ b/libs/vibrator/tests/ExternalVibrationTest.cpp @@ -0,0 +1,121 @@ +/* + * 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 <binder/Parcel.h> +#include <gtest/gtest.h> +#include <vibrator/ExternalVibration.h> + +using namespace android; +using namespace testing; + +using HapticLevel = os::HapticLevel; +using ScaleLevel = os::ExternalVibrationScale::ScaleLevel; + +class TestVibrationController : public os::IExternalVibrationController { +public: + explicit TestVibrationController() {} + IBinder *onAsBinder() override { return nullptr; } + binder::Status mute(/*out*/ bool *ret) override { + *ret = false; + return binder::Status::ok(); + }; + binder::Status unmute(/*out*/ bool *ret) override { + *ret = false; + return binder::Status::ok(); + }; +}; + +class ExternalVibrationTest : public Test { +protected: + HapticLevel toHapticLevel(ScaleLevel level) { + os::ExternalVibrationScale externalVibrationScale; + externalVibrationScale.scaleLevel = level; + os::HapticScale hapticScale = + os::ExternalVibration::externalVibrationScaleToHapticScale(externalVibrationScale); + return hapticScale.getLevel(); + } +}; + +TEST_F(ExternalVibrationTest, TestReadAndWriteToParcel) { + int32_t uid = 1; + std::string pkg("package.name"); + audio_attributes_t originalAttrs; + originalAttrs.content_type = AUDIO_CONTENT_TYPE_SONIFICATION; + 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); + EXPECT_EQ(original->getAudioAttributes().usage, originalAttrs.usage); + 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); + EXPECT_EQ(parceled->getAudioAttributes().usage, originalAttrs.usage); + EXPECT_EQ(parceled->getAudioAttributes().source, originalAttrs.source); + EXPECT_EQ(parceled->getAudioAttributes().flags, originalAttrs.flags); + // TestVibrationController does not implement onAsBinder, skip controller parcel in this test. +} + +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 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); + EXPECT_EQ(toHapticLevel(ScaleLevel::SCALE_LOW), HapticLevel::LOW); + EXPECT_EQ(toHapticLevel(ScaleLevel::SCALE_NONE), HapticLevel::NONE); + EXPECT_EQ(toHapticLevel(ScaleLevel::SCALE_HIGH), HapticLevel::HIGH); + EXPECT_EQ(toHapticLevel(ScaleLevel::SCALE_VERY_HIGH), HapticLevel::VERY_HIGH); +} diff --git a/libs/vibrator/tests/ExternalVibrationUtilsTest.cpp b/libs/vibrator/tests/ExternalVibrationUtilsTest.cpp new file mode 100644 index 0000000000..9369f80da3 --- /dev/null +++ b/libs/vibrator/tests/ExternalVibrationUtilsTest.cpp @@ -0,0 +1,380 @@ +/* + * 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 <android_os_vibrator.h> +#include <flag_macros.h> +#include <gtest/gtest.h> +#include <vibrator/ExternalVibrationUtils.h> + +#include "test_utils.h" + +#define FLAG_NS android::os::vibrator + +using namespace android; +using namespace testing; + +using HapticScale = os::HapticScale; +using HapticLevel = os::HapticLevel; + +static constexpr float TEST_TOLERANCE = 1e-2f; +static constexpr size_t TEST_BUFFER_LENGTH = 4; +static float TEST_BUFFER[TEST_BUFFER_LENGTH] = { 1, -1, 0.5f, -0.2f }; + +class ExternalVibrationUtilsTest : public Test { +public: + void SetUp() override { + std::copy(std::begin(TEST_BUFFER), std::end(TEST_BUFFER), std::begin(mBuffer)); + } + +protected: + void scaleBuffer(HapticLevel hapticLevel) { + scaleBuffer(HapticScale(hapticLevel)); + } + + void scaleBuffer(HapticLevel hapticLevel, float adaptiveScaleFactor) { + scaleBuffer(hapticLevel, adaptiveScaleFactor, 0 /* limit */); + } + + void scaleBuffer(HapticLevel hapticLevel, float adaptiveScaleFactor, float limit) { + scaleBuffer(HapticScale(hapticLevel, -1 /* scaleFactor */, adaptiveScaleFactor), limit); + } + + void scaleBuffer(HapticScale hapticScale) { + scaleBuffer(hapticScale, 0 /* limit */); + } + + void scaleBuffer(HapticScale hapticScale, float limit) { + std::copy(std::begin(TEST_BUFFER), std::end(TEST_BUFFER), std::begin(mBuffer)); + os::scaleHapticData(&mBuffer[0], TEST_BUFFER_LENGTH, hapticScale, limit); + } + + float mBuffer[TEST_BUFFER_LENGTH]; +}; + +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); + + scaleBuffer(HapticLevel::MUTE); + 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)), + 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); + + scaleBuffer(HapticLevel::MUTE); + EXPECT_FLOATS_NEARLY_EQ(expected, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); +} + +TEST_F_WITH_FLAGS( + 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)); + + scaleBuffer(HapticLevel::NONE); + 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)), + 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)); + + scaleBuffer(HapticLevel::NONE); + EXPECT_FLOATS_NEARLY_EQ(expected, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); +} + +TEST_F_WITH_FLAGS( + 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); + + float expectedHigh[TEST_BUFFER_LENGTH] = { 1, -1, 0.7f, -0.44f }; + scaleBuffer(HapticLevel::HIGH); + EXPECT_FLOATS_NEARLY_EQ(expectedHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); + + float expectedLow[TEST_BUFFER_LENGTH] = { 0.75f, -0.75f, 0.26f, -0.06f }; + scaleBuffer(HapticLevel::LOW); + EXPECT_FLOATS_NEARLY_EQ(expectedLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); + + float expectedVeryLow[TEST_BUFFER_LENGTH] = { 0.66f, -0.66f, 0.16f, -0.02f }; + scaleBuffer(HapticLevel::VERY_LOW); + 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)), + 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); + + float expectedHigh[TEST_BUFFER_LENGTH] = { 1, -1, 0.62f, -0.27f }; + scaleBuffer(HapticLevel::HIGH); + EXPECT_FLOATS_NEARLY_EQ(expectedHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); + + float expectedLow[TEST_BUFFER_LENGTH] = { 0.70f, -0.70f, 0.35f, -0.14f }; + scaleBuffer(HapticLevel::LOW); + EXPECT_FLOATS_NEARLY_EQ(expectedLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); + + float expectedVeryLow[TEST_BUFFER_LENGTH] = { 0.45f, -0.45f, 0.22f, -0.09f }; + scaleBuffer(HapticLevel::VERY_LOW); + EXPECT_FLOATS_NEARLY_EQ(expectedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); +} + +TEST_F_WITH_FLAGS( + 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 */); + 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, 1.06f, -0.67f }; + 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.45f, -0.45f, 0.15f, -0.04f }; + 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.33f, -1.33f, 0.33f, -0.05f }; + scaleBuffer(HapticLevel::VERY_LOW, 2 /* adaptiveScaleFactor */); + EXPECT_FLOATS_NEARLY_EQ(expectedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); +} + +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 */); + 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.93f, -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] = { 0.91f, -0.91f, 0.45f, -0.18f }; + scaleBuffer(HapticLevel::VERY_LOW, 2 /* adaptiveScaleFactor */); + EXPECT_FLOATS_NEARLY_EQ(expectedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); +} + +TEST_F_WITH_FLAGS( + 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 */); + EXPECT_FLOATS_NEARLY_EQ(expectedClippedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); + + // Scaled = { 1, -1, 0.5f, -0.2f }; + float expectedClippedVeryLow[TEST_BUFFER_LENGTH] = { 0.7f, -0.7f, 0.33f, -0.05f }; + 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, 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 */); + EXPECT_FLOATS_NEARLY_EQ(expectedClippedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); + + // Scaled = { 1, -1, 0.5f, -0.2f }; + float expectedClippedVeryLow[TEST_BUFFER_LENGTH] = { 0.7f, -0.7f, 0.45f, -0.18f }; + 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/libs/vibrator/tests/test_utils.h b/libs/vibrator/tests/test_utils.h new file mode 100644 index 0000000000..f491ea1f49 --- /dev/null +++ b/libs/vibrator/tests/test_utils.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. + */ + +#ifndef LIBVIBRATOR_TEST_UTILS_H +#define LIBVIBRATOR_TEST_UTILS_H + +#include <gtest/gtest.h> + +#if !defined(EXPECT_FLOATS_NEARLY_EQ) +#define EXPECT_FLOATS_NEARLY_EQ(expected, actual, length, epsilon) \ + for (size_t i = 0; i < length; i++) { \ + EXPECT_NEAR(expected[i], actual[i], epsilon) << " at Index: " << i; \ + } +#else +#error Macro EXPECT_FLOATS_NEARLY_EQ already defined +#endif + +#endif //LIBVIBRATOR_TEST_UTILS_H 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 a6af713830..6e35041662 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..00161e6f4c 100644 --- a/services/gpuservice/gpuwork/GpuWork.cpp +++ b/services/gpuservice/gpuwork/GpuWork.cpp @@ -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(); diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp index 70801dccb0..cb220abd80 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 { 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..e4d73fc974 100644 --- a/services/inputflinger/InputFilter.cpp +++ b/services/inputflinger/InputFilter.cpp @@ -67,10 +67,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()); 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/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/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 00dd6ba62b..397fedac4c 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); } } @@ -367,7 +364,8 @@ void PointerChoreographer::processTouchscreenAndStylusEventLocked(const NotifyMo const uint8_t actionIndex = MotionEvent::getActionIndex(args.action); std::array<uint32_t, MAX_POINTER_ID + 1> idToIndex; BitSet32 idBits; - if (maskedAction != AMOTION_EVENT_ACTION_UP && maskedAction != AMOTION_EVENT_ACTION_CANCEL) { + if (maskedAction != AMOTION_EVENT_ACTION_UP && maskedAction != AMOTION_EVENT_ACTION_CANCEL && + maskedAction != AMOTION_EVENT_ACTION_HOVER_EXIT) { for (size_t i = 0; i < args.getPointerCount(); i++) { if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP && actionIndex == i) { continue; @@ -513,8 +511,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"; 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..af9d2ebeda 100644 --- a/services/inputflinger/TEST_MAPPING +++ b/services/inputflinger/TEST_MAPPING @@ -149,6 +149,9 @@ }, { "name": "monkey_test" + }, + { + "name": "CtsSurfaceControlTests" } ], "postsubmit": [ @@ -290,6 +293,9 @@ }, { "name": "monkey_test" + }, + { + "name": "CtsInputRootTestCases" } ], "staged-platinum-postsubmit": [ 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/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 f9fbfef3ec..bcef35021e 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -558,7 +558,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 +693,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 +742,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; @@ -901,6 +902,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 --- @@ -1153,14 +1172,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); @@ -1308,7 +1319,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()); @@ -1392,7 +1403,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: @@ -1463,15 +1473,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. @@ -1554,7 +1576,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; @@ -1643,24 +1664,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) { @@ -1923,20 +1926,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); @@ -2041,19 +2045,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) { @@ -2114,19 +2127,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), @@ -2221,7 +2231,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: { @@ -2263,11 +2272,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 = @@ -2279,12 +2286,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. @@ -2304,17 +2311,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); } } @@ -2324,15 +2329,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 @@ -2349,12 +2352,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; } @@ -2378,9 +2379,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; @@ -2390,9 +2390,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. @@ -2432,8 +2429,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. @@ -2454,20 +2450,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. @@ -2490,18 +2491,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) { @@ -2601,8 +2600,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. @@ -2613,8 +2611,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); } @@ -2635,8 +2632,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 @@ -2689,7 +2685,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); } @@ -2716,7 +2712,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); @@ -2739,8 +2737,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); } } @@ -2797,8 +2794,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 @@ -2808,12 +2804,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) { @@ -3621,7 +3614,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()); @@ -3834,6 +3826,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); @@ -3878,7 +3874,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", @@ -4274,7 +4269,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", @@ -4357,7 +4351,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: @@ -4443,28 +4436,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 @@ -4552,13 +4528,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", @@ -4908,6 +4883,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; + } } } @@ -5680,7 +5659,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) { @@ -5692,6 +5671,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) { @@ -6028,17 +6023,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()) { @@ -7032,6 +7022,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 @@ -7107,9 +7104,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); @@ -7136,7 +7135,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); } } diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index e2fc7a0d4f..87dfd1d8a4 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; @@ -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..a3650030ac 100644 --- a/services/inputflinger/dispatcher/InputEventTimeline.cpp +++ b/services/inputflinger/dispatcher/InputEventTimeline.cpp @@ -68,7 +68,7 @@ bool ConnectionTimeline::operator!=(const ConnectionTimeline& rhs) const { InputEventTimeline::InputEventTimeline(bool isDown, nsecs_t eventTime, nsecs_t readTime, uint16_t vendorId, uint16_t productId, - std::set<InputDeviceUsageSource> sources) + const std::set<InputDeviceUsageSource>& sources) : isDown(isDown), eventTime(eventTime), readTime(readTime), diff --git a/services/inputflinger/dispatcher/InputEventTimeline.h b/services/inputflinger/dispatcher/InputEventTimeline.h index e9deb2d3cf..1756944d0e 100644 --- a/services/inputflinger/dispatcher/InputEventTimeline.h +++ b/services/inputflinger/dispatcher/InputEventTimeline.h @@ -76,7 +76,7 @@ private: struct InputEventTimeline { InputEventTimeline(bool isDown, nsecs_t eventTime, nsecs_t readTime, uint16_t vendorId, - uint16_t productId, std::set<InputDeviceUsageSource> sources); + uint16_t productId, const std::set<InputDeviceUsageSource>& sources); const bool isDown; // True if this is an ACTION_DOWN event const nsecs_t eventTime; const nsecs_t readTime; diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp index dfbe02f332..4df23c54d6 100644 --- a/services/inputflinger/dispatcher/InputState.cpp +++ b/services/inputflinger/dispatcher/InputState.cpp @@ -647,7 +647,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/TouchState.h b/services/inputflinger/dispatcher/TouchState.h index 9d4bb3d943..5a70dd5e2c 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); void removeHoveringPointer(DeviceId deviceId, int32_t pointerId); 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..7bec94eea1 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. @@ -227,6 +229,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; @@ -268,6 +273,7 @@ struct InputReaderConfiguration { touchpadNaturalScrollingEnabled(true), touchpadTapToClickEnabled(true), touchpadTapDraggingEnabled(false), + shouldNotifyTouchpadHardwareState(false), touchpadRightClickZoneEnabled(false), stylusButtonMotionEventsEnabled(true), stylusPointerIconEnabled(false) {} @@ -363,6 +369,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 +405,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 +462,10 @@ 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; + /* Gets the keyboard layout for a particular input device. */ virtual std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay( const InputDeviceIdentifier& identifier, 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/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..e11adb8c76 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, @@ -1879,7 +1876,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 +1884,6 @@ std::vector<RawEvent> EventHub::getEvents(int timeoutMillis) { if (mNeedToScanDevices) { mNeedToScanDevices = false; scanDevicesLocked(); - mNeedToSendFinishedDeviceScan = true; } while (!mOpeningDevices.empty()) { @@ -1917,18 +1912,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 +2486,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..70f024ea96 100644 --- a/services/inputflinger/reader/InputDevice.cpp +++ b/services/inputflinger/reader/InputDevice.cpp @@ -725,6 +725,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..a5b12490a3 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); @@ -825,6 +818,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 +912,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..657126a825 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; @@ -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; @@ -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..021978dee7 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; } @@ -141,6 +141,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 +308,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); @@ -380,8 +380,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 +433,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 +441,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..2cc0a00496 100644 --- a/services/inputflinger/reader/include/InputReader.h +++ b/services/inputflinger/reader/include/InputReader.h @@ -104,6 +104,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 +120,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 +203,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 +223,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..dbc28721d1 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, rawEvent.deviceId); + } 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); } @@ -501,4 +499,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..9924d0d491 100644 --- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp +++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp @@ -66,10 +66,11 @@ 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()), + // 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; 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/tests/Android.bp b/services/inputflinger/tests/Android.bp index cf0d46a75d..95283ba6ab 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,5 +119,8 @@ cc_test { test_options: { unit_test: true, }, - test_suites: ["device-tests"], + test_suites: [ + "device-tests", + "device-platinum-tests", + ], } diff --git a/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp b/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp index b738abf6d2..d39ad3fd1e 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), + WithPointerRelativeMotion(1, 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..31fbf209a3 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; @@ -263,18 +260,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 +412,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..3d8dddd532 100644 --- a/services/inputflinger/tests/FakeEventHub.h +++ b/services/inputflinger/tests/FakeEventHub.h @@ -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); @@ -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..d77d539f74 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,6 +25,12 @@ 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) { @@ -58,6 +65,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 +245,13 @@ 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(); +} + std::shared_ptr<KeyCharacterMap> FakeInputReaderPolicy::getKeyboardLayoutOverlay( const InputDeviceIdentifier&, const std::optional<KeyboardLayoutInfo>) { return nullptr; @@ -241,9 +266,11 @@ void FakeInputReaderPolicy::waitForInputDevices(std::function<void(bool)> proces base::ScopedLockAssertion assumeLocked(mLock); const bool devicesChanged = - mDevicesChangedCondition.wait_for(lock, WAIT_TIMEOUT, [this]() REQUIRES(mLock) { - return mInputDevicesChanged; - }); + mDevicesChangedCondition.wait_for(lock, + ADD_INPUT_DEVICE_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..e5ba620555 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,6 +83,8 @@ public: private: void getReaderConfiguration(InputReaderConfiguration* outConfig) override; void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) override; + void notifyTouchpadHardwareState(const SelfContainedHardwareState& schs, + int32_t deviceId) override; std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay( const InputDeviceIdentifier&, const std::optional<KeyboardLayoutInfo>) override; std::string getDeviceAlias(const InputDeviceIdentifier&) override; @@ -101,6 +104,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/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 2056372f2d..c2f174f6b4 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, @@ -4172,6 +4162,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", @@ -4376,6 +4483,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>(); @@ -4734,6 +5037,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) { @@ -5283,6 +5634,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", @@ -5328,6 +5680,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", @@ -5358,6 +5711,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 @@ -5365,6 +5985,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)", @@ -8111,6 +8732,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); 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..4a9e89319d 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 { @@ -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..5a3d79da5e 100644 --- a/services/inputflinger/tests/InterfaceMocks.h +++ b/services/inputflinger/tests/InterfaceMocks.h @@ -92,8 +92,8 @@ 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)); @@ -132,7 +132,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 +188,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 +198,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), ()); 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/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 9a5b6a73f5..18222ddb4b 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(); }, @@ -830,15 +826,20 @@ TEST_F(PointerChoreographerTest, TouchSetsSpots) { pc->assertSpotCount(DISPLAY_ID, 0); } +/** + * In this test, we simulate the complete event of the stylus approaching and clicking on the + * screen, and then leaving the screen. We should ensure that spots are displayed correctly. + */ TEST_F(PointerChoreographerTest, TouchSetsSpotsForStylusEvent) { mChoreographer.setShowTouchesEnabled(true); + mChoreographer.setStylusPointerIconEnabled(false); mChoreographer.notifyInputDevicesChanged( {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS, DISPLAY_ID)}}); - // Emit down event with stylus properties. - mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, + // First, the stylus begin to approach the screen. + mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS) .pointer(STYLUS_POINTER) .deviceId(DEVICE_ID) @@ -846,6 +847,72 @@ TEST_F(PointerChoreographerTest, TouchSetsSpotsForStylusEvent) { .build()); auto pc = assertPointerControllerCreated(ControllerType::TOUCH); pc->assertSpotCount(DISPLAY_ID, 1); + + mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, + AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS) + .pointer(STYLUS_POINTER) + .deviceId(DEVICE_ID) + .displayId(DISPLAY_ID) + .build()); + pc->assertSpotCount(DISPLAY_ID, 1); + + mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_EXIT, + AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS) + .pointer(STYLUS_POINTER) + .deviceId(DEVICE_ID) + .displayId(DISPLAY_ID) + .build()); + pc->assertSpotCount(DISPLAY_ID, 0); + + // Now, use stylus touch the screen. + mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, + AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS) + .pointer(STYLUS_POINTER) + .deviceId(DEVICE_ID) + .displayId(DISPLAY_ID) + .build()); + pc->assertSpotCount(DISPLAY_ID, 1); + + mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, + AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS) + .pointer(STYLUS_POINTER) + .deviceId(DEVICE_ID) + .displayId(DISPLAY_ID) + .build()); + pc->assertSpotCount(DISPLAY_ID, 1); + + mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_UP, + AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS) + .pointer(STYLUS_POINTER) + .deviceId(DEVICE_ID) + .displayId(DISPLAY_ID) + .build()); + pc->assertSpotCount(DISPLAY_ID, 0); + + // Then, the stylus start leave from the screen. + mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, + AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS) + .pointer(STYLUS_POINTER) + .deviceId(DEVICE_ID) + .displayId(DISPLAY_ID) + .build()); + pc->assertSpotCount(DISPLAY_ID, 1); + + mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, + AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS) + .pointer(STYLUS_POINTER) + .deviceId(DEVICE_ID) + .displayId(DISPLAY_ID) + .build()); + pc->assertSpotCount(DISPLAY_ID, 1); + + mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_EXIT, + AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS) + .pointer(STYLUS_POINTER) + .deviceId(DEVICE_ID) + .displayId(DISPLAY_ID) + .build()); + pc->assertSpotCount(DISPLAY_ID, 0); } TEST_F(PointerChoreographerTest, TouchSetsSpotsForTwoDisplays) { @@ -2294,7 +2361,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}, @@ -2358,15 +2431,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); @@ -2374,16 +2460,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()); @@ -2394,17 +2483,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, @@ -2420,14 +2511,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); @@ -2435,6 +2528,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()); @@ -2446,16 +2540,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); @@ -2463,6 +2560,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..082bbb8136 100644 --- a/services/inputflinger/tests/TestConstants.h +++ b/services/inputflinger/tests/TestConstants.h @@ -24,6 +24,9 @@ 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 = 500ms; + // 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..f3be04115a 100644 --- a/services/inputflinger/tests/TestEventMatchers.h +++ b/services/inputflinger/tests/TestEventMatchers.h @@ -654,6 +654,15 @@ MATCHER_P2(WithRelativeMotion, x, y, "InputEvent with specified relative motion" return argX == x && argY == y; } +MATCHER_P3(WithPointerRelativeMotion, pointer, x, y, + "InputEvent with specified relative motion for pointer") { + const auto argX = arg.pointerCoords[pointer].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X); + const auto argY = arg.pointerCoords[pointer].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y); + *result_listener << "expected pointer " << pointer << " to have relative motion (" << x << ", " + << y << "), but got (" << argX << ", " << argY << ")"; + return argX == x && argY == y; +} + MATCHER_P3(WithGestureOffset, dx, dy, epsilon, "InputEvent with specified touchpad gesture offset") { const auto argGestureX = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_X_OFFSET); @@ -720,6 +729,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); 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..3e4a19becd 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); } @@ -169,6 +173,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/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h index ff425ddfb7..ddc3310448 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 { @@ -197,9 +206,12 @@ public: 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 +281,8 @@ 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 {} std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay( const InputDeviceIdentifier& identifier, const std::optional<KeyboardLayoutInfo> layoutInfo) override { @@ -293,7 +307,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 +346,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/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 3446f58b96..130c112fd2 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 diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp index 31b7f8886c..3895ffe55e 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); @@ -1729,7 +1729,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) { 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 ad5a7609ec..b4ac9ba741 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", @@ -149,6 +149,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/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..0eced73ff9 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; @@ -677,7 +677,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 +810,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 +840,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 +1540,18 @@ Error AidlComposer::getClientTargetProperty( return error; } +Error AidlComposer::getDisplayLuts(Display display, std::vector<Lut>* 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::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..3669d4c996 100644 --- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h @@ -244,6 +244,9 @@ public: Error setRefreshRateChangedCallbackDebugEnabled(Display, bool) override; Error notifyExpectedPresent(Display, nsecs_t expectedPresentTime, int32_t frameIntervalNs) override; + Error getDisplayLuts( + Display display, + std::vector<aidl::android::hardware::graphics::composer3::Lut>* outLuts) 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..888dc08134 100644 --- a/services/surfaceflinger/DisplayHardware/ComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h @@ -41,6 +41,7 @@ #include <aidl/android/hardware/graphics/composer3/DisplayCapability.h> #include <aidl/android/hardware/graphics/composer3/DisplayConfiguration.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 +304,7 @@ public: virtual Error setRefreshRateChangedCallbackDebugEnabled(Display, bool) = 0; virtual Error notifyExpectedPresent(Display, nsecs_t expectedPresentTime, int32_t frameIntervalNs) = 0; + virtual Error getDisplayLuts(Display display, std::vector<V3_0::Lut>* outLuts) = 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..d5f65c6211 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.cpp +++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp @@ -41,6 +41,7 @@ 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::Lut; using aidl::android::hardware::graphics::composer3::OverlayProperties; namespace android { @@ -607,6 +608,18 @@ Error Display::getClientTargetProperty( return static_cast<Error>(error); } +Error Display::getDisplayLuts(std::vector<Lut>* outLuts) { + std::vector<Lut> tmpLuts; + const auto error = mComposer.getDisplayLuts(mId, &tmpLuts); + for (Lut& lut : tmpLuts) { + if (lut.pfd.get() >= 0) { + outLuts->push_back( + {lut.layer, ndk::ScopedFileDescriptor(lut.pfd.release()), lut.lutProperties}); + } + } + return static_cast<Error>(error); +} + Error Display::getDisplayDecorationSupport( std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>* support) { diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h index 5b948318ae..be2059a935 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> @@ -178,6 +179,8 @@ public: [[nodiscard]] virtual hal::Error getClientTargetProperty( aidl::android::hardware::graphics::composer3::ClientTargetPropertyWithBrightness* outClientTargetProperty) = 0; + [[nodiscard]] virtual hal::Error getDisplayLuts( + std::vector<aidl::android::hardware::graphics::composer3::Lut>* outLuts) = 0; [[nodiscard]] virtual hal::Error getDisplayDecorationSupport( std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>* support) = 0; @@ -261,6 +264,8 @@ public: hal::Error getClientTargetProperty( aidl::android::hardware::graphics::composer3::ClientTargetPropertyWithBrightness* outClientTargetProperty) override; + hal::Error getDisplayLuts( + std::vector<aidl::android::hardware::graphics::composer3::Lut>* outLuts) override; hal::Error getDisplayDecorationSupport( std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>* support) override; diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index 3d285a84ef..73fa855c3c 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" @@ -178,8 +177,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; @@ -428,14 +427,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 +454,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 +492,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; @@ -568,7 +567,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 +583,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 +896,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()); @@ -1149,7 +1148,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 +1167,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/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp index be95913b89..ec2a3ec024 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,7 @@ 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::Lut; using aidl::android::hardware::graphics::composer3::OverlayProperties; namespace android { @@ -588,7 +589,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 +677,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 +695,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 +1409,10 @@ Error HidlComposer::getClientTargetProperty( return Error::NONE; } +Error HidlComposer::getDisplayLuts(Display, 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..8bca5ad31f 100644 --- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h @@ -351,6 +351,8 @@ public: Hdr*) override; Error setRefreshRateChangedCallbackDebugEnabled(Display, bool) override; Error notifyExpectedPresent(Display, nsecs_t, int32_t) override; + Error getDisplayLuts(Display, + 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..3736abc79f 100644 --- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp +++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp @@ -22,14 +22,16 @@ #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; @@ -357,7 +359,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 +691,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); } } @@ -820,7 +850,7 @@ void SurfaceFrame::trace(int64_t displayFrameToken, nsecs_t monoBootOffset) cons 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()); @@ -866,7 +896,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 +932,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 +948,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 +958,7 @@ void FrameTimeline::setSfPresent(nsecs_t sfPresentTime, } void FrameTimeline::onCommitNotComposited() { - ATRACE_CALL(); + SFTRACE_CALL(); std::scoped_lock lock(mMutex); mCurrentDisplayFrame->onCommitNotComposited(); mCurrentDisplayFrame.reset(); @@ -1507,7 +1537,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/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..ac15b92175 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> @@ -317,6 +317,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 +364,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 +400,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 +434,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 +561,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 +569,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 +677,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 +690,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 +707,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 +783,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 +818,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 +854,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 +934,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 +956,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 +1012,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 +1076,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), @@ -1178,6 +1256,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..17d2610d7a 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> @@ -328,6 +328,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 = @@ -580,8 +581,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 +594,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 +609,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/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/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..636f7bdabf 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,7 +58,6 @@ #include <utils/Log.h> #include <utils/NativeHandle.h> #include <utils/StopWatch.h> -#include <utils/Trace.h> #include <algorithm> #include <mutex> @@ -317,8 +316,7 @@ void Layer::onRemovedFromCurrentState() { void Layer::addToCurrentState() { if (mRemovedFromDrawingState) { mRemovedFromDrawingState = false; - mFlinger->mScheduler->registerLayer(this); - mFlinger->removeFromOffscreenLayers(this); + mFlinger->mScheduler->registerLayer(this, FrameRateCompatibility::Default); } for (const auto& child : mCurrentChildren) { @@ -767,47 +765,12 @@ bool Layer::isSecure() const { 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(); + SFTRACE_CALL(); // TODO: This is unfortunate. mDrawingStateModified = mDrawingState.modified; @@ -1136,42 +1099,6 @@ bool Layer::setDimmingEnabled(const bool dimmingEnabled) { 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; }; @@ -1238,119 +1165,8 @@ StretchEffect Layer::getStretchEffect() const { 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,14 +1182,15 @@ 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; @@ -1392,17 +1209,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 +1239,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 +1253,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 +1270,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 +1286,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 +1295,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) { @@ -1553,14 +1349,6 @@ uint32_t Layer::getEffectiveUsage(uint32_t usage) const { 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 +1368,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); @@ -1710,26 +1447,12 @@ size_t Layer::getDescendantCount() const { 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) { @@ -1739,10 +1462,6 @@ ssize_t Layer::removeChild(const sp<Layer>& layer) { layer->setParent(nullptr); const auto removeResult = mCurrentChildren.remove(layer); - updateTreeHasFrameRateVote(); - layer->setGameModeForTree(GameMode::Unsupported); - layer->updateTreeHasFrameRateVote(); - return removeResult; } @@ -2579,10 +2298,6 @@ bool Layer::hasInputInfo() const { 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 +2313,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) { @@ -2821,7 +2532,7 @@ void Layer::callReleaseBufferCallback(const sp<ITransactionCompletedListener>& l if (!listener) { return; } - ATRACE_FORMAT_INSTANT("callReleaseBufferCallback %s - %" PRIu64, getDebugName(), framenumber); + SFTRACE_FORMAT_INSTANT("callReleaseBufferCallback %s - %" PRIu64, getDebugName(), framenumber); uint32_t currentMaxAcquiredBufferCount = mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(mOwnerUid); listener->onReleaseBuffer({buffer->getId(), framenumber}, @@ -2942,33 +2653,14 @@ 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->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,10 +2670,7 @@ 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 = {}; } @@ -3151,14 +2840,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(); @@ -3177,12 +2866,12 @@ bool Layer::setBuffer(std::shared_ptr<renderengine::ExternalTexture>& 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 +2910,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 +2942,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 +2954,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 +2990,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, @@ -3378,7 +3067,7 @@ bool Layer::setApi(int32_t api) { } 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) { @@ -3393,7 +3082,7 @@ bool Layer::setSidebandStream(const sp<NativeHandle>& sidebandStream, const Fram releasePreviousBuffer(); resetDrawingStateBufferInfo(); mDrawingState.bufferSurfaceFrameTX = nullptr; - setFrameTimelineVsyncForBufferlessTransaction(info, postTime); + setFrameTimelineVsyncForBufferlessTransaction(info, postTime, gameMode); } setTransactionFlags(eTransactionNeeded); if (!mSidebandStreamChanged.exchange(true)) { @@ -3451,9 +3140,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; @@ -3629,12 +3316,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)); } @@ -3698,7 +3385,7 @@ void Layer::decrementPendingBufferCount() { } void Layer::tracePendingBufferCount(int32_t pendingBuffers) { - ATRACE_INT(mBlastTransactionName.c_str(), pendingBuffers); + SFTRACE_INT(mBlastTransactionName.c_str(), pendingBuffers); } /* @@ -3776,41 +3463,41 @@ bool Layer::isSimpleBufferUpdate(const layer_state_t& s) const { : 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); + SFTRACE_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); + SFTRACE_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__); + SFTRACE_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__); + SFTRACE_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__); + SFTRACE_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__); + SFTRACE_FORMAT_INSTANT("%s: false [eBackgroundColorChanged changed]", __func__); return false; } } @@ -3820,92 +3507,92 @@ bool Layer::isSimpleBufferUpdate(const layer_state_t& s) const { mRequestedTransform.dtdy() != s.matrix.dtdy || mRequestedTransform.dtdx() != s.matrix.dtdx || mRequestedTransform.dsdy() != s.matrix.dsdy) { - ATRACE_FORMAT_INSTANT("%s: false [eMatrixChanged changed]", __func__); + SFTRACE_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__); + SFTRACE_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__); + SFTRACE_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__); + SFTRACE_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__); + SFTRACE_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__); + SFTRACE_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__); + SFTRACE_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__); + SFTRACE_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__); + SFTRACE_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__); + SFTRACE_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__); + SFTRACE_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__); + SFTRACE_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__); + SFTRACE_FORMAT_INSTANT("%s: false [eTrustedOverlayChanged changed]", __func__); return false; } } @@ -3914,28 +3601,28 @@ bool Layer::isSimpleBufferUpdate(const layer_state_t& s) const { StretchEffect temp = s.stretchEffect; temp.sanitize(); if (mDrawingState.stretchEffect != temp) { - ATRACE_FORMAT_INSTANT("%s: false [eStretchChanged changed]", __func__); + SFTRACE_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__); + SFTRACE_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__); + SFTRACE_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__); + SFTRACE_FORMAT_INSTANT("%s: false [eDimmingEnabledChanged changed]", __func__); return false; } } @@ -3943,14 +3630,14 @@ bool Layer::isSimpleBufferUpdate(const layer_state_t& s) const { if (s.what & layer_state_t::eExtendedRangeBrightnessChanged) { if (mDrawingState.currentHdrSdrRatio != s.currentHdrSdrRatio || mDrawingState.desiredHdrSdrRatio != s.desiredHdrSdrRatio) { - ATRACE_FORMAT_INSTANT("%s: false [eExtendedRangeBrightnessChanged changed]", __func__); + SFTRACE_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__); + SFTRACE_FORMAT_INSTANT("%s: false [eDesiredHdrHeadroomChanged changed]", __func__); return false; } } @@ -4053,7 +3740,8 @@ bool Layer::isVisible() const { 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 +3783,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 +3804,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, @@ -4142,8 +3835,8 @@ bool Layer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime) { } 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 +3847,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; } @@ -4299,13 +3992,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; } @@ -4384,38 +4070,6 @@ void Layer::updateChildrenSnapshots(bool 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(); diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index b9fcd5c333..f6eed6332b 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -312,7 +312,7 @@ public: 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); @@ -322,25 +322,18 @@ public: 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; @@ -439,7 +432,7 @@ public: 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*/); @@ -694,14 +687,9 @@ public: */ 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; @@ -788,15 +776,11 @@ 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,11 +789,12 @@ 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); @@ -832,13 +817,6 @@ public: */ 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; } @@ -899,9 +877,6 @@ public: // 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; } @@ -912,19 +887,9 @@ public: 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; @@ -980,7 +945,6 @@ protected: void preparePerFrameBufferCompositionState(); void preparePerFrameEffectsCompositionState(); void gatherBufferInfo(); - void onSurfaceFrameCreated(const std::shared_ptr<frametimeline::SurfaceFrame>&); bool isClonedFromAlive() { return getClonedFrom() != nullptr; } @@ -1136,7 +1100,6 @@ private: 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); @@ -1195,11 +1158,6 @@ private: 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; @@ -1250,13 +1208,11 @@ private: 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,14 +1224,10 @@ 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: 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..885c3d3460 100644 --- a/services/surfaceflinger/LayerProtoHelper.cpp +++ b/services/surfaceflinger/LayerProtoHelper.cpp @@ -263,9 +263,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 +305,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 +358,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) { diff --git a/services/surfaceflinger/LayerProtoHelper.h b/services/surfaceflinger/LayerProtoHelper.h index 20c226006c..c0198b62ec 100644 --- a/services/surfaceflinger/LayerProtoHelper.h +++ b/services/surfaceflinger/LayerProtoHelper.h @@ -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/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..7712d38f43 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()) { @@ -356,9 +356,8 @@ 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); + auto displayState = mFlinger.getSnapshotsFromMainThread(renderAreaBuilder, + getLayerSnapshotsFn, layerFEs); fenceResult = mFlinger.captureScreenshot(renderAreaBuilder, buffer, kRegionSampling, kGrayscale, kIsProtected, nullptr, displayState, layerFEs) @@ -394,7 +393,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/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp index 6b654499a2..d31fceab7e 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()); 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..dbc458cb7f 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,12 @@ bool LayerInfo::FrameRate::isVoteValidForMrr(bool isVrrDevice) const { return true; } + if (FlagManager::getInstance().view_set_requested_frame_rate_mrr() && + 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 dd86e4f426..9f6eab288b 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}; } @@ -847,13 +847,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}; } } @@ -889,8 +889,8 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi if (scores.front().frameRateMode.fps < touchRefreshRates.front().frameRateMode.fps) { ALOGV("Touch Boost"); - 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}}; } } @@ -901,13 +901,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}; } @@ -949,7 +949,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 {}; } @@ -1064,12 +1064,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); } @@ -1640,7 +1640,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..be00079b9c 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> @@ -122,6 +122,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 +264,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 +296,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); } @@ -403,14 +409,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 +430,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 +487,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 +526,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 +560,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 +647,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 +667,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 +711,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 +796,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 +827,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 +839,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 +880,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 +913,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 +1014,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 +1096,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 +1139,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 +1266,8 @@ void Scheduler::setGameModeFrameRateForUid(FrameRateOverride frameRateOverride) } else { mFrameRateOverrideMappings.setGameModeRefreshRateForUid(frameRateOverride); } + + onFrameRateOverridesChanged(); } void Scheduler::setGameDefaultFrameRateForUid(FrameRateOverride frameRateOverride) { @@ -1267,6 +1286,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..1367ec385e 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -154,13 +154,11 @@ public: 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 +187,7 @@ public: } } - void updatePhaseConfiguration(Fps); - void resetPhaseConfiguration(Fps) REQUIRES(kMainThreadContext); + void updatePhaseConfiguration(PhysicalDisplayId, Fps); const VsyncConfiguration& getVsyncConfiguration() const { return *mVsyncConfiguration; } @@ -222,7 +219,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 +323,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 +447,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 +457,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; @@ -583,13 +583,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/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp index 6d6b70d198..900bce0aa9 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,7 +98,7 @@ 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 + @@ -110,8 +110,8 @@ ScheduleResult VSyncDispatchTimerQueueEntry::schedule(VSyncDispatch::ScheduleTim 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 +154,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 +172,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 +188,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 +199,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 +208,9 @@ 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); + SFTRACE_FORMAT_INSTANT("Workload updated workDelta=%" PRId64 " readyDelta=%" PRId64 + " lastVsyncDelta=%" PRId64, + workDelta, readyDelta, lastVsyncDelta); mScheduleTiming = *mWorkloadUpdateInfo; mWorkloadUpdateInfo.reset(); } @@ -310,7 +310,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 +337,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 +383,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..4a7cff58dd 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; @@ -658,32 +697,38 @@ std::optional<TimePoint> VSyncPredictor::VsyncTimeline::nextAnticipatedVSyncTime // 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"); + 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 +782,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 +792,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..fa377e9323 100644 --- a/services/surfaceflinger/Scheduler/VsyncModulator.cpp +++ b/services/surfaceflinger/Scheduler/VsyncModulator.cpp @@ -22,8 +22,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> @@ -72,7 +72,7 @@ VsyncModulator::VsyncConfigOpt VsyncModulator::setTransactionSchedule(Transactio } if (mTraceDetailedInfo) { - ATRACE_INT("mEarlyWakeup", static_cast<int>(mEarlyWakeupRequests.size())); + SFTRACE_INT("mEarlyWakeup", static_cast<int>(mEarlyWakeupRequests.size())); } if (mEarlyWakeupRequests.empty() && schedule == Schedule::EarlyEnd) { @@ -172,9 +172,9 @@ VsyncConfig VsyncModulator::updateVsyncConfigLocked() { const bool isEarlyGpu = &offsets == &mVsyncConfigSet.earlyGpu; const bool isLate = &offsets == &mVsyncConfigSet.late; - ATRACE_INT("Vsync-EarlyOffsetsOn", isEarly); - ATRACE_INT("Vsync-EarlyGpuOffsetsOn", isEarlyGpu); - ATRACE_INT("Vsync-LateOffsetsOn", isLate); + SFTRACE_INT("Vsync-EarlyOffsetsOn", isEarly); + SFTRACE_INT("Vsync-EarlyGpuOffsetsOn", isEarlyGpu); + SFTRACE_INT("Vsync-LateOffsetsOn", isLate); } return offsets; 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 2c397bd18d..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,30 +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 { - const bool isTwoVsyncsAhead = targetsVsyncsAhead<2>(minFramePeriod); - size_t shift = 0; - 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. @@ -166,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/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 1aaa1281fe..a2322287ea 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,9 +531,6 @@ 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); @@ -728,6 +723,7 @@ void SurfaceFlinger::bootFinished() { mBootFinished = true; FlagManager::getMutableInstance().markBootCompleted(); + ::tracing_perfetto::registerWithPerfetto(); mInitBootPropsFuture.wait(); mRenderEnginePrimeCacheFuture.wait(); @@ -854,13 +850,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 +910,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); @@ -1280,20 +1280,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 +1306,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 +1334,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 +1353,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 +1405,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 +1435,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 +1459,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 +1520,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; - } - } + // HWC has requested to apply the mode change immediately rather than on the next frame. + finalizeDisplayModeChange(displayId); - if (displayToUpdateImmediately) { - const auto displayId = *displayToUpdateImmediately; - 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 +1535,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 +2146,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,14 +2168,6 @@ 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 && @@ -2225,9 +2192,9 @@ void SurfaceFlinger::onComposerHalVsync(hal::HWDisplayId hwcDisplayId, int64_t t } } - 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)) { @@ -2285,12 +2252,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 +2266,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; @@ -2316,37 +2283,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, vsyncId); - } - 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 +2354,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 @@ -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, vsyncId); 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,16 +2735,12 @@ 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) { + for (auto& [layer, _] : mLayersWithQueuedFrames) { if (const auto& layerFE = layer->getCompositionEngineLayerFE()) refreshArgs.layersWithQueuedFrames.push_back(layerFE); } @@ -2844,7 +2782,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,7 +2816,7 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( } refreshArgs.layersWithQueuedFrames.reserve(mLayersWithQueuedFrames.size()); - for (auto& layer : mLayersWithQueuedFrames) { + for (auto& [layer, _] : mLayersWithQueuedFrames) { if (const auto& layerFE = layer->getCompositionEngineLayerFE()) { refreshArgs.layersWithQueuedFrames.push_back(layerFE); // Some layers are not displayed and do not yet have a future release fence @@ -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,69 +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(); - 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; -} - -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); + SFTRACE_CALL(); mDebugInTransaction = systemTime(); // Here we're guaranteed that some transaction flags are set @@ -3411,7 +3278,6 @@ void SurfaceFlinger::commitTransactionsLegacy() { // mCurrentState won't change until the transaction is committed. mScheduler->modulateVsync({}, &VsyncModulator::onTransactionCommit); commitTransactionsLocked(clearTransactionFlags(eTransactionMask)); - mDebugInTransaction = 0; } @@ -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); @@ -3960,11 +3830,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,13 +3915,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) { @@ -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, @@ -4552,7 +4335,7 @@ void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) { } void SurfaceFlinger::doCommitTransactions() { - ATRACE_CALL(); + SFTRACE_CALL(); if (!mLayersPendingRemoval.isEmpty()) { // Notify removed layers now that they can't be drawn from @@ -4570,13 +4353,6 @@ void SurfaceFlinger::doCommitTransactions() { 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(); } @@ -4590,7 +4366,6 @@ void SurfaceFlinger::doCommitTransactions() { } } - commitOffscreenLayers(); if (mLayerMirrorRoots.size() > 0) { std::deque<Layer*> pendingUpdates; pendingUpdates.insert(pendingUpdates.end(), mLayerMirrorRoots.begin(), @@ -4612,17 +4387,6 @@ void SurfaceFlinger::doCommitTransactions() { } } -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) { for (const auto& [token, displayDevice] : FTL_FAKE_GUARD(mStateLock, mDisplays)) { auto display = displayDevice->getCompositionDisplay(); @@ -4632,160 +4396,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; } @@ -4810,7 +4444,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 +4452,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 +4477,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 +4490,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 +4527,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 +4542,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 +4554,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 +4570,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 +4587,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 +4605,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 +4617,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 @@ -5147,22 +4679,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; } @@ -5170,9 +4702,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; } } @@ -5182,12 +4714,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(); @@ -5197,7 +4729,7 @@ status_t SurfaceFlinger::setTransactionState( composerState.state.sanitize(permissions); } - for (DisplayState display : displays) { + for (DisplayState& display : displays) { display.sanitize(permissions); } @@ -5318,11 +4850,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 @@ -5330,27 +4857,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; @@ -5485,366 +4996,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, @@ -5881,10 +5032,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; } @@ -5899,6 +5048,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. @@ -5913,7 +5073,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) { @@ -5931,18 +5091,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) { @@ -6127,6 +5286,8 @@ void SurfaceFlinger::onHandleDestroyed(BBinder* handle, sp<Layer>& layer, uint32 mTransactionHandler.onLayerDestroyed(layerId); } + JankTracker::flushJankData(layerId); + Mutex::Autolock lock(mStateLock); markLayerPendingRemovalLocked(layer); layer->onHandleDestroyed(); @@ -6404,15 +5565,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; @@ -6429,7 +5598,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]); @@ -6464,8 +5633,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 { @@ -6489,11 +5658,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 { @@ -6656,53 +5820,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 { @@ -6717,9 +5863,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> @@ -6753,26 +5906,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) { @@ -6781,41 +5914,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) { @@ -6895,8 +5994,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); @@ -6995,15 +6093,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) { @@ -7497,15 +6586,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 @@ -7694,7 +6777,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__); @@ -7702,15 +6785,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(); } })); @@ -7732,8 +6809,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); @@ -7757,63 +6834,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: @@ -7839,7 +6859,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; } @@ -7932,9 +6953,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); @@ -7947,7 +6969,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; @@ -7973,7 +6995,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); @@ -7986,17 +7008,21 @@ 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, captureListener); } void SurfaceFlinger::captureDisplay(DisplayId displayId, const CaptureArgs& args, @@ -8049,10 +7075,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, captureListener); } ScreenCaptureResults SurfaceFlinger::captureLayersSync(const LayerCaptureArgs& args) { @@ -8063,22 +7090,25 @@ ScreenCaptureResults SurfaceFlinger::captureLayersSync(const LayerCaptureArgs& a void SurfaceFlinger::captureLayers(const LayerCaptureArgs& 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 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; @@ -8095,26 +7125,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); @@ -8140,8 +7171,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"); @@ -8150,14 +7182,15 @@ 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, captureListener); } // Creates a Future release fence for a layer and keeps track of it in a list to @@ -8191,12 +7224,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); @@ -8212,7 +7245,7 @@ void SurfaceFlinger::captureScreenCommon(RenderAreaBuilderVariant renderAreaBuil ui::Size bufferSize, ui::PixelFormat reqPixelFormat, bool allowProtected, bool grayscale, const sp<IScreenCaptureListener>& captureListener) { - ATRACE_CALL(); + SFTRACE_CALL(); if (exceedsMaxRenderTargetSize(bufferSize.getWidth(), bufferSize.getHeight())) { ALOGE("Attempted to capture screen with size (%" PRId32 ", %" PRId32 @@ -8226,8 +7259,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; @@ -8350,7 +7382,7 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshot( const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling, bool grayscale, bool isProtected, 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 = @@ -8390,7 +7422,7 @@ 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(); + SFTRACE_CALL(); auto takeScreenshotFn = [=, this, renderAreaBuilder = std::move(renderAreaBuilder)]() REQUIRES( kMainThreadContext) mutable -> ftl::SharedFuture<FenceResult> { @@ -8454,7 +7486,7 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl( bool grayscale, bool isProtected, 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(); @@ -8630,12 +7662,8 @@ 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()); } } @@ -8653,42 +7681,6 @@ void SurfaceFlinger::State::traverseInReverseZOrder(const LayerVector::Visitor& 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(); @@ -8710,7 +7702,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); @@ -8739,13 +7731,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); @@ -8769,10 +7757,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; } @@ -8803,7 +7788,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; @@ -8837,7 +7822,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; @@ -8865,13 +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); } @@ -8885,24 +7869,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) { @@ -8932,13 +7898,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; } @@ -9080,20 +8040,6 @@ void SurfaceFlinger::handleLayerCreatedLocked(const LayerCreatedState& state, Vs } 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() { @@ -9129,7 +8075,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); @@ -9138,8 +8084,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); @@ -9263,170 +8207,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; } @@ -9553,33 +8379,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) { @@ -9601,7 +8400,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)); @@ -9610,9 +8409,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) { @@ -10491,6 +9287,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 a3534b582c..873fac2c89 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> @@ -272,7 +273,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,9 +292,6 @@ 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. @@ -312,7 +310,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 @@ -449,7 +446,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 +556,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, @@ -697,7 +694,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 +704,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 +750,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 +762,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 +803,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 +821,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, VsyncId) @@ -885,9 +854,6 @@ private: 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,7 +863,7 @@ private: using OutputCompositionState = compositionengine::impl::OutputCompositionState; - std::optional<OutputCompositionState> getDisplayAndLayerSnapshotsFromMainThread( + std::optional<OutputCompositionState> getSnapshotsFromMainThread( RenderAreaBuilderVariant& renderAreaBuilder, GetLayerSnapshotsFunction getLayerSnapshotsFn, std::vector<sp<LayerFE>>& layerFEs); @@ -934,12 +900,6 @@ private: 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 +948,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 +1053,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 +1103,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 +1128,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 +1139,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); @@ -1299,18 +1242,20 @@ private: 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 @@ -1338,7 +1283,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,12 +1384,6 @@ 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 @@ -1455,22 +1394,8 @@ private: // 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 +1429,6 @@ private: } bool mPowerHintSessionEnabled; - - bool mLayerLifecycleManagerEnabled = false; // Whether a display should be turned on when initialized bool mSkipPowerOnForQuiescent; @@ -1694,6 +1617,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..881bf35b58 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,7 @@ status_t TransactionCallbackInvoker::addCallbackHandle(const sp<CallbackHandle>& handle->previousReleaseFence, handle->transformHint, handle->currentMaxAcquiredBufferCount, - eventStats, jankData, - handle->previousReleaseCallbackId); + eventStats, handle->previousReleaseCallbackId); } return NO_ERROR; } @@ -164,7 +160,7 @@ void TransactionCallbackInvoker::addPresentFence(sp<Fence> presentFence) { void TransactionCallbackInvoker::sendCallbacks(bool onCommitOnly) { // 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 +195,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 +205,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..7853a9f359 100644 --- a/services/surfaceflinger/TransactionCallbackInvoker.h +++ b/services/surfaceflinger/TransactionCallbackInvoker.h @@ -63,8 +63,7 @@ public: 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 +76,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, 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..07c720f670 100644 --- a/services/surfaceflinger/common/FlagManager.cpp +++ b/services/surfaceflinger/common/FlagManager.cpp @@ -119,6 +119,7 @@ void FlagManager::dump(std::string& result) const { DUMP_READ_ONLY_FLAG(connected_display); DUMP_READ_ONLY_FLAG(enable_small_area_detection); DUMP_READ_ONLY_FLAG(frame_rate_category_mrr); + DUMP_READ_ONLY_FLAG(view_set_requested_frame_rate_mrr); DUMP_READ_ONLY_FLAG(misc1); DUMP_READ_ONLY_FLAG(vrr_config); DUMP_READ_ONLY_FLAG(hotplug2); @@ -136,6 +137,7 @@ 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); @@ -221,6 +223,8 @@ FLAG_MANAGER_LEGACY_SERVER_FLAG(use_skia_tracing, PROPERTY_SKIA_ATRACE_ENABLED, FLAG_MANAGER_READ_ONLY_FLAG(connected_display, "") FLAG_MANAGER_READ_ONLY_FLAG(enable_small_area_detection, "") FLAG_MANAGER_READ_ONLY_FLAG(frame_rate_category_mrr, "debug.sf.frame_rate_category_mrr") +FLAG_MANAGER_READ_ONLY_FLAG(view_set_requested_frame_rate_mrr, + "debug.sf.view_set_requested_frame_rate_mrr") FLAG_MANAGER_READ_ONLY_FLAG(misc1, "") FLAG_MANAGER_READ_ONLY_FLAG(vrr_config, "debug.sf.enable_vrr_config") FLAG_MANAGER_READ_ONLY_FLAG(hotplug2, "") @@ -242,6 +246,7 @@ 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(latch_unsignaled_with_auto_refresh_changed, ""); diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h index ab7a474d2c..a4b4a2b9f1 100644 --- a/services/surfaceflinger/common/include/common/FlagManager.h +++ b/services/surfaceflinger/common/include/common/FlagManager.h @@ -56,6 +56,7 @@ public: /// Trunk stable readonly flags /// bool connected_display() const; bool frame_rate_category_mrr() const; + bool view_set_requested_frame_rate_mrr() const; bool enable_small_area_detection() const; bool misc1() const; bool vrr_config() const; @@ -73,6 +74,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; 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..886167e9f2 100644 --- a/services/surfaceflinger/surfaceflinger_flags_new.aconfig +++ b/services/surfaceflinger/surfaceflinger_flags_new.aconfig @@ -126,6 +126,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 +144,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/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..3104dd4720 --- /dev/null +++ b/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h @@ -0,0 +1,522 @@ +/* + * 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); + } + +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/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..9be0fc38b3 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" @@ -90,8 +92,12 @@ public: 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 +181,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 +355,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 +465,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 +479,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 +606,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 +636,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 +670,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 +702,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 +736,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 +770,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 +804,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 +839,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 +875,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 +923,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 +1858,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/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..7e84408f7d 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,50 @@ 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); + SET_FLAG_FOR_TEST(flags::view_set_requested_frame_rate_mrr, true); + + 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 +1448,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 +1489,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..b4efe0fe14 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); diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp index 8b9ac93310..2860345538 100644 --- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp @@ -56,6 +56,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 +292,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 +798,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 +1125,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 +1216,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 +1575,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 +1717,48 @@ 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()); +} } // 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 cf9a7d3e69..06c4e30989 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp @@ -1562,7 +1562,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. @@ -1609,6 +1609,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); @@ -2140,14 +2211,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}, }; @@ -2207,13 +2278,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..358f6b0cf5 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)); @@ -122,6 +128,7 @@ SchedulerTest::SchedulerTest() { .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))); 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..4b0a7c3a04 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, @@ -169,129 +214,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 +365,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 +387,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 +418,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 +437,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 +453,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 +483,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 +507,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 +523,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 +555,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_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 f0638094c7..df16b2e058 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) { @@ -176,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); @@ -188,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 4197cbd271..a6a275804d 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -326,9 +326,13 @@ public: auto& mutableStateLock() { return mFlinger->mStateLock; } - static auto findOutputLayerForDisplay(const sp<Layer>& layer, - const sp<const DisplayDevice>& display) { - return layer->findOutputLayerForDisplay(display.get()); + 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()); } static void setLayerSidebandStream(const sp<Layer>& layer, @@ -340,17 +344,14 @@ public: 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; } @@ -395,7 +396,7 @@ public: targets.try_emplace(id, &frameTargeter.target()); targeters.try_emplace(id, &frameTargeter); } - + mFlinger->setTransactionFlags(eTransactionFlushNeeded); mFlinger->commit(displayId, targets); if (composite) { @@ -505,10 +506,9 @@ public: 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, @@ -528,7 +528,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, @@ -584,8 +584,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); @@ -620,6 +618,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. */ @@ -637,12 +647,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); }; @@ -709,10 +731,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) { 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/TunnelModeEnabledReporterTest.cpp b/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp index 1cf14ae519..9f6065b683 100644 --- a/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp +++ b/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp @@ -127,7 +127,7 @@ TEST_F(TunnelModeEnabledReporterTest, callsNewListenerWithFreshInformation) { sp<NativeHandle> stream = NativeHandle::create(reinterpret_cast<native_handle_t*>(DEFAULT_SIDEBAND_STREAM), false); - layer->setSidebandStream(stream, FrameTimelineInfo{}, 20); + layer->setSidebandStream(stream, FrameTimelineInfo{}, 20, gui::GameMode::Unsupported); mFlinger.mutableCurrentState().layersSortedByZ.add(layer); mTunnelModeEnabledReporter->updateTunnelModeStatus(); mTunnelModeEnabledReporter->addListener(mTunnelModeEnabledListener); @@ -151,7 +151,8 @@ 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); 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..7c678bd811 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,17 +901,20 @@ 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)); + vrrTracker.onFrameBegin(TimePoint::fromNs(7000), + {TimePoint::fromNs(6500), TimePoint::fromNs(6500)}); EXPECT_EQ(10500, vrrTracker.nextAnticipatedVSyncTimeFrom(9000, 7000)); } @@ -913,7 +946,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 +955,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 +1026,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..e380e19797 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h @@ -180,6 +180,8 @@ public: MOCK_METHOD1(onHotplugDisconnect, void(Display)); MOCK_METHOD(Error, setRefreshRateChangedCallbackDebugEnabled, (Display, bool)); MOCK_METHOD(Error, notifyExpectedPresent, (Display, nsecs_t, int32_t)); + MOCK_METHOD(Error, getDisplayLuts, + (Display, 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..1eda3586c5 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h @@ -109,6 +109,8 @@ public: MOCK_METHOD(hal::Error, getOverlaySupport, (aidl::android::hardware::graphics::composer3::OverlayProperties *), (const override)); + MOCK_METHOD(hal::Error, getDisplayLuts, + (std::vector<aidl::android::hardware::graphics::composer3::Lut>*), (override)); }; class Layer : public HWC2::Layer { 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/MockLayer.h b/services/surfaceflinger/tests/unittests/mock/MockLayer.h index 002fa9fb55..fdb6f4dafe 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockLayer.h +++ b/services/surfaceflinger/tests/unittests/mock/MockLayer.h @@ -24,16 +24,10 @@ 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") {} 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..3d8124b81b 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 { @@ -96,6 +99,11 @@ Info HalWrapper::getInfo() { 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 +116,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()) { @@ -200,7 +214,7 @@ HalResult<std::vector<float>> HalWrapper::getMaxAmplitudesInternal() { // ------------------------------------------------------------------------------------------------- HalResult<void> AidlHalWrapper::ping() { - return HalResultFactory::fromStatus(IInterface::asBinder(getHal())->pingBinder()); + return HalResultFactory::fromStatus(AIBinder_ping(getHal()->asBinder().get())); } void AidlHalWrapper::tryReconnect() { @@ -208,7 +222,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 +234,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 +270,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 +285,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 +317,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 +370,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 +379,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 +389,59 @@ 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); } -sp<Aidl::IVibrator> AidlHalWrapper::getHal() { +std::shared_ptr<Aidl::IVibrator> AidlHalWrapper::getHal() { std::lock_guard<std::mutex> lock(mHandleMutex); return mHandle; } @@ -420,8 +450,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 +465,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 +475,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 +509,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 +528,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 +633,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 +648,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..ae0d9ab411 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; @@ -247,12 +261,9 @@ public: 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"); @@ -309,12 +320,12 @@ 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 = @@ -336,6 +347,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 +376,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 +406,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(); @@ -405,12 +429,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 +456,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; @@ -464,11 +497,11 @@ protected: HalResult<std::vector<float>> getMaxAmplitudesInternal() 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 +522,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 +539,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 +555,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 +569,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 +583,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 +599,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..ba7e1f07bf 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()); @@ -311,66 +241,70 @@ TEST_F(VibratorHalWrapperAidlTest, TestGetInfoDoesNotCacheFailedResult) { 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()))); vibrator::Info failed = mWrapper->getInfo(); ASSERT_TRUE(failed.capabilities.isFailed()); @@ -417,46 +351,46 @@ TEST_F(VibratorHalWrapperAidlTest, TestGetInfoCachesResult) { 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))); std::vector<std::thread> threads; for (int i = 0; i < 10; i++) { @@ -487,18 +421,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 +459,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 +493,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 +545,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 +601,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 +657,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 +682,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..83430d79a5 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; @@ -316,6 +321,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 +370,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/api_gen.cpp b/vulkan/libvulkan/api_gen.cpp index a9706bc871..a3fe33e67e 100644 --- a/vulkan/libvulkan/api_gen.cpp +++ b/vulkan/libvulkan/api_gen.cpp @@ -178,7 +178,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 +325,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 +659,8 @@ VKAPI_ATTR PFN_vkVoidFunction GetDeviceProcAddr(VkDevice device, const char* pNa "vkGetDrmDisplayEXT", "vkGetInstanceProcAddr", "vkGetPhysicalDeviceCalibrateableTimeDomainsEXT", + "vkGetPhysicalDeviceCalibrateableTimeDomainsKHR", + "vkGetPhysicalDeviceCooperativeMatrixPropertiesKHR", "vkGetPhysicalDeviceDisplayPlaneProperties2KHR", "vkGetPhysicalDeviceDisplayProperties2KHR", "vkGetPhysicalDeviceExternalBufferProperties", @@ -703,6 +705,7 @@ VKAPI_ATTR PFN_vkVoidFunction GetDeviceProcAddr(VkDevice device, const char* pNa "vkGetPhysicalDeviceToolProperties", "vkGetPhysicalDeviceToolPropertiesEXT", "vkGetPhysicalDeviceVideoCapabilitiesKHR", + "vkGetPhysicalDeviceVideoEncodeQualityLevelPropertiesKHR", "vkGetPhysicalDeviceVideoFormatPropertiesKHR", "vkSubmitDebugUtilsMessageEXT", }; diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp index 9e67725b58..ba2b8887bc 100644 --- a/vulkan/libvulkan/swapchain.cpp +++ b/vulkan/libvulkan/swapchain.cpp @@ -1215,8 +1215,15 @@ VkResult GetPhysicalDeviceSurfaceFormats2KHR( surfaceCompressionProps ->imageCompressionFixedRateFlags = compressionProps.imageCompressionFixedRateFlags; - } else { + } else if (compressionRes == + VK_ERROR_OUT_OF_HOST_MEMORY || + compressionRes == + VK_ERROR_OUT_OF_DEVICE_MEMORY) { return compressionRes; + } else { + // For any of the *_NOT_SUPPORTED errors we continue + // onto the next format + continue; } } } break; @@ -1473,23 +1480,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. 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..3624c1d032 100644 --- a/vulkan/scripts/null_generator.py +++ b/vulkan/scripts/null_generator.py @@ -89,6 +89,8 @@ def gen_cpp(): f.write(gencom.copyright_and_warning(2015)) f.write("""\ +#include <android/hardware_buffer.h> + #include <algorithm> #include "null_driver_gen.h" |