diff options
135 files changed, 4855 insertions, 1752 deletions
diff --git a/.clang-format b/.clang-format index 6725a1fde8..f63f670adc 100644 --- a/.clang-format +++ b/.clang-format @@ -12,3 +12,6 @@ IndentWidth: 4 PenaltyBreakBeforeFirstCallParameter: 100000 SpacesBeforeTrailingComments: 1 IncludeBlocks: Preserve + +DerivePointerAlignment: false +PointerAlignment: Left diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg index 3480d63f9a..c71c4a0251 100644 --- a/PREUPLOAD.cfg +++ b/PREUPLOAD.cfg @@ -19,6 +19,7 @@ clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp libs/gui/ libs/input/ libs/nativedisplay/ + libs/nativewindow/ libs/renderengine/ libs/ui/ libs/vr/ diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index 69a1df27b2..fdee3e5e6b 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -74,6 +74,7 @@ #include <android/hidl/manager/1.0/IServiceManager.h> #include <android/os/IIncidentCompanion.h> #include <binder/IServiceManager.h> +#include <cutils/multiuser.h> #include <cutils/native_handle.h> #include <cutils/properties.h> #include <cutils/sockets.h> @@ -2619,10 +2620,13 @@ bool Dumpstate::FinishZipFile() { return true; } -static void SendBroadcast(const std::string& action, const std::vector<std::string>& args) { +static void SendBroadcast(const std::string& action, + const std::vector<std::string>& args, + int32_t user_id) { // clang-format off - std::vector<std::string> am = {"/system/bin/cmd", "activity", "broadcast", "--user", "0", - "--receiver-foreground", "--receiver-include-background", "-a", action}; + std::vector<std::string> am = {"/system/bin/cmd", "activity", "broadcast", "--user", + std::to_string(user_id), "--receiver-foreground", + "--receiver-include-background", "-a", action}; // clang-format on am.insert(am.end(), args.begin(), args.end()); @@ -3057,7 +3061,8 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, }; // clang-format on // Send STARTED broadcast for apps that listen to bugreport generation events - SendBroadcast("com.android.internal.intent.action.BUGREPORT_STARTED", am_args); + SendBroadcast("com.android.internal.intent.action.BUGREPORT_STARTED", + am_args, multiuser_get_user_id(calling_uid)); if (options_->progress_updates_to_socket) { dprintf(control_socket_fd_, "BEGIN:%s\n", path_.c_str()); } @@ -3305,7 +3310,7 @@ void Dumpstate::MaybeAddUiTracesToZip() { } void Dumpstate::onUiIntensiveBugreportDumpsFinished(int32_t calling_uid) { - if (calling_uid == AID_SHELL || !CalledByApi()) { + if (multiuser_get_app_id(calling_uid) == AID_SHELL || !CalledByApi()) { return; } if (listener_ != nullptr) { @@ -3316,7 +3321,7 @@ void Dumpstate::onUiIntensiveBugreportDumpsFinished(int32_t calling_uid) { } void Dumpstate::MaybeCheckUserConsent(int32_t calling_uid, const std::string& calling_package) { - if (calling_uid == AID_SHELL || !CalledByApi()) { + if (multiuser_get_app_id(calling_uid) == AID_SHELL || !CalledByApi()) { // No need to get consent for shell triggered dumpstates, or not through // bugreporting API (i.e. no fd to copy back). return; @@ -3398,7 +3403,7 @@ Dumpstate::RunStatus Dumpstate::CopyBugreportIfUserConsented(int32_t calling_uid // If the caller has asked to copy the bugreport over to their directory, we need explicit // user consent (unless the caller is Shell). UserConsentResult consent_result; - if (calling_uid == AID_SHELL) { + if (multiuser_get_app_id(calling_uid) == AID_SHELL) { consent_result = UserConsentResult::APPROVED; } else { consent_result = consent_callback_->getResult(); diff --git a/cmds/dumpstate/dumpstate.rc b/cmds/dumpstate/dumpstate.rc index a80da4ec55..12a7cfface 100644 --- a/cmds/dumpstate/dumpstate.rc +++ b/cmds/dumpstate/dumpstate.rc @@ -8,6 +8,7 @@ service dumpstate /system/bin/dumpstate -s socket dumpstate stream 0660 shell log disabled oneshot + capabilities CHOWN DAC_OVERRIDE DAC_READ_SEARCH FOWNER FSETID KILL NET_ADMIN NET_RAW SETGID SETUID SYS_PTRACE SYS_RESOURCE BLOCK_SUSPEND SYSLOG # dumpstatez generates a zipped bugreport but also uses a socket to print the file location once # it is finished. @@ -16,9 +17,11 @@ service dumpstatez /system/bin/dumpstate -S class main disabled oneshot + capabilities CHOWN DAC_OVERRIDE DAC_READ_SEARCH FOWNER FSETID KILL NET_ADMIN NET_RAW SETGID SETUID SYS_PTRACE SYS_RESOURCE BLOCK_SUSPEND SYSLOG # bugreportd starts dumpstate binder service and makes it wait for a listener to connect. service bugreportd /system/bin/dumpstate -w class main disabled oneshot + capabilities CHOWN DAC_OVERRIDE DAC_READ_SEARCH FOWNER FSETID KILL NET_ADMIN NET_RAW SETGID SETUID SYS_PTRACE SYS_RESOURCE BLOCK_SUSPEND SYSLOG diff --git a/cmds/installd/installd.rc b/cmds/installd/installd.rc index 240aa495b7..5b08c776ac 100644 --- a/cmds/installd/installd.rc +++ b/cmds/installd/installd.rc @@ -1,6 +1,7 @@ service installd /system/bin/installd class main + capabilities CHOWN DAC_OVERRIDE DAC_READ_SEARCH FOWNER FSETID KILL SETGID SETUID SYS_ADMIN on early-boot mkdir /config/sdcardfs/extensions/1055 diff --git a/include/android/input.h b/include/android/input.h index 5d19c5cb13..d6f9d633b6 100644 --- a/include/android/input.h +++ b/include/android/input.h @@ -778,6 +778,9 @@ enum { * proportion of the touch pad's size. For example, if a touch pad is 1000 units wide, and a * swipe gesture starts at X = 500 then moves to X = 400, this axis would have a value of * -0.1. + * + * These values are relative to the state from the last event, not accumulated, so developers + * should make sure to process this axis value for all batched historical events. */ AMOTION_EVENT_AXIS_GESTURE_X_OFFSET = 48, /** @@ -786,6 +789,34 @@ enum { * The same as {@link AMOTION_EVENT_AXIS_GESTURE_X_OFFSET}, but for the Y axis. */ AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET = 49, + /** + * Axis constant: X scroll distance axis of a motion event. + * + * - For a touch pad, reports the distance that should be scrolled in the X axis as a result of + * the user's two-finger scroll gesture, in display pixels. + * + * These values are relative to the state from the last event, not accumulated, so developers + * should make sure to process this axis value for all batched historical events. + */ + AMOTION_EVENT_AXIS_GESTURE_SCROLL_X_DISTANCE = 50, + /** + * Axis constant: Y scroll distance axis of a motion event. + * + * The same as {@link AMOTION_EVENT_AXIS_GESTURE_SCROLL_X_DISTANCE}, but for the Y axis. + */ + AMOTION_EVENT_AXIS_GESTURE_SCROLL_Y_DISTANCE = 51, + /** + * Axis constant: pinch scale factor of a motion event. + * + * - For a touch pad, reports the change in distance between the fingers when the user is making + * a pinch gesture, as a proportion of that distance when the gesture was last reported. For + * example, if the fingers were 50 units apart and are now 52 units apart, the scale factor + * would be 1.04. + * + * These values are relative to the state from the last event, not accumulated, so developers + * should make sure to process this axis value for all batched historical events. + */ + AMOTION_EVENT_AXIS_GESTURE_PINCH_SCALE_FACTOR = 52, /** * Note: This is not an "Axis constant". It does not represent any axis, nor should it be used @@ -793,7 +824,7 @@ enum { * to make some computations (like iterating through all possible axes) cleaner. * Please update the value accordingly if you add a new axis. */ - AMOTION_EVENT_MAXIMUM_VALID_AXIS_VALUE = AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET, + AMOTION_EVENT_MAXIMUM_VALID_AXIS_VALUE = AMOTION_EVENT_AXIS_GESTURE_PINCH_SCALE_FACTOR, // NOTE: If you add a new axis here you must also add it to several other files. // Refer to frameworks/base/core/java/android/view/MotionEvent.java for the full list. @@ -870,6 +901,21 @@ enum AMotionClassification : uint32_t { * The current event stream represents the user swiping with two fingers on a touchpad. */ AMOTION_EVENT_CLASSIFICATION_TWO_FINGER_SWIPE = 3, + /** + * Classification constant: multi-finger swipe. + * + * The current event stream represents the user swiping with three or more fingers on a + * touchpad. Unlike two-finger swipes, these are only to be handled by the system UI, which is + * why they have a separate constant from two-finger swipes. + */ + AMOTION_EVENT_CLASSIFICATION_MULTI_FINGER_SWIPE = 4, + /** + * Classification constant: pinch. + * + * The current event stream represents the user pinching with two fingers on a touchpad. The + * gesture is centered around the current cursor position. + */ + AMOTION_EVENT_CLASSIFICATION_PINCH = 5, }; /** diff --git a/include/input/Input.h b/include/input/Input.h index 1a35196036..62d84e1c6c 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -302,6 +302,17 @@ enum class MotionClassification : uint8_t { * The current gesture represents the user swiping with two fingers on a touchpad. */ TWO_FINGER_SWIPE = AMOTION_EVENT_CLASSIFICATION_TWO_FINGER_SWIPE, + /** + * The current gesture represents the user swiping with three or more fingers on a touchpad. + * Unlike two-finger swipes, these are only to be handled by the system UI, which is why they + * have a separate constant from two-finger swipes. + */ + MULTI_FINGER_SWIPE = AMOTION_EVENT_CLASSIFICATION_MULTI_FINGER_SWIPE, + /** + * The current gesture represents the user pinching with two fingers on a touchpad. The gesture + * is centered around the current cursor position. + */ + PINCH = AMOTION_EVENT_CLASSIFICATION_PINCH, }; /** @@ -368,7 +379,7 @@ constexpr float AMOTION_EVENT_INVALID_CURSOR_POSITION = std::numeric_limits<floa * Pointer coordinate data. */ struct PointerCoords { - enum { MAX_AXES = 30 }; // 30 so that sizeof(PointerCoords) == 128 + enum { MAX_AXES = 30 }; // 30 so that sizeof(PointerCoords) == 136 // Bitfield of axes that are present in this structure. uint64_t bits __attribute__((aligned(8))); @@ -377,8 +388,15 @@ struct PointerCoords { // for each axis that is present in the structure according to 'bits'. std::array<float, MAX_AXES> values; + // Whether these coordinate data were generated by resampling. + bool isResampled; + + static_assert(sizeof(bool) == 1); // Ensure padding is correctly sized. + uint8_t empty[7]; + inline void clear() { BitSet64::clear(bits); + isResampled = false; } bool isEmpty() const { @@ -769,6 +787,10 @@ public: AMOTION_EVENT_AXIS_ORIENTATION, pointerIndex, historicalIndex); } + inline bool isResampled(size_t pointerIndex, size_t historicalIndex) const { + return getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->isResampled; + } + ssize_t findPointerIndex(int32_t pointerId) const; void initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId, diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h index 5fa9fda64b..09933d32b1 100644 --- a/include/input/InputDevice.h +++ b/include/input/InputDevice.h @@ -24,7 +24,6 @@ #include <vector> #include <android/os/IInputConstants.h> -#include "android/hardware/input/InputDeviceCountryCode.h" namespace android { @@ -236,9 +235,7 @@ public: void initialize(int32_t id, int32_t generation, int32_t controllerNumber, const InputDeviceIdentifier& identifier, const std::string& alias, - bool isExternal, bool hasMic, - hardware::input::InputDeviceCountryCode countryCode = - hardware::input::InputDeviceCountryCode::INVALID); + bool isExternal, bool hasMic); inline int32_t getId() const { return mId; } inline int32_t getControllerNumber() const { return mControllerNumber; } @@ -250,7 +247,6 @@ public: } inline bool isExternal() const { return mIsExternal; } inline bool hasMic() const { return mHasMic; } - inline hardware::input::InputDeviceCountryCode getCountryCode() const { return mCountryCode; } inline uint32_t getSources() const { return mSources; } const MotionRange* getMotionRange(int32_t axis, uint32_t source) const; @@ -310,7 +306,6 @@ private: std::string mAlias; bool mIsExternal; bool mHasMic; - hardware::input::InputDeviceCountryCode mCountryCode; std::optional<KeyboardLayoutInfo> mKeyboardLayoutInfo; uint32_t mSources; int32_t mKeyboardType; diff --git a/include/input/KeyCharacterMap.h b/include/input/KeyCharacterMap.h index 867a08955c..b5e6f65e48 100644 --- a/include/input/KeyCharacterMap.h +++ b/include/input/KeyCharacterMap.h @@ -87,6 +87,9 @@ public: /* Combines this key character map with the provided overlay. */ void combine(const KeyCharacterMap& overlay); + /* Clears already applied layout overlay */ + void clearLayoutOverlay(); + /* Gets the keyboard type. */ KeyboardType getKeyboardType() const; diff --git a/include/input/MotionPredictor.h b/include/input/MotionPredictor.h index 6c07849c15..045e61bea2 100644 --- a/include/input/MotionPredictor.h +++ b/include/input/MotionPredictor.h @@ -63,14 +63,10 @@ public: MotionPredictor(nsecs_t predictionTimestampOffsetNanos, std::function<bool()> checkEnableMotionPrediction = isMotionPredictionEnabled); void record(const MotionEvent& event); - std::vector<std::unique_ptr<MotionEvent>> predict(); + std::vector<std::unique_ptr<MotionEvent>> predict(nsecs_t timestamp); bool isPredictionAvailable(int32_t deviceId, int32_t source); - void setExpectedPresentationTimeNanos(int64_t expectedPresentationTimeNanos); private: - std::mutex mLock; - int64_t mExpectedPresentationTimeNanos GUARDED_BY(mLock) = 0; - int64_t getExpectedPresentationTimeNanos(); std::vector<MotionEvent> mEvents; const nsecs_t mPredictionTimestampOffsetNanos; const std::function<bool()> mCheckMotionPredictionEnabled; diff --git a/include/input/VelocityTracker.h b/include/input/VelocityTracker.h index 62c3ae15ce..da97c3e855 100644 --- a/include/input/VelocityTracker.h +++ b/include/input/VelocityTracker.h @@ -51,33 +51,24 @@ public: static const size_t MAX_DEGREE = 4; // Estimator time base. - nsecs_t time; + nsecs_t time = 0; // Polynomial coefficients describing motion. - float coeff[MAX_DEGREE + 1]; + std::array<float, MAX_DEGREE + 1> coeff{}; // Polynomial degree (number of coefficients), or zero if no information is // available. - uint32_t degree; + uint32_t degree = 0; // Confidence (coefficient of determination), between 0 (no fit) and 1 (perfect fit). - float confidence; - - inline void clear() { - time = 0; - degree = 0; - confidence = 0; - for (size_t i = 0; i <= MAX_DEGREE; i++) { - coeff[i] = 0; - } - } + float confidence = 0; }; /* * Contains all available velocity data from a VelocityTracker. */ struct ComputedVelocity { - inline std::optional<float> getVelocity(int32_t axis, uint32_t id) const { + inline std::optional<float> getVelocity(int32_t axis, int32_t id) const { const auto& axisVelocities = mVelocities.find(axis); if (axisVelocities == mVelocities.end()) { return {}; @@ -91,7 +82,7 @@ public: return axisIdVelocity->second; } - inline void addVelocity(int32_t axis, uint32_t id, float velocity) { + inline void addVelocity(int32_t axis, int32_t id, float velocity) { mVelocities[axis][id] = velocity; } @@ -112,19 +103,13 @@ public: // Resets the velocity tracker state. void clear(); - // Resets the velocity tracker state for specific pointers. + // Resets the velocity tracker state for a specific pointer. // Call this method when some pointers have changed and may be reusing // an id that was assigned to a different pointer earlier. - void clearPointers(BitSet32 idBits); + void clearPointer(int32_t pointerId); - // Adds movement information for a set of pointers. - // The idBits bitfield specifies the pointer ids of the pointers whose data points - // are included in the movement. - // The positions map contains a mapping of an axis to positions array. - // The positions arrays contain information for each pointer in order by increasing id. - // Each array's size should be equal to the number of one bits in idBits. - void addMovement(nsecs_t eventTime, BitSet32 idBits, - const std::map<int32_t, std::vector<float>>& positions); + // Adds movement information for a pointer for a specific axis + void addMovement(nsecs_t eventTime, int32_t pointerId, int32_t axis, float position); // Adds movement information for all pointers in a MotionEvent, including historical samples. void addMovement(const MotionEvent* event); @@ -132,7 +117,7 @@ public: // Returns the velocity of the specified pointer id and axis in position units per second. // Returns empty optional if there is insufficient movement information for the pointer, or if // the given axis is not supported for velocity tracking. - std::optional<float> getVelocity(int32_t axis, uint32_t id) const; + std::optional<float> getVelocity(int32_t axis, int32_t pointerId) const; // Returns a ComputedVelocity instance with all available velocity data, using the given units // (reference: units == 1 means "per millisecond"), and clamping each velocity between @@ -142,15 +127,15 @@ public: // Gets an estimator for the recent movements of the specified pointer id for the given axis. // Returns false and clears the estimator if there is no information available // about the pointer. - bool getEstimator(int32_t axis, uint32_t id, Estimator* outEstimator) const; + std::optional<Estimator> getEstimator(int32_t axis, int32_t pointerId) const; // Gets the active pointer id, or -1 if none. - inline int32_t getActivePointerId() const { return mActivePointerId; } + inline int32_t getActivePointerId() const { return mActivePointerId.value_or(-1); } private: nsecs_t mLastEventTime; BitSet32 mCurrentPointerIdBits; - int32_t mActivePointerId; + std::optional<int32_t> mActivePointerId; // An override strategy passed in the constructor to be used for all axes. // This strategy will apply to all axes, unless the default strategy is specified here. @@ -182,10 +167,9 @@ protected: public: virtual ~VelocityTrackerStrategy() { } - virtual void clearPointers(BitSet32 idBits) = 0; - virtual void addMovement(nsecs_t eventTime, BitSet32 idBits, - const std::vector<float>& positions) = 0; - virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const = 0; + virtual void clearPointer(int32_t pointerId) = 0; + virtual void addMovement(nsecs_t eventTime, int32_t pointerId, float position) = 0; + virtual std::optional<VelocityTracker::Estimator> getEstimator(int32_t pointerId) const = 0; }; @@ -194,29 +178,28 @@ public: */ class LeastSquaresVelocityTrackerStrategy : public VelocityTrackerStrategy { public: - enum Weighting { + enum class Weighting { // No weights applied. All data points are equally reliable. - WEIGHTING_NONE, + NONE, // Weight by time delta. Data points clustered together are weighted less. - WEIGHTING_DELTA, + DELTA, // Weight such that points within a certain horizon are weighed more than those // outside of that horizon. - WEIGHTING_CENTRAL, + CENTRAL, // Weight such that points older than a certain amount are weighed less. - WEIGHTING_RECENT, + RECENT, }; // Degree must be no greater than Estimator::MAX_DEGREE. - LeastSquaresVelocityTrackerStrategy(uint32_t degree, Weighting weighting = WEIGHTING_NONE); - virtual ~LeastSquaresVelocityTrackerStrategy(); + LeastSquaresVelocityTrackerStrategy(uint32_t degree, Weighting weighting = Weighting::NONE); + ~LeastSquaresVelocityTrackerStrategy() override; - virtual void clearPointers(BitSet32 idBits); - void addMovement(nsecs_t eventTime, BitSet32 idBits, - const std::vector<float>& positions) override; - virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const; + void clearPointer(int32_t pointerId) override; + void addMovement(nsecs_t eventTime, int32_t pointerId, float position) override; + std::optional<VelocityTracker::Estimator> getEstimator(int32_t pointerId) const override; private: // Sample horizon. @@ -229,18 +212,15 @@ private: struct Movement { nsecs_t eventTime; - BitSet32 idBits; - float positions[MAX_POINTERS]; - - inline float getPosition(uint32_t id) const { return positions[idBits.getIndexOfBit(id)]; } + float position; }; - float chooseWeight(uint32_t index) const; + float chooseWeight(int32_t pointerId, uint32_t index) const; const uint32_t mDegree; const Weighting mWeighting; - uint32_t mIndex; - Movement mMovements[HISTORY_SIZE]; + std::map<int32_t /*pointerId*/, size_t /*positionInArray*/> mIndex; + std::map<int32_t /*pointerId*/, std::array<Movement, HISTORY_SIZE>> mMovements; }; @@ -251,12 +231,11 @@ class IntegratingVelocityTrackerStrategy : public VelocityTrackerStrategy { public: // Degree must be 1 or 2. IntegratingVelocityTrackerStrategy(uint32_t degree); - ~IntegratingVelocityTrackerStrategy(); + ~IntegratingVelocityTrackerStrategy() override; - virtual void clearPointers(BitSet32 idBits); - void addMovement(nsecs_t eventTime, BitSet32 idBits, - const std::vector<float>& positions) override; - virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const; + void clearPointer(int32_t pointerId) override; + void addMovement(nsecs_t eventTime, int32_t pointerId, float positions) override; + std::optional<VelocityTracker::Estimator> getEstimator(int32_t pointerId) const override; private: // Current state estimate for a particular pointer. @@ -283,12 +262,11 @@ private: class LegacyVelocityTrackerStrategy : public VelocityTrackerStrategy { public: LegacyVelocityTrackerStrategy(); - virtual ~LegacyVelocityTrackerStrategy(); + ~LegacyVelocityTrackerStrategy() override; - virtual void clearPointers(BitSet32 idBits); - void addMovement(nsecs_t eventTime, BitSet32 idBits, - const std::vector<float>& positions) override; - virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const; + void clearPointer(int32_t pointerId) override; + void addMovement(nsecs_t eventTime, int32_t pointerId, float position) override; + std::optional<VelocityTracker::Estimator> getEstimator(int32_t pointerId) const override; private: // Oldest sample to consider when calculating the velocity. @@ -302,25 +280,21 @@ private: struct Movement { nsecs_t eventTime; - BitSet32 idBits; - float positions[MAX_POINTERS]; - - inline float getPosition(uint32_t id) const { return positions[idBits.getIndexOfBit(id)]; } + float position; }; - uint32_t mIndex; - Movement mMovements[HISTORY_SIZE]; + std::map<int32_t /*pointerId*/, size_t /*positionInArray*/> mIndex; + std::map<int32_t /*pointerId*/, std::array<Movement, HISTORY_SIZE>> mMovements; }; class ImpulseVelocityTrackerStrategy : public VelocityTrackerStrategy { public: ImpulseVelocityTrackerStrategy(bool deltaValues); - virtual ~ImpulseVelocityTrackerStrategy(); + ~ImpulseVelocityTrackerStrategy() override; - virtual void clearPointers(BitSet32 idBits); - void addMovement(nsecs_t eventTime, BitSet32 idBits, - const std::vector<float>& positions) override; - virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const; + void clearPointer(int32_t pointerId) override; + void addMovement(nsecs_t eventTime, int32_t pointerId, float position) override; + std::optional<VelocityTracker::Estimator> getEstimator(int32_t pointerId) const override; private: // Sample horizon. @@ -333,10 +307,7 @@ private: struct Movement { nsecs_t eventTime; - BitSet32 idBits; - float positions[MAX_POINTERS]; - - inline float getPosition(uint32_t id) const { return positions[idBits.getIndexOfBit(id)]; } + float position; }; // Whether or not the input movement values for the strategy come in the form of delta values. @@ -344,8 +315,8 @@ private: // velocity calculation. const bool mDeltaValues; - size_t mIndex; - Movement mMovements[HISTORY_SIZE]; + std::map<int32_t /*pointerId*/, size_t /*positionInArray*/> mIndex; + std::map<int32_t /*pointerId*/, std::array<Movement, HISTORY_SIZE>> mMovements; }; } // namespace android diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index f17bb7da14..6bf7049fae 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -511,6 +511,7 @@ cc_library { visibility: [ ":__subpackages__", "//packages/modules/Virtualization:__subpackages__", + "//device/google/cuttlefish/shared/minidroid:__subpackages__", ], } diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp index 5e725a91f2..da5affb274 100644 --- a/libs/binder/Binder.cpp +++ b/libs/binder/Binder.cpp @@ -407,8 +407,10 @@ status_t BBinder::transact( AutoMutex lock(e->mLock); if (mRecordingOn) { Parcel emptyReply; + timespec ts; + timespec_get(&ts, TIME_UTC); auto transaction = - android::binder::debug::RecordedTransaction::fromDetails(code, flags, data, + android::binder::debug::RecordedTransaction::fromDetails(code, flags, ts, data, reply ? *reply : emptyReply, err); diff --git a/libs/binder/BinderRecordReplay.cpp b/libs/binder/BinderRecordReplay.cpp index 90c02a80b9..58bb106b37 100644 --- a/libs/binder/BinderRecordReplay.cpp +++ b/libs/binder/BinderRecordReplay.cpp @@ -16,10 +16,13 @@ #include <android-base/file.h> #include <android-base/logging.h> +#include <android-base/unique_fd.h> #include <binder/BinderRecordReplay.h> +#include <sys/mman.h> #include <algorithm> using android::Parcel; +using android::base::borrowed_fd; using android::base::unique_fd; using android::binder::debug::RecordedTransaction; @@ -30,123 +33,278 @@ static_assert(PADDING8(1) == 7); static_assert(PADDING8(7) == 1); static_assert(PADDING8(8) == 0); -// Transactions are sequentially recorded to the file descriptor in the following format: +// Transactions are sequentially recorded to a file descriptor. // -// RecordedTransaction.TransactionHeader (32 bytes) -// Sent Parcel data (getDataSize() bytes) -// padding (enough bytes to align the reply Parcel data to 8 bytes) -// Reply Parcel data (getReplySize() bytes) -// padding (enough bytes to align the next header to 8 bytes) -// [repeats with next transaction] +// An individual RecordedTransaction is written with the following format: // -// Warning: This format is non-stable +// WARNING: Though the following format is designed to be stable and +// extensible, it is under active development and should be considered +// unstable until this warning is removed. +// +// A RecordedTransaction is written to a file as a sequence of Chunks. +// +// A Chunk consists of a ChunkDescriptor, Data, Padding, and a Checksum. +// +// The ChunkDescriptor identifies the type of Data in the chunk, and the size +// of the Data. +// +// The Data may be any uint32 number of bytes in length in [0-0xfffffff0]. +// +// Padding is between [0-7] zero-bytes after the Data such that the Chunk ends +// on an 8-byte boundary. The ChunkDescriptor's dataSize does not include the +// size of Padding. +// +// The checksum is a 64-bit wide XOR of all previous data from the start of the +// ChunkDescriptor to the end of Padding. +// +// ┌───────────────────────────┐ +// │Chunk │ +// │┌────────────────────────┐ │ +// ││ChunkDescriptor │ │ +// ││┌───────────┬──────────┐│ │ +// │││chunkType │dataSize ├┼─┼─┐ +// │││uint32_t │uint32_t ││ │ │ +// ││└───────────┴──────────┘│ │ │ +// │└────────────────────────┘ │ │ +// │┌─────────────────────────┐│ │ +// ││Data ││ │ +// ││bytes * dataSize │◀─┘ +// ││ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤│ +// ││ Padding ││ +// │└───┴─────────────────────┘│ +// │┌─────────────────────────┐│ +// ││checksum ││ +// ││uint64_t ││ +// │└─────────────────────────┘│ +// └───────────────────────────┘ +// +// A RecordedTransaction is written as a Header Chunk with fields about the +// transaction, a Data Parcel chunk, a Reply Parcel Chunk, and an End Chunk. +// ┌──────────────────────┐ +// │ Header Chunk │ +// ├──────────────────────┤ +// │ Sent Parcel Chunk │ +// ├──────────────────────┤ +// │ Reply Parcel Chunk │ +// ├──────────────────────┤ +// ║ End Chunk ║ +// ╚══════════════════════╝ +// +// On reading a RecordedTransaction, an unrecognized chunk is checksummed +// then skipped according to size information in the ChunkDescriptor. Chunks +// are read and either assimilated or skipped until an End Chunk is +// encountered. This has three notable implications: +// +// 1. Older and newer implementations should be able to read one another's +// Transactions, though there will be loss of information. +// 2. With the exception of the End Chunk, Chunks can appear in any order +// and even repeat, though this is not recommended. +// 3. If any Chunk is repeated, old values will be overwritten by versions +// encountered later in the file. +// +// No effort is made to ensure the expected chunks are present. A single +// End Chunk may therefore produce an empty, meaningless RecordedTransaction. RecordedTransaction::RecordedTransaction(RecordedTransaction&& t) noexcept { - mHeader = {t.getCode(), t.getFlags(), t.getDataSize(), - t.getReplySize(), t.getReturnedStatus(), t.getVersion()}; - mSent.setData(t.getDataParcel().data(), t.getDataSize()); - mReply.setData(t.getReplyParcel().data(), t.getReplySize()); + mHeader = t.mHeader; + mSent.setData(t.getDataParcel().data(), t.getDataParcel().dataSize()); + mReply.setData(t.getReplyParcel().data(), t.getReplyParcel().dataSize()); } std::optional<RecordedTransaction> RecordedTransaction::fromDetails(uint32_t code, uint32_t flags, + timespec timestamp, const Parcel& dataParcel, const Parcel& replyParcel, status_t err) { RecordedTransaction t; t.mHeader = {code, flags, - static_cast<uint64_t>(dataParcel.dataSize()), - static_cast<uint64_t>(replyParcel.dataSize()), static_cast<int32_t>(err), - dataParcel.isForRpc() ? static_cast<uint32_t>(1) : static_cast<uint32_t>(0)}; + dataParcel.isForRpc() ? static_cast<uint32_t>(1) : static_cast<uint32_t>(0), + static_cast<int64_t>(timestamp.tv_sec), + static_cast<int32_t>(timestamp.tv_nsec), + 0}; - if (t.mSent.setData(dataParcel.data(), t.getDataSize()) != android::NO_ERROR) { - LOG(INFO) << "Failed to set sent parcel data."; + if (t.mSent.setData(dataParcel.data(), dataParcel.dataSize()) != android::NO_ERROR) { + LOG(ERROR) << "Failed to set sent parcel data."; return std::nullopt; } - if (t.mReply.setData(replyParcel.data(), t.getReplySize()) != android::NO_ERROR) { - LOG(INFO) << "Failed to set reply parcel data."; + if (t.mReply.setData(replyParcel.data(), replyParcel.dataSize()) != android::NO_ERROR) { + LOG(ERROR) << "Failed to set reply parcel data."; return std::nullopt; } return std::optional<RecordedTransaction>(std::move(t)); } +enum { + HEADER_CHUNK = 1, + DATA_PARCEL_CHUNK = 2, + REPLY_PARCEL_CHUNK = 3, + END_CHUNK = 0x00ffffff, +}; + +struct ChunkDescriptor { + uint32_t chunkType = 0; + uint32_t dataSize = 0; +}; +static_assert(sizeof(ChunkDescriptor) % 8 == 0); + +constexpr uint32_t kMaxChunkDataSize = 0xfffffff0; +typedef uint64_t transaction_checksum_t; + +static android::status_t readChunkDescriptor(borrowed_fd fd, ChunkDescriptor* chunkOut, + transaction_checksum_t* sum) { + if (!android::base::ReadFully(fd, chunkOut, sizeof(ChunkDescriptor))) { + LOG(ERROR) << "Failed to read Chunk Descriptor from fd " << fd.get(); + return android::UNKNOWN_ERROR; + } + + *sum ^= *reinterpret_cast<transaction_checksum_t*>(chunkOut); + return android::NO_ERROR; +} + std::optional<RecordedTransaction> RecordedTransaction::fromFile(const unique_fd& fd) { RecordedTransaction t; - if (!android::base::ReadFully(fd, &t.mHeader, sizeof(mHeader))) { - LOG(INFO) << "Failed to read transactionHeader from fd " << fd.get(); - return std::nullopt; - } - if (t.getVersion() != 0) { - LOG(INFO) << "File corrupted: transaction version is not 0."; - return std::nullopt; - } + ChunkDescriptor chunk; + const long pageSize = sysconf(_SC_PAGE_SIZE); + do { + transaction_checksum_t checksum = 0; + if (NO_ERROR != readChunkDescriptor(fd, &chunk, &checksum)) { + LOG(ERROR) << "Failed to read chunk descriptor."; + return std::nullopt; + } + off_t fdCurrentPosition = lseek(fd.get(), 0, SEEK_CUR); + off_t mmapPageAlignedStart = (fdCurrentPosition / pageSize) * pageSize; + off_t mmapPayloadStartOffset = fdCurrentPosition - mmapPageAlignedStart; - std::vector<uint8_t> bytes; - bytes.resize(t.getDataSize()); - if (!android::base::ReadFully(fd, bytes.data(), t.getDataSize())) { - LOG(INFO) << "Failed to read sent parcel data from fd " << fd.get(); - return std::nullopt; - } - if (t.mSent.setData(bytes.data(), t.getDataSize()) != android::NO_ERROR) { - LOG(INFO) << "Failed to set sent parcel data."; - return std::nullopt; - } + if (chunk.dataSize > kMaxChunkDataSize) { + LOG(ERROR) << "Chunk data exceeds maximum size."; + return std::nullopt; + } - uint8_t padding[7]; - if (!android::base::ReadFully(fd, padding, PADDING8(t.getDataSize()))) { - LOG(INFO) << "Failed to read sent parcel padding from fd " << fd.get(); - return std::nullopt; - } - if (std::any_of(padding, padding + 7, [](uint8_t i) { return i != 0; })) { - LOG(INFO) << "File corrupted: padding isn't 0."; - return std::nullopt; - } + size_t chunkPayloadSize = + chunk.dataSize + PADDING8(chunk.dataSize) + sizeof(transaction_checksum_t); - bytes.resize(t.getReplySize()); - if (!android::base::ReadFully(fd, bytes.data(), t.getReplySize())) { - LOG(INFO) << "Failed to read reply parcel data from fd " << fd.get(); - return std::nullopt; - } - if (t.mReply.setData(bytes.data(), t.getReplySize()) != android::NO_ERROR) { - LOG(INFO) << "Failed to set reply parcel data."; - return std::nullopt; - } + if (PADDING8(chunkPayloadSize) != 0) { + LOG(ERROR) << "Invalid chunk size, not aligned " << chunkPayloadSize; + return std::nullopt; + } - if (!android::base::ReadFully(fd, padding, PADDING8(t.getReplySize()))) { - LOG(INFO) << "Failed to read parcel padding from fd " << fd.get(); - return std::nullopt; - } - if (std::any_of(padding, padding + 7, [](uint8_t i) { return i != 0; })) { - LOG(INFO) << "File corrupted: padding isn't 0."; - return std::nullopt; - } + transaction_checksum_t* payloadMap = reinterpret_cast<transaction_checksum_t*>( + mmap(NULL, chunkPayloadSize + mmapPayloadStartOffset, PROT_READ, MAP_SHARED, + fd.get(), mmapPageAlignedStart)); + payloadMap += mmapPayloadStartOffset / + sizeof(transaction_checksum_t); // Skip chunk descriptor and required mmap + // page-alignment + if (payloadMap == MAP_FAILED) { + LOG(ERROR) << "Memory mapping failed for fd " << fd.get() << ": " << errno << " " + << strerror(errno); + return std::nullopt; + } + for (size_t checksumIndex = 0; + checksumIndex < chunkPayloadSize / sizeof(transaction_checksum_t); checksumIndex++) { + checksum ^= payloadMap[checksumIndex]; + } + if (checksum != 0) { + LOG(ERROR) << "Checksum failed."; + return std::nullopt; + } + lseek(fd.get(), chunkPayloadSize, SEEK_CUR); + + switch (chunk.chunkType) { + case HEADER_CHUNK: { + if (chunk.dataSize != static_cast<uint32_t>(sizeof(TransactionHeader))) { + LOG(ERROR) << "Header Chunk indicated size " << chunk.dataSize << "; Expected " + << sizeof(TransactionHeader) << "."; + return std::nullopt; + } + t.mHeader = *reinterpret_cast<TransactionHeader*>(payloadMap); + break; + } + case DATA_PARCEL_CHUNK: { + if (t.mSent.setData(reinterpret_cast<const unsigned char*>(payloadMap), + chunk.dataSize) != android::NO_ERROR) { + LOG(ERROR) << "Failed to set sent parcel data."; + return std::nullopt; + } + break; + } + case REPLY_PARCEL_CHUNK: { + if (t.mReply.setData(reinterpret_cast<const unsigned char*>(payloadMap), + chunk.dataSize) != android::NO_ERROR) { + LOG(ERROR) << "Failed to set reply parcel data."; + return std::nullopt; + } + break; + } + case END_CHUNK: + break; + default: + LOG(INFO) << "Unrecognized chunk."; + continue; + } + } while (chunk.chunkType != END_CHUNK); return std::optional<RecordedTransaction>(std::move(t)); } -android::status_t RecordedTransaction::dumpToFile(const unique_fd& fd) const { - if (!android::base::WriteFully(fd, &mHeader, sizeof(mHeader))) { - LOG(INFO) << "Failed to write transactionHeader to fd " << fd.get(); +android::status_t RecordedTransaction::writeChunk(borrowed_fd fd, uint32_t chunkType, + size_t byteCount, const uint8_t* data) const { + if (byteCount > kMaxChunkDataSize) { + LOG(ERROR) << "Chunk data exceeds maximum size"; + return BAD_VALUE; + } + ChunkDescriptor descriptor = {.chunkType = chunkType, + .dataSize = static_cast<uint32_t>(byteCount)}; + // Prepare Chunk content as byte * + const std::byte* descriptorBytes = reinterpret_cast<const std::byte*>(&descriptor); + const std::byte* dataBytes = reinterpret_cast<const std::byte*>(data); + + // Add Chunk to intermediate buffer, except checksum + std::vector<std::byte> buffer; + buffer.insert(buffer.end(), descriptorBytes, descriptorBytes + sizeof(ChunkDescriptor)); + buffer.insert(buffer.end(), dataBytes, dataBytes + byteCount); + std::byte zero{0}; + buffer.insert(buffer.end(), PADDING8(byteCount), zero); + + // Calculate checksum from buffer + transaction_checksum_t* checksumData = reinterpret_cast<transaction_checksum_t*>(buffer.data()); + transaction_checksum_t checksumValue = 0; + for (size_t idx = 0; idx < (buffer.size() / sizeof(transaction_checksum_t)); idx++) { + checksumValue ^= checksumData[idx]; + } + + // Write checksum to buffer + std::byte* checksumBytes = reinterpret_cast<std::byte*>(&checksumValue); + buffer.insert(buffer.end(), checksumBytes, checksumBytes + sizeof(transaction_checksum_t)); + + // Write buffer to file + if (!android::base::WriteFully(fd, buffer.data(), buffer.size())) { + LOG(ERROR) << "Failed to write chunk fd " << fd.get(); return UNKNOWN_ERROR; } - if (!android::base::WriteFully(fd, mSent.data(), getDataSize())) { - LOG(INFO) << "Failed to write sent parcel data to fd " << fd.get(); + return NO_ERROR; +} + +android::status_t RecordedTransaction::dumpToFile(const unique_fd& fd) const { + if (NO_ERROR != + writeChunk(fd, HEADER_CHUNK, sizeof(TransactionHeader), + reinterpret_cast<const uint8_t*>(&mHeader))) { + LOG(ERROR) << "Failed to write transactionHeader to fd " << fd.get(); return UNKNOWN_ERROR; } - const uint8_t zeros[7] = {0}; - if (!android::base::WriteFully(fd, zeros, PADDING8(getDataSize()))) { - LOG(INFO) << "Failed to write sent parcel padding to fd " << fd.get(); + if (NO_ERROR != writeChunk(fd, DATA_PARCEL_CHUNK, mSent.dataSize(), mSent.data())) { + LOG(ERROR) << "Failed to write sent Parcel to fd " << fd.get(); return UNKNOWN_ERROR; } - if (!android::base::WriteFully(fd, mReply.data(), getReplySize())) { - LOG(INFO) << "Failed to write reply parcel data to fd " << fd.get(); + if (NO_ERROR != writeChunk(fd, REPLY_PARCEL_CHUNK, mReply.dataSize(), mReply.data())) { + LOG(ERROR) << "Failed to write reply Parcel to fd " << fd.get(); return UNKNOWN_ERROR; } - if (!android::base::WriteFully(fd, zeros, PADDING8(getReplySize()))) { - LOG(INFO) << "Failed to write reply parcel padding to fd " << fd.get(); + if (NO_ERROR != writeChunk(fd, END_CHUNK, 0, NULL)) { + LOG(ERROR) << "Failed to write end chunk to fd " << fd.get(); return UNKNOWN_ERROR; } return NO_ERROR; @@ -160,18 +318,16 @@ uint32_t RecordedTransaction::getFlags() const { return mHeader.flags; } -uint64_t RecordedTransaction::getDataSize() const { - return mHeader.dataSize; -} - -uint64_t RecordedTransaction::getReplySize() const { - return mHeader.replySize; -} - int32_t RecordedTransaction::getReturnedStatus() const { return mHeader.statusReturned; } +timespec RecordedTransaction::getTimestamp() const { + time_t sec = mHeader.timestampSeconds; + int32_t nsec = mHeader.timestampNanoseconds; + return (timespec){.tv_sec = sec, .tv_nsec = nsec}; +} + uint32_t RecordedTransaction::getVersion() const { return mHeader.version; } diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp index 6d64e1ed3e..da58251149 100644 --- a/libs/binder/IPCThreadState.cpp +++ b/libs/binder/IPCThreadState.cpp @@ -1221,6 +1221,10 @@ status_t IPCThreadState::talkWithDriver(bool doReceive) return NO_ERROR; } + ALOGE_IF(mProcess->mDriverFD >= 0, + "Driver returned error (%s). This is a bug in either libbinder or the driver. This " + "thread's connection to %s will no longer work.", + statusToString(err).c_str(), mProcess->mDriverName.c_str()); return err; } diff --git a/libs/binder/include/binder/BinderRecordReplay.h b/libs/binder/include/binder/BinderRecordReplay.h index 25ed5e5ff8..ff983f08c2 100644 --- a/libs/binder/include/binder/BinderRecordReplay.h +++ b/libs/binder/include/binder/BinderRecordReplay.h @@ -34,17 +34,16 @@ public: static std::optional<RecordedTransaction> fromFile(const android::base::unique_fd& fd); // Filled with the arguments. static std::optional<RecordedTransaction> fromDetails(uint32_t code, uint32_t flags, - const Parcel& data, const Parcel& reply, - status_t err); + timespec timestamp, const Parcel& data, + const Parcel& reply, status_t err); RecordedTransaction(RecordedTransaction&& t) noexcept; [[nodiscard]] status_t dumpToFile(const android::base::unique_fd& fd) const; uint32_t getCode() const; uint32_t getFlags() const; - uint64_t getDataSize() const; - uint64_t getReplySize() const; int32_t getReturnedStatus() const; + timespec getTimestamp() const; uint32_t getVersion() const; const Parcel& getDataParcel() const; const Parcel& getReplyParcel() const; @@ -52,15 +51,19 @@ public: private: RecordedTransaction() = default; + android::status_t writeChunk(const android::base::borrowed_fd, uint32_t chunkType, + size_t byteCount, const uint8_t* data) const; + #pragma clang diagnostic push #pragma clang diagnostic error "-Wpadded" struct TransactionHeader { uint32_t code = 0; uint32_t flags = 0; - uint64_t dataSize = 0; - uint64_t replySize = 0; int32_t statusReturned = 0; uint32_t version = 0; // !0 iff Rpc + int64_t timestampSeconds = 0; + int32_t timestampNanoseconds = 0; + int32_t reserved = 0; }; #pragma clang diagnostic pop static_assert(sizeof(TransactionHeader) == 32); @@ -69,10 +72,6 @@ private: TransactionHeader mHeader; Parcel mSent; Parcel mReply; -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunused-private-field" - uint8_t mReserved[40]; -#pragma clang diagnostic pop }; } // namespace binder::debug diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h index 2af512ea3f..c78f870153 100644 --- a/libs/binder/include/binder/IServiceManager.h +++ b/libs/binder/include/binder/IServiceManager.h @@ -198,7 +198,10 @@ status_t getService(const String16& name, sp<INTERFACE>* outService) { const sp<IServiceManager> sm = defaultServiceManager(); if (sm != nullptr) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" *outService = interface_cast<INTERFACE>(sm->getService(name)); +#pragma clang diagnostic pop // getService deprecation if ((*outService) != nullptr) return NO_ERROR; } return NAME_NOT_FOUND; diff --git a/libs/binder/include/binder/ParcelFileDescriptor.h b/libs/binder/include/binder/ParcelFileDescriptor.h index 9896fd7a51..08d8e43106 100644 --- a/libs/binder/include/binder/ParcelFileDescriptor.h +++ b/libs/binder/include/binder/ParcelFileDescriptor.h @@ -42,6 +42,7 @@ public: android::status_t writeToParcel(android::Parcel* parcel) const override; android::status_t readFromParcel(const android::Parcel* parcel) override; + inline std::string toString() const { return "ParcelFileDescriptor:" + std::to_string(get()); } inline bool operator!=(const ParcelFileDescriptor& rhs) const { return mFd.get() != rhs.mFd.get(); } diff --git a/libs/binder/include/binder/ParcelableHolder.h b/libs/binder/include/binder/ParcelableHolder.h index 88790a803b..40fd30ae3f 100644 --- a/libs/binder/include/binder/ParcelableHolder.h +++ b/libs/binder/include/binder/ParcelableHolder.h @@ -111,6 +111,11 @@ public: Stability getStability() const override { return mStability; } + inline std::string toString() const { + return "ParcelableHolder:" + + (mParcelableName ? std::string(String8(mParcelableName.value()).c_str()) + : "<parceled>"); + } inline bool operator!=(const ParcelableHolder& rhs) const { return this != &rhs; } diff --git a/libs/binder/libbinder_rpc_unstable.cpp b/libs/binder/libbinder_rpc_unstable.cpp index 78dae4bd1f..e7943ddf2f 100644 --- a/libs/binder/libbinder_rpc_unstable.cpp +++ b/libs/binder/libbinder_rpc_unstable.cpp @@ -112,6 +112,13 @@ ARpcServer* ARpcServer_newInitUnixDomain(AIBinder* service, const char* name) { LOG(ERROR) << "Failed to get fd for the socket:" << name; return nullptr; } + // Control socket fds are inherited from init, so they don't have O_CLOEXEC set. + // But we don't want any child processes to inherit the socket we are running + // the server on, so attempt to set the flag now. + if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0) { + LOG(WARNING) << "Failed to set CLOEXEC on control socket with name " << name + << " error: " << errno; + } if (status_t status = server->setupRawSocketServer(std::move(fd)); status != OK) { LOG(ERROR) << "Failed to set up Unix Domain RPC server with name " << name << " error: " << statusToString(status).c_str(); diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp index 8ae7537486..58ed4188e2 100644 --- a/libs/binder/ndk/Android.bp +++ b/libs/binder/ndk/Android.bp @@ -66,10 +66,14 @@ cc_library { "service_manager.cpp", ], - shared_libs: [ + static_libs: [ "libandroid_runtime_lazy", "libbase", + ], + + shared_libs: [ "libbinder", + "liblog", "libutils", ], diff --git a/libs/binder/ndk/include_cpp/android/binder_to_string.h b/libs/binder/ndk/include_cpp/android/binder_to_string.h index 2a00736bc3..9b0d222147 100644 --- a/libs/binder/ndk/include_cpp/android/binder_to_string.h +++ b/libs/binder/ndk/include_cpp/android/binder_to_string.h @@ -49,12 +49,17 @@ #include <android/binder_interface_utils.h> #include <android/binder_parcelable_utils.h> #define HAS_NDK_INTERFACE -#else +#endif + +// TODO: some things include libbinder without having access to libbase. This is +// due to frameworks/native/include, which symlinks to libbinder headers, so even +// though we don't use it here, we detect a different header, so that we are more +// confident libbase will be included +#if __has_include(<binder/RpcSession.h>) #include <binder/IBinder.h> #include <binder/IInterface.h> -#include <binder/ParcelFileDescriptor.h> -#include <binder/ParcelableHolder.h> -#endif //_has_include +#define HAS_CPP_INTERFACE +#endif namespace android { namespace internal { @@ -104,10 +109,12 @@ class IsPointerLike { IsInstantiationOf<_U, sp>::value || // for IBinder and interface types in the C++ // backend #endif - IsInstantiationOf<_U, std::optional>::value || // for @nullable types in the - // C++/NDK backends - IsInstantiationOf<_U, std::shared_ptr>::value, // for interface types in the - // NDK backends + IsInstantiationOf<_U, std::optional>::value || // for @nullable types in the + // C++/NDK backends + IsInstantiationOf<_U, std::unique_ptr>::value || // for @nullable(heap=true) + // in C++/NDK backends + IsInstantiationOf<_U, std::shared_ptr>::value, // for interface types in the + // NDK backends std::true_type> _test(int); @@ -134,19 +141,19 @@ class IsIterable { template <typename _T> class ToEmptyString { template <typename _U> - static std::enable_if_t< + static std::enable_if_t<false #ifdef HAS_NDK_INTERFACE - std::is_base_of_v<::ndk::ICInterface, _U> + || std::is_base_of_v<::ndk::ICInterface, _U> #if __ANDROID_API__ >= 31 - || std::is_same_v<::ndk::AParcelableHolder, _U> + || std::is_same_v<::ndk::AParcelableHolder, _U> #endif -#else - std::is_base_of_v<IInterface, _U> || std::is_same_v<IBinder, _U> || - std::is_same_v<os::ParcelFileDescriptor, _U> || - std::is_same_v<os::ParcelableHolder, _U> +#endif // HAS_NDK_INTERFACE +#ifdef HAS_CPP_INTERFACE + || std::is_base_of_v<IInterface, _U> || + std::is_same_v<IBinder, _U> #endif - , - std::true_type> + , + std::true_type> _test(int); template <typename _U> static std::false_type _test(...); @@ -155,6 +162,11 @@ class ToEmptyString { enum { value = decltype(_test<_T>(0))::value }; }; +template <typename _T> +struct TypeDependentFalse { + enum { value = false }; +}; + } // namespace details template <typename _T> @@ -214,11 +226,27 @@ std::string ToString(const _T& t) { out << "]"; return out.str(); } else { - return "{no toString() implemented}"; + static_assert(details::TypeDependentFalse<_T>::value, "no toString implemented, huh?"); } } } // namespace internal } // namespace android +#ifdef HAS_STRONG_POINTER +#undef HAS_STRONG_POINTER +#endif + +#ifdef HAS_STRING16 +#undef HAS_STRING16 +#endif + +#ifdef HAS_NDK_INTERFACE +#undef HAS_NDK_INTERFACE +#endif + +#ifdef HAS_CPP_INTERFACE +#undef HAS_CPP_INTERFACE +#endif + /** @} */ diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h index 05677a8e5d..db2d2c1b09 100644 --- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h +++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h @@ -594,6 +594,9 @@ typedef void (*AIBinder_DeathRecipient_onBinderDied)(void* cookie) __INTRODUCED_ * * See also AIBinder_linkToDeath/AIBinder_unlinkToDeath. * + * WARNING: Make sure the lifetime of this cookie is long enough. If it is dynamically + * allocated, it should be deleted with AIBinder_DeathRecipient_setOnUnlinked. + * * Available since API level 33. * * \param cookie the cookie passed to AIBinder_linkToDeath. @@ -605,6 +608,9 @@ typedef void (*AIBinder_DeathRecipient_onBinderUnlinked)(void* cookie) __INTRODU * * Available since API level 29. * + * WARNING: Make sure the lifetime of this cookie is long enough. If it is dynamically + * allocated, it should be deleted with AIBinder_DeathRecipient_setOnUnlinked. + * * \param onBinderDied the callback to call when this death recipient is invoked. * * \return the newly constructed object (or null if onBinderDied is null). diff --git a/libs/binder/rust/src/parcel.rs b/libs/binder/rust/src/parcel.rs index 53a24afcf0..e4c568eab2 100644 --- a/libs/binder/rust/src/parcel.rs +++ b/libs/binder/rust/src/parcel.rs @@ -566,9 +566,6 @@ pub struct ReadableSubParcel<'a> { impl<'a> ReadableSubParcel<'a> { /// Read a type that implements [`Deserialize`] from the sub-parcel. pub fn read<D: Deserialize>(&self) -> Result<D> { - // The caller should have checked this, - // but it can't hurt to double-check - assert!(self.has_more_data()); D::deserialize(&self.parcel) } diff --git a/libs/binder/rust/src/parcel/parcelable.rs b/libs/binder/rust/src/parcel/parcelable.rs index c241e4d2d3..6f4c37530f 100644 --- a/libs/binder/rust/src/parcel/parcelable.rs +++ b/libs/binder/rust/src/parcel/parcelable.rs @@ -23,7 +23,7 @@ use crate::sys; use std::convert::{TryFrom, TryInto}; use std::ffi::c_void; use std::mem::{self, ManuallyDrop, MaybeUninit}; -use std::os::raw::{c_char, c_ulong}; +use std::os::raw::c_char; use std::ptr; use std::slice; @@ -103,12 +103,8 @@ pub trait SerializeArray: Serialize + Sized { unsafe extern "C" fn serialize_element<T: Serialize>( parcel: *mut sys::AParcel, array: *const c_void, - index: c_ulong, + index: usize, ) -> status_t { - // c_ulong and usize are the same, but we need the explicitly sized version - // so the function signature matches what bindgen generates. - let index = index as usize; - let slice: &[T] = slice::from_raw_parts(array.cast(), index + 1); let mut parcel = match BorrowedParcel::from_raw(parcel) { @@ -158,12 +154,8 @@ pub trait DeserializeArray: Deserialize { unsafe extern "C" fn deserialize_element<T: Deserialize>( parcel: *const sys::AParcel, array: *mut c_void, - index: c_ulong, + index: usize, ) -> status_t { - // c_ulong and usize are the same, but we need the explicitly sized version - // so the function signature matches what bindgen generates. - let index = index as usize; - let vec = &mut *(array as *mut Option<Vec<MaybeUninit<T>>>); let vec = match vec { Some(v) => v, diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp index a999d59d35..5db3187320 100644 --- a/libs/binder/tests/Android.bp +++ b/libs/binder/tests/Android.bp @@ -100,6 +100,7 @@ cc_test { "binderBinderUnitTest.cpp", "binderStatusUnitTest.cpp", "binderMemoryHeapBaseUnitTest.cpp", + "binderRecordedTransactionTest.cpp", ], shared_libs: [ "libbinder", diff --git a/libs/binder/tests/binderAllocationLimits.cpp b/libs/binder/tests/binderAllocationLimits.cpp index 6a6e0080a4..bc40864020 100644 --- a/libs/binder/tests/binderAllocationLimits.cpp +++ b/libs/binder/tests/binderAllocationLimits.cpp @@ -180,7 +180,11 @@ TEST(BinderAllocation, InterfaceDescriptorTransaction) { mallocs++; // Happens to be SM package length. We could switch to forking // and registering our own service if it became an issue. +#if defined(__LP64__) EXPECT_EQ(bytes, 78); +#else + EXPECT_EQ(bytes, 70); +#endif }); a_binder->getInterfaceDescriptor(); diff --git a/libs/binder/tests/binderRecordedTransactionTest.cpp b/libs/binder/tests/binderRecordedTransactionTest.cpp new file mode 100644 index 0000000000..67553fc811 --- /dev/null +++ b/libs/binder/tests/binderRecordedTransactionTest.cpp @@ -0,0 +1,134 @@ +/* + * 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. + */ + +#include <binder/BinderRecordReplay.h> +#include <gtest/gtest.h> +#include <utils/Errors.h> + +using android::Parcel; +using android::status_t; +using android::base::unique_fd; +using android::binder::debug::RecordedTransaction; + +TEST(BinderRecordedTransaction, RoundTripEncoding) { + Parcel d; + d.writeInt32(12); + d.writeInt64(2); + Parcel r; + r.writeInt32(99); + timespec ts = {1232456, 567890}; + auto transaction = RecordedTransaction::fromDetails(1, 42, ts, d, r, 0); + + auto file = std::tmpfile(); + auto fd = unique_fd(fcntl(fileno(file), F_DUPFD, 1)); + + status_t status = transaction->dumpToFile(fd); + ASSERT_EQ(android::NO_ERROR, status); + + std::rewind(file); + + auto retrievedTransaction = RecordedTransaction::fromFile(fd); + + EXPECT_EQ(retrievedTransaction->getCode(), 1); + EXPECT_EQ(retrievedTransaction->getFlags(), 42); + EXPECT_EQ(retrievedTransaction->getTimestamp().tv_sec, ts.tv_sec); + EXPECT_EQ(retrievedTransaction->getTimestamp().tv_nsec, ts.tv_nsec); + EXPECT_EQ(retrievedTransaction->getDataParcel().dataSize(), 12); + EXPECT_EQ(retrievedTransaction->getReplyParcel().dataSize(), 4); + EXPECT_EQ(retrievedTransaction->getReturnedStatus(), 0); + EXPECT_EQ(retrievedTransaction->getVersion(), 0); + + EXPECT_EQ(retrievedTransaction->getDataParcel().readInt32(), 12); + EXPECT_EQ(retrievedTransaction->getDataParcel().readInt64(), 2); + EXPECT_EQ(retrievedTransaction->getReplyParcel().readInt32(), 99); +} + +TEST(BinderRecordedTransaction, Checksum) { + Parcel d; + d.writeInt32(12); + d.writeInt64(2); + Parcel r; + r.writeInt32(99); + timespec ts = {1232456, 567890}; + auto transaction = RecordedTransaction::fromDetails(1, 42, ts, d, r, 0); + + auto file = std::tmpfile(); + auto fd = unique_fd(fcntl(fileno(file), F_DUPFD, 1)); + + status_t status = transaction->dumpToFile(fd); + ASSERT_EQ(android::NO_ERROR, status); + + lseek(fd.get(), 9, SEEK_SET); + uint32_t badData = 0xffffffff; + write(fd.get(), &badData, sizeof(uint32_t)); + std::rewind(file); + + auto retrievedTransaction = RecordedTransaction::fromFile(fd); + + EXPECT_FALSE(retrievedTransaction.has_value()); +} + +TEST(BinderRecordedTransaction, PayloadsExceedPageBoundaries) { + // File contents are read with mmap. + // This test verifies that transactions are read from portions + // of files that cross page boundaries and don't start at a + // page boundary offset of the fd. + const size_t pageSize = sysconf(_SC_PAGE_SIZE); + const size_t largeDataSize = pageSize + 100; + std::vector<uint8_t> largePayload; + uint8_t filler = 0xaa; + largePayload.insert(largePayload.end(), largeDataSize, filler); + Parcel d; + d.writeInt32(12); + d.writeInt64(2); + d.writeByteVector(largePayload); + Parcel r; + r.writeInt32(99); + timespec ts = {1232456, 567890}; + auto transaction = RecordedTransaction::fromDetails(1, 42, ts, d, r, 0); + + auto file = std::tmpfile(); + auto fd = unique_fd(fcntl(fileno(file), F_DUPFD, 1)); + + // Write to file twice + status_t status = transaction->dumpToFile(fd); + ASSERT_EQ(android::NO_ERROR, status); + status = transaction->dumpToFile(fd); + ASSERT_EQ(android::NO_ERROR, status); + + std::rewind(file); + + for (int i = 0; i < 2; i++) { + auto retrievedTransaction = RecordedTransaction::fromFile(fd); + + EXPECT_EQ(retrievedTransaction->getCode(), 1); + EXPECT_EQ(retrievedTransaction->getFlags(), 42); + EXPECT_EQ(retrievedTransaction->getTimestamp().tv_sec, ts.tv_sec); + EXPECT_EQ(retrievedTransaction->getTimestamp().tv_nsec, ts.tv_nsec); + EXPECT_EQ(retrievedTransaction->getDataParcel().dataSize(), d.dataSize()); + EXPECT_EQ(retrievedTransaction->getReplyParcel().dataSize(), 4); + EXPECT_EQ(retrievedTransaction->getReturnedStatus(), 0); + EXPECT_EQ(retrievedTransaction->getVersion(), 0); + + EXPECT_EQ(retrievedTransaction->getDataParcel().readInt32(), 12); + EXPECT_EQ(retrievedTransaction->getDataParcel().readInt64(), 2); + std::optional<std::vector<uint8_t>> payloadOut; + EXPECT_EQ(retrievedTransaction->getDataParcel().readByteVector(&payloadOut), android::OK); + EXPECT_EQ(payloadOut.value(), largePayload); + + EXPECT_EQ(retrievedTransaction->getReplyParcel().readInt32(), 99); + } +} diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp index 739c2171f3..8afa49b911 100644 --- a/libs/binder/tests/binderRpcTest.cpp +++ b/libs/binder/tests/binderRpcTest.cpp @@ -372,12 +372,12 @@ TEST_P(BinderRpc, ThreadPoolGreaterThanEqualRequested) { ts.push_back(std::thread([&] { proc.rootIface->lockUnlock(); })); } - usleep(10000); // give chance for calls on other threads + usleep(100000); // give chance for calls on other threads // other calls still work EXPECT_EQ(OK, proc.rootBinder->pingBinder()); - constexpr size_t blockTimeMs = 50; + constexpr size_t blockTimeMs = 100; size_t epochMsBefore = epochMillis(); // after this, we should never see a response within this time EXPECT_OK(proc.rootIface->unlockInMsAsync(blockTimeMs)); diff --git a/libs/binder/tests/binderUtilsHostTest.cpp b/libs/binder/tests/binderUtilsHostTest.cpp index 4330e3e615..25e286ce0c 100644 --- a/libs/binder/tests/binderUtilsHostTest.cpp +++ b/libs/binder/tests/binderUtilsHostTest.cpp @@ -37,17 +37,24 @@ TEST(UtilsHost, ExecuteImmediately) { EXPECT_EQ(result->stdoutStr, "foo\n"); } +template <typename T> +auto millisSince(std::chrono::time_point<T> now) { + auto elapsed = std::chrono::system_clock::now() - now; + return std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count(); +} + TEST(UtilsHost, ExecuteLongRunning) { - auto now = std::chrono::system_clock::now(); + auto start = std::chrono::system_clock::now(); { - std::vector<std::string> args{"sh", "-c", - "sleep 0.5 && echo -n f && sleep 0.5 && echo oo && sleep 1"}; - auto result = execute(std::move(args), [](const CommandResult& commandResult) { + std::vector<std::string> + args{"sh", "-c", "sleep 0.5 && echo -n f && sleep 0.5 && echo oo && sleep 100"}; + auto result = execute(std::move(args), [&](const CommandResult& commandResult) { + std::cout << millisSince(start) + << "ms: GOT PARTIAL COMMAND RESULT:" << commandResult.stdoutStr << std::endl; return android::base::EndsWith(commandResult.stdoutStr, "\n"); }); - auto elapsed = std::chrono::system_clock::now() - now; - auto elapsedMs = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count(); + auto elapsedMs = millisSince(start); EXPECT_GE(elapsedMs, 1000); EXPECT_LT(elapsedMs, 2000); @@ -58,22 +65,21 @@ TEST(UtilsHost, ExecuteLongRunning) { // ~CommandResult() called, child process is killed. // Assert that the second sleep does not finish. - auto elapsed = std::chrono::system_clock::now() - now; - auto elapsedMs = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count(); - EXPECT_LT(elapsedMs, 2000); + EXPECT_LT(millisSince(start), 2000); } TEST(UtilsHost, ExecuteLongRunning2) { - auto now = std::chrono::system_clock::now(); + auto start = std::chrono::system_clock::now(); { std::vector<std::string> args{"sh", "-c", - "sleep 2 && echo -n f && sleep 2 && echo oo && sleep 2"}; - auto result = execute(std::move(args), [](const CommandResult& commandResult) { + "sleep 2 && echo -n f && sleep 2 && echo oo && sleep 100"}; + auto result = execute(std::move(args), [&](const CommandResult& commandResult) { + std::cout << millisSince(start) + << "ms: GOT PARTIAL COMMAND RESULT:" << commandResult.stdoutStr << std::endl; return android::base::EndsWith(commandResult.stdoutStr, "\n"); }); - auto elapsed = std::chrono::system_clock::now() - now; - auto elapsedMs = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count(); + auto elapsedMs = millisSince(start); EXPECT_GE(elapsedMs, 4000); EXPECT_LT(elapsedMs, 6000); @@ -84,9 +90,7 @@ TEST(UtilsHost, ExecuteLongRunning2) { // ~CommandResult() called, child process is killed. // Assert that the second sleep does not finish. - auto elapsed = std::chrono::system_clock::now() - now; - auto elapsedMs = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count(); - EXPECT_LT(elapsedMs, 6000); + EXPECT_LT(millisSince(start), 6000); } TEST(UtilsHost, KillWithSigKill) { diff --git a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp index 53e7de4350..08eb27a8a7 100644 --- a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp +++ b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp @@ -199,5 +199,25 @@ std::vector<ParcelRead<NdkParcelAdapter>> BINDER_NDK_PARCEL_READ_FUNCTIONS{ binder_status_t status = genericDataParcelable.readFromParcel(p.aParcel()); FUZZ_LOG() << "status: " << status; }, + [](const NdkParcelAdapter& p, FuzzedDataProvider& provider) { + FUZZ_LOG() << "about to marshal AParcel"; + size_t start = provider.ConsumeIntegral<size_t>(); + // limit 1MB to avoid OOM issues + size_t len = provider.ConsumeIntegralInRange<size_t>(0, 1000000); + uint8_t buffer[len]; + binder_status_t status = AParcel_marshal(p.aParcel(), buffer, start, len); + FUZZ_LOG() << "status: " << status; + }, + [](const NdkParcelAdapter& /*p*/, FuzzedDataProvider& provider) { + FUZZ_LOG() << "about to unmarshal AParcel"; + size_t len = provider.ConsumeIntegralInRange<size_t>(0, provider.remaining_bytes()); + std::vector<uint8_t> parcelData = provider.ConsumeBytes<uint8_t>(len); + const uint8_t* buffer = parcelData.data(); + const size_t bufferLen = parcelData.size(); + NdkParcelAdapter adapter; + binder_status_t status = AParcel_unmarshal(adapter.aParcel(), buffer, bufferLen); + FUZZ_LOG() << "status: " << status; + }, + }; // clang-format on diff --git a/libs/binder/tests/rpc_fuzzer/main.cpp b/libs/binder/tests/rpc_fuzzer/main.cpp index f68a56144d..b8ae84d5c0 100644 --- a/libs/binder/tests/rpc_fuzzer/main.cpp +++ b/libs/binder/tests/rpc_fuzzer/main.cpp @@ -133,8 +133,13 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { bool hangupBeforeShutdown = provider.ConsumeBool(); + // b/260736889 - limit arbitrarily, due to thread resource exhaustion, which currently + // aborts. Servers should consider RpcServer::setConnectionFilter instead. + constexpr size_t kMaxConnections = 1000; + while (provider.remaining_bytes() > 0) { - if (connections.empty() || provider.ConsumeBool()) { + if (connections.empty() || + (connections.size() < kMaxConnections && provider.ConsumeBool())) { base::unique_fd fd(TEMP_FAILURE_RETRY(socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0))); CHECK_NE(fd.get(), -1); CHECK_EQ(0, diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index 97e45c6d47..797d6aedcc 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -483,20 +483,18 @@ void BLASTBufferQueue::releaseBuffer(const ReleaseCallbackId& callbackId, mSyncedFrameNumbers.erase(callbackId.framenumber); } -void BLASTBufferQueue::acquireNextBufferLocked( +status_t BLASTBufferQueue::acquireNextBufferLocked( const std::optional<SurfaceComposerClient::Transaction*> transaction) { // If the next transaction is set, we want to guarantee the our acquire will not fail, so don't // include the extra buffer when checking if we can acquire the next buffer. - const bool includeExtraAcquire = !transaction; - const bool maxAcquired = maxBuffersAcquired(includeExtraAcquire); - if (mNumFrameAvailable == 0 || maxAcquired) { - BQA_LOGV("Can't process next buffer maxBuffersAcquired=%s", boolToString(maxAcquired)); - return; + if (mNumFrameAvailable == 0) { + BQA_LOGV("Can't process next buffer. No available frames"); + return NOT_ENOUGH_DATA; } if (mSurfaceControl == nullptr) { BQA_LOGE("ERROR : surface control is null"); - return; + return NAME_NOT_FOUND; } SurfaceComposerClient::Transaction localTransaction; @@ -513,10 +511,10 @@ void BLASTBufferQueue::acquireNextBufferLocked( mBufferItemConsumer->acquireBuffer(&bufferItem, 0 /* expectedPresent */, false); if (status == BufferQueue::NO_BUFFER_AVAILABLE) { BQA_LOGV("Failed to acquire a buffer, err=NO_BUFFER_AVAILABLE"); - return; + return status; } else if (status != OK) { BQA_LOGE("Failed to acquire a buffer, err=%s", statusToString(status).c_str()); - return; + return status; } auto buffer = bufferItem.mGraphicBuffer; @@ -526,7 +524,7 @@ void BLASTBufferQueue::acquireNextBufferLocked( if (buffer == nullptr) { mBufferItemConsumer->releaseBuffer(bufferItem, Fence::NO_FENCE); BQA_LOGE("Buffer was empty"); - return; + return BAD_VALUE; } if (rejectBuffer(bufferItem)) { @@ -535,8 +533,7 @@ void BLASTBufferQueue::acquireNextBufferLocked( mSize.width, mSize.height, mRequestedSize.width, mRequestedSize.height, buffer->getWidth(), buffer->getHeight(), bufferItem.mTransform); mBufferItemConsumer->releaseBuffer(bufferItem, Fence::NO_FENCE); - acquireNextBufferLocked(transaction); - return; + return acquireNextBufferLocked(transaction); } mNumAcquired++; @@ -624,6 +621,7 @@ void BLASTBufferQueue::acquireNextBufferLocked( bufferItem.mTimestamp, bufferItem.mIsAutoTimestamp ? "(auto)" : "", static_cast<uint32_t>(mPendingTransactions.size()), bufferItem.mGraphicBuffer->getId(), bufferItem.mAutoRefresh ? " mAutoRefresh" : "", bufferItem.mTransform); + return OK; } Rect BLASTBufferQueue::computeCrop(const BufferItem& item) { @@ -647,32 +645,6 @@ void BLASTBufferQueue::acquireAndReleaseBuffer() { mBufferItemConsumer->releaseBuffer(bufferItem, bufferItem.mFence); } -void BLASTBufferQueue::flushAndWaitForFreeBuffer(std::unique_lock<std::mutex>& lock) { - BBQ_TRACE(); - if (!mSyncedFrameNumbers.empty() && mNumFrameAvailable > 0) { - // We are waiting on a previous sync's transaction callback so allow another sync - // transaction to proceed. - // - // We need to first flush out the transactions that were in between the two syncs. - // We do this by merging them into mSyncTransaction so any buffer merging will get - // a release callback invoked. The release callback will be async so we need to wait - // on max acquired to make sure we have the capacity to acquire another buffer. - if (maxBuffersAcquired(false /* includeExtraAcquire */)) { - BQA_LOGD("waiting to flush shadow queue..."); - mCallbackCV.wait(lock); - } - while (mNumFrameAvailable > 0) { - // flush out the shadow queue - acquireAndReleaseBuffer(); - } - } - - while (maxBuffersAcquired(false /* includeExtraAcquire */)) { - BQA_LOGD("waiting for free buffer."); - mCallbackCV.wait(lock); - } -} - void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) { std::function<void(SurfaceComposerClient::Transaction*)> prevCallback = nullptr; SurfaceComposerClient::Transaction* prevTransaction = nullptr; @@ -685,7 +657,6 @@ void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) { BQA_LOGV("onFrameAvailable-start syncTransactionSet=%s", boolToString(syncTransactionSet)); if (syncTransactionSet) { - bool mayNeedToWaitForBuffer = true; // If we are going to re-use the same mSyncTransaction, release the buffer that may // already be set in the Transaction. This is to allow us a free slot early to continue // processing a new buffer. @@ -696,14 +667,20 @@ void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) { bufferData->frameNumber); releaseBuffer(bufferData->generateReleaseCallbackId(), bufferData->acquireFence); - // Because we just released a buffer, we know there's no need to wait for a free - // buffer. - mayNeedToWaitForBuffer = false; } } - if (mayNeedToWaitForBuffer) { - flushAndWaitForFreeBuffer(_lock); + if (waitForTransactionCallback) { + // We are waiting on a previous sync's transaction callback so allow another sync + // transaction to proceed. + // + // We need to first flush out the transactions that were in between the two syncs. + // We do this by merging them into mSyncTransaction so any buffer merging will get + // a release callback invoked. + while (mNumFrameAvailable > 0) { + // flush out the shadow queue + acquireAndReleaseBuffer(); + } } } @@ -719,7 +696,12 @@ void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) { item.mFrameNumber, boolToString(syncTransactionSet)); if (syncTransactionSet) { - acquireNextBufferLocked(mSyncTransaction); + // If there's no available buffer and we're in a sync transaction, we need to wait + // instead of returning since we guarantee a buffer will be acquired for the sync. + while (acquireNextBufferLocked(mSyncTransaction) == BufferQueue::NO_BUFFER_AVAILABLE) { + BQA_LOGD("waiting for available buffer"); + mCallbackCV.wait(_lock); + } // Only need a commit callback when syncing to ensure the buffer that's synced has been // sent to SF @@ -829,15 +811,6 @@ bool BLASTBufferQueue::rejectBuffer(const BufferItem& item) { return mSize != bufferSize; } -// Check if we have acquired the maximum number of buffers. -// Consumer can acquire an additional buffer if that buffer is not droppable. Set -// includeExtraAcquire is true to include this buffer to the count. Since this depends on the state -// of the buffer, the next acquire may return with NO_BUFFER_AVAILABLE. -bool BLASTBufferQueue::maxBuffersAcquired(bool includeExtraAcquire) const { - int maxAcquiredBuffers = mMaxAcquiredBuffers + (includeExtraAcquire ? 2 : 1); - return mNumAcquired >= maxAcquiredBuffers; -} - class BBQSurface : public Surface { private: std::mutex mMutex; diff --git a/libs/gui/aidl/android/gui/OverlayProperties.aidl b/libs/gui/aidl/android/gui/OverlayProperties.aidl index 75cea157aa..1af574655b 100644 --- a/libs/gui/aidl/android/gui/OverlayProperties.aidl +++ b/libs/gui/aidl/android/gui/OverlayProperties.aidl @@ -23,4 +23,6 @@ parcelable OverlayProperties { int[] dataspaces; } SupportedBufferCombinations[] combinations; + + boolean supportMixedColorSpaces; } diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h index 47dcc42e16..001d8e5233 100644 --- a/libs/gui/include/gui/BLASTBufferQueue.h +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -130,12 +130,11 @@ private: void createBufferQueue(sp<IGraphicBufferProducer>* outProducer, sp<IGraphicBufferConsumer>* outConsumer); - void acquireNextBufferLocked( + status_t acquireNextBufferLocked( const std::optional<SurfaceComposerClient::Transaction*> transaction) REQUIRES(mMutex); Rect computeCrop(const BufferItem& item) REQUIRES(mMutex); // Return true if we need to reject the buffer based on the scaling mode and the buffer size. bool rejectBuffer(const BufferItem& item) REQUIRES(mMutex); - bool maxBuffersAcquired(bool includeExtraAcquire) const REQUIRES(mMutex); static PixelFormat convertBufferFormat(PixelFormat& format); void mergePendingTransactions(SurfaceComposerClient::Transaction* t, uint64_t frameNumber) REQUIRES(mMutex); @@ -144,7 +143,6 @@ private: void acquireAndReleaseBuffer() REQUIRES(mMutex); void releaseBuffer(const ReleaseCallbackId& callbackId, const sp<Fence>& releaseFence) REQUIRES(mMutex); - void flushAndWaitForFreeBuffer(std::unique_lock<std::mutex>& lock); std::string mName; // Represents the queued buffer count from buffer queue, diff --git a/libs/input/Android.bp b/libs/input/Android.bp index 8ddd18d84a..8f41cc1872 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -26,7 +26,6 @@ package { filegroup { name: "inputconstants_aidl", srcs: [ - "android/hardware/input/InputDeviceCountryCode.aidl", "android/os/IInputConstants.aidl", "android/os/InputEventInjectionResult.aidl", "android/os/InputEventInjectionSync.aidl", diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index d893cb99ba..c7964393e0 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -72,6 +72,10 @@ const char* motionClassificationToString(MotionClassification classification) { return "DEEP_PRESS"; case MotionClassification::TWO_FINGER_SWIPE: return "TWO_FINGER_SWIPE"; + case MotionClassification::MULTI_FINGER_SWIPE: + return "MULTI_FINGER_SWIPE"; + case MotionClassification::PINCH: + return "PINCH"; } } @@ -411,6 +415,8 @@ status_t PointerCoords::readFromParcel(Parcel* parcel) { for (uint32_t i = 0; i < count; i++) { values[i] = parcel->readFloat(); } + + isResampled = parcel->readBool(); return OK; } @@ -421,6 +427,8 @@ status_t PointerCoords::writeToParcel(Parcel* parcel) const { for (uint32_t i = 0; i < count; i++) { parcel->writeFloat(values[i]); } + + parcel->writeBool(isResampled); return OK; } #endif @@ -440,6 +448,9 @@ bool PointerCoords::operator==(const PointerCoords& other) const { return false; } } + if (isResampled != other.isResampled) { + return false; + } return true; } diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp index fb6c590b6e..87333f2c76 100644 --- a/libs/input/InputDevice.cpp +++ b/libs/input/InputDevice.cpp @@ -26,7 +26,6 @@ #include <input/InputEventLabels.h> using android::base::StringPrintf; -using android::hardware::input::InputDeviceCountryCode; namespace android { @@ -178,7 +177,6 @@ InputDeviceInfo::InputDeviceInfo(const InputDeviceInfo& other) mAlias(other.mAlias), mIsExternal(other.mIsExternal), mHasMic(other.mHasMic), - mCountryCode(other.mCountryCode), mKeyboardLayoutInfo(other.mKeyboardLayoutInfo), mSources(other.mSources), mKeyboardType(other.mKeyboardType), @@ -197,7 +195,7 @@ InputDeviceInfo::~InputDeviceInfo() { void InputDeviceInfo::initialize(int32_t id, int32_t generation, int32_t controllerNumber, const InputDeviceIdentifier& identifier, const std::string& alias, - bool isExternal, bool hasMic, InputDeviceCountryCode countryCode) { + bool isExternal, bool hasMic) { mId = id; mGeneration = generation; mControllerNumber = controllerNumber; @@ -205,7 +203,6 @@ void InputDeviceInfo::initialize(int32_t id, int32_t generation, int32_t control mAlias = alias; mIsExternal = isExternal; mHasMic = hasMic; - mCountryCode = countryCode; mSources = 0; mKeyboardType = AINPUT_KEYBOARD_TYPE_NONE; mHasVibrator = false; diff --git a/libs/input/InputEventLabels.cpp b/libs/input/InputEventLabels.cpp index dd7cbb5ba9..7159e27b13 100644 --- a/libs/input/InputEventLabels.cpp +++ b/libs/input/InputEventLabels.cpp @@ -394,7 +394,10 @@ namespace android { DEFINE_AXIS(GENERIC_15), \ DEFINE_AXIS(GENERIC_16), \ DEFINE_AXIS(GESTURE_X_OFFSET), \ - DEFINE_AXIS(GESTURE_Y_OFFSET) + DEFINE_AXIS(GESTURE_Y_OFFSET), \ + DEFINE_AXIS(GESTURE_SCROLL_X_DISTANCE), \ + DEFINE_AXIS(GESTURE_SCROLL_Y_DISTANCE), \ + DEFINE_AXIS(GESTURE_PINCH_SCALE_FACTOR) // NOTE: If you add new LEDs here, you must also add them to Input.h #define LEDS_SEQUENCE \ diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index 8d8433b973..9f0a314041 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -267,6 +267,8 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { memcpy(&msg->body.motion.pointers[i].coords.values[0], &body.motion.pointers[i].coords.values[0], count * (sizeof(body.motion.pointers[i].coords.values[0]))); + msg->body.motion.pointers[i].coords.isResampled = + body.motion.pointers[i].coords.isResampled; } break; } @@ -1079,6 +1081,7 @@ void InputConsumer::rewriteMessage(TouchState& state, InputMessage& msg) { #endif msgCoords.setAxisValue(AMOTION_EVENT_AXIS_X, resampleCoords.getX()); msgCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, resampleCoords.getY()); + msgCoords.isResampled = true; } else { state.lastResample.idBits.clearBit(id); } @@ -1191,6 +1194,8 @@ void InputConsumer::resampleTouchState(nsecs_t sampleTime, MotionEvent* event, // We maintain the previously resampled value for this pointer (stored in // oldLastResample) when the coordinates for this pointer haven't changed since then. // This way we don't introduce artificial jitter when pointers haven't actually moved. + // The isResampled flag isn't cleared as the values don't reflect what the device is + // actually reporting. // We know here that the coordinates for the pointer haven't changed because we // would've cleared the resampled bit in rewriteMessage if they had. We can't modify @@ -1209,6 +1214,7 @@ void InputConsumer::resampleTouchState(nsecs_t sampleTime, MotionEvent* event, lerp(currentCoords.getX(), otherCoords.getX(), alpha)); resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, lerp(currentCoords.getY(), otherCoords.getY(), alpha)); + resampledCoords.isResampled = true; #if DEBUG_RESAMPLING ALOGD("[%d] - out (%0.3f, %0.3f), cur (%0.3f, %0.3f), " "other (%0.3f, %0.3f), alpha %0.3f", diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp index fa5c41f657..6bfac40932 100644 --- a/libs/input/KeyCharacterMap.cpp +++ b/libs/input/KeyCharacterMap.cpp @@ -255,6 +255,13 @@ void KeyCharacterMap::combine(const KeyCharacterMap& overlay) { mLayoutOverlayApplied = true; } +void KeyCharacterMap::clearLayoutOverlay() { + if (mLayoutOverlayApplied) { + reloadBaseFromFile(); + mLayoutOverlayApplied = false; + } +} + KeyCharacterMap::KeyboardType KeyCharacterMap::getKeyboardType() const { return mType; } diff --git a/libs/input/MotionPredictor.cpp b/libs/input/MotionPredictor.cpp index 0719fe5f6c..0fa0f1229c 100644 --- a/libs/input/MotionPredictor.cpp +++ b/libs/input/MotionPredictor.cpp @@ -55,7 +55,7 @@ void MotionPredictor::record(const MotionEvent& event) { * The returned event should not contain any of the real, existing data. It should only * contain the predicted samples. */ -std::vector<std::unique_ptr<MotionEvent>> MotionPredictor::predict() { +std::vector<std::unique_ptr<MotionEvent>> MotionPredictor::predict(nsecs_t timestamp) { if (mEvents.size() < 2) { return {}; } @@ -67,7 +67,7 @@ std::vector<std::unique_ptr<MotionEvent>> MotionPredictor::predict() { std::unique_ptr<MotionEvent> prediction = std::make_unique<MotionEvent>(); std::vector<PointerCoords> futureCoords; - const int64_t futureTime = getExpectedPresentationTimeNanos() + mPredictionTimestampOffsetNanos; + const nsecs_t futureTime = timestamp + mPredictionTimestampOffsetNanos; const nsecs_t currentTime = event.getEventTime(); const MotionEvent& previous = mEvents.rbegin()[1]; const nsecs_t oldTime = previous.getEventTime(); @@ -78,16 +78,19 @@ std::vector<std::unique_ptr<MotionEvent>> MotionPredictor::predict() { for (size_t i = 0; i < event.getPointerCount(); i++) { const int32_t pointerId = event.getPointerId(i); + const PointerCoords* currentPointerCoords = event.getRawPointerCoords(i); + const float currentX = currentPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_X); + const float currentY = currentPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_Y); + PointerCoords coords; coords.clear(); ssize_t index = previous.findPointerIndex(pointerId); if (index >= 0) { // We have old data for this pointer. Compute the prediction. - const float oldX = previous.getRawX(index); - const float oldY = previous.getRawY(index); - const float currentX = event.getRawX(i); - const float currentY = event.getRawY(i); + const PointerCoords* oldPointerCoords = previous.getRawPointerCoords(index); + const float oldX = oldPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_X); + const float oldY = oldPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_Y); // Let's do a linear interpolation while waiting for a real model const float scale = @@ -97,13 +100,15 @@ std::vector<std::unique_ptr<MotionEvent>> MotionPredictor::predict() { coords.setAxisValue(AMOTION_EVENT_AXIS_X, futureX); coords.setAxisValue(AMOTION_EVENT_AXIS_Y, futureY); + ALOGD_IF(isDebug(), + "Prediction by %.1f ms, (%.1f, %.1f), (%.1f, %.1f) --> (%.1f, %.1f)", + (futureTime - event.getEventTime()) * 1E-6, oldX, oldY, currentX, currentY, + futureX, futureY); } futureCoords.push_back(coords); } - ALOGD_IF(isDebug(), "Prediction is %.1f ms away from the event", - (futureTime - event.getEventTime()) * 1E-6); /** * The process of adding samples is different for the first and subsequent samples: * 1. Add the first sample via 'initialize' as below @@ -143,14 +148,4 @@ bool MotionPredictor::isPredictionAvailable(int32_t /*deviceId*/, int32_t source return true; } -int64_t MotionPredictor::getExpectedPresentationTimeNanos() { - std::scoped_lock lock(mLock); - return mExpectedPresentationTimeNanos; -} - -void MotionPredictor::setExpectedPresentationTimeNanos(int64_t expectedPresentationTimeNanos) { - std::scoped_lock lock(mLock); - mExpectedPresentationTimeNanos = expectedPresentationTimeNanos; -} - } // namespace android diff --git a/libs/input/VelocityControl.cpp b/libs/input/VelocityControl.cpp index 5c008b1158..5720099033 100644 --- a/libs/input/VelocityControl.cpp +++ b/libs/input/VelocityControl.cpp @@ -70,9 +70,10 @@ void VelocityControl::move(nsecs_t eventTime, float* deltaX, float* deltaY) { if (deltaY) { mRawPositionY += *deltaY; } - mVelocityTracker.addMovement(eventTime, BitSet32(BitSet32::valueForBit(0)), - {{AMOTION_EVENT_AXIS_X, {mRawPositionX}}, - {AMOTION_EVENT_AXIS_Y, {mRawPositionY}}}); + mVelocityTracker.addMovement(eventTime, /*pointerId=*/0, AMOTION_EVENT_AXIS_X, + mRawPositionX); + mVelocityTracker.addMovement(eventTime, /*pointerId=*/0, AMOTION_EVENT_AXIS_Y, + mRawPositionY); std::optional<float> vx = mVelocityTracker.getVelocity(AMOTION_EVENT_AXIS_X, 0); std::optional<float> vy = mVelocityTracker.getVelocity(AMOTION_EVENT_AXIS_Y, 0); diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp index 19b4684e4a..1cd782d7a7 100644 --- a/libs/input/VelocityTracker.cpp +++ b/libs/input/VelocityTracker.cpp @@ -23,6 +23,7 @@ #include <optional> #include <android-base/stringprintf.h> +#include <input/PrintTools.h> #include <input/VelocityTracker.h> #include <utils/BitSet.h> #include <utils/Timers.h> @@ -142,10 +143,7 @@ static std::string matrixToString(const float* a, uint32_t m, uint32_t n, bool r // --- VelocityTracker --- VelocityTracker::VelocityTracker(const Strategy strategy) - : mLastEventTime(0), - mCurrentPointerIdBits(0), - mActivePointerId(-1), - mOverrideStrategy(strategy) {} + : mLastEventTime(0), mCurrentPointerIdBits(0), mOverrideStrategy(strategy) {} VelocityTracker::~VelocityTracker() { } @@ -191,17 +189,17 @@ std::unique_ptr<VelocityTrackerStrategy> VelocityTracker::createStrategy( return std::make_unique< LeastSquaresVelocityTrackerStrategy>(2, LeastSquaresVelocityTrackerStrategy:: - WEIGHTING_DELTA); + Weighting::DELTA); case VelocityTracker::Strategy::WLSQ2_CENTRAL: return std::make_unique< LeastSquaresVelocityTrackerStrategy>(2, LeastSquaresVelocityTrackerStrategy:: - WEIGHTING_CENTRAL); + Weighting::CENTRAL); case VelocityTracker::Strategy::WLSQ2_RECENT: return std::make_unique< LeastSquaresVelocityTrackerStrategy>(2, LeastSquaresVelocityTrackerStrategy:: - WEIGHTING_RECENT); + Weighting::RECENT); case VelocityTracker::Strategy::INT1: return std::make_unique<IntegratingVelocityTrackerStrategy>(1); @@ -220,30 +218,30 @@ std::unique_ptr<VelocityTrackerStrategy> VelocityTracker::createStrategy( void VelocityTracker::clear() { mCurrentPointerIdBits.clear(); - mActivePointerId = -1; + mActivePointerId = std::nullopt; mConfiguredStrategies.clear(); } -void VelocityTracker::clearPointers(BitSet32 idBits) { - BitSet32 remainingIdBits(mCurrentPointerIdBits.value & ~idBits.value); - mCurrentPointerIdBits = remainingIdBits; +void VelocityTracker::clearPointer(int32_t pointerId) { + mCurrentPointerIdBits.clearBit(pointerId); - if (mActivePointerId >= 0 && idBits.hasBit(mActivePointerId)) { - mActivePointerId = !remainingIdBits.isEmpty() ? remainingIdBits.firstMarkedBit() : -1; + if (mActivePointerId && *mActivePointerId == pointerId) { + // The active pointer id is being removed. Mark it invalid and try to find a new one + // from the remaining pointers. + mActivePointerId = std::nullopt; + if (!mCurrentPointerIdBits.isEmpty()) { + mActivePointerId = mCurrentPointerIdBits.firstMarkedBit(); + } } for (const auto& [_, strategy] : mConfiguredStrategies) { - strategy->clearPointers(idBits); + strategy->clearPointer(pointerId); } } -void VelocityTracker::addMovement(nsecs_t eventTime, BitSet32 idBits, - const std::map<int32_t /*axis*/, std::vector<float>>& positions) { - while (idBits.count() > MAX_POINTERS) { - idBits.clearLastMarkedBit(); - } - - if ((mCurrentPointerIdBits.value & idBits.value) && +void VelocityTracker::addMovement(nsecs_t eventTime, int32_t pointerId, int32_t axis, + float position) { + if (mCurrentPointerIdBits.hasBit(pointerId) && std::chrono::nanoseconds(eventTime - mLastEventTime) > ASSUME_POINTER_STOPPED_TIME) { ALOGD_IF(DEBUG_VELOCITY, "VelocityTracker: stopped for %s, clearing state.", toString(std::chrono::nanoseconds(eventTime - mLastEventTime)).c_str()); @@ -254,39 +252,28 @@ void VelocityTracker::addMovement(nsecs_t eventTime, BitSet32 idBits, } mLastEventTime = eventTime; - mCurrentPointerIdBits = idBits; - if (mActivePointerId < 0 || !idBits.hasBit(mActivePointerId)) { - mActivePointerId = idBits.isEmpty() ? -1 : idBits.firstMarkedBit(); + mCurrentPointerIdBits.markBit(pointerId); + if (!mActivePointerId) { + // Let this be the new active pointer if no active pointer is currently set + mActivePointerId = pointerId; } - for (const auto& [axis, positionValues] : positions) { - LOG_ALWAYS_FATAL_IF(idBits.count() != positionValues.size(), - "Mismatching number of pointers, idBits=%" PRIu32 ", positions=%zu", - idBits.count(), positionValues.size()); - if (mConfiguredStrategies.find(axis) == mConfiguredStrategies.end()) { - configureStrategy(axis); - } - mConfiguredStrategies[axis]->addMovement(eventTime, idBits, positionValues); + if (mConfiguredStrategies.find(axis) == mConfiguredStrategies.end()) { + configureStrategy(axis); } + mConfiguredStrategies[axis]->addMovement(eventTime, pointerId, position); if (DEBUG_VELOCITY) { - ALOGD("VelocityTracker: addMovement eventTime=%" PRId64 - ", idBits=0x%08x, activePointerId=%d", - eventTime, idBits.value, mActivePointerId); - for (const auto& positionsEntry : positions) { - for (BitSet32 iterBits(idBits); !iterBits.isEmpty();) { - uint32_t id = iterBits.firstMarkedBit(); - uint32_t index = idBits.getIndexOfBit(id); - iterBits.clearBit(id); - Estimator estimator; - getEstimator(positionsEntry.first, id, &estimator); - ALOGD(" %d: axis=%d, position=%0.3f, " - "estimator (degree=%d, coeff=%s, confidence=%f)", - id, positionsEntry.first, positionsEntry.second[index], int(estimator.degree), - vectorToString(estimator.coeff, estimator.degree + 1).c_str(), - estimator.confidence); - } - } + ALOGD("VelocityTracker: addMovement eventTime=%" PRId64 ", pointerId=%" PRId32 + ", activePointerId=%s", + eventTime, pointerId, toString(mActivePointerId).c_str()); + + std::optional<Estimator> estimator = getEstimator(axis, pointerId); + ALOGD(" %d: axis=%d, position=%0.3f, " + "estimator (degree=%d, coeff=%s, confidence=%f)", + pointerId, axis, position, int((*estimator).degree), + vectorToString((*estimator).coeff.data(), (*estimator).degree + 1).c_str(), + (*estimator).confidence); } } @@ -296,94 +283,73 @@ void VelocityTracker::addMovement(const MotionEvent* event) { int32_t actionMasked = event->getActionMasked(); switch (actionMasked) { - case AMOTION_EVENT_ACTION_DOWN: - case AMOTION_EVENT_ACTION_HOVER_ENTER: - // Clear all pointers on down before adding the new movement. - clear(); - axesToProcess.insert(PLANAR_AXES.begin(), PLANAR_AXES.end()); - break; - case AMOTION_EVENT_ACTION_POINTER_DOWN: { - // Start a new movement trace for a pointer that just went down. - // We do this on down instead of on up because the client may want to query the - // final velocity for a pointer that just went up. - BitSet32 downIdBits; - downIdBits.markBit(event->getPointerId(event->getActionIndex())); - clearPointers(downIdBits); - axesToProcess.insert(PLANAR_AXES.begin(), PLANAR_AXES.end()); - break; - } - case AMOTION_EVENT_ACTION_MOVE: - case AMOTION_EVENT_ACTION_HOVER_MOVE: - axesToProcess.insert(PLANAR_AXES.begin(), PLANAR_AXES.end()); - break; - case AMOTION_EVENT_ACTION_POINTER_UP: - case AMOTION_EVENT_ACTION_UP: { - std::chrono::nanoseconds delaySinceLastEvent(event->getEventTime() - mLastEventTime); - if (delaySinceLastEvent > ASSUME_POINTER_STOPPED_TIME) { - ALOGD_IF(DEBUG_VELOCITY, - "VelocityTracker: stopped for %s, clearing state upon pointer liftoff.", - toString(delaySinceLastEvent).c_str()); - // We have not received any movements for too long. Assume that all pointers - // have stopped. - for (int32_t axis : PLANAR_AXES) { - mConfiguredStrategies.erase(axis); + case AMOTION_EVENT_ACTION_DOWN: + case AMOTION_EVENT_ACTION_HOVER_ENTER: + // Clear all pointers on down before adding the new movement. + clear(); + axesToProcess.insert(PLANAR_AXES.begin(), PLANAR_AXES.end()); + break; + case AMOTION_EVENT_ACTION_POINTER_DOWN: { + // Start a new movement trace for a pointer that just went down. + // We do this on down instead of on up because the client may want to query the + // final velocity for a pointer that just went up. + clearPointer(event->getPointerId(event->getActionIndex())); + axesToProcess.insert(PLANAR_AXES.begin(), PLANAR_AXES.end()); + break; + } + case AMOTION_EVENT_ACTION_MOVE: + case AMOTION_EVENT_ACTION_HOVER_MOVE: + axesToProcess.insert(PLANAR_AXES.begin(), PLANAR_AXES.end()); + break; + case AMOTION_EVENT_ACTION_POINTER_UP: + case AMOTION_EVENT_ACTION_UP: { + std::chrono::nanoseconds delaySinceLastEvent(event->getEventTime() - mLastEventTime); + if (delaySinceLastEvent > ASSUME_POINTER_STOPPED_TIME) { + ALOGD_IF(DEBUG_VELOCITY, + "VelocityTracker: stopped for %s, clearing state upon pointer liftoff.", + toString(delaySinceLastEvent).c_str()); + // We have not received any movements for too long. Assume that all pointers + // have stopped. + for (int32_t axis : PLANAR_AXES) { + mConfiguredStrategies.erase(axis); + } } + // These actions because they do not convey any new information about + // pointer movement. We also want to preserve the last known velocity of the pointers. + // Note that ACTION_UP and ACTION_POINTER_UP always report the last known position + // of the pointers that went up. ACTION_POINTER_UP does include the new position of + // pointers that remained down but we will also receive an ACTION_MOVE with this + // information if any of them actually moved. Since we don't know how many pointers + // will be going up at once it makes sense to just wait for the following ACTION_MOVE + // before adding the movement. + return; } - // These actions because they do not convey any new information about - // pointer movement. We also want to preserve the last known velocity of the pointers. - // Note that ACTION_UP and ACTION_POINTER_UP always report the last known position - // of the pointers that went up. ACTION_POINTER_UP does include the new position of - // pointers that remained down but we will also receive an ACTION_MOVE with this - // information if any of them actually moved. Since we don't know how many pointers - // will be going up at once it makes sense to just wait for the following ACTION_MOVE - // before adding the movement. - return; - } - case AMOTION_EVENT_ACTION_SCROLL: - axesToProcess.insert(AMOTION_EVENT_AXIS_SCROLL); - break; - default: - // Ignore all other actions. - return; - } - - size_t pointerCount = event->getPointerCount(); - if (pointerCount > MAX_POINTERS) { - pointerCount = MAX_POINTERS; - } - - BitSet32 idBits; - for (size_t i = 0; i < pointerCount; i++) { - idBits.markBit(event->getPointerId(i)); - } - - uint32_t pointerIndex[MAX_POINTERS]; - for (size_t i = 0; i < pointerCount; i++) { - pointerIndex[i] = idBits.getIndexOfBit(event->getPointerId(i)); - } - - std::map<int32_t, std::vector<float>> positions; - for (int32_t axis : axesToProcess) { - positions[axis].resize(pointerCount); + case AMOTION_EVENT_ACTION_SCROLL: + axesToProcess.insert(AMOTION_EVENT_AXIS_SCROLL); + break; + default: + // Ignore all other actions. + return; } size_t historySize = event->getHistorySize(); for (size_t h = 0; h <= historySize; h++) { nsecs_t eventTime = event->getHistoricalEventTime(h); - for (int32_t axis : axesToProcess) { - for (size_t i = 0; i < pointerCount; i++) { - positions[axis][pointerIndex[i]] = event->getHistoricalAxisValue(axis, i, h); + for (size_t i = 0; i < event->getPointerCount(); i++) { + // TODO(b/167946721): skip resampled samples + const int32_t pointerId = event->getPointerId(i); + for (int32_t axis : axesToProcess) { + const float position = event->getHistoricalAxisValue(axis, i, h); + addMovement(eventTime, pointerId, axis, position); } } - addMovement(eventTime, idBits, positions); } } -std::optional<float> VelocityTracker::getVelocity(int32_t axis, uint32_t id) const { - Estimator estimator; - bool validVelocity = getEstimator(axis, id, &estimator) && estimator.degree >= 1; - if (validVelocity) { - return estimator.coeff[1]; +std::optional<float> VelocityTracker::getVelocity(int32_t axis, int32_t pointerId) const { + std::optional<Estimator> estimator = getEstimator(axis, pointerId); + if (estimator && (*estimator).degree >= 1) { + return (*estimator).coeff[1]; } return {}; } @@ -406,50 +372,55 @@ VelocityTracker::ComputedVelocity VelocityTracker::getComputedVelocity(int32_t u return computedVelocity; } -bool VelocityTracker::getEstimator(int32_t axis, uint32_t id, Estimator* outEstimator) const { +std::optional<VelocityTracker::Estimator> VelocityTracker::getEstimator(int32_t axis, + int32_t pointerId) const { const auto& it = mConfiguredStrategies.find(axis); if (it == mConfiguredStrategies.end()) { - return false; + return std::nullopt; } - return it->second->getEstimator(id, outEstimator); + return it->second->getEstimator(pointerId); } // --- LeastSquaresVelocityTrackerStrategy --- LeastSquaresVelocityTrackerStrategy::LeastSquaresVelocityTrackerStrategy(uint32_t degree, Weighting weighting) - : mDegree(degree), mWeighting(weighting), mIndex(0) {} + : mDegree(degree), mWeighting(weighting) {} LeastSquaresVelocityTrackerStrategy::~LeastSquaresVelocityTrackerStrategy() { } -void LeastSquaresVelocityTrackerStrategy::clearPointers(BitSet32 idBits) { - BitSet32 remainingIdBits(mMovements[mIndex].idBits.value & ~idBits.value); - mMovements[mIndex].idBits = remainingIdBits; +void LeastSquaresVelocityTrackerStrategy::clearPointer(int32_t pointerId) { + mIndex.erase(pointerId); + mMovements.erase(pointerId); } -void LeastSquaresVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits, - const std::vector<float>& positions) { - if (mMovements[mIndex].eventTime != eventTime) { +void LeastSquaresVelocityTrackerStrategy::addMovement(nsecs_t eventTime, int32_t pointerId, + float position) { + // If data for this pointer already exists, we have a valid entry at the position of + // mIndex[pointerId] and mMovements[pointerId]. In that case, we need to advance the index + // to the next position in the circular buffer and write the new Movement there. Otherwise, + // if this is a first movement for this pointer, we initialize the maps mIndex and mMovements + // for this pointer and write to the first position. + auto [movementIt, inserted] = mMovements.insert({pointerId, {}}); + auto [indexIt, _] = mIndex.insert({pointerId, 0}); + size_t& index = indexIt->second; + if (!inserted && movementIt->second[index].eventTime != eventTime) { // When ACTION_POINTER_DOWN happens, we will first receive ACTION_MOVE with the coordinates // of the existing pointers, and then ACTION_POINTER_DOWN with the coordinates that include // the new pointer. If the eventtimes for both events are identical, just update the data // for this time. // We only compare against the last value, as it is likely that addMovement is called // in chronological order as events occur. - mIndex++; + index++; } - if (mIndex == HISTORY_SIZE) { - mIndex = 0; + if (index == HISTORY_SIZE) { + index = 0; } - Movement& movement = mMovements[mIndex]; + Movement& movement = movementIt->second[index]; movement.eventTime = eventTime; - movement.idBits = idBits; - uint32_t count = idBits.count(); - for (uint32_t i = 0; i < count; i++) { - movement.positions[i] = positions[i]; - } + movement.position = position; } /** @@ -502,7 +473,9 @@ void LeastSquaresVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet3 * http://en.wikipedia.org/wiki/Gram-Schmidt */ static bool solveLeastSquares(const std::vector<float>& x, const std::vector<float>& y, - const std::vector<float>& w, uint32_t n, float* outB, float* outDet) { + const std::vector<float>& w, uint32_t n, + std::array<float, VelocityTracker::Estimator::MAX_DEGREE + 1>& outB, + float* outDet) { const size_t m = x.size(); ALOGD_IF(DEBUG_STRATEGY, "solveLeastSquares: m=%d, n=%d, x=%s, y=%s, w=%s", int(m), int(n), @@ -583,7 +556,7 @@ static bool solveLeastSquares(const std::vector<float>& x, const std::vector<flo outB[i] /= r[i][i]; } - ALOGD_IF(DEBUG_STRATEGY, " - b=%s", vectorToString(outB, n).c_str()); + ALOGD_IF(DEBUG_STRATEGY, " - b=%s", vectorToString(outB.data(), n).c_str()); // Calculate the coefficient of determination as 1 - (SSerr / SStot) where // SSerr is the residual sum of squares (variance of the error), @@ -671,37 +644,47 @@ static std::optional<std::array<float, 3>> solveUnweightedLeastSquaresDeg2( return std::make_optional(std::array<float, 3>({c, b, a})); } -bool LeastSquaresVelocityTrackerStrategy::getEstimator(uint32_t id, - VelocityTracker::Estimator* outEstimator) const { - outEstimator->clear(); - +std::optional<VelocityTracker::Estimator> LeastSquaresVelocityTrackerStrategy::getEstimator( + int32_t pointerId) const { + const auto movementIt = mMovements.find(pointerId); + if (movementIt == mMovements.end()) { + return std::nullopt; // no data + } // Iterate over movement samples in reverse time order and collect samples. std::vector<float> positions; std::vector<float> w; std::vector<float> time; - uint32_t index = mIndex; - const Movement& newestMovement = mMovements[mIndex]; + uint32_t index = mIndex.at(pointerId); + const Movement& newestMovement = movementIt->second[index]; do { - const Movement& movement = mMovements[index]; - if (!movement.idBits.hasBit(id)) { - break; - } + const Movement& movement = movementIt->second[index]; nsecs_t age = newestMovement.eventTime - movement.eventTime; if (age > HORIZON) { break; } - - positions.push_back(movement.getPosition(id)); - w.push_back(chooseWeight(index)); + if (movement.eventTime == 0 && index != 0) { + // All eventTime's are initialized to 0. In this fixed-width circular buffer, it's + // possible that not all entries are valid. We use a time=0 as a signal for those + // uninitialized values. If we encounter a time of 0 in a position + // that's > 0, it means that we hit the block where the data wasn't initialized. + // We still don't know whether the value at index=0, with eventTime=0 is valid. + // However, that's only possible when the value is by itself. So there's no hard in + // processing it anyways, since the velocity for a single point is zero, and this + // situation will only be encountered in artificial circumstances (in tests). + // In practice, time will never be 0. + break; + } + positions.push_back(movement.position); + w.push_back(chooseWeight(pointerId, index)); time.push_back(-age * 0.000000001f); index = (index == 0 ? HISTORY_SIZE : index) - 1; } while (positions.size() < HISTORY_SIZE); const size_t m = positions.size(); if (m == 0) { - return false; // no data + return std::nullopt; // no data } // Calculate a least squares polynomial fit. @@ -710,112 +693,116 @@ bool LeastSquaresVelocityTrackerStrategy::getEstimator(uint32_t id, degree = m - 1; } - if (degree == 2 && mWeighting == WEIGHTING_NONE) { + if (degree == 2 && mWeighting == Weighting::NONE) { // Optimize unweighted, quadratic polynomial fit std::optional<std::array<float, 3>> coeff = solveUnweightedLeastSquaresDeg2(time, positions); if (coeff) { - outEstimator->time = newestMovement.eventTime; - outEstimator->degree = 2; - outEstimator->confidence = 1; - for (size_t i = 0; i <= outEstimator->degree; i++) { - outEstimator->coeff[i] = (*coeff)[i]; + VelocityTracker::Estimator estimator; + estimator.time = newestMovement.eventTime; + estimator.degree = 2; + estimator.confidence = 1; + for (size_t i = 0; i <= estimator.degree; i++) { + estimator.coeff[i] = (*coeff)[i]; } - return true; + return estimator; } } else if (degree >= 1) { // General case for an Nth degree polynomial fit float det; uint32_t n = degree + 1; - if (solveLeastSquares(time, positions, w, n, outEstimator->coeff, &det)) { - outEstimator->time = newestMovement.eventTime; - outEstimator->degree = degree; - outEstimator->confidence = det; + VelocityTracker::Estimator estimator; + if (solveLeastSquares(time, positions, w, n, estimator.coeff, &det)) { + estimator.time = newestMovement.eventTime; + estimator.degree = degree; + estimator.confidence = det; ALOGD_IF(DEBUG_STRATEGY, "estimate: degree=%d, coeff=%s, confidence=%f", - int(outEstimator->degree), vectorToString(outEstimator->coeff, n).c_str(), - outEstimator->confidence); + int(estimator.degree), vectorToString(estimator.coeff.data(), n).c_str(), + estimator.confidence); - return true; + return estimator; } } // No velocity data available for this pointer, but we do have its current position. - outEstimator->coeff[0] = positions[0]; - outEstimator->time = newestMovement.eventTime; - outEstimator->degree = 0; - outEstimator->confidence = 1; - return true; + VelocityTracker::Estimator estimator; + estimator.coeff[0] = positions[0]; + estimator.time = newestMovement.eventTime; + estimator.degree = 0; + estimator.confidence = 1; + return estimator; } -float LeastSquaresVelocityTrackerStrategy::chooseWeight(uint32_t index) const { +float LeastSquaresVelocityTrackerStrategy::chooseWeight(int32_t pointerId, uint32_t index) const { + const std::array<Movement, HISTORY_SIZE>& movements = mMovements.at(pointerId); switch (mWeighting) { - case WEIGHTING_DELTA: { - // Weight points based on how much time elapsed between them and the next - // point so that points that "cover" a shorter time span are weighed less. - // delta 0ms: 0.5 - // delta 10ms: 1.0 - if (index == mIndex) { + case Weighting::DELTA: { + // Weight points based on how much time elapsed between them and the next + // point so that points that "cover" a shorter time span are weighed less. + // delta 0ms: 0.5 + // delta 10ms: 1.0 + if (index == mIndex.at(pointerId)) { + return 1.0f; + } + uint32_t nextIndex = (index + 1) % HISTORY_SIZE; + float deltaMillis = + (movements[nextIndex].eventTime - movements[index].eventTime) * 0.000001f; + if (deltaMillis < 0) { + return 0.5f; + } + if (deltaMillis < 10) { + return 0.5f + deltaMillis * 0.05; + } return 1.0f; } - uint32_t nextIndex = (index + 1) % HISTORY_SIZE; - float deltaMillis = (mMovements[nextIndex].eventTime- mMovements[index].eventTime) - * 0.000001f; - if (deltaMillis < 0) { + + case Weighting::CENTRAL: { + // Weight points based on their age, weighing very recent and very old points less. + // age 0ms: 0.5 + // age 10ms: 1.0 + // age 50ms: 1.0 + // age 60ms: 0.5 + float ageMillis = + (movements[mIndex.at(pointerId)].eventTime - movements[index].eventTime) * + 0.000001f; + if (ageMillis < 0) { + return 0.5f; + } + if (ageMillis < 10) { + return 0.5f + ageMillis * 0.05; + } + if (ageMillis < 50) { + return 1.0f; + } + if (ageMillis < 60) { + return 0.5f + (60 - ageMillis) * 0.05; + } return 0.5f; } - if (deltaMillis < 10) { - return 0.5f + deltaMillis * 0.05; - } - return 1.0f; - } - case WEIGHTING_CENTRAL: { - // Weight points based on their age, weighing very recent and very old points less. - // age 0ms: 0.5 - // age 10ms: 1.0 - // age 50ms: 1.0 - // age 60ms: 0.5 - float ageMillis = (mMovements[mIndex].eventTime - mMovements[index].eventTime) - * 0.000001f; - if (ageMillis < 0) { + case Weighting::RECENT: { + // Weight points based on their age, weighing older points less. + // age 0ms: 1.0 + // age 50ms: 1.0 + // age 100ms: 0.5 + float ageMillis = + (movements[mIndex.at(pointerId)].eventTime - movements[index].eventTime) * + 0.000001f; + if (ageMillis < 50) { + return 1.0f; + } + if (ageMillis < 100) { + return 0.5f + (100 - ageMillis) * 0.01f; + } return 0.5f; } - if (ageMillis < 10) { - return 0.5f + ageMillis * 0.05; - } - if (ageMillis < 50) { - return 1.0f; - } - if (ageMillis < 60) { - return 0.5f + (60 - ageMillis) * 0.05; - } - return 0.5f; - } - case WEIGHTING_RECENT: { - // Weight points based on their age, weighing older points less. - // age 0ms: 1.0 - // age 50ms: 1.0 - // age 100ms: 0.5 - float ageMillis = (mMovements[mIndex].eventTime - mMovements[index].eventTime) - * 0.000001f; - if (ageMillis < 50) { + case Weighting::NONE: return 1.0f; - } - if (ageMillis < 100) { - return 0.5f + (100 - ageMillis) * 0.01f; - } - return 0.5f; - } - - case WEIGHTING_NONE: - default: - return 1.0f; } } - // --- IntegratingVelocityTrackerStrategy --- IntegratingVelocityTrackerStrategy::IntegratingVelocityTrackerStrategy(uint32_t degree) : @@ -825,38 +812,32 @@ IntegratingVelocityTrackerStrategy::IntegratingVelocityTrackerStrategy(uint32_t IntegratingVelocityTrackerStrategy::~IntegratingVelocityTrackerStrategy() { } -void IntegratingVelocityTrackerStrategy::clearPointers(BitSet32 idBits) { - mPointerIdBits.value &= ~idBits.value; +void IntegratingVelocityTrackerStrategy::clearPointer(int32_t pointerId) { + mPointerIdBits.clearBit(pointerId); } -void IntegratingVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits, - const std::vector<float>& positions) { - uint32_t index = 0; - for (BitSet32 iterIdBits(idBits); !iterIdBits.isEmpty();) { - uint32_t id = iterIdBits.clearFirstMarkedBit(); - State& state = mPointerState[id]; - const float position = positions[index++]; - if (mPointerIdBits.hasBit(id)) { - updateState(state, eventTime, position); - } else { - initState(state, eventTime, position); - } +void IntegratingVelocityTrackerStrategy::addMovement(nsecs_t eventTime, int32_t pointerId, + float position) { + State& state = mPointerState[pointerId]; + if (mPointerIdBits.hasBit(pointerId)) { + updateState(state, eventTime, position); + } else { + initState(state, eventTime, position); } - mPointerIdBits = idBits; + mPointerIdBits.markBit(pointerId); } -bool IntegratingVelocityTrackerStrategy::getEstimator(uint32_t id, - VelocityTracker::Estimator* outEstimator) const { - outEstimator->clear(); - - if (mPointerIdBits.hasBit(id)) { - const State& state = mPointerState[id]; - populateEstimator(state, outEstimator); - return true; +std::optional<VelocityTracker::Estimator> IntegratingVelocityTrackerStrategy::getEstimator( + int32_t pointerId) const { + if (mPointerIdBits.hasBit(pointerId)) { + const State& state = mPointerState[pointerId]; + VelocityTracker::Estimator estimator; + populateEstimator(state, &estimator); + return estimator; } - return false; + return std::nullopt; } void IntegratingVelocityTrackerStrategy::initState(State& state, nsecs_t eventTime, @@ -916,49 +897,60 @@ void IntegratingVelocityTrackerStrategy::populateEstimator(const State& state, // --- LegacyVelocityTrackerStrategy --- -LegacyVelocityTrackerStrategy::LegacyVelocityTrackerStrategy() : mIndex(0) {} +LegacyVelocityTrackerStrategy::LegacyVelocityTrackerStrategy() {} LegacyVelocityTrackerStrategy::~LegacyVelocityTrackerStrategy() { } -void LegacyVelocityTrackerStrategy::clearPointers(BitSet32 idBits) { - BitSet32 remainingIdBits(mMovements[mIndex].idBits.value & ~idBits.value); - mMovements[mIndex].idBits = remainingIdBits; +void LegacyVelocityTrackerStrategy::clearPointer(int32_t pointerId) { + mIndex.erase(pointerId); + mMovements.erase(pointerId); } -void LegacyVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits, - const std::vector<float>& positions) { - if (++mIndex == HISTORY_SIZE) { - mIndex = 0; +void LegacyVelocityTrackerStrategy::addMovement(nsecs_t eventTime, int32_t pointerId, + float position) { + // If data for this pointer already exists, we have a valid entry at the position of + // mIndex[pointerId] and mMovements[pointerId]. In that case, we need to advance the index + // to the next position in the circular buffer and write the new Movement there. Otherwise, + // if this is a first movement for this pointer, we initialize the maps mIndex and mMovements + // for this pointer and write to the first position. + auto [movementIt, inserted] = mMovements.insert({pointerId, {}}); + auto [indexIt, _] = mIndex.insert({pointerId, 0}); + size_t& index = indexIt->second; + if (!inserted && movementIt->second[index].eventTime != eventTime) { + // When ACTION_POINTER_DOWN happens, we will first receive ACTION_MOVE with the coordinates + // of the existing pointers, and then ACTION_POINTER_DOWN with the coordinates that include + // the new pointer. If the eventtimes for both events are identical, just update the data + // for this time. + // We only compare against the last value, as it is likely that addMovement is called + // in chronological order as events occur. + index++; + } + if (index == HISTORY_SIZE) { + index = 0; } - Movement& movement = mMovements[mIndex]; + Movement& movement = movementIt->second[index]; movement.eventTime = eventTime; - movement.idBits = idBits; - uint32_t count = idBits.count(); - for (uint32_t i = 0; i < count; i++) { - movement.positions[i] = positions[i]; - } + movement.position = position; } -bool LegacyVelocityTrackerStrategy::getEstimator(uint32_t id, - VelocityTracker::Estimator* outEstimator) const { - outEstimator->clear(); - - const Movement& newestMovement = mMovements[mIndex]; - if (!newestMovement.idBits.hasBit(id)) { - return false; // no data +std::optional<VelocityTracker::Estimator> LegacyVelocityTrackerStrategy::getEstimator( + int32_t pointerId) const { + const auto movementIt = mMovements.find(pointerId); + if (movementIt == mMovements.end()) { + return std::nullopt; // no data } + const Movement& newestMovement = movementIt->second[mIndex.at(pointerId)]; // Find the oldest sample that contains the pointer and that is not older than HORIZON. nsecs_t minTime = newestMovement.eventTime - HORIZON; - uint32_t oldestIndex = mIndex; + uint32_t oldestIndex = mIndex.at(pointerId); uint32_t numTouches = 1; do { uint32_t nextOldestIndex = (oldestIndex == 0 ? HISTORY_SIZE : oldestIndex) - 1; - const Movement& nextOldestMovement = mMovements[nextOldestIndex]; - if (!nextOldestMovement.idBits.hasBit(id) - || nextOldestMovement.eventTime < minTime) { + const Movement& nextOldestMovement = mMovements.at(pointerId)[nextOldestIndex]; + if (nextOldestMovement.eventTime < minTime) { break; } oldestIndex = nextOldestIndex; @@ -978,22 +970,22 @@ bool LegacyVelocityTrackerStrategy::getEstimator(uint32_t id, float accumV = 0; uint32_t index = oldestIndex; uint32_t samplesUsed = 0; - const Movement& oldestMovement = mMovements[oldestIndex]; - float oldestPosition = oldestMovement.getPosition(id); + const Movement& oldestMovement = mMovements.at(pointerId)[oldestIndex]; + float oldestPosition = oldestMovement.position; nsecs_t lastDuration = 0; while (numTouches-- > 1) { if (++index == HISTORY_SIZE) { index = 0; } - const Movement& movement = mMovements[index]; + const Movement& movement = mMovements.at(pointerId)[index]; nsecs_t duration = movement.eventTime - oldestMovement.eventTime; // If the duration between samples is small, we may significantly overestimate // the velocity. Consequently, we impose a minimum duration constraint on the // samples that we include in the calculation. if (duration >= MIN_DURATION) { - float position = movement.getPosition(id); + float position = movement.position; float scale = 1000000000.0f / duration; // one over time delta in seconds float v = (position - oldestPosition) * scale; accumV = (accumV * lastDuration + v * duration) / (duration + lastDuration); @@ -1003,54 +995,59 @@ bool LegacyVelocityTrackerStrategy::getEstimator(uint32_t id, } // Report velocity. - float newestPosition = newestMovement.getPosition(id); - outEstimator->time = newestMovement.eventTime; - outEstimator->confidence = 1; - outEstimator->coeff[0] = newestPosition; + float newestPosition = newestMovement.position; + VelocityTracker::Estimator estimator; + estimator.time = newestMovement.eventTime; + estimator.confidence = 1; + estimator.coeff[0] = newestPosition; if (samplesUsed) { - outEstimator->coeff[1] = accumV; - outEstimator->degree = 1; + estimator.coeff[1] = accumV; + estimator.degree = 1; } else { - outEstimator->degree = 0; + estimator.degree = 0; } - return true; + return estimator; } // --- ImpulseVelocityTrackerStrategy --- ImpulseVelocityTrackerStrategy::ImpulseVelocityTrackerStrategy(bool deltaValues) - : mDeltaValues(deltaValues), mIndex(0) {} + : mDeltaValues(deltaValues) {} ImpulseVelocityTrackerStrategy::~ImpulseVelocityTrackerStrategy() { } -void ImpulseVelocityTrackerStrategy::clearPointers(BitSet32 idBits) { - BitSet32 remainingIdBits(mMovements[mIndex].idBits.value & ~idBits.value); - mMovements[mIndex].idBits = remainingIdBits; +void ImpulseVelocityTrackerStrategy::clearPointer(int32_t pointerId) { + mIndex.erase(pointerId); + mMovements.erase(pointerId); } -void ImpulseVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits, - const std::vector<float>& positions) { - if (mMovements[mIndex].eventTime != eventTime) { +void ImpulseVelocityTrackerStrategy::addMovement(nsecs_t eventTime, int32_t pointerId, + float position) { + // If data for this pointer already exists, we have a valid entry at the position of + // mIndex[pointerId] and mMovements[pointerId]. In that case, we need to advance the index + // to the next position in the circular buffer and write the new Movement there. Otherwise, + // if this is a first movement for this pointer, we initialize the maps mIndex and mMovements + // for this pointer and write to the first position. + auto [movementIt, inserted] = mMovements.insert({pointerId, {}}); + auto [indexIt, _] = mIndex.insert({pointerId, 0}); + size_t& index = indexIt->second; + if (!inserted && movementIt->second[index].eventTime != eventTime) { // When ACTION_POINTER_DOWN happens, we will first receive ACTION_MOVE with the coordinates // of the existing pointers, and then ACTION_POINTER_DOWN with the coordinates that include // the new pointer. If the eventtimes for both events are identical, just update the data // for this time. // We only compare against the last value, as it is likely that addMovement is called // in chronological order as events occur. - mIndex++; + index++; } - if (mIndex == HISTORY_SIZE) { - mIndex = 0; + if (index == HISTORY_SIZE) { + index = 0; } - Movement& movement = mMovements[mIndex]; + Movement& movement = movementIt->second[index]; movement.eventTime = eventTime; - movement.idBits = idBits; - uint32_t count = idBits.count(); - for (uint32_t i = 0; i < count; i++) { - movement.positions[i] = positions[i]; - } + movement.position = position; } /** @@ -1178,55 +1175,61 @@ static float calculateImpulseVelocity(const nsecs_t* t, const float* x, size_t c return kineticEnergyToVelocity(work); } -bool ImpulseVelocityTrackerStrategy::getEstimator(uint32_t id, - VelocityTracker::Estimator* outEstimator) const { - outEstimator->clear(); +std::optional<VelocityTracker::Estimator> ImpulseVelocityTrackerStrategy::getEstimator( + int32_t pointerId) const { + const auto movementIt = mMovements.find(pointerId); + if (movementIt == mMovements.end()) { + return std::nullopt; // no data + } // Iterate over movement samples in reverse time order and collect samples. float positions[HISTORY_SIZE]; nsecs_t time[HISTORY_SIZE]; size_t m = 0; // number of points that will be used for fitting - size_t index = mIndex; - const Movement& newestMovement = mMovements[mIndex]; + size_t index = mIndex.at(pointerId); + const Movement& newestMovement = movementIt->second[index]; do { - const Movement& movement = mMovements[index]; - if (!movement.idBits.hasBit(id)) { - break; - } + const Movement& movement = movementIt->second[index]; nsecs_t age = newestMovement.eventTime - movement.eventTime; if (age > HORIZON) { break; } + if (movement.eventTime == 0 && index != 0) { + // All eventTime's are initialized to 0. If we encounter a time of 0 in a position + // that's >0, it means that we hit the block where the data wasn't initialized. + // It's also possible that the sample at 0 would be invalid, but there's no harm in + // processing it, since it would be just a single point, and will only be encountered + // in artificial circumstances (in tests). + break; + } - positions[m] = movement.getPosition(id); + positions[m] = movement.position; time[m] = movement.eventTime; index = (index == 0 ? HISTORY_SIZE : index) - 1; } while (++m < HISTORY_SIZE); if (m == 0) { - return false; // no data + return std::nullopt; // no data } - outEstimator->coeff[0] = 0; - outEstimator->coeff[1] = calculateImpulseVelocity(time, positions, m, mDeltaValues); - outEstimator->coeff[2] = 0; + VelocityTracker::Estimator estimator; + estimator.coeff[0] = 0; + estimator.coeff[1] = calculateImpulseVelocity(time, positions, m, mDeltaValues); + estimator.coeff[2] = 0; - outEstimator->time = newestMovement.eventTime; - outEstimator->degree = 2; // similar results to 2nd degree fit - outEstimator->confidence = 1; + estimator.time = newestMovement.eventTime; + estimator.degree = 2; // similar results to 2nd degree fit + estimator.confidence = 1; - ALOGD_IF(DEBUG_STRATEGY, "velocity: %.1f", outEstimator->coeff[1]); + ALOGD_IF(DEBUG_STRATEGY, "velocity: %.1f", estimator.coeff[1]); if (DEBUG_IMPULSE) { // TODO(b/134179997): delete this block once the switch to 'impulse' is complete. // Calculate the lsq2 velocity for the same inputs to allow runtime comparisons. // X axis chosen arbitrarily for velocity comparisons. VelocityTracker lsq2(VelocityTracker::Strategy::LSQ2); - BitSet32 idBits; - const uint32_t pointerId = 0; - idBits.markBit(pointerId); for (ssize_t i = m - 1; i >= 0; i--) { - lsq2.addMovement(time[i], idBits, {{AMOTION_EVENT_AXIS_X, {positions[i]}}}); + lsq2.addMovement(time[i], pointerId, AMOTION_EVENT_AXIS_X, positions[i]); } std::optional<float> v = lsq2.getVelocity(AMOTION_EVENT_AXIS_X, pointerId); if (v) { @@ -1235,7 +1238,7 @@ bool ImpulseVelocityTrackerStrategy::getEstimator(uint32_t id, ALOGD("lsq2 velocity: could not compute velocity"); } } - return true; + return estimator; } } // namespace android diff --git a/libs/input/android/hardware/input/InputDeviceCountryCode.aidl b/libs/input/android/hardware/input/InputDeviceCountryCode.aidl deleted file mode 100644 index 6bb1a60dda..0000000000 --- a/libs/input/android/hardware/input/InputDeviceCountryCode.aidl +++ /dev/null @@ -1,212 +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. - */ - -package android.hardware.input; - -/** - * Constant for HID country code declared by a HID device. These constants are declared as AIDL to - * be used by java and native input code. - * - * @hide - */ -@Backing(type="int") -enum InputDeviceCountryCode { - /** - * Used as default value where country code is not set in the device HID descriptor - */ - INVALID = -1, - - /** - * Used as default value when country code is not supported by the HID device. The HID - * descriptor sets "00" as the country code in this case. - */ - NOT_SUPPORTED = 0, - - /** - * Arabic - */ - ARABIC = 1, - - /** - * Belgian - */ - BELGIAN = 2, - - /** - * Canadian (Bilingual) - */ - CANADIAN_BILINGUAL = 3, - - /** - * Canadian (French) - */ - CANADIAN_FRENCH = 4, - - /** - * Czech Republic - */ - CZECH_REPUBLIC = 5, - - /** - * Danish - */ - DANISH = 6, - - /** - * Finnish - */ - FINNISH = 7, - - /** - * French - */ - FRENCH = 8, - - /** - * German - */ - GERMAN = 9, - - /** - * Greek - */ - GREEK = 10, - - /** - * Hebrew - */ - HEBREW = 11, - - /** - * Hungary - */ - HUNGARY = 12, - - /** - * International (ISO) - */ - INTERNATIONAL = 13, - - /** - * Italian - */ - ITALIAN = 14, - - /** - * Japan (Katakana) - */ - JAPAN = 15, - - /** - * Korean - */ - KOREAN = 16, - - /** - * Latin American - */ - LATIN_AMERICAN = 17, - - /** - * Netherlands (Dutch) - */ - DUTCH = 18, - - /** - * Norwegian - */ - NORWEGIAN = 19, - - /** - * Persian - */ - PERSIAN = 20, - - /** - * Poland - */ - POLAND = 21, - - /** - * Portuguese - */ - PORTUGUESE = 22, - - /** - * Russia - */ - RUSSIA = 23, - - /** - * Slovakia - */ - SLOVAKIA = 24, - - /** - * Spanish - */ - SPANISH = 25, - - /** - * Swedish - */ - SWEDISH = 26, - - /** - * Swiss (French) - */ - SWISS_FRENCH = 27, - - /** - * Swiss (German) - */ - SWISS_GERMAN = 28, - - /** - * Switzerland - */ - SWITZERLAND = 29, - - /** - * Taiwan - */ - TAIWAN = 30, - - /** - * Turkish_Q - */ - TURKISH_Q = 31, - - /** - * UK - */ - UK = 32, - - /** - * US - */ - US = 33, - - /** - * Yugoslavia - */ - YUGOSLAVIA = 34, - - /** - * Turkish_F - */ - TURKISH_F = 35, -}
\ No newline at end of file diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp index 4b3124636b..8a6e983bb5 100644 --- a/libs/input/tests/InputEvent_test.cpp +++ b/libs/input/tests/InputEvent_test.cpp @@ -46,6 +46,7 @@ TEST_F(PointerCoordsTest, ClearSetsBitsToZero) { coords.clear(); ASSERT_EQ(0ULL, coords.bits); + ASSERT_FALSE(coords.isResampled); } TEST_F(PointerCoordsTest, AxisValues) { @@ -158,11 +159,13 @@ TEST_F(PointerCoordsTest, Parcel) { outCoords.readFromParcel(&parcel); ASSERT_EQ(0ULL, outCoords.bits); + ASSERT_FALSE(outCoords.isResampled); // Round trip with some values. parcel.freeData(); inCoords.setAxisValue(2, 5); inCoords.setAxisValue(5, 8); + inCoords.isResampled = true; inCoords.writeToParcel(&parcel); parcel.setDataPosition(0); @@ -171,6 +174,7 @@ TEST_F(PointerCoordsTest, Parcel) { ASSERT_EQ(outCoords.bits, inCoords.bits); ASSERT_EQ(outCoords.values[0], inCoords.values[0]); ASSERT_EQ(outCoords.values[1], inCoords.values[1]); + ASSERT_TRUE(outCoords.isResampled); } @@ -263,6 +267,7 @@ void MotionEventTest::initializeEventWithHistory(MotionEvent* event) { pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 16); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 17); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 18); + pointerCoords[0].isResampled = true; pointerCoords[1].clear(); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 20); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 21); @@ -281,6 +286,7 @@ void MotionEventTest::initializeEventWithHistory(MotionEvent* event) { mRawTransform, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 2, pointerProperties, pointerCoords); + pointerCoords[0].clear(); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 110); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 111); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 112); @@ -290,6 +296,8 @@ void MotionEventTest::initializeEventWithHistory(MotionEvent* event) { pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 116); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 117); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 118); + pointerCoords[0].isResampled = true; + pointerCoords[1].clear(); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 120); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 121); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 122); @@ -299,8 +307,10 @@ void MotionEventTest::initializeEventWithHistory(MotionEvent* event) { pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 126); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 127); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 128); + pointerCoords[1].isResampled = true; event->addSample(ARBITRARY_EVENT_TIME + 1, pointerCoords); + pointerCoords[0].clear(); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 210); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 211); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 212); @@ -310,6 +320,7 @@ void MotionEventTest::initializeEventWithHistory(MotionEvent* event) { pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 216); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 217); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 218); + pointerCoords[1].clear(); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 220); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 221); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 222); @@ -457,6 +468,13 @@ void MotionEventTest::assertEqualsEventWithHistory(const MotionEvent* event) { ASSERT_EQ(toScaledOrientation(128), event->getHistoricalOrientation(1, 1)); ASSERT_EQ(toScaledOrientation(218), event->getOrientation(0)); ASSERT_EQ(toScaledOrientation(228), event->getOrientation(1)); + + ASSERT_TRUE(event->isResampled(0, 0)); + ASSERT_FALSE(event->isResampled(1, 0)); + ASSERT_TRUE(event->isResampled(0, 1)); + ASSERT_TRUE(event->isResampled(1, 1)); + ASSERT_FALSE(event->isResampled(0, 2)); + ASSERT_FALSE(event->isResampled(1, 2)); } TEST_F(MotionEventTest, Properties) { diff --git a/libs/input/tests/MotionPredictor_test.cpp b/libs/input/tests/MotionPredictor_test.cpp index 0f39055fc9..d2b59a1ac6 100644 --- a/libs/input/tests/MotionPredictor_test.cpp +++ b/libs/input/tests/MotionPredictor_test.cpp @@ -63,8 +63,7 @@ TEST(MotionPredictorTest, LinearPrediction) { predictor.record(getMotionEvent(MOVE, 1, 3, 10)); predictor.record(getMotionEvent(MOVE, 2, 5, 20)); predictor.record(getMotionEvent(MOVE, 3, 7, 30)); - predictor.setExpectedPresentationTimeNanos(40); - std::vector<std::unique_ptr<MotionEvent>> predicted = predictor.predict(); + std::vector<std::unique_ptr<MotionEvent>> predicted = predictor.predict(40); ASSERT_EQ(1u, predicted.size()); ASSERT_EQ(predicted[0]->getX(0), 4); ASSERT_EQ(predicted[0]->getY(0), 9); @@ -81,8 +80,7 @@ TEST(MotionPredictorTest, StationaryPrediction) { predictor.record(getMotionEvent(MOVE, 0, 1, 10)); predictor.record(getMotionEvent(MOVE, 0, 1, 20)); predictor.record(getMotionEvent(MOVE, 0, 1, 30)); - predictor.setExpectedPresentationTimeNanos(40); - std::vector<std::unique_ptr<MotionEvent>> predicted = predictor.predict(); + std::vector<std::unique_ptr<MotionEvent>> predicted = predictor.predict(40); ASSERT_EQ(1u, predicted.size()); ASSERT_EQ(predicted[0]->getX(0), 0); ASSERT_EQ(predicted[0]->getY(0), 1); @@ -98,21 +96,19 @@ TEST(MotionPredictorTest, IsPredictionAvailable) { TEST(MotionPredictorTest, Offset) { MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/1, []() { return true /*enable prediction*/; }); - predictor.setExpectedPresentationTimeNanos(40); predictor.record(getMotionEvent(DOWN, 0, 1, 30)); predictor.record(getMotionEvent(MOVE, 0, 1, 35)); - std::vector<std::unique_ptr<MotionEvent>> predicted = predictor.predict(); + std::vector<std::unique_ptr<MotionEvent>> predicted = predictor.predict(40); ASSERT_EQ(1u, predicted.size()); ASSERT_GE(predicted[0]->getEventTime(), 41); } -TEST(MotionPredictionTest, FlagDisablesPrediction) { +TEST(MotionPredictorTest, FlagDisablesPrediction) { MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/0, []() { return false /*disable prediction*/; }); - predictor.setExpectedPresentationTimeNanos(40); predictor.record(getMotionEvent(DOWN, 0, 1, 30)); predictor.record(getMotionEvent(MOVE, 0, 1, 35)); - std::vector<std::unique_ptr<MotionEvent>> predicted = predictor.predict(); + std::vector<std::unique_ptr<MotionEvent>> predicted = predictor.predict(40); ASSERT_EQ(0u, predicted.size()); ASSERT_FALSE(predictor.isPredictionAvailable(/*deviceId=*/1, AINPUT_SOURCE_STYLUS)); ASSERT_FALSE(predictor.isPredictionAvailable(/*deviceId=*/1, AINPUT_SOURCE_TOUCHSCREEN)); diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp index 1c8658b69a..024b6d3d43 100644 --- a/libs/input/tests/StructLayout_test.cpp +++ b/libs/input/tests/StructLayout_test.cpp @@ -117,7 +117,7 @@ void TestHeaderSize() { void TestBodySize() { static_assert(sizeof(InputMessage::Body::Key) == 96); - static_assert(sizeof(InputMessage::Body::Motion::Pointer) == 136); + static_assert(sizeof(InputMessage::Body::Motion::Pointer) == 144); static_assert(sizeof(InputMessage::Body::Motion) == offsetof(InputMessage::Body::Motion, pointers) + sizeof(InputMessage::Body::Motion::Pointer) * MAX_POINTERS); @@ -137,8 +137,8 @@ void TestBodySize() { static_assert(sizeof(InputMessage::Body) == offsetof(InputMessage::Body::Motion, pointers) + sizeof(InputMessage::Body::Motion::Pointer) * MAX_POINTERS); - static_assert(sizeof(InputMessage::Body) == 160 + 136 * 16); - static_assert(sizeof(InputMessage::Body) == 2336); + static_assert(sizeof(InputMessage::Body) == 160 + 144 * 16); + static_assert(sizeof(InputMessage::Body) == 2464); } /** @@ -148,8 +148,8 @@ void TestBodySize() { * still helpful to compute to get an idea of the sizes that are involved. */ void TestWorstCaseInputMessageSize() { - static_assert(sizeof(InputMessage) == /*header*/ 8 + /*body*/ 2336); - static_assert(sizeof(InputMessage) == 2344); + static_assert(sizeof(InputMessage) == /*header*/ 8 + /*body*/ 2464); + static_assert(sizeof(InputMessage) == 2472); } /** @@ -159,8 +159,8 @@ void CalculateSinglePointerInputMessageSize() { constexpr size_t pointerCount = 1; constexpr size_t bodySize = offsetof(InputMessage::Body::Motion, pointers) + sizeof(InputMessage::Body::Motion::Pointer) * pointerCount; - static_assert(bodySize == 160 + 136); - static_assert(bodySize == 296); // For the total message size, add the small header + static_assert(bodySize == 160 + 144); + static_assert(bodySize == 304); // For the total message size, add the small header } // --- VerifiedInputEvent --- diff --git a/libs/input/tests/TouchResampling_test.cpp b/libs/input/tests/TouchResampling_test.cpp index c09a8e9358..d01258c783 100644 --- a/libs/input/tests/TouchResampling_test.cpp +++ b/libs/input/tests/TouchResampling_test.cpp @@ -31,6 +31,7 @@ struct Pointer { int32_t id; float x; float y; + bool isResampled = false; }; struct InputEventEntry { @@ -190,6 +191,8 @@ void TouchResamplingTest::consumeInputEventEntries(const std::vector<InputEventE ASSERT_EQ(entry.pointers[p].y, motionEvent->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, motionEventPointerIndex, i)); + ASSERT_EQ(entry.pointers[p].isResampled, + motionEvent->isResampled(motionEventPointerIndex, i)); } } @@ -244,7 +247,7 @@ TEST_F(TouchResamplingTest, EventIsResampled) { // id x y {10ms, {{0, 20, 30}}, AMOTION_EVENT_ACTION_MOVE}, {20ms, {{0, 30, 30}}, AMOTION_EVENT_ACTION_MOVE}, - {25ms, {{0, 35, 30}}, AMOTION_EVENT_ACTION_MOVE}, // resampled value + {25ms, {{0, 35, 30, .isResampled = true}}, AMOTION_EVENT_ACTION_MOVE}, }; consumeInputEventEntries(expectedEntries, frameTime); } @@ -283,7 +286,7 @@ TEST_F(TouchResamplingTest, EventIsResampledWithDifferentId) { // id x y {10ms, {{1, 20, 30}}, AMOTION_EVENT_ACTION_MOVE}, {20ms, {{1, 30, 30}}, AMOTION_EVENT_ACTION_MOVE}, - {25ms, {{1, 35, 30}}, AMOTION_EVENT_ACTION_MOVE}, // resampled value + {25ms, {{1, 35, 30, .isResampled = true}}, AMOTION_EVENT_ACTION_MOVE}, }; consumeInputEventEntries(expectedEntries, frameTime); } @@ -361,7 +364,7 @@ TEST_F(TouchResamplingTest, ResampledValueIsUsedForIdenticalCoordinates) { // id x y {10ms, {{0, 20, 30}}, AMOTION_EVENT_ACTION_MOVE}, {20ms, {{0, 30, 30}}, AMOTION_EVENT_ACTION_MOVE}, - {25ms, {{0, 35, 30}}, AMOTION_EVENT_ACTION_MOVE}, // resampled value + {25ms, {{0, 35, 30, .isResampled = true}}, AMOTION_EVENT_ACTION_MOVE}, }; consumeInputEventEntries(expectedEntries, frameTime); @@ -375,8 +378,12 @@ TEST_F(TouchResamplingTest, ResampledValueIsUsedForIdenticalCoordinates) { frameTime = 45ms + 5ms /*RESAMPLE_LATENCY*/; expectedEntries = { // id x y - {40ms, {{0, 35, 30}}, AMOTION_EVENT_ACTION_MOVE}, // original event, rewritten - {45ms, {{0, 35, 30}}, AMOTION_EVENT_ACTION_MOVE}, // resampled event, rewritten + {40ms, + {{0, 35, 30, .isResampled = true}}, + AMOTION_EVENT_ACTION_MOVE}, // original event, rewritten + {45ms, + {{0, 35, 30, .isResampled = true}}, + AMOTION_EVENT_ACTION_MOVE}, // resampled event, rewritten }; consumeInputEventEntries(expectedEntries, frameTime); } @@ -411,7 +418,7 @@ TEST_F(TouchResamplingTest, OldEventReceivedAfterResampleOccurs) { // id x y {10ms, {{0, 20, 30}}, AMOTION_EVENT_ACTION_MOVE}, {20ms, {{0, 30, 30}}, AMOTION_EVENT_ACTION_MOVE}, - {25ms, {{0, 35, 30}}, AMOTION_EVENT_ACTION_MOVE}, // resampled value + {25ms, {{0, 35, 30, .isResampled = true}}, AMOTION_EVENT_ACTION_MOVE}, }; consumeInputEventEntries(expectedEntries, frameTime); // Above, the resampled event is at 25ms rather than at 30 ms = 35ms - RESAMPLE_LATENCY @@ -428,8 +435,12 @@ TEST_F(TouchResamplingTest, OldEventReceivedAfterResampleOccurs) { frameTime = 50ms; expectedEntries = { // id x y - {24ms, {{0, 35, 30}}, AMOTION_EVENT_ACTION_MOVE}, // original event, rewritten - {26ms, {{0, 45, 30}}, AMOTION_EVENT_ACTION_MOVE}, // resampled event, rewritten + {24ms, + {{0, 35, 30, .isResampled = true}}, + AMOTION_EVENT_ACTION_MOVE}, // original event, rewritten + {26ms, + {{0, 45, 30, .isResampled = true}}, + AMOTION_EVENT_ACTION_MOVE}, // resampled event, rewritten }; consumeInputEventEntries(expectedEntries, frameTime); } @@ -499,7 +510,9 @@ TEST_F(TouchResamplingTest, TwoPointersAreResampledIndependently) { // id x y {30ms, {{0, 100, 100}, {1, 500, 500}}, AMOTION_EVENT_ACTION_MOVE}, {40ms, {{0, 120, 120}, {1, 600, 600}}, AMOTION_EVENT_ACTION_MOVE}, - {45ms, {{0, 130, 130}, {1, 650, 650}}, AMOTION_EVENT_ACTION_MOVE}, // resampled value + {45ms, + {{0, 130, 130, .isResampled = true}, {1, 650, 650, .isResampled = true}}, + AMOTION_EVENT_ACTION_MOVE}, }; consumeInputEventEntries(expectedEntries, frameTime); @@ -518,11 +531,13 @@ TEST_F(TouchResamplingTest, TwoPointersAreResampledIndependently) { */ expectedEntries = { {60ms, - {{0, 130, 130}, // not 120! because it matches previous real event - {1, 650, 650}}, + {{0, 130, 130, .isResampled = true}, // not 120! because it matches previous real event + {1, 650, 650, .isResampled = true}}, AMOTION_EVENT_ACTION_MOVE}, {70ms, {{0, 130, 130}, {1, 700, 700}}, AMOTION_EVENT_ACTION_MOVE}, - {75ms, {{0, 135, 135}, {1, 750, 750}}, AMOTION_EVENT_ACTION_MOVE}, // resampled value + {75ms, + {{0, 135, 135, .isResampled = true}, {1, 750, 750, .isResampled = true}}, + AMOTION_EVENT_ACTION_MOVE}, }; consumeInputEventEntries(expectedEntries, frameTime); @@ -554,7 +569,7 @@ TEST_F(TouchResamplingTest, TwoPointersAreResampledIndependently) { * The latest event with ACTION_MOVE was at t = 70, coord = 700. * Use that value for resampling here: (600 - 700) / (90 - 70) * 5 + 600 */ - {95ms, {{1, 575, 575}}, AMOTION_EVENT_ACTION_MOVE}, // resampled value + {95ms, {{1, 575, 575, .isResampled = true}}, AMOTION_EVENT_ACTION_MOVE}, }; consumeInputEventEntries(expectedEntries, frameTime); } diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp index 54feea2644..2678f2f2fd 100644 --- a/libs/input/tests/VelocityTracker_test.cpp +++ b/libs/input/tests/VelocityTracker_test.cpp @@ -288,13 +288,13 @@ static void computeAndCheckQuadraticEstimate(const std::vector<PlanarMotionEvent for (MotionEvent event : events) { vt.addMovement(&event); } - VelocityTracker::Estimator estimatorX; - VelocityTracker::Estimator estimatorY; - EXPECT_TRUE(vt.getEstimator(AMOTION_EVENT_AXIS_X, 0, &estimatorX)); - EXPECT_TRUE(vt.getEstimator(AMOTION_EVENT_AXIS_Y, 0, &estimatorY)); + std::optional<VelocityTracker::Estimator> estimatorX = vt.getEstimator(AMOTION_EVENT_AXIS_X, 0); + std::optional<VelocityTracker::Estimator> estimatorY = vt.getEstimator(AMOTION_EVENT_AXIS_Y, 0); + EXPECT_TRUE(estimatorX); + EXPECT_TRUE(estimatorY); for (size_t i = 0; i< coefficients.size(); i++) { - checkCoefficient(estimatorX.coeff[i], coefficients[i]); - checkCoefficient(estimatorY.coeff[i], coefficients[i]); + checkCoefficient((*estimatorX).coeff[i], coefficients[i]); + checkCoefficient((*estimatorY).coeff[i], coefficients[i]); } } @@ -420,8 +420,7 @@ TEST_F(VelocityTrackerTest, TestApiInteractionsWithNoMotionEvents) { EXPECT_FALSE(vt.getVelocity(AMOTION_EVENT_AXIS_X, DEFAULT_POINTER_ID)); - VelocityTracker::Estimator estimator; - EXPECT_FALSE(vt.getEstimator(AMOTION_EVENT_AXIS_X, DEFAULT_POINTER_ID, &estimator)); + EXPECT_FALSE(vt.getEstimator(AMOTION_EVENT_AXIS_X, DEFAULT_POINTER_ID)); VelocityTracker::ComputedVelocity computedVelocity = vt.getComputedVelocity(1000, 1000); for (uint32_t id = 0; id <= MAX_POINTER_ID; id++) { @@ -432,7 +431,7 @@ TEST_F(VelocityTrackerTest, TestApiInteractionsWithNoMotionEvents) { EXPECT_EQ(-1, vt.getActivePointerId()); // Make sure that the clearing functions execute without an issue. - vt.clearPointers(BitSet32(7U)); + vt.clearPointer(7U); vt.clear(); } diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegdecoder.h b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegdecoder.h index 39c79c9695..d0de48ff87 100644 --- a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegdecoder.h +++ b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegdecoder.h @@ -36,11 +36,11 @@ public: JpegDecoder(); ~JpegDecoder(); /* - * Decompresses JPEG image to raw image (YUV420planer or grey-scale) format. After calling - * this method, call getDecompressedImage() to get the image. + * Decompresses JPEG image to raw image (YUV420planer, grey-scale or RGBA) format. After + * calling this method, call getDecompressedImage() to get the image. * Returns false if decompressing the image fails. */ - bool decompressImage(const void* image, int length); + bool decompressImage(const void* image, int length, bool decodeToRGBA = false); /* * Returns the decompressed raw image buffer pointer. This method must be called only after * calling decompressImage(). @@ -86,22 +86,23 @@ public: */ int getEXIFPos() { return mExifPos; } /* - * Decompresses metadata of the image. + * Decompresses metadata of the image. All vectors are owned by the caller. */ bool getCompressedImageParameters(const void* image, int length, size_t* pWidth, size_t* pHeight, - std::vector<uint8_t>* &iccData, - std::vector<uint8_t>* &exifData); + std::vector<uint8_t>* iccData, + std::vector<uint8_t>* exifData); /* * Extracts EXIF package and updates the EXIF position / length without decoding the image. */ bool extractEXIF(const void* image, int length); private: - bool decode(const void* image, int length); + bool decode(const void* image, int length, bool decodeToRGBA); // Returns false if errors occur. bool decompress(jpeg_decompress_struct* cinfo, const uint8_t* dest, bool isSingleChannel); bool decompressYUV(jpeg_decompress_struct* cinfo, const uint8_t* dest); + bool decompressRGBA(jpeg_decompress_struct* cinfo, const uint8_t* dest); bool decompressSingleChannel(jpeg_decompress_struct* cinfo, const uint8_t* dest); // Process 16 lines of Y and 16 lines of U/V each time. // We must pass at least 16 scanlines according to libjpeg documentation. diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h index 905bf16c1a..35979032be 100644 --- a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h +++ b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h @@ -244,7 +244,8 @@ public: * * The output is filled jpegr_info structure * @param compressed_jpegr_image compressed JPEGR image - * @param jpegr_info pointer to output JPEGR info + * @param jpegr_info pointer to output JPEGR info. Members of jpegr_info + * are owned by the caller * @return NO_ERROR if JPEGR parsing succeeds, error code otherwise */ status_t getJPEGRInfo(jr_compressed_ptr compressed_jpegr_image, @@ -339,11 +340,11 @@ private: /* * This method will tone map a HDR image to an SDR image. * - * @param uncompressed_p010_image (input) uncompressed P010 image + * @param src (input) uncompressed P010 image * @param dest (output) tone mapping result as a YUV_420 image * @return NO_ERROR if calculation succeeds, error code if error occurs. */ - status_t toneMap(jr_uncompressed_ptr uncompressed_p010_image, + status_t toneMap(jr_uncompressed_ptr src, jr_uncompressed_ptr dest); }; diff --git a/libs/jpegrecoverymap/jpegdecoder.cpp b/libs/jpegrecoverymap/jpegdecoder.cpp index c2a8f45dbd..0ae6a63970 100644 --- a/libs/jpegrecoverymap/jpegdecoder.cpp +++ b/libs/jpegrecoverymap/jpegdecoder.cpp @@ -93,7 +93,7 @@ JpegDecoder::JpegDecoder() { JpegDecoder::~JpegDecoder() { } -bool JpegDecoder::decompressImage(const void* image, int length) { +bool JpegDecoder::decompressImage(const void* image, int length, bool decodeToRGBA) { if (image == nullptr || length <= 0) { ALOGE("Image size can not be handled: %d", length); return false; @@ -101,7 +101,7 @@ bool JpegDecoder::decompressImage(const void* image, int length) { mResultBuffer.clear(); mXMPBuffer.clear(); - if (!decode(image, length)) { + if (!decode(image, length, decodeToRGBA)) { return false; } @@ -140,7 +140,7 @@ size_t JpegDecoder::getDecompressedImageHeight() { return mHeight; } -bool JpegDecoder::decode(const void* image, int length) { +bool JpegDecoder::decode(const void* image, int length, bool decodeToRGBA) { jpeg_decompress_struct cinfo; jpegr_source_mgr mgr(static_cast<const uint8_t*>(image), length); jpegrerror_mgr myerr; @@ -210,15 +210,26 @@ bool JpegDecoder::decode(const void* image, int length) { mWidth = cinfo.image_width; mHeight = cinfo.image_height; - if (cinfo.jpeg_color_space == JCS_YCbCr) { - mResultBuffer.resize(cinfo.image_width * cinfo.image_height * 3 / 2, 0); - } else if (cinfo.jpeg_color_space == JCS_GRAYSCALE) { - mResultBuffer.resize(cinfo.image_width * cinfo.image_height, 0); + if (decodeToRGBA) { + if (cinfo.jpeg_color_space == JCS_GRAYSCALE) { + // We don't intend to support decoding grayscale to RGBA + return false; + } + // 4 bytes per pixel + mResultBuffer.resize(cinfo.image_width * cinfo.image_height * 4); + cinfo.out_color_space = JCS_EXT_RGBA; + } else { + if (cinfo.jpeg_color_space == JCS_YCbCr) { + // 1 byte per pixel for Y, 0.5 byte per pixel for U+V + mResultBuffer.resize(cinfo.image_width * cinfo.image_height * 3 / 2, 0); + } else if (cinfo.jpeg_color_space == JCS_GRAYSCALE) { + mResultBuffer.resize(cinfo.image_width * cinfo.image_height, 0); + } + cinfo.out_color_space = cinfo.jpeg_color_space; + cinfo.raw_data_out = TRUE; } - cinfo.raw_data_out = TRUE; cinfo.dct_method = JDCT_IFAST; - cinfo.out_color_space = cinfo.jpeg_color_space; jpeg_start_decompress(&cinfo); @@ -292,12 +303,15 @@ bool JpegDecoder::decompress(jpeg_decompress_struct* cinfo, const uint8_t* dest, if (isSingleChannel) { return decompressSingleChannel(cinfo, dest); } - return decompressYUV(cinfo, dest); + if (cinfo->out_color_space == JCS_EXT_RGBA) + return decompressRGBA(cinfo, dest); + else + return decompressYUV(cinfo, dest); } bool JpegDecoder::getCompressedImageParameters(const void* image, int length, size_t *pWidth, size_t *pHeight, - std::vector<uint8_t> *&iccData , std::vector<uint8_t> *&exifData) { + std::vector<uint8_t> *iccData , std::vector<uint8_t> *exifData) { jpeg_decompress_struct cinfo; jpegr_source_mgr mgr(static_cast<const uint8_t*>(image), length); jpegrerror_mgr myerr; @@ -322,15 +336,46 @@ bool JpegDecoder::getCompressedImageParameters(const void* image, int length, *pWidth = cinfo.image_width; *pHeight = cinfo.image_height; - //TODO: Parse iccProfile and exifData + //TODO: Parse iccProfile (void)iccData; - (void)exifData; + if (exifData != nullptr) { + bool exifAppears = false; + for (jpeg_marker_struct* marker = cinfo.marker_list; marker && !exifAppears; + marker = marker->next) { + if (marker->marker != kAPP1Marker) { + continue; + } + + const unsigned int len = marker->data_length; + if (len >= kExifIdCode.size() && + !strncmp(reinterpret_cast<const char*>(marker->data), kExifIdCode.c_str(), + kExifIdCode.size())) { + exifData->resize(len, 0); + memcpy(static_cast<void*>(exifData->data()), marker->data, len); + exifAppears = true; + } + } + } jpeg_destroy_decompress(&cinfo); return true; } +bool JpegDecoder::decompressRGBA(jpeg_decompress_struct* cinfo, const uint8_t* dest) { + JSAMPLE* decodeDst = (JSAMPLE*) dest; + uint32_t lines = 0; + // TODO: use batches for more effectiveness + while (lines < cinfo->image_height) { + uint32_t ret = jpeg_read_scanlines(cinfo, &decodeDst, 1); + if (ret == 0) { + break; + } + decodeDst += cinfo->image_width * 4; + lines++; + } + return lines == cinfo->image_height; +} bool JpegDecoder::decompressYUV(jpeg_decompress_struct* cinfo, const uint8_t* dest) { diff --git a/libs/jpegrecoverymap/recoverymap.cpp b/libs/jpegrecoverymap/recoverymap.cpp index 53fa8ce9e3..ee68043c79 100644 --- a/libs/jpegrecoverymap/recoverymap.cpp +++ b/libs/jpegrecoverymap/recoverymap.cpp @@ -25,7 +25,6 @@ #include <image_io/jpeg/jpeg_scanner.h> #include <image_io/jpeg/jpeg_info_builder.h> #include <image_io/base/data_segment_data_source.h> -#include <utils/Log.h> #include <memory> #include <sstream> @@ -192,6 +191,31 @@ status_t updateExif(jr_exif_ptr exif, jr_exif_ptr dest) { return NO_ERROR; } +/* + * Helper function copies the JPEG image from without EXIF. + * + * @param dest destination of the data to be written. + * @param source source of data being written. + * @param exif_pos position of the EXIF package, which is aligned with jpegdecoder.getEXIFPos(). + * (4 bypes offset to FF sign, the byte after FF E1 XX XX <this byte>). + * @param exif_size exif size without the initial 4 bytes, aligned with jpegdecoder.getEXIFSize(). + */ +void copyJpegWithoutExif(jr_compressed_ptr dest, + jr_compressed_ptr source, + size_t exif_pos, + size_t exif_size) { + memcpy(dest, source, sizeof(jpegr_compressed_struct)); + + const size_t exif_offset = 4; //exif_pos has 4 bypes offset to the FF sign + dest->length = source->length - exif_size - exif_offset; + dest->data = malloc(dest->length); + + memcpy(dest->data, source->data, exif_pos - exif_offset); + memcpy((uint8_t*)dest->data + exif_pos - exif_offset, + (uint8_t*)source->data + exif_pos + exif_size, + source->length - exif_pos - exif_size); +} + /* Encode API-0 */ status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image, jpegr_transfer_function hdr_tf, @@ -214,6 +238,9 @@ status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image, } jpegr_uncompressed_struct uncompressed_yuv_420_image; + unique_ptr<uint8_t[]> uncompressed_yuv_420_image_data = make_unique<uint8_t[]>( + uncompressed_p010_image->width * uncompressed_p010_image->height * 3 / 2); + uncompressed_yuv_420_image.data = uncompressed_yuv_420_image_data.get(); JPEGR_CHECK(toneMap(uncompressed_p010_image, &uncompressed_yuv_420_image)); jpegr_uncompressed_struct map; @@ -240,7 +267,7 @@ status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image, jpeg.length = jpeg_encoder.getCompressedImageSize(); jpegr_exif_struct new_exif; - if (exif->data == nullptr) { + if (exif == nullptr || exif->data == nullptr) { new_exif.length = PSEUDO_EXIF_PACKAGE_LENGTH; } else { new_exif.length = exif->length + EXIF_J_R_ENTRY_LENGTH; @@ -367,17 +394,18 @@ status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image, return ERROR_JPEGR_DECODE_ERROR; } + // Update exif. jpegr_exif_struct exif; exif.data = nullptr; exif.length = 0; - // Delete EXIF package if it appears, and update exif. + jpegr_compressed_struct new_jpeg_image; + new_jpeg_image.data = nullptr; + new_jpeg_image.length = 0; if (jpeg_decoder.getEXIFPos() != 0) { - int new_length = compressed_jpeg_image->length - jpeg_decoder.getEXIFSize() - 4; - memcpy((uint8_t*)compressed_jpeg_image->data + jpeg_decoder.getEXIFPos() - 4, - (uint8_t*)compressed_jpeg_image->data + jpeg_decoder.getEXIFPos() - + jpeg_decoder.getEXIFSize(), - compressed_jpeg_image->length - jpeg_decoder.getEXIFPos() - jpeg_decoder.getEXIFSize()); - compressed_jpeg_image->length = new_length; + copyJpegWithoutExif(&new_jpeg_image, + compressed_jpeg_image, + jpeg_decoder.getEXIFPos(), + jpeg_decoder.getEXIFSize()); exif.data = jpeg_decoder.getEXIFPtr(); exif.length = jpeg_decoder.getEXIFSize(); } @@ -395,7 +423,12 @@ status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image, JPEGR_CHECK(updateExif(&exif, &new_exif)); JPEGR_CHECK(appendRecoveryMap( - compressed_jpeg_image, &compressed_map, &new_exif, &metadata, dest)); + new_jpeg_image.data == nullptr ? compressed_jpeg_image : &new_jpeg_image, + &compressed_map, &new_exif, &metadata, dest)); + + if (new_jpeg_image.data != nullptr) { + free(new_jpeg_image.data); + } return NO_ERROR; } @@ -421,17 +454,18 @@ status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image, uncompressed_yuv_420_image.height = jpeg_decoder.getDecompressedImageHeight(); uncompressed_yuv_420_image.colorGamut = compressed_jpeg_image->colorGamut; + // Update exif. jpegr_exif_struct exif; exif.data = nullptr; exif.length = 0; - // Delete EXIF package if it appears, and update exif. + jpegr_compressed_struct new_jpeg_image; + new_jpeg_image.data = nullptr; + new_jpeg_image.length = 0; if (jpeg_decoder.getEXIFPos() != 0) { - int new_length = compressed_jpeg_image->length - jpeg_decoder.getEXIFSize() - 4; - memcpy((uint8_t*)compressed_jpeg_image->data + jpeg_decoder.getEXIFPos() - 4, - (uint8_t*)compressed_jpeg_image->data + jpeg_decoder.getEXIFPos() - + jpeg_decoder.getEXIFSize(), - compressed_jpeg_image->length - jpeg_decoder.getEXIFPos() - jpeg_decoder.getEXIFSize()); - compressed_jpeg_image->length = new_length; + copyJpegWithoutExif(&new_jpeg_image, + compressed_jpeg_image, + jpeg_decoder.getEXIFPos(), + jpeg_decoder.getEXIFSize()); exif.data = jpeg_decoder.getEXIFPtr(); exif.length = jpeg_decoder.getEXIFSize(); } @@ -472,7 +506,12 @@ status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image, JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map)); JPEGR_CHECK(appendRecoveryMap( - compressed_jpeg_image, &compressed_map, &new_exif, &metadata, dest)); + new_jpeg_image.data == nullptr ? compressed_jpeg_image : &new_jpeg_image, + &compressed_map, &new_exif, &metadata, dest)); + + if (new_jpeg_image.data != nullptr) { + free(new_jpeg_image.data); + } return NO_ERROR; } @@ -505,23 +544,37 @@ status_t RecoveryMap::decodeJPEGR(jr_compressed_ptr compressed_jpegr_image, if (compressed_jpegr_image == nullptr || dest == nullptr) { return ERROR_JPEGR_INVALID_NULL_PTR; } - // TODO: fill EXIF data (void) exif; + if (request_sdr) { + JpegDecoder jpeg_decoder; + if (!jpeg_decoder.decompressImage(compressed_jpegr_image->data, compressed_jpegr_image->length, + true)) { + return ERROR_JPEGR_DECODE_ERROR; + } + jpegr_uncompressed_struct uncompressed_rgba_image; + uncompressed_rgba_image.data = jpeg_decoder.getDecompressedImagePtr(); + uncompressed_rgba_image.width = jpeg_decoder.getDecompressedImageWidth(); + uncompressed_rgba_image.height = jpeg_decoder.getDecompressedImageHeight(); + memcpy(dest->data, uncompressed_rgba_image.data, + uncompressed_rgba_image.width * uncompressed_rgba_image.height * 4); + dest->width = uncompressed_rgba_image.width; + dest->height = uncompressed_rgba_image.height; + return NO_ERROR; + } + jpegr_compressed_struct compressed_map; jpegr_metadata metadata; JPEGR_CHECK(extractRecoveryMap(compressed_jpegr_image, &compressed_map)); - JpegDecoder jpeg_decoder; if (!jpeg_decoder.decompressImage(compressed_jpegr_image->data, compressed_jpegr_image->length)) { return ERROR_JPEGR_DECODE_ERROR; } JpegDecoder recovery_map_decoder; - if (!recovery_map_decoder.decompressImage(compressed_map.data, - compressed_map.length)) { + if (!recovery_map_decoder.decompressImage(compressed_map.data, compressed_map.length)) { return ERROR_JPEGR_DECODE_ERROR; } @@ -530,26 +583,17 @@ status_t RecoveryMap::decodeJPEGR(jr_compressed_ptr compressed_jpegr_image, map.width = recovery_map_decoder.getDecompressedImageWidth(); map.height = recovery_map_decoder.getDecompressedImageHeight(); - jpegr_uncompressed_struct uncompressed_yuv_420_image; uncompressed_yuv_420_image.data = jpeg_decoder.getDecompressedImagePtr(); uncompressed_yuv_420_image.width = jpeg_decoder.getDecompressedImageWidth(); uncompressed_yuv_420_image.height = jpeg_decoder.getDecompressedImageHeight(); if (!getMetadataFromXMP(static_cast<uint8_t*>(jpeg_decoder.getXMPPtr()), - jpeg_decoder.getXMPSize(), &metadata)) { + jpeg_decoder.getXMPSize(), &metadata)) { return ERROR_JPEGR_DECODE_ERROR; } - if (request_sdr) { - memcpy(dest->data, uncompressed_yuv_420_image.data, - uncompressed_yuv_420_image.width*uncompressed_yuv_420_image.height *3 / 2); - dest->width = uncompressed_yuv_420_image.width; - dest->height = uncompressed_yuv_420_image.height; - } else { - JPEGR_CHECK(applyRecoveryMap(&uncompressed_yuv_420_image, &map, &metadata, dest)); - } - + JPEGR_CHECK(applyRecoveryMap(&uncompressed_yuv_420_image, &map, &metadata, dest)); return NO_ERROR; } @@ -864,7 +908,7 @@ status_t RecoveryMap::appendRecoveryMap(jr_compressed_ptr compressed_jpeg_image, // 2 bytes: representing the length of the package // 29 bytes: length of name space "http://ns.adobe.com/xap/1.0/\0", // x bytes: length of xmp packet - const int length = 3 + nameSpaceLength + xmp.size(); + const int length = 2 + nameSpaceLength + xmp.size(); const uint8_t lengthH = ((length >> 8) & 0xff); const uint8_t lengthL = (length & 0xff); JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos)); @@ -889,18 +933,39 @@ status_t RecoveryMap::appendRecoveryMap(jr_compressed_ptr compressed_jpeg_image, return NO_ERROR; } -status_t RecoveryMap::toneMap(jr_uncompressed_ptr uncompressed_p010_image, +status_t RecoveryMap::toneMap(jr_uncompressed_ptr src, jr_uncompressed_ptr dest) { - if (uncompressed_p010_image == nullptr || dest == nullptr) { + if (src == nullptr || dest == nullptr) { return ERROR_JPEGR_INVALID_NULL_PTR; } - dest->width = uncompressed_p010_image->width; - dest->height = uncompressed_p010_image->height; - unique_ptr<uint8_t[]> dest_data = make_unique<uint8_t[]>(dest->width * dest->height * 3 / 2); - dest->data = dest_data.get(); + dest->width = src->width; + dest->height = src->height; + + size_t pixel_count = src->width * src->height; + for (size_t y = 0; y < src->height; ++y) { + for (size_t x = 0; x < src->width; ++x) { + size_t pixel_y_idx = x + y * src->width; + size_t pixel_uv_idx = x / 2 + (y / 2) * (src->width / 2); + + uint16_t y_uint = reinterpret_cast<uint16_t*>(src->data)[pixel_y_idx] + >> 6; + uint16_t u_uint = reinterpret_cast<uint16_t*>(src->data)[pixel_count + pixel_uv_idx * 2] + >> 6; + uint16_t v_uint = reinterpret_cast<uint16_t*>(src->data)[pixel_count + pixel_uv_idx * 2 + 1] + >> 6; + + uint8_t* y = &reinterpret_cast<uint8_t*>(dest->data)[pixel_y_idx]; + uint8_t* u = &reinterpret_cast<uint8_t*>(dest->data)[pixel_count + pixel_uv_idx]; + uint8_t* v = &reinterpret_cast<uint8_t*>(dest->data)[pixel_count * 5 / 4 + pixel_uv_idx]; + + *y = static_cast<uint8_t>((y_uint >> 2) & 0xff); + *u = static_cast<uint8_t>((u_uint >> 2) & 0xff); + *v = static_cast<uint8_t>((v_uint >> 2) & 0xff); + } + } - // TODO: Tone map algorighm here. + dest->colorGamut = src->colorGamut; return NO_ERROR; } diff --git a/libs/jpegrecoverymap/tests/recoverymap_test.cpp b/libs/jpegrecoverymap/tests/recoverymap_test.cpp index c3c6fd4e2b..8ff12fb528 100644 --- a/libs/jpegrecoverymap/tests/recoverymap_test.cpp +++ b/libs/jpegrecoverymap/tests/recoverymap_test.cpp @@ -114,58 +114,57 @@ TEST_F(RecoveryMapTest, writeXmpThenRead) { } /* Test Encode API-0 and decode */ -// TODO: enable when tonemapper is ready. -//TEST_F(RecoveryMapTest, encodeFromP010ThenDecode) { -// int ret; -// -// // Load input files. -// if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) { -// FAIL() << "Load file " << RAW_P010_IMAGE << " failed"; -// } -// mRawP010Image.width = TEST_IMAGE_WIDTH; -// mRawP010Image.height = TEST_IMAGE_HEIGHT; -// mRawP010Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT2100; -// -// RecoveryMap recoveryMap; -// -// jpegr_compressed_struct jpegR; -// jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t); -// jpegR.data = malloc(jpegR.maxLength); -// ret = recoveryMap.encodeJPEGR( -// &mRawP010Image, jpegr_transfer_function::JPEGR_TF_HLG, &jpegR, 90, nullptr); -// if (ret != OK) { -// FAIL() << "Error code is " << ret; -// } -// if (SAVE_ENCODING_RESULT) { -// // Output image data to file -// std::string filePath = "/sdcard/Documents/encoded_from_jpeg_input.jpgr"; -// std::ofstream imageFile(filePath.c_str(), std::ofstream::binary); -// if (!imageFile.is_open()) { -// ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str()); -// } -// imageFile.write((const char*)jpegR.data, jpegR.length); -// } -// -// jpegr_uncompressed_struct decodedJpegR; -// int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 4; -// decodedJpegR.data = malloc(decodedJpegRSize); -// ret = recoveryMap.decodeJPEGR(&jpegR, &decodedJpegR); -// if (ret != OK) { -// FAIL() << "Error code is " << ret; -// } -// if (SAVE_DECODING_RESULT) { -// // Output image data to file -// std::string filePath = "/sdcard/Documents/decoded_from_jpeg_input.rgb10"; -// std::ofstream imageFile(filePath.c_str(), std::ofstream::binary); -// if (!imageFile.is_open()) { -// ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str()); -// } -// imageFile.write((const char*)decodedJpegR.data, decodedJpegRSize); -// } -// -// free(jpegR.data); -// free(decodedJpegR.data); -//} +TEST_F(RecoveryMapTest, encodeFromP010ThenDecode) { + int ret; + + // Load input files. + if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) { + FAIL() << "Load file " << RAW_P010_IMAGE << " failed"; + } + mRawP010Image.width = TEST_IMAGE_WIDTH; + mRawP010Image.height = TEST_IMAGE_HEIGHT; + mRawP010Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT2100; + + RecoveryMap recoveryMap; + + jpegr_compressed_struct jpegR; + jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t); + jpegR.data = malloc(jpegR.maxLength); + ret = recoveryMap.encodeJPEGR( + &mRawP010Image, jpegr_transfer_function::JPEGR_TF_HLG, &jpegR, DEFAULT_JPEG_QUALITY, nullptr); + if (ret != OK) { + FAIL() << "Error code is " << ret; + } + if (SAVE_ENCODING_RESULT) { + // Output image data to file + std::string filePath = "/sdcard/Documents/encoded_from_jpeg_input.jpgr"; + std::ofstream imageFile(filePath.c_str(), std::ofstream::binary); + if (!imageFile.is_open()) { + ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str()); + } + imageFile.write((const char*)jpegR.data, jpegR.length); + } + + jpegr_uncompressed_struct decodedJpegR; + int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 4; + decodedJpegR.data = malloc(decodedJpegRSize); + ret = recoveryMap.decodeJPEGR(&jpegR, &decodedJpegR); + if (ret != OK) { + FAIL() << "Error code is " << ret; + } + if (SAVE_DECODING_RESULT) { + // Output image data to file + std::string filePath = "/sdcard/Documents/decoded_from_jpeg_input.rgb10"; + std::ofstream imageFile(filePath.c_str(), std::ofstream::binary); + if (!imageFile.is_open()) { + ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str()); + } + imageFile.write((const char*)decodedJpegR.data, decodedJpegRSize); + } + + free(jpegR.data); + free(decodedJpegR.data); +} /* Test Encode API-1 and decode */ TEST_F(RecoveryMapTest, encodeFromRawHdrAndSdrThenDecode) { diff --git a/libs/nativewindow/include/android/hardware_buffer_aidl.h b/libs/nativewindow/include/android/hardware_buffer_aidl.h index 906d9c6f6b..1659d54539 100644 --- a/libs/nativewindow/include/android/hardware_buffer_aidl.h +++ b/libs/nativewindow/include/android/hardware_buffer_aidl.h @@ -83,7 +83,7 @@ namespace aidl::android::hardware { class HardwareBuffer { public: HardwareBuffer() noexcept {} - explicit HardwareBuffer(HardwareBuffer&& other) noexcept : mBuffer(other.release()) {} + HardwareBuffer(HardwareBuffer&& other) noexcept : mBuffer(other.release()) {} ~HardwareBuffer() { reset(); @@ -119,6 +119,13 @@ public: inline AHardwareBuffer* _Nullable get() const { return mBuffer; } inline explicit operator bool () const { return mBuffer != nullptr; } + inline bool operator!=(const HardwareBuffer& rhs) const { return get() != rhs.get(); } + inline bool operator<(const HardwareBuffer& rhs) const { return get() < rhs.get(); } + inline bool operator<=(const HardwareBuffer& rhs) const { return get() <= rhs.get(); } + inline bool operator==(const HardwareBuffer& rhs) const { return get() == rhs.get(); } + inline bool operator>(const HardwareBuffer& rhs) const { return get() > rhs.get(); } + inline bool operator>=(const HardwareBuffer& rhs) const { return get() >= rhs.get(); } + HardwareBuffer& operator=(HardwareBuffer&& other) noexcept { reset(other.release()); return *this; diff --git a/libs/renderengine/include/renderengine/mock/FakeExternalTexture.h b/libs/renderengine/include/renderengine/mock/FakeExternalTexture.h index 974e0fddde..b95f011753 100644 --- a/libs/renderengine/include/renderengine/mock/FakeExternalTexture.h +++ b/libs/renderengine/include/renderengine/mock/FakeExternalTexture.h @@ -23,7 +23,9 @@ namespace renderengine { namespace mock { class FakeExternalTexture : public renderengine::ExternalTexture { - const sp<GraphicBuffer> mNullBuffer = nullptr; + const sp<GraphicBuffer> mEmptyBuffer = + sp<GraphicBuffer>::make(1u, 1u, PIXEL_FORMAT_RGBA_8888, + GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_SW_READ_OFTEN); uint32_t mWidth; uint32_t mHeight; uint64_t mId; @@ -34,7 +36,7 @@ public: FakeExternalTexture(uint32_t width, uint32_t height, uint64_t id, PixelFormat pixelFormat, uint64_t usage) : mWidth(width), mHeight(height), mId(id), mPixelFormat(pixelFormat), mUsage(usage) {} - const sp<GraphicBuffer>& getBuffer() const { return mNullBuffer; } + const sp<GraphicBuffer>& getBuffer() const { return mEmptyBuffer; } bool hasSameBuffer(const renderengine::ExternalTexture& other) const override { return getId() == other.getId(); } diff --git a/libs/renderengine/skia/filters/GaussianBlurFilter.cpp b/libs/renderengine/skia/filters/GaussianBlurFilter.cpp index f3b6ab9885..511d7c9350 100644 --- a/libs/renderengine/skia/filters/GaussianBlurFilter.cpp +++ b/libs/renderengine/skia/filters/GaussianBlurFilter.cpp @@ -25,6 +25,7 @@ #include <SkSize.h> #include <SkString.h> #include <SkSurface.h> +#include "include/gpu/GpuTypes.h" // from Skia #include <log/log.h> #include <utils/Trace.h> @@ -44,7 +45,8 @@ sk_sp<SkImage> GaussianBlurFilter::generate(GrRecordingContext* context, const u // Create blur surface with the bit depth and colorspace of the original surface SkImageInfo scaledInfo = input->imageInfo().makeWH(std::ceil(blurRect.width() * kInputScale), std::ceil(blurRect.height() * kInputScale)); - sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, scaledInfo); + sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(context, + skgpu::Budgeted::kNo, scaledInfo); SkPaint paint; paint.setBlendMode(SkBlendMode::kSrc); diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp index d33dd34d4e..ec0ab4e464 100644 --- a/libs/ui/Android.bp +++ b/libs/ui/Android.bp @@ -48,7 +48,6 @@ cc_defaults { integer_overflow: true, misc_undefined: ["bounds"], }, - } cc_library_static { @@ -135,6 +134,7 @@ cc_library_shared { "Gralloc2.cpp", "Gralloc3.cpp", "Gralloc4.cpp", + "Gralloc5.cpp", "GraphicBuffer.cpp", "GraphicBufferAllocator.cpp", "GraphicBufferMapper.cpp", @@ -176,6 +176,7 @@ cc_library_shared { "libsync", "libutils", "liblog", + "libvndksupport", ], export_shared_lib_headers: [ @@ -214,6 +215,8 @@ cc_library_shared { "libnativewindow_headers", "libhardware_headers", "libui_headers", + "libimapper_stablec", + "libimapper_providerutils", ], export_static_lib_headers: [ diff --git a/libs/ui/Gralloc2.cpp b/libs/ui/Gralloc2.cpp index f23f10a1a9..e9b5decee8 100644 --- a/libs/ui/Gralloc2.cpp +++ b/libs/ui/Gralloc2.cpp @@ -161,7 +161,7 @@ status_t Gralloc2Mapper::createDescriptor(void* bufferDescriptorInfo, return static_cast<status_t>((ret.isOk()) ? error : kTransactionError); } -status_t Gralloc2Mapper::importBuffer(const hardware::hidl_handle& rawHandle, +status_t Gralloc2Mapper::importBuffer(const native_handle_t* rawHandle, buffer_handle_t* outBufferHandle) const { Error error; auto ret = mMapper->importBuffer(rawHandle, diff --git a/libs/ui/Gralloc3.cpp b/libs/ui/Gralloc3.cpp index 15c60bcadf..474d381dbb 100644 --- a/libs/ui/Gralloc3.cpp +++ b/libs/ui/Gralloc3.cpp @@ -138,7 +138,7 @@ status_t Gralloc3Mapper::createDescriptor(void* bufferDescriptorInfo, return static_cast<status_t>((ret.isOk()) ? error : kTransactionError); } -status_t Gralloc3Mapper::importBuffer(const hardware::hidl_handle& rawHandle, +status_t Gralloc3Mapper::importBuffer(const native_handle_t* rawHandle, buffer_handle_t* outBufferHandle) const { Error error; auto ret = mMapper->importBuffer(rawHandle, [&](const auto& tmpError, const auto& tmpBuffer) { diff --git a/libs/ui/Gralloc4.cpp b/libs/ui/Gralloc4.cpp index f6ab7b2a5e..7459466d31 100644 --- a/libs/ui/Gralloc4.cpp +++ b/libs/ui/Gralloc4.cpp @@ -196,7 +196,7 @@ status_t Gralloc4Mapper::createDescriptor(void* bufferDescriptorInfo, return static_cast<status_t>((ret.isOk()) ? error : kTransactionError); } -status_t Gralloc4Mapper::importBuffer(const hardware::hidl_handle& rawHandle, +status_t Gralloc4Mapper::importBuffer(const native_handle_t* rawHandle, buffer_handle_t* outBufferHandle) const { Error error; auto ret = mMapper->importBuffer(rawHandle, [&](const auto& tmpError, const auto& tmpBuffer) { @@ -1233,7 +1233,10 @@ status_t Gralloc4Allocator::allocate(std::string requestorName, uint32_t width, if (mAidlAllocator) { AllocationResult result; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" auto status = mAidlAllocator->allocate(descriptor, bufferCount, &result); +#pragma clang diagnostic pop // deprecation if (!status.isOk()) { error = status.getExceptionCode(); if (error == EX_SERVICE_SPECIFIC) { diff --git a/libs/ui/Gralloc5.cpp b/libs/ui/Gralloc5.cpp new file mode 100644 index 0000000000..6f196b86d5 --- /dev/null +++ b/libs/ui/Gralloc5.cpp @@ -0,0 +1,903 @@ +/* + * 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. + */ + +#define LOG_TAG "Gralloc5" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include <ui/Gralloc5.h> + +#include <aidlcommonsupport/NativeHandle.h> +#include <android/binder_manager.h> +#include <android/hardware/graphics/mapper/utils/IMapperMetadataTypes.h> +#include <binder/IPCThreadState.h> +#include <dlfcn.h> +#include <ui/FatVector.h> +#include <vndksupport/linker.h> + +using namespace aidl::android::hardware::graphics::allocator; +using namespace aidl::android::hardware::graphics::common; +using namespace ::android::hardware::graphics::mapper; + +namespace android { + +static const auto kIAllocatorServiceName = IAllocator::descriptor + std::string("/default"); +static const auto kIAllocatorMinimumVersion = 2; + +// TODO(b/72323293, b/72703005): Remove these invalid bits from callers +static constexpr uint64_t kRemovedUsageBits = static_cast<uint64_t>((1 << 10) | (1 << 13)); + +typedef AIMapper_Error (*AIMapper_loadIMapperFn)(AIMapper *_Nullable *_Nonnull outImplementation); + +struct Gralloc5 { + std::shared_ptr<IAllocator> allocator; + AIMapper *mapper = nullptr; +}; + +static std::shared_ptr<IAllocator> waitForAllocator() { + if (__builtin_available(android 31, *)) { + if (!AServiceManager_isDeclared(kIAllocatorServiceName.c_str())) { + return nullptr; + } + auto allocator = IAllocator::fromBinder( + ndk::SpAIBinder(AServiceManager_waitForService(kIAllocatorServiceName.c_str()))); + if (!allocator) { + ALOGE("AIDL IAllocator declared but failed to get service"); + return nullptr; + } + + int32_t version = 0; + if (!allocator->getInterfaceVersion(&version).isOk()) { + ALOGE("Failed to query interface version"); + return nullptr; + } + if (version < kIAllocatorMinimumVersion) { + return nullptr; + } + return allocator; + } else { + // TODO: LOG_ALWAYS_FATAL("libui is not backwards compatible"); + return nullptr; + } +} + +static void *loadIMapperLibrary() { + static void *imapperLibrary = []() -> void * { + auto allocator = waitForAllocator(); + std::string mapperSuffix; + auto status = allocator->getIMapperLibrarySuffix(&mapperSuffix); + if (!status.isOk()) { + ALOGE("Failed to get IMapper library suffix"); + return nullptr; + } + + std::string lib_name = "mapper." + mapperSuffix + ".so"; + void *so = android_load_sphal_library(lib_name.c_str(), RTLD_LOCAL | RTLD_NOW); + if (!so) { + ALOGE("Failed to load %s", lib_name.c_str()); + } + return so; + }(); + return imapperLibrary; +} + +static const Gralloc5 &getInstance() { + static Gralloc5 instance = []() { + auto allocator = waitForAllocator(); + if (!allocator) { + return Gralloc5{}; + } + void *so = loadIMapperLibrary(); + if (!so) { + return Gralloc5{}; + } + auto loadIMapper = (AIMapper_loadIMapperFn)dlsym(so, "AIMapper_loadIMapper"); + AIMapper *mapper = nullptr; + AIMapper_Error error = loadIMapper(&mapper); + if (error != AIMAPPER_ERROR_NONE) { + ALOGE("AIMapper_loadIMapper failed %d", error); + return Gralloc5{}; + } + return Gralloc5{std::move(allocator), mapper}; + }(); + return instance; +} + +template <StandardMetadataType T> +static auto getStandardMetadata(AIMapper *mapper, buffer_handle_t bufferHandle) + -> decltype(StandardMetadata<T>::value::decode(nullptr, 0)) { + using Value = typename StandardMetadata<T>::value; + // TODO: Tune for common-case better + FatVector<uint8_t, 128> buffer; + int32_t sizeRequired = mapper->v5.getStandardMetadata(bufferHandle, static_cast<int64_t>(T), + buffer.data(), buffer.size()); + if (sizeRequired < 0) { + ALOGW_IF(-AIMAPPER_ERROR_UNSUPPORTED != sizeRequired, + "Unexpected error %d from valid getStandardMetadata call", -sizeRequired); + return std::nullopt; + } + if ((size_t)sizeRequired > buffer.size()) { + buffer.resize(sizeRequired); + sizeRequired = mapper->v5.getStandardMetadata(bufferHandle, static_cast<int64_t>(T), + buffer.data(), buffer.size()); + } + if (sizeRequired < 0 || (size_t)sizeRequired > buffer.size()) { + ALOGW("getStandardMetadata failed, received %d with buffer size %zd", sizeRequired, + buffer.size()); + // Generate a fail type + return std::nullopt; + } + return Value::decode(buffer.data(), sizeRequired); +} + +template <StandardMetadataType T> +static AIMapper_Error setStandardMetadata(AIMapper *mapper, buffer_handle_t bufferHandle, + const typename StandardMetadata<T>::value_type &value) { + using Value = typename StandardMetadata<T>::value; + int32_t sizeRequired = Value::encode(value, nullptr, 0); + if (sizeRequired < 0) { + ALOGW("Failed to calculate required size"); + return static_cast<AIMapper_Error>(-sizeRequired); + } + FatVector<uint8_t, 128> buffer; + buffer.resize(sizeRequired); + sizeRequired = Value::encode(value, buffer.data(), buffer.size()); + if (sizeRequired < 0 || (size_t)sizeRequired > buffer.size()) { + ALOGW("Failed to encode with calculated size %d; buffer size %zd", sizeRequired, + buffer.size()); + return static_cast<AIMapper_Error>(-sizeRequired); + } + return mapper->v5.setStandardMetadata(bufferHandle, static_cast<int64_t>(T), buffer.data(), + sizeRequired); +} + +Gralloc5Allocator::Gralloc5Allocator(const Gralloc5Mapper &mapper) : mMapper(mapper) { + mAllocator = getInstance().allocator; +} + +bool Gralloc5Allocator::isLoaded() const { + return mAllocator != nullptr; +} + +static uint64_t getValidUsageBits() { + static const uint64_t validUsageBits = []() -> uint64_t { + uint64_t bits = 0; + for (const auto bit : ndk::enum_range<BufferUsage>{}) { + bits |= static_cast<int64_t>(bit); + } + return bits; + }(); + return validUsageBits | kRemovedUsageBits; +} + +static std::optional<BufferDescriptorInfo> makeDescriptor(std::string requestorName, uint32_t width, + uint32_t height, PixelFormat format, + uint32_t layerCount, uint64_t usage) { + uint64_t validUsageBits = getValidUsageBits(); + if (usage & ~validUsageBits) { + ALOGE("buffer descriptor contains invalid usage bits 0x%" PRIx64, usage & ~validUsageBits); + return std::nullopt; + } + + BufferDescriptorInfo descriptorInfo{ + .width = static_cast<int32_t>(width), + .height = static_cast<int32_t>(height), + .layerCount = static_cast<int32_t>(layerCount), + .format = static_cast<::aidl::android::hardware::graphics::common::PixelFormat>(format), + .usage = static_cast<BufferUsage>(usage), + }; + auto nameLength = std::min(requestorName.length(), descriptorInfo.name.size() - 1); + memcpy(descriptorInfo.name.data(), requestorName.data(), nameLength); + requestorName.data()[nameLength] = 0; + return descriptorInfo; +} + +std::string Gralloc5Allocator::dumpDebugInfo(bool less) const { + return mMapper.dumpBuffers(less); +} + +status_t Gralloc5Allocator::allocate(std::string requestorName, uint32_t width, uint32_t height, + android::PixelFormat format, uint32_t layerCount, + uint64_t usage, uint32_t bufferCount, uint32_t *outStride, + buffer_handle_t *outBufferHandles, bool importBuffers) const { + auto descriptorInfo = makeDescriptor(requestorName, width, height, format, layerCount, usage); + if (!descriptorInfo) { + return BAD_VALUE; + } + + AllocationResult result; + auto status = mAllocator->allocate2(*descriptorInfo, bufferCount, &result); + if (!status.isOk()) { + auto error = status.getExceptionCode(); + if (error == EX_SERVICE_SPECIFIC) { + error = status.getServiceSpecificError(); + } + if (error == OK) { + error = UNKNOWN_ERROR; + } + return error; + } + + if (importBuffers) { + for (uint32_t i = 0; i < bufferCount; i++) { + auto handle = makeFromAidl(result.buffers[i]); + auto error = mMapper.importBuffer(handle, &outBufferHandles[i]); + native_handle_delete(handle); + if (error != NO_ERROR) { + for (uint32_t j = 0; j < i; j++) { + mMapper.freeBuffer(outBufferHandles[j]); + outBufferHandles[j] = nullptr; + } + return error; + } + } + } else { + for (uint32_t i = 0; i < bufferCount; i++) { + outBufferHandles[i] = dupFromAidl(result.buffers[i]); + if (!outBufferHandles[i]) { + for (uint32_t j = 0; j < i; j++) { + auto buffer = const_cast<native_handle_t *>(outBufferHandles[j]); + native_handle_close(buffer); + native_handle_delete(buffer); + outBufferHandles[j] = nullptr; + } + return NO_MEMORY; + } + } + } + + *outStride = result.stride; + + // Release all the resources held by AllocationResult (specifically any remaining FDs) + result = {}; + // make sure the kernel driver sees BC_FREE_BUFFER and closes the fds now + // TODO: Re-enable this at some point if it's necessary. We can't do it now because libui + // is marked apex_available (b/214400477) and libbinder isn't (which of course is correct) + // IPCThreadState::self()->flushCommands(); + + return OK; +} + +void Gralloc5Mapper::preload() { + // TODO(b/261858155): Implement. We can't bounce off of IAllocator for this because zygote can't + // use binder. So when an alternate strategy of retrieving the library prefix is available, + // use that here. +} + +Gralloc5Mapper::Gralloc5Mapper() { + mMapper = getInstance().mapper; +} + +bool Gralloc5Mapper::isLoaded() const { + return mMapper != nullptr && mMapper->version >= AIMAPPER_VERSION_5; +} + +std::string Gralloc5Mapper::dumpBuffer(buffer_handle_t bufferHandle, bool less) const { + // TODO(b/261858392): Implement + (void)bufferHandle; + (void)less; + return {}; +} + +std::string Gralloc5Mapper::dumpBuffers(bool less) const { + // TODO(b/261858392): Implement + (void)less; + return {}; +} + +status_t Gralloc5Mapper::importBuffer(const native_handle_t *rawHandle, + buffer_handle_t *outBufferHandle) const { + return mMapper->v5.importBuffer(rawHandle, outBufferHandle); +} + +void Gralloc5Mapper::freeBuffer(buffer_handle_t bufferHandle) const { + mMapper->v5.freeBuffer(bufferHandle); +} + +status_t Gralloc5Mapper::validateBufferSize(buffer_handle_t bufferHandle, uint32_t width, + uint32_t height, PixelFormat format, + uint32_t layerCount, uint64_t usage, + uint32_t stride) const { + { + auto value = getStandardMetadata<StandardMetadataType::WIDTH>(mMapper, bufferHandle); + if (width != value) { + ALOGW("Width didn't match, expected %d got %" PRId64, width, value.value_or(-1)); + return BAD_VALUE; + } + } + { + auto value = getStandardMetadata<StandardMetadataType::HEIGHT>(mMapper, bufferHandle); + if (height != value) { + ALOGW("Height didn't match, expected %d got %" PRId64, height, value.value_or(-1)); + return BAD_VALUE; + } + } + { + auto value = + getStandardMetadata<StandardMetadataType::PIXEL_FORMAT_REQUESTED>(mMapper, + bufferHandle); + if (static_cast<::aidl::android::hardware::graphics::common::PixelFormat>(format) != + value) { + ALOGW("Format didn't match, expected %d got %s", format, + value.has_value() ? toString(*value).c_str() : "<null>"); + return BAD_VALUE; + } + } + { + auto value = getStandardMetadata<StandardMetadataType::LAYER_COUNT>(mMapper, bufferHandle); + if (layerCount != value) { + ALOGW("Layer count didn't match, expected %d got %" PRId64, layerCount, + value.value_or(-1)); + return BAD_VALUE; + } + } + { + auto value = getStandardMetadata<StandardMetadataType::USAGE>(mMapper, bufferHandle); + if (static_cast<BufferUsage>(usage) != value) { + ALOGW("Usage didn't match, expected %" PRIu64 " got %" PRId64, usage, + static_cast<int64_t>(value.value_or(BufferUsage::CPU_READ_NEVER))); + return BAD_VALUE; + } + } + { + (void)stride; + // TODO(b/261856851): Add StandardMetadataType::STRIDE && enable this + // auto value = getStandardMetadata<StandardMetadataType::STRIDE>(mMapper, + // bufferHandle); if (static_cast<BufferUsage>(usage) != value) { + // ALOGW("Layer count didn't match, expected %" PRIu64 " got %" PRId64, usage, + // static_cast<int64_t>(value.value_or(BufferUsage::CPU_READ_NEVER))); + // return BAD_VALUE; + // } + } + return OK; +} + +void Gralloc5Mapper::getTransportSize(buffer_handle_t bufferHandle, uint32_t *outNumFds, + uint32_t *outNumInts) const { + mMapper->v5.getTransportSize(bufferHandle, outNumFds, outNumInts); +} + +status_t Gralloc5Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect &bounds, + int acquireFence, void **outData, int32_t *outBytesPerPixel, + int32_t *outBytesPerStride) const { + std::vector<ui::PlaneLayout> planeLayouts; + status_t err = getPlaneLayouts(bufferHandle, &planeLayouts); + + if (err == NO_ERROR && !planeLayouts.empty()) { + if (outBytesPerPixel) { + int32_t bitsPerPixel = planeLayouts.front().sampleIncrementInBits; + for (const auto &planeLayout : planeLayouts) { + if (bitsPerPixel != planeLayout.sampleIncrementInBits) { + bitsPerPixel = -1; + } + } + if (bitsPerPixel >= 0 && bitsPerPixel % 8 == 0) { + *outBytesPerPixel = bitsPerPixel / 8; + } else { + *outBytesPerPixel = -1; + } + } + if (outBytesPerStride) { + int32_t bytesPerStride = planeLayouts.front().strideInBytes; + for (const auto &planeLayout : planeLayouts) { + if (bytesPerStride != planeLayout.strideInBytes) { + bytesPerStride = -1; + } + } + if (bytesPerStride >= 0) { + *outBytesPerStride = bytesPerStride; + } else { + *outBytesPerStride = -1; + } + } + } + + auto status = mMapper->v5.lock(bufferHandle, usage, bounds, acquireFence, outData); + + ALOGW_IF(status != AIMAPPER_ERROR_NONE, "lock(%p, ...) failed: %d", bufferHandle, status); + return static_cast<status_t>(status); +} + +status_t Gralloc5Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect &bounds, + int acquireFence, android_ycbcr *outYcbcr) const { + if (!outYcbcr) { + return BAD_VALUE; + } + + // TODO(b/262279301): Change the return type of ::unlock to unique_fd instead of int so that + // ignoring the return value "just works" instead + auto unlock = [this](buffer_handle_t bufferHandle) { + int fence = this->unlock(bufferHandle); + if (fence != -1) { + ::close(fence); + } + }; + + std::vector<ui::PlaneLayout> planeLayouts; + status_t error = getPlaneLayouts(bufferHandle, &planeLayouts); + if (error != NO_ERROR) { + return error; + } + + void *data = nullptr; + error = lock(bufferHandle, usage, bounds, acquireFence, &data, nullptr, nullptr); + if (error != NO_ERROR) { + return error; + } + + android_ycbcr ycbcr; + + ycbcr.y = nullptr; + ycbcr.cb = nullptr; + ycbcr.cr = nullptr; + ycbcr.ystride = 0; + ycbcr.cstride = 0; + ycbcr.chroma_step = 0; + + for (const auto &planeLayout : planeLayouts) { + for (const auto &planeLayoutComponent : planeLayout.components) { + if (!gralloc4::isStandardPlaneLayoutComponentType(planeLayoutComponent.type)) { + continue; + } + + uint8_t *tmpData = static_cast<uint8_t *>(data) + planeLayout.offsetInBytes; + + // Note that `offsetInBits` may not be a multiple of 8 for packed formats (e.g. P010) + // but we still want to point to the start of the first byte. + tmpData += (planeLayoutComponent.offsetInBits / 8); + + uint64_t sampleIncrementInBytes; + + auto type = static_cast<PlaneLayoutComponentType>(planeLayoutComponent.type.value); + switch (type) { + case PlaneLayoutComponentType::Y: + if ((ycbcr.y != nullptr) || (planeLayout.sampleIncrementInBits % 8 != 0)) { + unlock(bufferHandle); + return BAD_VALUE; + } + ycbcr.y = tmpData; + ycbcr.ystride = planeLayout.strideInBytes; + break; + + case PlaneLayoutComponentType::CB: + case PlaneLayoutComponentType::CR: + if (planeLayout.sampleIncrementInBits % 8 != 0) { + unlock(bufferHandle); + return BAD_VALUE; + } + + sampleIncrementInBytes = planeLayout.sampleIncrementInBits / 8; + if ((sampleIncrementInBytes != 1) && (sampleIncrementInBytes != 2) && + (sampleIncrementInBytes != 4)) { + unlock(bufferHandle); + return BAD_VALUE; + } + + if (ycbcr.cstride == 0 && ycbcr.chroma_step == 0) { + ycbcr.cstride = planeLayout.strideInBytes; + ycbcr.chroma_step = sampleIncrementInBytes; + } else { + if ((static_cast<int64_t>(ycbcr.cstride) != planeLayout.strideInBytes) || + (ycbcr.chroma_step != sampleIncrementInBytes)) { + unlock(bufferHandle); + return BAD_VALUE; + } + } + + if (type == PlaneLayoutComponentType::CB) { + if (ycbcr.cb != nullptr) { + unlock(bufferHandle); + return BAD_VALUE; + } + ycbcr.cb = tmpData; + } else { + if (ycbcr.cr != nullptr) { + unlock(bufferHandle); + return BAD_VALUE; + } + ycbcr.cr = tmpData; + } + break; + default: + break; + }; + } + } + + *outYcbcr = ycbcr; + return OK; +} + +int Gralloc5Mapper::unlock(buffer_handle_t bufferHandle) const { + int fence = -1; + AIMapper_Error error = mMapper->v5.unlock(bufferHandle, &fence); + if (error != AIMAPPER_ERROR_NONE) { + ALOGW("unlock failed with error %d", error); + } + return fence; +} + +status_t Gralloc5Mapper::isSupported(uint32_t width, uint32_t height, PixelFormat format, + uint32_t layerCount, uint64_t usage, + bool *outSupported) const { + auto descriptorInfo = makeDescriptor("", width, height, format, layerCount, usage); + if (!descriptorInfo) { + *outSupported = false; + return OK; + } + auto status = getInstance().allocator->isSupported(*descriptorInfo, outSupported); + if (!status.isOk()) { + ALOGW("IAllocator::isSupported error %d (%s)", status.getStatus(), status.getMessage()); + *outSupported = false; + } + return OK; +} + +status_t Gralloc5Mapper::getBufferId(buffer_handle_t bufferHandle, uint64_t *outBufferId) const { + auto value = getStandardMetadata<StandardMetadataType::BUFFER_ID>(mMapper, bufferHandle); + if (value.has_value()) { + *outBufferId = *value; + return OK; + } + return UNKNOWN_TRANSACTION; +} + +status_t Gralloc5Mapper::getName(buffer_handle_t bufferHandle, std::string *outName) const { + auto value = getStandardMetadata<StandardMetadataType::NAME>(mMapper, bufferHandle); + if (value.has_value()) { + *outName = *value; + return OK; + } + return UNKNOWN_TRANSACTION; +} + +status_t Gralloc5Mapper::getWidth(buffer_handle_t bufferHandle, uint64_t *outWidth) const { + auto value = getStandardMetadata<StandardMetadataType::WIDTH>(mMapper, bufferHandle); + if (value.has_value()) { + *outWidth = *value; + return OK; + } + return UNKNOWN_TRANSACTION; +} + +status_t Gralloc5Mapper::getHeight(buffer_handle_t bufferHandle, uint64_t *outHeight) const { + auto value = getStandardMetadata<StandardMetadataType::HEIGHT>(mMapper, bufferHandle); + if (value.has_value()) { + *outHeight = *value; + return OK; + } + return UNKNOWN_TRANSACTION; +} + +status_t Gralloc5Mapper::getLayerCount(buffer_handle_t bufferHandle, + uint64_t *outLayerCount) const { + auto value = getStandardMetadata<StandardMetadataType::LAYER_COUNT>(mMapper, bufferHandle); + if (value.has_value()) { + *outLayerCount = *value; + return OK; + } + return UNKNOWN_TRANSACTION; +} + +status_t Gralloc5Mapper::getPixelFormatRequested(buffer_handle_t bufferHandle, + ui::PixelFormat *outPixelFormatRequested) const { + auto value = getStandardMetadata<StandardMetadataType::PIXEL_FORMAT_REQUESTED>(mMapper, + bufferHandle); + if (value.has_value()) { + *outPixelFormatRequested = static_cast<ui::PixelFormat>(*value); + return OK; + } + return UNKNOWN_TRANSACTION; +} + +status_t Gralloc5Mapper::getPixelFormatFourCC(buffer_handle_t bufferHandle, + uint32_t *outPixelFormatFourCC) const { + auto value = + getStandardMetadata<StandardMetadataType::PIXEL_FORMAT_FOURCC>(mMapper, bufferHandle); + if (value.has_value()) { + *outPixelFormatFourCC = *value; + return OK; + } + return UNKNOWN_TRANSACTION; +} + +status_t Gralloc5Mapper::getPixelFormatModifier(buffer_handle_t bufferHandle, + uint64_t *outPixelFormatModifier) const { + auto value = + getStandardMetadata<StandardMetadataType::PIXEL_FORMAT_MODIFIER>(mMapper, bufferHandle); + if (value.has_value()) { + *outPixelFormatModifier = *value; + return OK; + } + return UNKNOWN_TRANSACTION; +} + +status_t Gralloc5Mapper::getUsage(buffer_handle_t bufferHandle, uint64_t *outUsage) const { + auto value = getStandardMetadata<StandardMetadataType::USAGE>(mMapper, bufferHandle); + if (value.has_value()) { + *outUsage = static_cast<uint64_t>(*value); + return OK; + } + return UNKNOWN_TRANSACTION; +} + +status_t Gralloc5Mapper::getAllocationSize(buffer_handle_t bufferHandle, + uint64_t *outAllocationSize) const { + auto value = getStandardMetadata<StandardMetadataType::ALLOCATION_SIZE>(mMapper, bufferHandle); + if (value.has_value()) { + *outAllocationSize = *value; + return OK; + } + return UNKNOWN_TRANSACTION; +} + +status_t Gralloc5Mapper::getProtectedContent(buffer_handle_t bufferHandle, + uint64_t *outProtectedContent) const { + auto value = + getStandardMetadata<StandardMetadataType::PROTECTED_CONTENT>(mMapper, bufferHandle); + if (value.has_value()) { + *outProtectedContent = *value; + return OK; + } + return UNKNOWN_TRANSACTION; +} + +status_t Gralloc5Mapper::getCompression( + buffer_handle_t bufferHandle, + aidl::android::hardware::graphics::common::ExtendableType *outCompression) const { + auto value = getStandardMetadata<StandardMetadataType::COMPRESSION>(mMapper, bufferHandle); + if (value.has_value()) { + *outCompression = *value; + return OK; + } + return UNKNOWN_TRANSACTION; +} + +status_t Gralloc5Mapper::getCompression(buffer_handle_t bufferHandle, + ui::Compression *outCompression) const { + auto value = getStandardMetadata<StandardMetadataType::COMPRESSION>(mMapper, bufferHandle); + if (!value.has_value()) { + return UNKNOWN_TRANSACTION; + } + if (!gralloc4::isStandardCompression(*value)) { + return BAD_TYPE; + } + *outCompression = gralloc4::getStandardCompressionValue(*value); + return OK; +} + +status_t Gralloc5Mapper::getInterlaced( + buffer_handle_t bufferHandle, + aidl::android::hardware::graphics::common::ExtendableType *outInterlaced) const { + auto value = getStandardMetadata<StandardMetadataType::INTERLACED>(mMapper, bufferHandle); + if (value.has_value()) { + *outInterlaced = *value; + return OK; + } + return UNKNOWN_TRANSACTION; +} + +status_t Gralloc5Mapper::getInterlaced(buffer_handle_t bufferHandle, + ui::Interlaced *outInterlaced) const { + if (!outInterlaced) { + return BAD_VALUE; + } + ExtendableType interlaced; + status_t error = getInterlaced(bufferHandle, &interlaced); + if (error) { + return error; + } + if (!gralloc4::isStandardInterlaced(interlaced)) { + return BAD_TYPE; + } + *outInterlaced = gralloc4::getStandardInterlacedValue(interlaced); + return NO_ERROR; +} + +status_t Gralloc5Mapper::getChromaSiting( + buffer_handle_t bufferHandle, + aidl::android::hardware::graphics::common::ExtendableType *outChromaSiting) const { + auto value = getStandardMetadata<StandardMetadataType::CHROMA_SITING>(mMapper, bufferHandle); + if (value.has_value()) { + *outChromaSiting = *value; + return OK; + } + return UNKNOWN_TRANSACTION; +} + +status_t Gralloc5Mapper::getChromaSiting(buffer_handle_t bufferHandle, + ui::ChromaSiting *outChromaSiting) const { + if (!outChromaSiting) { + return BAD_VALUE; + } + ExtendableType chromaSiting; + status_t error = getChromaSiting(bufferHandle, &chromaSiting); + if (error) { + return error; + } + if (!gralloc4::isStandardChromaSiting(chromaSiting)) { + return BAD_TYPE; + } + *outChromaSiting = gralloc4::getStandardChromaSitingValue(chromaSiting); + return NO_ERROR; +} + +status_t Gralloc5Mapper::getPlaneLayouts(buffer_handle_t bufferHandle, + std::vector<ui::PlaneLayout> *outPlaneLayouts) const { + auto value = getStandardMetadata<StandardMetadataType::PLANE_LAYOUTS>(mMapper, bufferHandle); + if (value.has_value()) { + *outPlaneLayouts = *value; + return OK; + } + return UNKNOWN_TRANSACTION; +} + +status_t Gralloc5Mapper::getDataspace(buffer_handle_t bufferHandle, + ui::Dataspace *outDataspace) const { + auto value = getStandardMetadata<StandardMetadataType::DATASPACE>(mMapper, bufferHandle); + if (value.has_value()) { + *outDataspace = static_cast<ui::Dataspace>(*value); + return OK; + } + return UNKNOWN_TRANSACTION; +} + +status_t Gralloc5Mapper::setDataspace(buffer_handle_t bufferHandle, ui::Dataspace dataspace) const { + return setStandardMetadata<StandardMetadataType::DATASPACE>(mMapper, bufferHandle, + static_cast<Dataspace>(dataspace)); +} + +status_t Gralloc5Mapper::getBlendMode(buffer_handle_t bufferHandle, + ui::BlendMode *outBlendMode) const { + auto value = getStandardMetadata<StandardMetadataType::BLEND_MODE>(mMapper, bufferHandle); + if (value.has_value()) { + *outBlendMode = static_cast<ui::BlendMode>(*value); + return OK; + } + return UNKNOWN_TRANSACTION; +} + +status_t Gralloc5Mapper::getSmpte2086(buffer_handle_t bufferHandle, + std::optional<ui::Smpte2086> *outSmpte2086) const { + auto value = getStandardMetadata<StandardMetadataType::SMPTE2086>(mMapper, bufferHandle); + if (value.has_value()) { + *outSmpte2086 = *value; + return OK; + } + return UNKNOWN_TRANSACTION; +} + +status_t Gralloc5Mapper::setSmpte2086(buffer_handle_t bufferHandle, + std::optional<ui::Smpte2086> smpte2086) const { + return setStandardMetadata<StandardMetadataType::SMPTE2086>(mMapper, bufferHandle, smpte2086); +} + +status_t Gralloc5Mapper::getCta861_3(buffer_handle_t bufferHandle, + std::optional<ui::Cta861_3> *outCta861_3) const { + auto value = getStandardMetadata<StandardMetadataType::CTA861_3>(mMapper, bufferHandle); + if (value.has_value()) { + *outCta861_3 = *value; + return OK; + } + return UNKNOWN_TRANSACTION; +} + +status_t Gralloc5Mapper::setCta861_3(buffer_handle_t bufferHandle, + std::optional<ui::Cta861_3> cta861_3) const { + return setStandardMetadata<StandardMetadataType::CTA861_3>(mMapper, bufferHandle, cta861_3); +} + +status_t Gralloc5Mapper::getSmpte2094_40( + buffer_handle_t bufferHandle, std::optional<std::vector<uint8_t>> *outSmpte2094_40) const { + auto value = getStandardMetadata<StandardMetadataType::SMPTE2094_40>(mMapper, bufferHandle); + if (value.has_value()) { + *outSmpte2094_40 = std::move(*value); + return OK; + } + return UNKNOWN_TRANSACTION; +} + +status_t Gralloc5Mapper::setSmpte2094_40(buffer_handle_t bufferHandle, + std::optional<std::vector<uint8_t>> smpte2094_40) const { + return setStandardMetadata<StandardMetadataType::SMPTE2094_40>(mMapper, bufferHandle, + smpte2094_40); +} + +status_t Gralloc5Mapper::getSmpte2094_10( + buffer_handle_t bufferHandle, std::optional<std::vector<uint8_t>> *outSmpte2094_10) const { + auto value = getStandardMetadata<StandardMetadataType::SMPTE2094_10>(mMapper, bufferHandle); + if (value.has_value()) { + *outSmpte2094_10 = std::move(*value); + return OK; + } + return UNKNOWN_TRANSACTION; +} + +status_t Gralloc5Mapper::setSmpte2094_10(buffer_handle_t bufferHandle, + std::optional<std::vector<uint8_t>> smpte2094_10) const { + return setStandardMetadata<StandardMetadataType::SMPTE2094_10>(mMapper, bufferHandle, + smpte2094_10); +} + +status_t Gralloc5Mapper::getDefaultPixelFormatFourCC(uint32_t, uint32_t, PixelFormat, uint32_t, + uint64_t, uint32_t *) const { + // TODO(b/261857910): Remove + return UNKNOWN_TRANSACTION; +} + +status_t Gralloc5Mapper::getDefaultPixelFormatModifier(uint32_t, uint32_t, PixelFormat, uint32_t, + uint64_t, uint64_t *) const { + // TODO(b/261857910): Remove + return UNKNOWN_TRANSACTION; +} + +status_t Gralloc5Mapper::getDefaultAllocationSize(uint32_t, uint32_t, PixelFormat, uint32_t, + uint64_t, uint64_t *) const { + // TODO(b/261857910): Remove + return UNKNOWN_TRANSACTION; +} + +status_t Gralloc5Mapper::getDefaultProtectedContent(uint32_t, uint32_t, PixelFormat, uint32_t, + uint64_t, uint64_t *) const { + // TODO(b/261857910): Remove + return UNKNOWN_TRANSACTION; +} + +status_t Gralloc5Mapper::getDefaultCompression( + uint32_t, uint32_t, PixelFormat, uint32_t, uint64_t, + aidl::android::hardware::graphics::common::ExtendableType *) const { + // TODO(b/261857910): Remove + return UNKNOWN_TRANSACTION; +} + +status_t Gralloc5Mapper::getDefaultCompression(uint32_t, uint32_t, PixelFormat, uint32_t, uint64_t, + ui::Compression *) const { + // TODO(b/261857910): Remove + return UNKNOWN_TRANSACTION; +} + +status_t Gralloc5Mapper::getDefaultInterlaced( + uint32_t, uint32_t, PixelFormat, uint32_t, uint64_t, + aidl::android::hardware::graphics::common::ExtendableType *) const { + // TODO(b/261857910): Remove + return UNKNOWN_TRANSACTION; +} + +status_t Gralloc5Mapper::getDefaultInterlaced(uint32_t, uint32_t, PixelFormat, uint32_t, uint64_t, + ui::Interlaced *) const { + // TODO(b/261857910): Remove + return UNKNOWN_TRANSACTION; +} + +status_t Gralloc5Mapper::getDefaultChromaSiting( + uint32_t, uint32_t, PixelFormat, uint32_t, uint64_t, + aidl::android::hardware::graphics::common::ExtendableType *) const { + // TODO(b/261857910): Remove + return UNKNOWN_TRANSACTION; +} + +status_t Gralloc5Mapper::getDefaultChromaSiting(uint32_t, uint32_t, PixelFormat, uint32_t, uint64_t, + ui::ChromaSiting *) const { + // TODO(b/261857910): Remove + return UNKNOWN_TRANSACTION; +} + +status_t Gralloc5Mapper::getDefaultPlaneLayouts(uint32_t, uint32_t, PixelFormat, uint32_t, uint64_t, + std::vector<ui::PlaneLayout> *) const { + // TODO(b/261857910): Remove + return UNKNOWN_TRANSACTION; +} + +} // namespace android
\ No newline at end of file diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp index 3f958ba68f..c0abec23e0 100644 --- a/libs/ui/GraphicBufferAllocator.cpp +++ b/libs/ui/GraphicBufferAllocator.cpp @@ -34,6 +34,7 @@ #include <ui/Gralloc2.h> #include <ui/Gralloc3.h> #include <ui/Gralloc4.h> +#include <ui/Gralloc5.h> #include <ui/GraphicBufferMapper.h> namespace android { @@ -48,23 +49,27 @@ KeyedVector<buffer_handle_t, GraphicBufferAllocator::alloc_rec_t> GraphicBufferAllocator::sAllocList; GraphicBufferAllocator::GraphicBufferAllocator() : mMapper(GraphicBufferMapper::getInstance()) { - mAllocator = std::make_unique<const Gralloc4Allocator>( - reinterpret_cast<const Gralloc4Mapper&>(mMapper.getGrallocMapper())); - if (mAllocator->isLoaded()) { - return; + switch (mMapper.getMapperVersion()) { + case GraphicBufferMapper::GRALLOC_5: + mAllocator = std::make_unique<const Gralloc5Allocator>( + reinterpret_cast<const Gralloc5Mapper&>(mMapper.getGrallocMapper())); + break; + case GraphicBufferMapper::GRALLOC_4: + mAllocator = std::make_unique<const Gralloc4Allocator>( + reinterpret_cast<const Gralloc4Mapper&>(mMapper.getGrallocMapper())); + break; + case GraphicBufferMapper::GRALLOC_3: + mAllocator = std::make_unique<const Gralloc3Allocator>( + reinterpret_cast<const Gralloc3Mapper&>(mMapper.getGrallocMapper())); + break; + case GraphicBufferMapper::GRALLOC_2: + mAllocator = std::make_unique<const Gralloc2Allocator>( + reinterpret_cast<const Gralloc2Mapper&>(mMapper.getGrallocMapper())); + break; } - mAllocator = std::make_unique<const Gralloc3Allocator>( - reinterpret_cast<const Gralloc3Mapper&>(mMapper.getGrallocMapper())); - if (mAllocator->isLoaded()) { - return; - } - mAllocator = std::make_unique<const Gralloc2Allocator>( - reinterpret_cast<const Gralloc2Mapper&>(mMapper.getGrallocMapper())); - if (mAllocator->isLoaded()) { - return; - } - - LOG_ALWAYS_FATAL("gralloc-allocator is missing"); + LOG_ALWAYS_FATAL_IF(!mAllocator->isLoaded(), + "Failed to load matching allocator for mapper version %d", + mMapper.getMapperVersion()); } GraphicBufferAllocator::~GraphicBufferAllocator() {} diff --git a/libs/ui/GraphicBufferMapper.cpp b/libs/ui/GraphicBufferMapper.cpp index a98e697eb1..6002a6d29e 100644 --- a/libs/ui/GraphicBufferMapper.cpp +++ b/libs/ui/GraphicBufferMapper.cpp @@ -36,6 +36,7 @@ #include <ui/Gralloc2.h> #include <ui/Gralloc3.h> #include <ui/Gralloc4.h> +#include <ui/Gralloc5.h> #include <ui/GraphicBuffer.h> #include <system/graphics.h> @@ -49,9 +50,15 @@ void GraphicBufferMapper::preloadHal() { Gralloc2Mapper::preload(); Gralloc3Mapper::preload(); Gralloc4Mapper::preload(); + Gralloc5Mapper::preload(); } GraphicBufferMapper::GraphicBufferMapper() { + mMapper = std::make_unique<const Gralloc5Mapper>(); + if (mMapper->isLoaded()) { + mMapperVersion = Version::GRALLOC_5; + return; + } mMapper = std::make_unique<const Gralloc4Mapper>(); if (mMapper->isLoaded()) { mMapperVersion = Version::GRALLOC_4; @@ -82,15 +89,14 @@ void GraphicBufferMapper::dumpBufferToSystemLog(buffer_handle_t bufferHandle, bo ALOGD("%s", s.c_str()); } -status_t GraphicBufferMapper::importBuffer(buffer_handle_t rawHandle, - uint32_t width, uint32_t height, uint32_t layerCount, - PixelFormat format, uint64_t usage, uint32_t stride, - buffer_handle_t* outHandle) -{ +status_t GraphicBufferMapper::importBuffer(const native_handle_t* rawHandle, uint32_t width, + uint32_t height, uint32_t layerCount, PixelFormat format, + uint64_t usage, uint32_t stride, + buffer_handle_t* outHandle) { ATRACE_CALL(); buffer_handle_t bufferHandle; - status_t error = mMapper->importBuffer(hardware::hidl_handle(rawHandle), &bufferHandle); + status_t error = mMapper->importBuffer(rawHandle, &bufferHandle); if (error != NO_ERROR) { ALOGW("importBuffer(%p) failed: %d", rawHandle, error); return error; @@ -109,6 +115,11 @@ status_t GraphicBufferMapper::importBuffer(buffer_handle_t rawHandle, return NO_ERROR; } +status_t GraphicBufferMapper::importBufferNoValidate(const native_handle_t* rawHandle, + buffer_handle_t* outHandle) { + return mMapper->importBuffer(rawHandle, outHandle); +} + void GraphicBufferMapper::getTransportSize(buffer_handle_t handle, uint32_t* outTransportNumFds, uint32_t* outTransportNumInts) { diff --git a/libs/ui/include/ui/Gralloc.h b/libs/ui/include/ui/Gralloc.h index 6101d4b43b..b494cbe4fa 100644 --- a/libs/ui/include/ui/Gralloc.h +++ b/libs/ui/include/ui/Gralloc.h @@ -39,14 +39,11 @@ public: return ""; } - virtual status_t createDescriptor(void* bufferDescriptorInfo, - void* outBufferDescriptor) const = 0; - // Import a buffer that is from another HAL, another process, or is // cloned. // // The returned handle must be freed with freeBuffer. - virtual status_t importBuffer(const hardware::hidl_handle& rawHandle, + virtual status_t importBuffer(const native_handle_t* rawHandle, buffer_handle_t* outBufferHandle) const = 0; virtual void freeBuffer(buffer_handle_t bufferHandle) const = 0; @@ -269,11 +266,6 @@ public: std::vector<ui::PlaneLayout>* /*outPlaneLayouts*/) const { return INVALID_OPERATION; } - - virtual std::vector<android::hardware::graphics::mapper::V4_0::IMapper::MetadataTypeDescription> - listSupportedMetadataTypes() const { - return {}; - } }; // A wrapper to IAllocator diff --git a/libs/ui/include/ui/Gralloc2.h b/libs/ui/include/ui/Gralloc2.h index f570c428b6..a7b6f49206 100644 --- a/libs/ui/include/ui/Gralloc2.h +++ b/libs/ui/include/ui/Gralloc2.h @@ -38,9 +38,9 @@ public: bool isLoaded() const override; - status_t createDescriptor(void* bufferDescriptorInfo, void* outBufferDescriptor) const override; + status_t createDescriptor(void* bufferDescriptorInfo, void* outBufferDescriptor) const; - status_t importBuffer(const hardware::hidl_handle& rawHandle, + status_t importBuffer(const native_handle_t* rawHandle, buffer_handle_t* outBufferHandle) const override; void freeBuffer(buffer_handle_t bufferHandle) const override; diff --git a/libs/ui/include/ui/Gralloc3.h b/libs/ui/include/ui/Gralloc3.h index 93a5077313..7367549964 100644 --- a/libs/ui/include/ui/Gralloc3.h +++ b/libs/ui/include/ui/Gralloc3.h @@ -37,9 +37,9 @@ public: bool isLoaded() const override; - status_t createDescriptor(void* bufferDescriptorInfo, void* outBufferDescriptor) const override; + status_t createDescriptor(void* bufferDescriptorInfo, void* outBufferDescriptor) const; - status_t importBuffer(const hardware::hidl_handle& rawHandle, + status_t importBuffer(const native_handle_t* rawHandle, buffer_handle_t* outBufferHandle) const override; void freeBuffer(buffer_handle_t bufferHandle) const override; diff --git a/libs/ui/include/ui/Gralloc4.h b/libs/ui/include/ui/Gralloc4.h index cf023c9bcc..6bc5ce5296 100644 --- a/libs/ui/include/ui/Gralloc4.h +++ b/libs/ui/include/ui/Gralloc4.h @@ -42,9 +42,9 @@ public: std::string dumpBuffer(buffer_handle_t bufferHandle, bool less = true) const override; std::string dumpBuffers(bool less = true) const; - status_t createDescriptor(void* bufferDescriptorInfo, void* outBufferDescriptor) const override; + status_t createDescriptor(void* bufferDescriptorInfo, void* outBufferDescriptor) const; - status_t importBuffer(const hardware::hidl_handle& rawHandle, + status_t importBuffer(const native_handle_t* rawHandle, buffer_handle_t* outBufferHandle) const override; void freeBuffer(buffer_handle_t bufferHandle) const override; diff --git a/libs/ui/include/ui/Gralloc5.h b/libs/ui/include/ui/Gralloc5.h new file mode 100644 index 0000000000..bc1016944a --- /dev/null +++ b/libs/ui/include/ui/Gralloc5.h @@ -0,0 +1,238 @@ +/* + * 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 <aidl/android/hardware/graphics/allocator/IAllocator.h> +#include <android/hardware/graphics/mapper/IMapper.h> +#include <ui/Gralloc.h> + +namespace android { + +class Gralloc5Mapper : public GrallocMapper { +public: +public: + static void preload(); + + Gralloc5Mapper(); + + [[nodiscard]] bool isLoaded() const override; + + [[nodiscard]] std::string dumpBuffer(buffer_handle_t bufferHandle, bool less) const override; + + [[nodiscard]] std::string dumpBuffers(bool less = true) const; + + [[nodiscard]] status_t importBuffer(const native_handle_t *rawHandle, + buffer_handle_t *outBufferHandle) const override; + + void freeBuffer(buffer_handle_t bufferHandle) const override; + + [[nodiscard]] status_t validateBufferSize(buffer_handle_t bufferHandle, uint32_t width, + uint32_t height, PixelFormat format, + uint32_t layerCount, uint64_t usage, + uint32_t stride) const override; + + void getTransportSize(buffer_handle_t bufferHandle, uint32_t *outNumFds, + uint32_t *outNumInts) const override; + + [[nodiscard]] status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect &bounds, + int acquireFence, void **outData, int32_t *outBytesPerPixel, + int32_t *outBytesPerStride) const override; + + [[nodiscard]] status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect &bounds, + int acquireFence, android_ycbcr *ycbcr) const override; + + [[nodiscard]] int unlock(buffer_handle_t bufferHandle) const override; + + [[nodiscard]] status_t isSupported(uint32_t width, uint32_t height, PixelFormat format, + uint32_t layerCount, uint64_t usage, + bool *outSupported) const override; + + [[nodiscard]] status_t getBufferId(buffer_handle_t bufferHandle, + uint64_t *outBufferId) const override; + + [[nodiscard]] status_t getName(buffer_handle_t bufferHandle, + std::string *outName) const override; + + [[nodiscard]] status_t getWidth(buffer_handle_t bufferHandle, + uint64_t *outWidth) const override; + + [[nodiscard]] status_t getHeight(buffer_handle_t bufferHandle, + uint64_t *outHeight) const override; + + [[nodiscard]] status_t getLayerCount(buffer_handle_t bufferHandle, + uint64_t *outLayerCount) const override; + + [[nodiscard]] status_t getPixelFormatRequested( + buffer_handle_t bufferHandle, ui::PixelFormat *outPixelFormatRequested) const override; + + [[nodiscard]] status_t getPixelFormatFourCC(buffer_handle_t bufferHandle, + uint32_t *outPixelFormatFourCC) const override; + + [[nodiscard]] status_t getPixelFormatModifier(buffer_handle_t bufferHandle, + uint64_t *outPixelFormatModifier) const override; + + [[nodiscard]] status_t getUsage(buffer_handle_t bufferHandle, + uint64_t *outUsage) const override; + + [[nodiscard]] status_t getAllocationSize(buffer_handle_t bufferHandle, + uint64_t *outAllocationSize) const override; + + [[nodiscard]] status_t getProtectedContent(buffer_handle_t bufferHandle, + uint64_t *outProtectedContent) const override; + + [[nodiscard]] status_t getCompression(buffer_handle_t bufferHandle, + aidl::android::hardware::graphics::common::ExtendableType + *outCompression) const override; + + [[nodiscard]] status_t getCompression(buffer_handle_t bufferHandle, + ui::Compression *outCompression) const override; + + [[nodiscard]] status_t getInterlaced(buffer_handle_t bufferHandle, + aidl::android::hardware::graphics::common::ExtendableType + *outInterlaced) const override; + + [[nodiscard]] status_t getInterlaced(buffer_handle_t bufferHandle, + ui::Interlaced *outInterlaced) const override; + + [[nodiscard]] status_t getChromaSiting(buffer_handle_t bufferHandle, + aidl::android::hardware::graphics::common::ExtendableType + *outChromaSiting) const override; + + [[nodiscard]] status_t getChromaSiting(buffer_handle_t bufferHandle, + ui::ChromaSiting *outChromaSiting) const override; + + [[nodiscard]] status_t getPlaneLayouts( + buffer_handle_t bufferHandle, + std::vector<ui::PlaneLayout> *outPlaneLayouts) const override; + + [[nodiscard]] status_t getDataspace(buffer_handle_t bufferHandle, + ui::Dataspace *outDataspace) const override; + + [[nodiscard]] status_t setDataspace(buffer_handle_t bufferHandle, + ui::Dataspace dataspace) const override; + + [[nodiscard]] status_t getBlendMode(buffer_handle_t bufferHandle, + ui::BlendMode *outBlendMode) const override; + + [[nodiscard]] status_t getSmpte2086(buffer_handle_t bufferHandle, + std::optional<ui::Smpte2086> *outSmpte2086) const override; + + [[nodiscard]] status_t setSmpte2086(buffer_handle_t bufferHandle, + std::optional<ui::Smpte2086> smpte2086) const override; + + [[nodiscard]] status_t getCta861_3(buffer_handle_t bufferHandle, + std::optional<ui::Cta861_3> *outCta861_3) const override; + + [[nodiscard]] status_t setCta861_3(buffer_handle_t bufferHandle, + std::optional<ui::Cta861_3> cta861_3) const override; + + [[nodiscard]] status_t getSmpte2094_40( + buffer_handle_t bufferHandle, + std::optional<std::vector<uint8_t>> *outSmpte2094_40) const override; + + [[nodiscard]] status_t setSmpte2094_40( + buffer_handle_t bufferHandle, + std::optional<std::vector<uint8_t>> smpte2094_40) const override; + + [[nodiscard]] status_t getSmpte2094_10( + buffer_handle_t bufferHandle, + std::optional<std::vector<uint8_t>> *outSmpte2094_10) const override; + + [[nodiscard]] status_t setSmpte2094_10( + buffer_handle_t bufferHandle, + std::optional<std::vector<uint8_t>> smpte2094_10) const override; + + [[nodiscard]] status_t getDefaultPixelFormatFourCC( + uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, + uint64_t usage, uint32_t *outPixelFormatFourCC) const override; + + [[nodiscard]] status_t getDefaultPixelFormatModifier( + uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, + uint64_t usage, uint64_t *outPixelFormatModifier) const override; + + [[nodiscard]] status_t getDefaultAllocationSize(uint32_t width, uint32_t height, + PixelFormat format, uint32_t layerCount, + uint64_t usage, + uint64_t *outAllocationSize) const override; + + [[nodiscard]] status_t getDefaultProtectedContent(uint32_t width, uint32_t height, + PixelFormat format, uint32_t layerCount, + uint64_t usage, + uint64_t *outProtectedContent) const override; + + [[nodiscard]] status_t getDefaultCompression( + uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, + uint64_t usage, + aidl::android::hardware::graphics::common::ExtendableType *outCompression) + const override; + + [[nodiscard]] status_t getDefaultCompression(uint32_t width, uint32_t height, + PixelFormat format, uint32_t layerCount, + uint64_t usage, + ui::Compression *outCompression) const override; + + [[nodiscard]] status_t getDefaultInterlaced( + uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, + uint64_t usage, + aidl::android::hardware::graphics::common::ExtendableType *outInterlaced) + const override; + + [[nodiscard]] status_t getDefaultInterlaced(uint32_t width, uint32_t height, PixelFormat format, + uint32_t layerCount, uint64_t usage, + ui::Interlaced *outInterlaced) const override; + + [[nodiscard]] status_t getDefaultChromaSiting( + uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, + uint64_t usage, + aidl::android::hardware::graphics::common::ExtendableType *outChromaSiting) + const override; + + [[nodiscard]] status_t getDefaultChromaSiting(uint32_t width, uint32_t height, + PixelFormat format, uint32_t layerCount, + uint64_t usage, + ui::ChromaSiting *outChromaSiting) const override; + + [[nodiscard]] status_t getDefaultPlaneLayouts( + uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, + uint64_t usage, std::vector<ui::PlaneLayout> *outPlaneLayouts) const override; + +private: + void unlockBlocking(buffer_handle_t bufferHandle) const; + + AIMapper *mMapper = nullptr; +}; + +class Gralloc5Allocator : public GrallocAllocator { +public: + Gralloc5Allocator(const Gralloc5Mapper &mapper); + + [[nodiscard]] bool isLoaded() const override; + + [[nodiscard]] std::string dumpDebugInfo(bool less) const override; + + [[nodiscard]] status_t allocate(std::string requestorName, uint32_t width, uint32_t height, + PixelFormat format, uint32_t layerCount, uint64_t usage, + uint32_t bufferCount, uint32_t *outStride, + buffer_handle_t *outBufferHandles, + bool importBuffers) const override; + +private: + const Gralloc5Mapper &mMapper; + std::shared_ptr<aidl::android::hardware::graphics::allocator::IAllocator> mAllocator; +}; + +} // namespace android diff --git a/libs/ui/include/ui/GraphicBufferMapper.h b/libs/ui/include/ui/GraphicBufferMapper.h index 507fa355a4..51c6e92f43 100644 --- a/libs/ui/include/ui/GraphicBufferMapper.h +++ b/libs/ui/include/ui/GraphicBufferMapper.h @@ -42,9 +42,10 @@ class GraphicBufferMapper : public Singleton<GraphicBufferMapper> { public: enum Version { - GRALLOC_2, + GRALLOC_2 = 2, GRALLOC_3, GRALLOC_4, + GRALLOC_5, }; static void preloadHal(); static inline GraphicBufferMapper& get() { return getInstance(); } @@ -54,10 +55,11 @@ public: // The imported outHandle must be freed with freeBuffer when no longer // needed. rawHandle is owned by the caller. - status_t importBuffer(buffer_handle_t rawHandle, - uint32_t width, uint32_t height, uint32_t layerCount, - PixelFormat format, uint64_t usage, uint32_t stride, - buffer_handle_t* outHandle); + status_t importBuffer(const native_handle_t* rawHandle, uint32_t width, uint32_t height, + uint32_t layerCount, PixelFormat format, uint64_t usage, uint32_t stride, + buffer_handle_t* outHandle); + + status_t importBufferNoValidate(const native_handle_t* rawHandle, buffer_handle_t* outHandle); status_t freeBuffer(buffer_handle_t handle); diff --git a/libs/ui/include_types/ui/DataspaceUtils.h b/libs/ui/include_types/ui/DataspaceUtils.h index cd3116760b..a461cb4e68 100644 --- a/libs/ui/include_types/ui/DataspaceUtils.h +++ b/libs/ui/include_types/ui/DataspaceUtils.h @@ -22,10 +22,8 @@ namespace android { inline bool isHdrDataspace(ui::Dataspace dataspace) { const auto transfer = dataspace & HAL_DATASPACE_TRANSFER_MASK; - const auto range = dataspace & HAL_DATASPACE_RANGE_MASK; - return transfer == HAL_DATASPACE_TRANSFER_ST2084 || transfer == HAL_DATASPACE_TRANSFER_HLG || - range == HAL_DATASPACE_RANGE_EXTENDED; + return transfer == HAL_DATASPACE_TRANSFER_ST2084 || transfer == HAL_DATASPACE_TRANSFER_HLG; } -} // namespace android +} // namespace android
\ No newline at end of file diff --git a/libs/ui/tests/DataspaceUtils_test.cpp b/libs/ui/tests/DataspaceUtils_test.cpp index ffe64382d0..3e0967182b 100644 --- a/libs/ui/tests/DataspaceUtils_test.cpp +++ b/libs/ui/tests/DataspaceUtils_test.cpp @@ -29,13 +29,12 @@ TEST_F(DataspaceUtilsTest, isHdrDataspace) { EXPECT_TRUE(isHdrDataspace(ui::Dataspace::BT2020_ITU_PQ)); EXPECT_TRUE(isHdrDataspace(ui::Dataspace::BT2020_PQ)); EXPECT_TRUE(isHdrDataspace(ui::Dataspace::BT2020_HLG)); - // The original formulation of scRGB indicates the same white points as that - // of sRGB, however scRGB may be used to implement HDR. - EXPECT_TRUE(isHdrDataspace(ui::Dataspace::V0_SCRGB_LINEAR)); - EXPECT_TRUE(isHdrDataspace(ui::Dataspace::V0_SCRGB)); EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_SRGB_LINEAR)); + // scRGB defines a very wide gamut but not an expanded luminance range + EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_SCRGB_LINEAR)); EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_SRGB)); + EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_SCRGB)); EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_JFIF)); EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_BT601_625)); EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_BT601_525)); diff --git a/libs/ui/tests/colorspace_test.cpp b/libs/ui/tests/colorspace_test.cpp index 0a4873c8d3..3fb33b4a12 100644 --- a/libs/ui/tests/colorspace_test.cpp +++ b/libs/ui/tests/colorspace_test.cpp @@ -111,6 +111,7 @@ TEST_F(ColorSpaceTest, TransferFunctions) { EXPECT_NEAR(1.0f, sRGB.getEOTF()(1.0f), 1e-6f); EXPECT_NEAR(1.0f, sRGB.getOETF()(1.0f), 1e-6f); + // NOLINTNEXTLINE(clang-analyzer-security.FloatLoopCounter,cert-flp30-c) for (float v = 0.0f; v <= 0.5f; v += 1e-3f) { ASSERT_TRUE(v >= sRGB.getEOTF()(v)); ASSERT_TRUE(v <= sRGB.getOETF()(v)); @@ -118,6 +119,7 @@ TEST_F(ColorSpaceTest, TransferFunctions) { float previousEOTF = std::numeric_limits<float>::lowest(); float previousOETF = std::numeric_limits<float>::lowest(); + // NOLINTNEXTLINE(clang-analyzer-security.FloatLoopCounter,cert-flp30-c) for (float v = 0.0f; v <= 1.0f; v += 1e-3f) { ASSERT_TRUE(previousEOTF < sRGB.getEOTF()(v)); previousEOTF = sRGB.getEOTF()(v); @@ -131,6 +133,7 @@ TEST_F(ColorSpaceTest, TransferFunctions) { {0.3127f, 0.3290f} // linear transfer functions ); + // NOLINTNEXTLINE(clang-analyzer-security.FloatLoopCounter,cert-flp30-c) for (float v = 0.0f; v <= 1.0f; v += 1e-3f) { ASSERT_EQ(v, sRGB2.getEOTF()(v)); ASSERT_EQ(v, sRGB2.getOETF()(v)); diff --git a/opengl/libs/EGL/egl_cache.cpp b/opengl/libs/EGL/egl_cache.cpp index 6225c26c9b..1e8a34863d 100644 --- a/opengl/libs/EGL/egl_cache.cpp +++ b/opengl/libs/EGL/egl_cache.cpp @@ -68,7 +68,10 @@ static EGLsizeiANDROID getBlob(const void* key, EGLsizeiANDROID keySize, void* v // egl_cache_t definition // egl_cache_t::egl_cache_t() - : mInitialized(false), mMultifileMode(true), mCacheByteLimit(maxTotalSize) {} + : mInitialized(false), + mMultifileMode(false), + mCacheByteLimit(maxTotalSize), + mMultifileCleanupPending(false) {} egl_cache_t::~egl_cache_t() {} @@ -111,8 +114,6 @@ void egl_cache_t::initialize(egl_display_t* display) { } } - mMultifileMode = true; - // Allow forcing monolithic cache for debug purposes if (base::GetProperty("debug.egl.blobcache.multifilemode", "") == "false") { ALOGD("Forcing monolithic cache due to debug.egl.blobcache.multifilemode == \"false\""); diff --git a/opengl/tests/EGLTest/egl_cache_test.cpp b/opengl/tests/EGLTest/egl_cache_test.cpp index c4fa65f044..265bec492e 100644 --- a/opengl/tests/EGLTest/egl_cache_test.cpp +++ b/opengl/tests/EGLTest/egl_cache_test.cpp @@ -134,6 +134,9 @@ std::string EGLCacheTest::getCachefileName() { } TEST_F(EGLCacheTest, ModifiedCacheMisses) { + // Turn this back on if multifile becomes the default + GTEST_SKIP() << "Skipping test designed for multifile, see b/263574392 and b/246966894"; + uint8_t buf[4] = { 0xee, 0xee, 0xee, 0xee }; mCache->initialize(egl_display_t::get(EGL_DEFAULT_DISPLAY)); diff --git a/services/inputflinger/InputCommonConverter.cpp b/services/inputflinger/InputCommonConverter.cpp index 628ce6fc9a..0c93f5ce40 100644 --- a/services/inputflinger/InputCommonConverter.cpp +++ b/services/inputflinger/InputCommonConverter.cpp @@ -263,11 +263,12 @@ static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_GENERIC_13) == common static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_GENERIC_14) == common::Axis::GENERIC_14); static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_GENERIC_15) == common::Axis::GENERIC_15); static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_GENERIC_16) == common::Axis::GENERIC_16); -// TODO(hcutts): add GESTURE_X_OFFSET and GESTURE_Y_OFFSET. +// TODO(b/251196347): add GESTURE_{X,Y}_OFFSET, GESTURE_SCROLL_{X,Y}_DISTANCE, and +// GESTURE_PINCH_SCALE_FACTOR. // If you added a new axis, consider whether this should also be exposed as a HAL axis. Update the // static_assert below and add the new axis here, or leave a comment summarizing your decision. static_assert(static_cast<common::Axis>(AMOTION_EVENT_MAXIMUM_VALID_AXIS_VALUE) == - static_cast<common::Axis>(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET)); + static_cast<common::Axis>(AMOTION_EVENT_AXIS_GESTURE_PINCH_SCALE_FACTOR)); static common::VideoFrame getHalVideoFrame(const TouchVideoFrame& frame) { common::VideoFrame out; diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h index d2c940ff47..d55ab28ff6 100644 --- a/services/inputflinger/include/InputReaderBase.h +++ b/services/inputflinger/include/InputReaderBase.h @@ -197,6 +197,9 @@ struct InputReaderConfiguration { // The keyboard layout association has changed. CHANGE_KEYBOARD_LAYOUT_ASSOCIATION = 1 << 11, + // The stylus button reporting configurations has changed. + CHANGE_STYLUS_BUTTON_REPORTING = 1 << 12, + // All devices must be reopened. CHANGE_MUST_REOPEN = 1 << 31, }; @@ -309,6 +312,10 @@ struct InputReaderConfiguration { // The set of currently disabled input devices. std::set<int32_t> disabledDevices; + // True if stylus button reporting through motion events should be enabled, in which case + // stylus button state changes are reported through motion events. + bool stylusButtonMotionEventsEnabled; + InputReaderConfiguration() : virtualKeyQuietTime(0), pointerVelocityControlParameters(1.0f, 500.0f, 3000.0f, @@ -329,7 +336,8 @@ struct InputReaderConfiguration { pointerGestureMovementSpeedRatio(0.8f), pointerGestureZoomSpeedRatio(0.3f), showTouches(false), - pointerCaptureRequest() {} + pointerCaptureRequest(), + stylusButtonMotionEventsEnabled(true) {} static std::string changesToString(uint32_t changes); diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp index e26bc8c6dc..43b67cae8b 100644 --- a/services/inputflinger/reader/EventHub.cpp +++ b/services/inputflinger/reader/EventHub.cpp @@ -62,7 +62,6 @@ #define INDENT3 " " using android::base::StringPrintf; -using android::hardware::input::InputDeviceCountryCode; namespace android { @@ -135,6 +134,46 @@ const std::unordered_map<std::string, LightColor> LIGHT_COLORS = {{"red", LightC {"green", LightColor::GREEN}, {"blue", LightColor::BLUE}}; +// Mapping for country code to Layout info. +// See bCountryCode in 6.2.1 of https://usb.org/sites/default/files/hid1_11.pdf. +const std::unordered_map<std::int32_t, RawLayoutInfo> LAYOUT_INFOS = + {{0, RawLayoutInfo{.languageTag = "", .layoutType = ""}}, // NOT_SUPPORTED + {1, RawLayoutInfo{.languageTag = "ar-Arab", .layoutType = ""}}, // ARABIC + {2, RawLayoutInfo{.languageTag = "fr-BE", .layoutType = ""}}, // BELGIAN + {3, RawLayoutInfo{.languageTag = "fr-CA", .layoutType = ""}}, // CANADIAN_BILINGUAL + {4, RawLayoutInfo{.languageTag = "fr-CA", .layoutType = ""}}, // CANADIAN_FRENCH + {5, RawLayoutInfo{.languageTag = "cs", .layoutType = ""}}, // CZECH_REPUBLIC + {6, RawLayoutInfo{.languageTag = "da", .layoutType = ""}}, // DANISH + {7, RawLayoutInfo{.languageTag = "fi", .layoutType = ""}}, // FINNISH + {8, RawLayoutInfo{.languageTag = "fr-FR", .layoutType = ""}}, // FRENCH + {9, RawLayoutInfo{.languageTag = "de", .layoutType = ""}}, // GERMAN + {10, RawLayoutInfo{.languageTag = "el", .layoutType = ""}}, // GREEK + {11, RawLayoutInfo{.languageTag = "iw", .layoutType = ""}}, // HEBREW + {12, RawLayoutInfo{.languageTag = "hu", .layoutType = ""}}, // HUNGARY + {13, RawLayoutInfo{.languageTag = "en", .layoutType = "extended"}}, // INTERNATIONAL (ISO) + {14, RawLayoutInfo{.languageTag = "it", .layoutType = ""}}, // ITALIAN + {15, RawLayoutInfo{.languageTag = "ja", .layoutType = ""}}, // JAPAN + {16, RawLayoutInfo{.languageTag = "ko", .layoutType = ""}}, // KOREAN + {17, RawLayoutInfo{.languageTag = "es-419", .layoutType = ""}}, // LATIN_AMERICA + {18, RawLayoutInfo{.languageTag = "nl", .layoutType = ""}}, // DUTCH + {19, RawLayoutInfo{.languageTag = "nb", .layoutType = ""}}, // NORWEGIAN + {20, RawLayoutInfo{.languageTag = "fa", .layoutType = ""}}, // PERSIAN + {21, RawLayoutInfo{.languageTag = "pl", .layoutType = ""}}, // POLAND + {22, RawLayoutInfo{.languageTag = "pt", .layoutType = ""}}, // PORTUGUESE + {23, RawLayoutInfo{.languageTag = "ru", .layoutType = ""}}, // RUSSIA + {24, RawLayoutInfo{.languageTag = "sk", .layoutType = ""}}, // SLOVAKIA + {25, RawLayoutInfo{.languageTag = "es-ES", .layoutType = ""}}, // SPANISH + {26, RawLayoutInfo{.languageTag = "sv", .layoutType = ""}}, // SWEDISH + {27, RawLayoutInfo{.languageTag = "fr-CH", .layoutType = ""}}, // SWISS_FRENCH + {28, RawLayoutInfo{.languageTag = "de-CH", .layoutType = ""}}, // SWISS_GERMAN + {29, RawLayoutInfo{.languageTag = "de-CH", .layoutType = ""}}, // SWITZERLAND + {30, RawLayoutInfo{.languageTag = "zh-TW", .layoutType = ""}}, // TAIWAN + {31, RawLayoutInfo{.languageTag = "tr", .layoutType = "turkish_q"}}, // TURKISH_Q + {32, RawLayoutInfo{.languageTag = "en-GB", .layoutType = ""}}, // UK + {33, RawLayoutInfo{.languageTag = "en-US", .layoutType = ""}}, // US + {34, RawLayoutInfo{.languageTag = "", .layoutType = ""}}, // YUGOSLAVIA + {35, RawLayoutInfo{.languageTag = "tr", .layoutType = "turkish_f"}}}; // TURKISH_F + static std::string sha1(const std::string& in) { SHA_CTX ctx; SHA1_Init(&ctx); @@ -311,21 +350,27 @@ static std::optional<std::array<LightColor, COLOR_NUM>> getColorIndexArray( } /** - * Read country code information exposed through the sysfs path. + * Read country code information exposed through the sysfs path and convert it to Layout info. */ -static InputDeviceCountryCode readCountryCodeLocked(const std::filesystem::path& sysfsRootPath) { +static std::optional<RawLayoutInfo> readLayoutConfiguration( + const std::filesystem::path& sysfsRootPath) { // Check the sysfs root path - int hidCountryCode = static_cast<int>(InputDeviceCountryCode::INVALID); + int32_t hidCountryCode = -1; std::string str; if (base::ReadFileToString(sysfsRootPath / "country", &str)) { hidCountryCode = std::stoi(str, nullptr, 16); - LOG_ALWAYS_FATAL_IF(hidCountryCode > 35 || hidCountryCode < 0, - "HID country code should be in range [0, 35]. Found country code " - "to be %d", - hidCountryCode); + // Update this condition if new supported country codes are added to HID spec. + if (hidCountryCode > 35 || hidCountryCode < 0) { + ALOGE("HID country code should be in range [0, 35], but for sysfs path %s it was %d", + sysfsRootPath.c_str(), hidCountryCode); + } + } + const auto it = LAYOUT_INFOS.find(hidCountryCode); + if (it != LAYOUT_INFOS.end()) { + return it->second; } - return static_cast<InputDeviceCountryCode>(hidCountryCode); + return std::nullopt; } /** @@ -1298,13 +1343,13 @@ void EventHub::setLightIntensities(int32_t deviceId, int32_t lightId, } } -InputDeviceCountryCode EventHub::getCountryCode(int32_t deviceId) const { +std::optional<RawLayoutInfo> EventHub::getRawLayoutInfo(int32_t deviceId) const { std::scoped_lock _l(mLock); Device* device = getDeviceLocked(deviceId); if (device == nullptr || !device->associatedDevice) { - return InputDeviceCountryCode::INVALID; + return std::nullopt; } - return device->associatedDevice->countryCode; + return device->associatedDevice->layoutInfo; } void EventHub::setExcludedDevices(const std::vector<std::string>& devices) { @@ -1371,12 +1416,17 @@ const std::shared_ptr<KeyCharacterMap> EventHub::getKeyCharacterMap(int32_t devi return nullptr; } +// If provided map is null, it will reset key character map to default KCM. bool EventHub::setKeyboardLayoutOverlay(int32_t deviceId, std::shared_ptr<KeyCharacterMap> map) { std::scoped_lock _l(mLock); Device* device = getDeviceLocked(deviceId); - if (device == nullptr || map == nullptr || device->keyMap.keyCharacterMap == nullptr) { + if (device == nullptr || device->keyMap.keyCharacterMap == nullptr) { return false; } + if (map == nullptr) { + device->keyMap.keyCharacterMap->clearLayoutOverlay(); + return true; + } device->keyMap.keyCharacterMap->combine(*map); return true; } @@ -1448,9 +1498,9 @@ std::shared_ptr<const EventHub::AssociatedDevice> EventHub::obtainAssociatedDevi std::shared_ptr<const AssociatedDevice> associatedDevice = std::make_shared<AssociatedDevice>( AssociatedDevice{.sysfsRootPath = path, - .countryCode = readCountryCodeLocked(path), .batteryInfos = readBatteryConfiguration(path), - .lightInfos = readLightsConfiguration(path)}); + .lightInfos = readLightsConfiguration(path), + .layoutInfo = readLayoutConfiguration(path)}); bool associatedDeviceChanged = false; for (const auto& [id, dev] : mDevices) { @@ -2685,9 +2735,12 @@ void EventHub::dump(std::string& dump) const { device->keyMap.keyLayoutFile.c_str()); dump += StringPrintf(INDENT3 "KeyCharacterMapFile: %s\n", device->keyMap.keyCharacterMapFile.c_str()); - dump += StringPrintf(INDENT3 "CountryCode: %d\n", - device->associatedDevice ? device->associatedDevice->countryCode - : InputDeviceCountryCode::INVALID); + if (device->associatedDevice && device->associatedDevice->layoutInfo) { + dump += StringPrintf(INDENT3 "LanguageTag: %s\n", + device->associatedDevice->layoutInfo->languageTag.c_str()); + dump += StringPrintf(INDENT3 "LayoutType: %s\n", + device->associatedDevice->layoutInfo->layoutType.c_str()); + } dump += StringPrintf(INDENT3 "ConfigurationFile: %s\n", device->configurationFile.c_str()); dump += StringPrintf(INDENT3 "VideoDevice: %s\n", diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp index 13f40ee8e4..6e78e82c7e 100644 --- a/services/inputflinger/reader/InputDevice.cpp +++ b/services/inputflinger/reader/InputDevice.cpp @@ -37,8 +37,6 @@ #include "TouchpadInputMapper.h" #include "VibratorInputMapper.h" -using android::hardware::input::InputDeviceCountryCode; - namespace android { InputDevice::InputDevice(InputReaderContext* context, int32_t id, int32_t generation, @@ -256,7 +254,6 @@ std::list<NotifyArgs> InputDevice::configure(nsecs_t when, const InputReaderConf mSources = 0; mClasses = ftl::Flags<InputDeviceClass>(0); mControllerNumber = 0; - mCountryCode = InputDeviceCountryCode::INVALID; for_each_subdevice([this](InputDeviceContext& context) { mClasses |= context.getDeviceClasses(); @@ -268,16 +265,6 @@ std::list<NotifyArgs> InputDevice::configure(nsecs_t when, const InputReaderConf } mControllerNumber = controllerNumber; } - - InputDeviceCountryCode countryCode = context.getCountryCode(); - if (countryCode != InputDeviceCountryCode::INVALID) { - if (mCountryCode != InputDeviceCountryCode::INVALID && mCountryCode != countryCode) { - ALOGW("InputDevice::configure(): %s device contains multiple unique country " - "codes", - getName().c_str()); - } - mCountryCode = countryCode; - } }); mIsExternal = mClasses.test(InputDeviceClass::EXTERNAL); @@ -465,7 +452,7 @@ std::list<NotifyArgs> InputDevice::updateExternalStylusState(const StylusState& InputDeviceInfo InputDevice::getDeviceInfo() { InputDeviceInfo outDeviceInfo; outDeviceInfo.initialize(mId, mGeneration, mControllerNumber, mIdentifier, mAlias, mIsExternal, - mHasMic, mCountryCode); + mHasMic); for_each_mapper( [&outDeviceInfo](InputMapper& mapper) { mapper.populateDeviceInfo(&outDeviceInfo); }); diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h index 8a844b2088..a3ecf418bb 100644 --- a/services/inputflinger/reader/include/EventHub.h +++ b/services/inputflinger/reader/include/EventHub.h @@ -42,7 +42,6 @@ #include "TouchVideoDevice.h" #include "VibrationElement.h" -#include "android/hardware/input/InputDeviceCountryCode.h" struct inotify_event; @@ -207,6 +206,15 @@ struct RawBatteryInfo { bool operator!=(const RawBatteryInfo&) const = default; }; +/* Layout information associated with the device */ +struct RawLayoutInfo { + std::string languageTag; + std::string layoutType; + + bool operator==(const RawLayoutInfo&) const = default; + bool operator!=(const RawLayoutInfo&) const = default; +}; + /* * Gets the class that owns an axis, in cases where multiple classes might claim * the same axis for different purposes. @@ -308,8 +316,8 @@ public: int32_t deviceId, int32_t lightId) const = 0; virtual void setLightIntensities(int32_t deviceId, int32_t lightId, std::unordered_map<LightColor, int32_t> intensities) = 0; - /* Query Country code associated with the input device. */ - virtual hardware::input::InputDeviceCountryCode getCountryCode(int32_t deviceId) const = 0; + /* Query Layout info associated with the input device. */ + virtual std::optional<RawLayoutInfo> getRawLayoutInfo(int32_t deviceId) const = 0; /* Query current input state. */ virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const = 0; virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const = 0; @@ -493,7 +501,7 @@ public: void setLightIntensities(int32_t deviceId, int32_t lightId, std::unordered_map<LightColor, int32_t> intensities) override final; - hardware::input::InputDeviceCountryCode getCountryCode(int32_t deviceId) const override final; + std::optional<RawLayoutInfo> getRawLayoutInfo(int32_t deviceId) const override final; void setExcludedDevices(const std::vector<std::string>& devices) override final; @@ -556,9 +564,9 @@ private: struct AssociatedDevice { // The sysfs root path of the misc device. std::filesystem::path sysfsRootPath; - hardware::input::InputDeviceCountryCode countryCode; std::unordered_map<int32_t /*batteryId*/, RawBatteryInfo> batteryInfos; std::unordered_map<int32_t /*lightId*/, RawLightInfo> lightInfos; + std::optional<RawLayoutInfo> layoutInfo; bool operator==(const AssociatedDevice&) const = default; bool operator!=(const AssociatedDevice&) const = default; diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h index b173618d87..7867029ab7 100644 --- a/services/inputflinger/reader/include/InputDevice.h +++ b/services/inputflinger/reader/include/InputDevice.h @@ -167,7 +167,6 @@ private: int32_t mId; int32_t mGeneration; int32_t mControllerNumber; - hardware::input::InputDeviceCountryCode mCountryCode; InputDeviceIdentifier mIdentifier; std::string mAlias; ftl::Flags<InputDeviceClass> mClasses; @@ -326,9 +325,6 @@ public: } inline std::vector<TouchVideoFrame> getVideoFrames() { return mEventHub->getVideoFrames(mId); } - inline hardware::input::InputDeviceCountryCode getCountryCode() const { - return mEventHub->getCountryCode(mId); - } inline int32_t getScanCodeState(int32_t scanCode) const { return mEventHub->getScanCodeState(mId, scanCode); } @@ -361,6 +357,9 @@ public: inline bool setKeyboardLayoutOverlay(std::shared_ptr<KeyCharacterMap> map) { return mEventHub->setKeyboardLayoutOverlay(mId, map); } + inline const std::optional<RawLayoutInfo> getRawLayoutInfo() { + return mEventHub->getRawLayoutInfo(mId); + } inline void vibrate(const VibrationElement& element) { return mEventHub->vibrate(mId, element); } diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp index 6f01449083..d147d60f4e 100644 --- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp +++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp @@ -123,6 +123,12 @@ void KeyboardInputMapper::populateDeviceInfo(InputDeviceInfo* info) { if (mKeyboardLayoutInfo) { info->setKeyboardLayoutInfo(*mKeyboardLayoutInfo); + } else { + std::optional<RawLayoutInfo> layoutInfo = getDeviceContext().getRawLayoutInfo(); + if (layoutInfo) { + info->setKeyboardLayoutInfo( + KeyboardLayoutInfo(layoutInfo->languageTag, layoutInfo->layoutType)); + } } } diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index 9a7af40456..8cd2cf0255 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -88,6 +88,14 @@ static ui::Size getNaturalDisplaySize(const DisplayViewport& viewport) { return rotatedDisplaySize; } +static int32_t filterButtonState(InputReaderConfiguration& config, int32_t buttonState) { + if (!config.stylusButtonMotionEventsEnabled) { + buttonState &= + ~(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY | AMOTION_EVENT_BUTTON_STYLUS_SECONDARY); + } + return buttonState; +} + // --- RawPointerData --- void RawPointerData::getCentroidOfTouchingPointers(float* outX, float* outY) const { @@ -1400,8 +1408,9 @@ std::list<NotifyArgs> TouchInputMapper::sync(nsecs_t when, nsecs_t readTime) { next.readTime = readTime; // Sync button state. - next.buttonState = - mTouchButtonAccumulator.getButtonState() | mCursorButtonAccumulator.getButtonState(); + next.buttonState = filterButtonState(mConfig, + mTouchButtonAccumulator.getButtonState() | + mCursorButtonAccumulator.getButtonState()); // Sync scroll next.rawVScroll = mCursorScrollAccumulator.getRelativeVWheel(); @@ -1640,7 +1649,9 @@ bool TouchInputMapper::isTouchScreen() { void TouchInputMapper::applyExternalStylusButtonState(nsecs_t when) { if (mDeviceMode == DeviceMode::DIRECT && hasExternalStylus()) { // If any of the external buttons are already pressed by the touch device, ignore them. - const int32_t pressedButtons = ~mCurrentRawState.buttonState & mExternalStylusState.buttons; + const int32_t pressedButtons = + filterButtonState(mConfig, + ~mCurrentRawState.buttonState & mExternalStylusState.buttons); const int32_t releasedButtons = mExternalStylusButtonsApplied & ~mExternalStylusState.buttons; @@ -2692,18 +2703,15 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi // Update the velocity tracker. { - std::vector<float> positionsX; - std::vector<float> positionsY; for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty();) { uint32_t id = idBits.clearFirstMarkedBit(); const RawPointerData::Pointer& pointer = mCurrentRawState.rawPointerData.pointerForId(id); - positionsX.push_back(pointer.x * mPointerXMovementScale); - positionsY.push_back(pointer.y * mPointerYMovementScale); + const float x = pointer.x * mPointerXMovementScale; + const float y = pointer.y * mPointerYMovementScale; + mPointerGesture.velocityTracker.addMovement(when, id, AMOTION_EVENT_AXIS_X, x); + mPointerGesture.velocityTracker.addMovement(when, id, AMOTION_EVENT_AXIS_Y, y); } - mPointerGesture.velocityTracker.addMovement(when, mCurrentCookedState.fingerIdBits, - {{AMOTION_EVENT_AXIS_X, positionsX}, - {AMOTION_EVENT_AXIS_Y, positionsY}}); } // If the gesture ever enters a mode other than TAP, HOVER or TAP_DRAG, without first returning diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp index c563dbaad0..3b51be83ab 100644 --- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp @@ -16,11 +16,14 @@ #include "../Macros.h" +#include <optional> + #include <android/input.h> #include <linux/input-event-codes.h> #include <log/log_main.h> #include "TouchCursorInputMapperCommon.h" #include "TouchpadInputMapper.h" +#include "ui/Rotation.h" namespace android { @@ -89,7 +92,7 @@ TouchpadInputMapper::TouchpadInputMapper(InputDeviceContext& deviceContext) mGestureInterpreter(NewGestureInterpreter(), DeleteGestureInterpreter), mPointerController(getContext()->getPointerController(getDeviceId())), mStateConverter(deviceContext), - mGestureConverter(*getContext(), getDeviceId()) { + mGestureConverter(*getContext(), deviceContext, getDeviceId()) { mGestureInterpreter->Initialize(GESTURES_DEVCLASS_TOUCHPAD); mGestureInterpreter->SetHardwareProperties(createHardwareProperties(deviceContext)); // Even though we don't explicitly delete copy/move semantics, it's safe to @@ -111,6 +114,22 @@ uint32_t TouchpadInputMapper::getSources() const { return AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD; } +std::list<NotifyArgs> TouchpadInputMapper::configure(nsecs_t when, + const InputReaderConfiguration* config, + uint32_t changes) { + if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { + std::optional<int32_t> displayId = mPointerController->getDisplayId(); + ui::Rotation orientation = ui::ROTATION_0; + if (displayId.has_value()) { + if (auto viewport = config->getDisplayViewportById(*displayId); viewport) { + orientation = getInverseRotation(viewport->orientation); + } + } + mGestureConverter.setOrientation(orientation); + } + return {}; +} + std::list<NotifyArgs> TouchpadInputMapper::reset(nsecs_t when) { mStateConverter.reset(); mGestureConverter.reset(); diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.h b/services/inputflinger/reader/mapper/TouchpadInputMapper.h index b3bc831baf..3a922115a1 100644 --- a/services/inputflinger/reader/mapper/TouchpadInputMapper.h +++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.h @@ -25,6 +25,7 @@ #include "EventHub.h" #include "InputDevice.h" #include "InputMapper.h" +#include "InputReaderBase.h" #include "NotifyArgs.h" #include "gestures/GestureConverter.h" #include "gestures/HardwareStateConverter.h" @@ -39,6 +40,9 @@ public: ~TouchpadInputMapper(); uint32_t getSources() const override; + [[nodiscard]] std::list<NotifyArgs> configure(nsecs_t when, + const InputReaderConfiguration* config, + uint32_t changes) override; [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override; [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override; diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp index 23216d35c5..11ffd286dd 100644 --- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp +++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp @@ -17,6 +17,8 @@ #include "gestures/GestureConverter.h" #include <android/input.h> +#include <linux/input-event-codes.h> +#include <log/log_main.h> #include "TouchCursorInputMapperCommon.h" #include "input/Input.h" @@ -44,10 +46,14 @@ uint32_t gesturesButtonToMotionEventButton(uint32_t gesturesButton) { } // namespace -GestureConverter::GestureConverter(InputReaderContext& readerContext, int32_t deviceId) +GestureConverter::GestureConverter(InputReaderContext& readerContext, + const InputDeviceContext& deviceContext, int32_t deviceId) : mDeviceId(deviceId), mReaderContext(readerContext), - mPointerController(readerContext.getPointerController(deviceId)) {} + mPointerController(readerContext.getPointerController(deviceId)) { + deviceContext.getAbsoluteAxisInfo(ABS_MT_POSITION_X, &mXAxisInfo); + deviceContext.getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &mYAxisInfo); +} void GestureConverter::reset() { mButtonState = 0; @@ -60,6 +66,21 @@ std::list<NotifyArgs> GestureConverter::handleGesture(nsecs_t when, nsecs_t read return {handleMove(when, readTime, gesture)}; case kGestureTypeButtonsChange: return handleButtonsChange(when, readTime, gesture); + case kGestureTypeScroll: + return handleScroll(when, readTime, gesture); + case kGestureTypeFling: + return {handleFling(when, readTime, gesture)}; + case kGestureTypeSwipe: + return handleMultiFingerSwipe(when, readTime, 3, gesture.details.swipe.dx, + gesture.details.swipe.dy); + case kGestureTypeFourFingerSwipe: + return handleMultiFingerSwipe(when, readTime, 4, gesture.details.four_finger_swipe.dx, + gesture.details.four_finger_swipe.dy); + case kGestureTypeSwipeLift: + case kGestureTypeFourFingerSwipeLift: + return handleMultiFingerSwipeLift(when, readTime); + case kGestureTypePinch: + return handlePinch(when, readTime, gesture); default: // TODO(b/251196347): handle more gesture types. return {}; @@ -67,13 +88,12 @@ std::list<NotifyArgs> GestureConverter::handleGesture(nsecs_t when, nsecs_t read } NotifyArgs GestureConverter::handleMove(nsecs_t when, nsecs_t readTime, const Gesture& gesture) { - PointerProperties props; - props.clear(); - props.id = 0; - props.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + float deltaX = gesture.details.move.dx; + float deltaY = gesture.details.move.dy; + rotateDelta(mOrientation, &deltaX, &deltaY); mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER); - mPointerController->move(gesture.details.move.dx, gesture.details.move.dy); + mPointerController->move(deltaX, deltaY); mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE); float xCursorPosition, yCursorPosition; mPointerController->getPosition(&xCursorPosition, &yCursorPosition); @@ -82,14 +102,15 @@ NotifyArgs GestureConverter::handleMove(nsecs_t when, nsecs_t readTime, const Ge coords.clear(); coords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition); coords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition); - coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, gesture.details.move.dx); - coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, gesture.details.move.dy); + coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX); + coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY); const bool down = isPointerDown(mButtonState); coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, down ? 1.0f : 0.0f); const int32_t action = down ? AMOTION_EVENT_ACTION_MOVE : AMOTION_EVENT_ACTION_HOVER_MOVE; return makeMotionArgs(when, readTime, action, /* actionButton= */ 0, mButtonState, - /* pointerCount= */ 1, &props, &coords, xCursorPosition, yCursorPosition); + /* pointerCount= */ 1, mFingerProps.data(), &coords, xCursorPosition, + yCursorPosition); } std::list<NotifyArgs> GestureConverter::handleButtonsChange(nsecs_t when, nsecs_t readTime, @@ -99,11 +120,6 @@ std::list<NotifyArgs> GestureConverter::handleButtonsChange(nsecs_t when, nsecs_ mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER); mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE); - PointerProperties props; - props.clear(); - props.id = 0; - props.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; - float xCursorPosition, yCursorPosition; mPointerController->getPosition(&xCursorPosition, &yCursorPosition); @@ -127,15 +143,16 @@ std::list<NotifyArgs> GestureConverter::handleButtonsChange(nsecs_t when, nsecs_ newButtonState |= actionButton; pressEvents.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, newButtonState, - /* pointerCount= */ 1, &props, &coords, - xCursorPosition, yCursorPosition)); + /* pointerCount= */ 1, mFingerProps.data(), + &coords, xCursorPosition, yCursorPosition)); } } if (!isPointerDown(mButtonState) && isPointerDown(newButtonState)) { mDownTime = when; out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN, /* actionButton= */ 0, newButtonState, /* pointerCount= */ 1, - &props, &coords, xCursorPosition, yCursorPosition)); + mFingerProps.data(), &coords, xCursorPosition, + yCursorPosition)); } out.splice(out.end(), pressEvents); @@ -151,19 +168,237 @@ std::list<NotifyArgs> GestureConverter::handleButtonsChange(nsecs_t when, nsecs_ newButtonState &= ~actionButton; out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, newButtonState, /* pointerCount= */ 1, - &props, &coords, xCursorPosition, yCursorPosition)); + mFingerProps.data(), &coords, xCursorPosition, + yCursorPosition)); } } if (isPointerDown(mButtonState) && !isPointerDown(newButtonState)) { coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.0f); out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /* actionButton= */ 0, - newButtonState, /* pointerCount= */ 1, &props, &coords, - xCursorPosition, yCursorPosition)); + newButtonState, /* pointerCount= */ 1, mFingerProps.data(), + &coords, xCursorPosition, yCursorPosition)); } mButtonState = newButtonState; return out; } +std::list<NotifyArgs> GestureConverter::handleScroll(nsecs_t when, nsecs_t readTime, + const Gesture& gesture) { + std::list<NotifyArgs> out; + PointerCoords& coords = mFakeFingerCoords[0]; + float xCursorPosition, yCursorPosition; + mPointerController->getPosition(&xCursorPosition, &yCursorPosition); + if (mCurrentClassification != MotionClassification::TWO_FINGER_SWIPE) { + mCurrentClassification = MotionClassification::TWO_FINGER_SWIPE; + coords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition); + coords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition); + coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f); + mDownTime = when; + out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN, + /* actionButton= */ 0, mButtonState, /* pointerCount= */ 1, + mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition, + yCursorPosition)); + } + float deltaX = gesture.details.scroll.dx; + float deltaY = gesture.details.scroll.dy; + rotateDelta(mOrientation, &deltaX, &deltaY); + + coords.setAxisValue(AMOTION_EVENT_AXIS_X, coords.getAxisValue(AMOTION_EVENT_AXIS_X) - deltaX); + coords.setAxisValue(AMOTION_EVENT_AXIS_Y, coords.getAxisValue(AMOTION_EVENT_AXIS_Y) - deltaY); + // TODO(b/262876643): set AXIS_GESTURE_{X,Y}_OFFSET. + coords.setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_X_DISTANCE, gesture.details.scroll.dx); + coords.setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_Y_DISTANCE, gesture.details.scroll.dy); + out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_MOVE, /* actionButton= */ 0, + mButtonState, /* pointerCount= */ 1, mFingerProps.data(), + mFakeFingerCoords.data(), xCursorPosition, yCursorPosition)); + return out; +} + +NotifyArgs GestureConverter::handleFling(nsecs_t when, nsecs_t readTime, const Gesture& gesture) { + // We don't actually want to use the gestures library's fling velocity values (to ensure + // consistency between touchscreen and touchpad flings), so we're just using the "start fling" + // gestures as a marker for the end of a two-finger scroll gesture. + if (gesture.details.fling.fling_state != GESTURES_FLING_START || + mCurrentClassification != MotionClassification::TWO_FINGER_SWIPE) { + return {}; + } + + float xCursorPosition, yCursorPosition; + mPointerController->getPosition(&xCursorPosition, &yCursorPosition); + mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_X_DISTANCE, 0); + mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_Y_DISTANCE, 0); + NotifyArgs args = makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, + /* actionButton= */ 0, mButtonState, /* pointerCount= */ 1, + mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition, + yCursorPosition); + mCurrentClassification = MotionClassification::NONE; + return args; +} + +[[nodiscard]] std::list<NotifyArgs> GestureConverter::handleMultiFingerSwipe(nsecs_t when, + nsecs_t readTime, + uint32_t fingerCount, + float dx, float dy) { + std::list<NotifyArgs> out = {}; + float xCursorPosition, yCursorPosition; + mPointerController->getPosition(&xCursorPosition, &yCursorPosition); + if (mCurrentClassification != MotionClassification::MULTI_FINGER_SWIPE) { + // If the user changes the number of fingers mid-way through a swipe (e.g. they start with + // three and then put a fourth finger down), the gesture library will treat it as two + // separate swipes with an appropriate lift event between them, so we don't have to worry + // about the finger count changing mid-swipe. + mCurrentClassification = MotionClassification::MULTI_FINGER_SWIPE; + mSwipeFingerCount = fingerCount; + + constexpr float FAKE_FINGER_SPACING = 100; + float xCoord = xCursorPosition - FAKE_FINGER_SPACING * (mSwipeFingerCount - 1) / 2; + for (size_t i = 0; i < mSwipeFingerCount; i++) { + PointerCoords& coords = mFakeFingerCoords[i]; + coords.clear(); + coords.setAxisValue(AMOTION_EVENT_AXIS_X, xCoord); + coords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition); + coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f); + xCoord += FAKE_FINGER_SPACING; + } + + mDownTime = when; + out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN, + /* actionButton= */ 0, mButtonState, /* pointerCount= */ 1, + mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition, + yCursorPosition)); + for (size_t i = 1; i < mSwipeFingerCount; i++) { + out.push_back(makeMotionArgs(when, readTime, + AMOTION_EVENT_ACTION_POINTER_DOWN | + (i << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + /* actionButton= */ 0, mButtonState, + /* pointerCount= */ i + 1, mFingerProps.data(), + mFakeFingerCoords.data(), xCursorPosition, + yCursorPosition)); + } + } + // TODO(b/251196347): Set the gesture properties appropriately to avoid needing to negate the Y + // values. + float rotatedDeltaX = dx, rotatedDeltaY = -dy; + rotateDelta(mOrientation, &rotatedDeltaX, &rotatedDeltaY); + for (size_t i = 0; i < mSwipeFingerCount; i++) { + PointerCoords& coords = mFakeFingerCoords[i]; + coords.setAxisValue(AMOTION_EVENT_AXIS_X, + coords.getAxisValue(AMOTION_EVENT_AXIS_X) + rotatedDeltaX); + coords.setAxisValue(AMOTION_EVENT_AXIS_Y, + coords.getAxisValue(AMOTION_EVENT_AXIS_Y) + rotatedDeltaY); + } + float xOffset = dx / (mXAxisInfo.maxValue - mXAxisInfo.minValue); + // TODO(b/251196347): Set the gesture properties appropriately to avoid needing to negate the Y + // values. + float yOffset = -dy / (mYAxisInfo.maxValue - mYAxisInfo.minValue); + mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_X_OFFSET, xOffset); + mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET, yOffset); + out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_MOVE, /* actionButton= */ 0, + mButtonState, /* pointerCount= */ mSwipeFingerCount, + mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition, + yCursorPosition)); + return out; +} + +[[nodiscard]] std::list<NotifyArgs> GestureConverter::handleMultiFingerSwipeLift(nsecs_t when, + nsecs_t readTime) { + std::list<NotifyArgs> out = {}; + if (mCurrentClassification != MotionClassification::MULTI_FINGER_SWIPE) { + return out; + } + float xCursorPosition, yCursorPosition; + mPointerController->getPosition(&xCursorPosition, &yCursorPosition); + mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_X_OFFSET, 0); + mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET, 0); + + for (size_t i = mSwipeFingerCount; i > 1; i--) { + out.push_back(makeMotionArgs(when, readTime, + AMOTION_EVENT_ACTION_POINTER_UP | + ((i - 1) << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + /* actionButton= */ 0, mButtonState, /* pointerCount= */ i, + mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition, + yCursorPosition)); + } + out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, + /* actionButton= */ 0, mButtonState, /* pointerCount= */ 1, + mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition, + yCursorPosition)); + mCurrentClassification = MotionClassification::NONE; + mSwipeFingerCount = 0; + return out; +} + +[[nodiscard]] std::list<NotifyArgs> GestureConverter::handlePinch(nsecs_t when, nsecs_t readTime, + const Gesture& gesture) { + std::list<NotifyArgs> out; + float xCursorPosition, yCursorPosition; + mPointerController->getPosition(&xCursorPosition, &yCursorPosition); + + // Pinch gesture phases are reported a little differently from others, in that the same details + // struct is used for all phases of the gesture, just with different zoom_state values. When + // zoom_state is START or END, dz will always be 1, so we don't need to move the pointers in + // those cases. + + if (mCurrentClassification != MotionClassification::PINCH) { + LOG_ALWAYS_FATAL_IF(gesture.details.pinch.zoom_state != GESTURES_ZOOM_START, + "First pinch gesture does not have the START zoom state (%d instead).", + gesture.details.pinch.zoom_state); + mCurrentClassification = MotionClassification::PINCH; + mPinchFingerSeparation = INITIAL_PINCH_SEPARATION_PX; + mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_PINCH_SCALE_FACTOR, 1.0); + mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, + xCursorPosition - mPinchFingerSeparation / 2); + mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition); + mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f); + mFakeFingerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, + xCursorPosition + mPinchFingerSeparation / 2); + mFakeFingerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition); + mFakeFingerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f); + mDownTime = when; + out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN, + /* actionButton= */ 0, mButtonState, /* pointerCount= */ 1, + mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition, + yCursorPosition)); + out.push_back(makeMotionArgs(when, readTime, + AMOTION_EVENT_ACTION_POINTER_DOWN | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT, + /* actionButton= */ 0, mButtonState, /* pointerCount= */ 2, + mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition, + yCursorPosition)); + return out; + } + + if (gesture.details.pinch.zoom_state == GESTURES_ZOOM_END) { + mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_PINCH_SCALE_FACTOR, 1.0); + out.push_back(makeMotionArgs(when, readTime, + AMOTION_EVENT_ACTION_POINTER_UP | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT, + /* actionButton= */ 0, mButtonState, /* pointerCount= */ 2, + mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition, + yCursorPosition)); + out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /* actionButton= */ 0, + mButtonState, /* pointerCount= */ 1, mFingerProps.data(), + mFakeFingerCoords.data(), xCursorPosition, yCursorPosition)); + mCurrentClassification = MotionClassification::NONE; + mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_PINCH_SCALE_FACTOR, 0); + return out; + } + + mPinchFingerSeparation *= gesture.details.pinch.dz; + mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_PINCH_SCALE_FACTOR, + gesture.details.pinch.dz); + mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, + xCursorPosition - mPinchFingerSeparation / 2); + mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition); + mFakeFingerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, + xCursorPosition + mPinchFingerSeparation / 2); + mFakeFingerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition); + out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_MOVE, /* actionButton= */ 0, + mButtonState, /* pointerCount= */ 2, mFingerProps.data(), + mFakeFingerCoords.data(), xCursorPosition, yCursorPosition)); + return out; +} + NotifyMotionArgs GestureConverter::makeMotionArgs(nsecs_t when, nsecs_t readTime, int32_t action, int32_t actionButton, int32_t buttonState, uint32_t pointerCount, @@ -174,13 +409,13 @@ NotifyMotionArgs GestureConverter::makeMotionArgs(nsecs_t when, nsecs_t readTime const uint32_t source = AINPUT_SOURCE_MOUSE; return NotifyMotionArgs(mReaderContext.getNextId(), when, readTime, mDeviceId, source, - mPointerController->getDisplayId(), /* policyFlags= */ 0, action, - /* actionButton= */ actionButton, /* flags= */ 0, + mPointerController->getDisplayId(), /* policyFlags= */ POLICY_FLAG_WAKE, + action, /* actionButton= */ actionButton, /* flags= */ 0, mReaderContext.getGlobalMetaState(), buttonState, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount, - pointerProperties, pointerCoords, - /* xPrecision= */ 1.0f, /* yPrecision= */ 1.0f, xCursorPosition, - yCursorPosition, /* downTime= */ mDownTime, /* videoFrames= */ {}); + mCurrentClassification, AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount, + pointerProperties, pointerCoords, /* xPrecision= */ 1.0f, + /* yPrecision= */ 1.0f, xCursorPosition, yCursorPosition, + /* downTime= */ mDownTime, /* videoFrames= */ {}); } } // namespace android diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.h b/services/inputflinger/reader/mapper/gestures/GestureConverter.h index dc11f2476b..8e8e3d9de3 100644 --- a/services/inputflinger/reader/mapper/gestures/GestureConverter.h +++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.h @@ -16,14 +16,18 @@ #pragma once +#include <array> #include <list> #include <memory> #include <PointerControllerInterface.h> #include <utils/Timers.h> +#include "EventHub.h" +#include "InputDevice.h" #include "InputReaderContext.h" #include "NotifyArgs.h" +#include "ui/Rotation.h" #include "include/gestures.h" @@ -33,17 +37,28 @@ namespace android { // PointerController calls. class GestureConverter { public: - GestureConverter(InputReaderContext& readerContext, int32_t deviceId); + GestureConverter(InputReaderContext& readerContext, const InputDeviceContext& deviceContext, + int32_t deviceId); + void setOrientation(ui::Rotation orientation) { mOrientation = orientation; } void reset(); [[nodiscard]] std::list<NotifyArgs> handleGesture(nsecs_t when, nsecs_t readTime, const Gesture& gesture); private: - NotifyArgs handleMove(nsecs_t when, nsecs_t readTime, const Gesture& gesture); + [[nodiscard]] NotifyArgs handleMove(nsecs_t when, nsecs_t readTime, const Gesture& gesture); [[nodiscard]] std::list<NotifyArgs> handleButtonsChange(nsecs_t when, nsecs_t readTime, const Gesture& gesture); + [[nodiscard]] std::list<NotifyArgs> handleScroll(nsecs_t when, nsecs_t readTime, + const Gesture& gesture); + [[nodiscard]] NotifyArgs handleFling(nsecs_t when, nsecs_t readTime, const Gesture& gesture); + [[nodiscard]] std::list<NotifyArgs> handleMultiFingerSwipe(nsecs_t when, nsecs_t readTime, + uint32_t fingerCount, float dx, + float dy); + [[nodiscard]] std::list<NotifyArgs> handleMultiFingerSwipeLift(nsecs_t when, nsecs_t readTime); + [[nodiscard]] std::list<NotifyArgs> handlePinch(nsecs_t when, nsecs_t readTime, + const Gesture& gesture); NotifyMotionArgs makeMotionArgs(nsecs_t when, nsecs_t readTime, int32_t action, int32_t actionButton, int32_t buttonState, @@ -56,10 +71,31 @@ private: InputReaderContext& mReaderContext; std::shared_ptr<PointerControllerInterface> mPointerController; + ui::Rotation mOrientation = ui::ROTATION_0; + RawAbsoluteAxisInfo mXAxisInfo; + RawAbsoluteAxisInfo mYAxisInfo; + // The current button state according to the gestures library, but converted into MotionEvent // button values (AMOTION_EVENT_BUTTON_...). uint32_t mButtonState = 0; nsecs_t mDownTime = 0; + + MotionClassification mCurrentClassification = MotionClassification::NONE; + // Only used when mCurrentClassification is MULTI_FINGER_SWIPE. + uint32_t mSwipeFingerCount = 0; + static constexpr float INITIAL_PINCH_SEPARATION_PX = 200.0; + // Only used when mCurrentClassification is PINCH. + float mPinchFingerSeparation; + static constexpr size_t MAX_FAKE_FINGERS = 4; + // We never need any PointerProperties other than the finger tool type, so we can just keep a + // const array of them. + const std::array<PointerProperties, MAX_FAKE_FINGERS> mFingerProps = {{ + {.id = 0, .toolType = AMOTION_EVENT_TOOL_TYPE_FINGER}, + {.id = 1, .toolType = AMOTION_EVENT_TOOL_TYPE_FINGER}, + {.id = 2, .toolType = AMOTION_EVENT_TOOL_TYPE_FINGER}, + {.id = 3, .toolType = AMOTION_EVENT_TOOL_TYPE_FINGER}, + }}; + std::array<PointerCoords, MAX_FAKE_FINGERS> mFakeFingerCoords = {}; }; } // namespace android diff --git a/services/inputflinger/tests/FakeEventHub.cpp b/services/inputflinger/tests/FakeEventHub.cpp index 289a7809cc..6ac0bfb541 100644 --- a/services/inputflinger/tests/FakeEventHub.cpp +++ b/services/inputflinger/tests/FakeEventHub.cpp @@ -120,8 +120,8 @@ void FakeEventHub::setKeyCodeState(int32_t deviceId, int32_t keyCode, int32_t st getDevice(deviceId)->keyCodeStates.replaceValueFor(keyCode, state); } -void FakeEventHub::setCountryCode(int32_t deviceId, InputDeviceCountryCode countryCode) { - getDevice(deviceId)->countryCode = countryCode; +void FakeEventHub::setRawLayoutInfo(int32_t deviceId, RawLayoutInfo info) { + getDevice(deviceId)->layoutInfo = info; } void FakeEventHub::setScanCodeState(int32_t deviceId, int32_t scanCode, int32_t state) { @@ -389,9 +389,9 @@ int32_t FakeEventHub::getScanCodeState(int32_t deviceId, int32_t scanCode) const return AKEY_STATE_UNKNOWN; } -InputDeviceCountryCode FakeEventHub::getCountryCode(int32_t deviceId) const { +std::optional<RawLayoutInfo> FakeEventHub::getRawLayoutInfo(int32_t deviceId) const { Device* device = getDevice(deviceId); - return device ? device->countryCode : InputDeviceCountryCode::INVALID; + return device ? device->layoutInfo : std::nullopt; } int32_t FakeEventHub::getKeyCodeState(int32_t deviceId, int32_t keyCode) const { diff --git a/services/inputflinger/tests/FakeEventHub.h b/services/inputflinger/tests/FakeEventHub.h index fb3c8596c7..72f8ac03a5 100644 --- a/services/inputflinger/tests/FakeEventHub.h +++ b/services/inputflinger/tests/FakeEventHub.h @@ -30,10 +30,6 @@ #include <utils/Errors.h> #include <utils/KeyedVector.h> -#include "android/hardware/input/InputDeviceCountryCode.h" - -using android::hardware::input::InputDeviceCountryCode; - namespace android { class FakeEventHub : public EventHubInterface { @@ -67,7 +63,7 @@ class FakeEventHub : public EventHubInterface { BitArray<MSC_MAX> mscBitmask; std::vector<VirtualKeyDefinition> virtualKeys; bool enabled; - InputDeviceCountryCode countryCode; + std::optional<RawLayoutInfo> layoutInfo; status_t enable() { enabled = true; @@ -124,7 +120,7 @@ public: void addRelativeAxis(int32_t deviceId, int32_t axis); void setAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t value); - void setCountryCode(int32_t deviceId, InputDeviceCountryCode countryCode); + void setRawLayoutInfo(int32_t deviceId, RawLayoutInfo info); void setKeyCodeState(int32_t deviceId, int32_t keyCode, int32_t state); void setScanCodeState(int32_t deviceId, int32_t scanCode, int32_t state); @@ -180,7 +176,7 @@ private: std::vector<RawEvent> getEvents(int) override; std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) override; int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const override; - InputDeviceCountryCode getCountryCode(int32_t deviceId) const override; + 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; diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.cpp b/services/inputflinger/tests/FakeInputReaderPolicy.cpp index f7553563cf..bb8a30e5ba 100644 --- a/services/inputflinger/tests/FakeInputReaderPolicy.cpp +++ b/services/inputflinger/tests/FakeInputReaderPolicy.cpp @@ -201,6 +201,10 @@ void FakeInputReaderPolicy::setVelocityControlParams(const VelocityControlParame mConfig.wheelVelocityControlParameters = params; } +void FakeInputReaderPolicy::setStylusButtonMotionEventsEnabled(bool enabled) { + mConfig.stylusButtonMotionEventsEnabled = enabled; +} + void FakeInputReaderPolicy::getReaderConfiguration(InputReaderConfiguration* outConfig) { *outConfig = mConfig; } diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.h b/services/inputflinger/tests/FakeInputReaderPolicy.h index 862ff0b21a..9ec3217d3e 100644 --- a/services/inputflinger/tests/FakeInputReaderPolicy.h +++ b/services/inputflinger/tests/FakeInputReaderPolicy.h @@ -75,6 +75,7 @@ public: float getPointerGestureMovementSpeedRatio(); float getPointerGestureZoomSpeedRatio(); void setVelocityControlParams(const VelocityControlParameters& params); + void setStylusButtonMotionEventsEnabled(bool enabled); private: void getReaderConfiguration(InputReaderConfiguration* outConfig) override; diff --git a/services/inputflinger/tests/GestureConverter_test.cpp b/services/inputflinger/tests/GestureConverter_test.cpp index 91efd1a52d..36a39bb2c6 100644 --- a/services/inputflinger/tests/GestureConverter_test.cpp +++ b/services/inputflinger/tests/GestureConverter_test.cpp @@ -29,6 +29,7 @@ #include "TestInputListener.h" #include "TestInputListenerMatchers.h" #include "include/gestures.h" +#include "ui/Rotation.h" namespace android { @@ -37,8 +38,9 @@ using testing::AllOf; class GestureConverterTest : public testing::Test { protected: static constexpr int32_t DEVICE_ID = END_RESERVED_ID + 1000; + static constexpr int32_t EVENTHUB_ID = 1; static constexpr stime_t ARBITRARY_GESTURE_TIME = 1.2; - static constexpr float POINTER_X = 100; + static constexpr float POINTER_X = 500; static constexpr float POINTER_Y = 200; void SetUp() { @@ -47,6 +49,9 @@ protected: mFakeListener = std::make_unique<TestInputListener>(); mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy, *mFakeListener); + mDevice = newDevice(); + mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_X, -500, 500, 0, 0, 20); + mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_Y, -500, 500, 0, 0, 20); mFakePointerController = std::make_shared<FakePointerController>(); mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1); @@ -54,15 +59,32 @@ protected: mFakePolicy->setPointerController(mFakePointerController); } + std::shared_ptr<InputDevice> newDevice() { + InputDeviceIdentifier identifier; + identifier.name = "device"; + identifier.location = "USB1"; + identifier.bus = 0; + std::shared_ptr<InputDevice> device = + std::make_shared<InputDevice>(mReader->getContext(), DEVICE_ID, /* generation= */ 2, + identifier); + mReader->pushNextDevice(device); + mFakeEventHub->addDevice(EVENTHUB_ID, identifier.name, InputDeviceClass::TOUCHPAD, + identifier.bus); + mReader->loopOnce(); + return device; + } + std::shared_ptr<FakeEventHub> mFakeEventHub; sp<FakeInputReaderPolicy> mFakePolicy; std::unique_ptr<TestInputListener> mFakeListener; std::unique_ptr<InstrumentedInputReader> mReader; + std::shared_ptr<InputDevice> mDevice; std::shared_ptr<FakePointerController> mFakePointerController; }; TEST_F(GestureConverterTest, Move) { - GestureConverter converter(*mReader->getContext(), DEVICE_ID); + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10); std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture); @@ -74,11 +96,30 @@ TEST_F(GestureConverterTest, Move) { WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER), WithButtonState(0), WithPressure(0.0f))); - ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(95, 210)); + ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(POINTER_X - 5, POINTER_Y + 10)); +} + +TEST_F(GestureConverterTest, Move_Rotated) { + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + converter.setOrientation(ui::ROTATION_90); + + Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10); + std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture); + ASSERT_EQ(1u, args.size()); + + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithCoords(POINTER_X + 10, POINTER_Y + 5), WithRelativeMotion(10, 5), + WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER), WithButtonState(0), + WithPressure(0.0f))); + + ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(POINTER_X + 10, POINTER_Y + 5)); } TEST_F(GestureConverterTest, ButtonsChange) { - GestureConverter converter(*mReader->getContext(), DEVICE_ID); + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); // Press left and right buttons at once Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, @@ -143,7 +184,8 @@ TEST_F(GestureConverterTest, ButtonsChange) { } TEST_F(GestureConverterTest, DragWithButton) { - GestureConverter converter(*mReader->getContext(), DEVICE_ID); + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); // Press the button Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, @@ -176,7 +218,7 @@ TEST_F(GestureConverterTest, DragWithButton) { WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER), WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f))); - ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(95, 210)); + ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(POINTER_X - 5, POINTER_Y + 10)); // Release the button Gesture upGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, @@ -197,4 +239,546 @@ TEST_F(GestureConverterTest, DragWithButton) { WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER))); } +TEST_F(GestureConverterTest, Scroll) { + const nsecs_t downTime = 12345; + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + + Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, 10); + std::list<NotifyArgs> args = converter.handleGesture(downTime, READ_TIME, startGesture); + ASSERT_EQ(2u, args.size()); + + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(POINTER_X, POINTER_Y), + WithGestureScrollDistance(0, 0, EPSILON), + WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), + WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER), WithDownTime(downTime))); + args.pop_front(); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithCoords(POINTER_X, POINTER_Y - 10), + WithGestureScrollDistance(0, 10, EPSILON), + WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), + WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER))); + + Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, 5); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture); + ASSERT_EQ(1u, args.size()); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithCoords(POINTER_X, POINTER_Y - 15), + WithGestureScrollDistance(0, 5, EPSILON), + WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), + WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER))); + + Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1, + GESTURES_FLING_START); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture); + ASSERT_EQ(1u, args.size()); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithCoords(POINTER_X, POINTER_Y - 15), + WithGestureScrollDistance(0, 0, EPSILON), + WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), + WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER))); +} + +TEST_F(GestureConverterTest, Scroll_Rotated) { + const nsecs_t downTime = 12345; + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + converter.setOrientation(ui::ROTATION_90); + + Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, 10); + std::list<NotifyArgs> args = converter.handleGesture(downTime, READ_TIME, startGesture); + ASSERT_EQ(2u, args.size()); + + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(POINTER_X, POINTER_Y), + WithGestureScrollDistance(0, 0, EPSILON), + WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), + WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER), WithDownTime(downTime))); + args.pop_front(); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithCoords(POINTER_X - 10, POINTER_Y), + WithGestureScrollDistance(0, 10, EPSILON), + WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), + WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER))); + + Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, 5); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture); + ASSERT_EQ(1u, args.size()); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithCoords(POINTER_X - 15, POINTER_Y), + WithGestureScrollDistance(0, 5, EPSILON), + WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), + WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER))); + + Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1, + GESTURES_FLING_START); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture); + ASSERT_EQ(1u, args.size()); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithCoords(POINTER_X - 15, POINTER_Y), + WithGestureScrollDistance(0, 0, EPSILON), + WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), + WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER))); +} + +TEST_F(GestureConverterTest, Scroll_ClearsClassificationAndOffsetsAfterGesture) { + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + + Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, 10); + std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + + Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, 5); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture); + + Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1, + GESTURES_FLING_START); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture); + + Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture); + ASSERT_EQ(1u, args.size()); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionClassification(MotionClassification::NONE), + WithGestureScrollDistance(0, 0, EPSILON))); +} + +TEST_F(GestureConverterTest, ThreeFingerSwipe_ClearsClassificationAndOffsetsAfterGesture) { + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + + Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dx= */ 0, + /* dy= */ 0); + std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + + Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, liftGesture); + + Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dx= */ -5, + /* dy= */ 10); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture); + ASSERT_EQ(1u, args.size()); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionClassification(MotionClassification::NONE), + WithGestureOffset(0, 0, EPSILON))); +} + +TEST_F(GestureConverterTest, ThreeFingerSwipe_Vertical) { + // The gestures library will "lock" a swipe into the dimension it starts in. For example, if you + // start swiping up and then start moving left or right, it'll return gesture events with only Y + // deltas until you lift your fingers and start swiping again. That's why each of these tests + // only checks movement in one dimension. + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + + Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dx= */ 0, + /* dy= */ 10); + std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + ASSERT_EQ(4u, args.size()); + + // Three fake fingers should be created. We don't actually care where they are, so long as they + // move appropriately. + NotifyMotionArgs arg = std::get<NotifyMotionArgs>(args.front()); + ASSERT_THAT(arg, + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithGestureOffset(0, 0, EPSILON), + WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(1u), WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER))); + PointerCoords finger0Start = arg.pointerCoords[0]; + args.pop_front(); + arg = std::get<NotifyMotionArgs>(args.front()); + ASSERT_THAT(arg, + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), + WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(2u), WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER))); + PointerCoords finger1Start = arg.pointerCoords[1]; + args.pop_front(); + arg = std::get<NotifyMotionArgs>(args.front()); + ASSERT_THAT(arg, + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN | + 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), + WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(3u), WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER))); + PointerCoords finger2Start = arg.pointerCoords[2]; + args.pop_front(); + + arg = std::get<NotifyMotionArgs>(args.front()); + ASSERT_THAT(arg, + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithGestureOffset(0, -0.01, EPSILON), + WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(3u), WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER))); + EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX()); + EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX()); + EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX()); + EXPECT_EQ(arg.pointerCoords[0].getY(), finger0Start.getY() - 10); + EXPECT_EQ(arg.pointerCoords[1].getY(), finger1Start.getY() - 10); + EXPECT_EQ(arg.pointerCoords[2].getY(), finger2Start.getY() - 10); + + Gesture continueGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, + /* dx= */ 0, /* dy= */ 5); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture); + ASSERT_EQ(1u, args.size()); + arg = std::get<NotifyMotionArgs>(args.front()); + ASSERT_THAT(arg, + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithGestureOffset(0, -0.005, EPSILON), + WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(3u), WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER))); + EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX()); + EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX()); + EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX()); + EXPECT_EQ(arg.pointerCoords[0].getY(), finger0Start.getY() - 15); + EXPECT_EQ(arg.pointerCoords[1].getY(), finger1Start.getY() - 15); + EXPECT_EQ(arg.pointerCoords[2].getY(), finger2Start.getY() - 15); + + Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, liftGesture); + ASSERT_EQ(3u, args.size()); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | + 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), + WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(3u), WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER))); + args.pop_front(); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), + WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(2u), WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER))); + args.pop_front(); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithGestureOffset(0, 0, EPSILON), + WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(1u), WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER))); +} + +TEST_F(GestureConverterTest, ThreeFingerSwipe_Rotated) { + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + converter.setOrientation(ui::ROTATION_90); + + Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dx= */ 0, + /* dy= */ 10); + std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + ASSERT_EQ(4u, args.size()); + + // Three fake fingers should be created. We don't actually care where they are, so long as they + // move appropriately. + NotifyMotionArgs arg = std::get<NotifyMotionArgs>(args.front()); + ASSERT_THAT(arg, + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithGestureOffset(0, 0, EPSILON), + WithPointerCount(1u))); + PointerCoords finger0Start = arg.pointerCoords[0]; + args.pop_front(); + arg = std::get<NotifyMotionArgs>(args.front()); + ASSERT_THAT(arg, + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), WithPointerCount(2u))); + PointerCoords finger1Start = arg.pointerCoords[1]; + args.pop_front(); + arg = std::get<NotifyMotionArgs>(args.front()); + ASSERT_THAT(arg, + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN | + 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), WithPointerCount(3u))); + PointerCoords finger2Start = arg.pointerCoords[2]; + args.pop_front(); + + arg = std::get<NotifyMotionArgs>(args.front()); + ASSERT_THAT(arg, + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithGestureOffset(0, -0.01, EPSILON), WithPointerCount(3u))); + EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX() - 10); + EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX() - 10); + EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX() - 10); + EXPECT_EQ(arg.pointerCoords[0].getY(), finger0Start.getY()); + EXPECT_EQ(arg.pointerCoords[1].getY(), finger1Start.getY()); + EXPECT_EQ(arg.pointerCoords[2].getY(), finger2Start.getY()); + + Gesture continueGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, + /* dx= */ 0, /* dy= */ 5); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture); + ASSERT_EQ(1u, args.size()); + arg = std::get<NotifyMotionArgs>(args.front()); + ASSERT_THAT(arg, + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithGestureOffset(0, -0.005, EPSILON), WithPointerCount(3u))); + EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX() - 15); + EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX() - 15); + EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX() - 15); + EXPECT_EQ(arg.pointerCoords[0].getY(), finger0Start.getY()); + EXPECT_EQ(arg.pointerCoords[1].getY(), finger1Start.getY()); + EXPECT_EQ(arg.pointerCoords[2].getY(), finger2Start.getY()); + + Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, liftGesture); + ASSERT_EQ(3u, args.size()); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | + 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), WithPointerCount(3u))); + args.pop_front(); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), WithPointerCount(2u))); + args.pop_front(); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithGestureOffset(0, 0, EPSILON), + WithPointerCount(1u))); +} + +TEST_F(GestureConverterTest, FourFingerSwipe_Horizontal) { + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + + Gesture startGesture(kGestureFourFingerSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, + /* dx= */ 10, /* dy= */ 0); + std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + ASSERT_EQ(5u, args.size()); + + // Four fake fingers should be created. We don't actually care where they are, so long as they + // move appropriately. + NotifyMotionArgs arg = std::get<NotifyMotionArgs>(args.front()); + ASSERT_THAT(arg, + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithGestureOffset(0, 0, EPSILON), + WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(1u), WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER))); + PointerCoords finger0Start = arg.pointerCoords[0]; + args.pop_front(); + arg = std::get<NotifyMotionArgs>(args.front()); + ASSERT_THAT(arg, + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), + WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(2u), WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER))); + PointerCoords finger1Start = arg.pointerCoords[1]; + args.pop_front(); + arg = std::get<NotifyMotionArgs>(args.front()); + ASSERT_THAT(arg, + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN | + 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), + WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(3u), WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER))); + PointerCoords finger2Start = arg.pointerCoords[2]; + args.pop_front(); + arg = std::get<NotifyMotionArgs>(args.front()); + ASSERT_THAT(arg, + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN | + 3 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), + WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(4u), WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER))); + PointerCoords finger3Start = arg.pointerCoords[3]; + args.pop_front(); + + arg = std::get<NotifyMotionArgs>(args.front()); + ASSERT_THAT(arg, + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithGestureOffset(0.01, 0, EPSILON), + WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(4u), WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER))); + EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX() + 10); + EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX() + 10); + EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX() + 10); + EXPECT_EQ(arg.pointerCoords[3].getX(), finger3Start.getX() + 10); + EXPECT_EQ(arg.pointerCoords[0].getY(), finger0Start.getY()); + EXPECT_EQ(arg.pointerCoords[1].getY(), finger1Start.getY()); + EXPECT_EQ(arg.pointerCoords[2].getY(), finger2Start.getY()); + EXPECT_EQ(arg.pointerCoords[3].getY(), finger3Start.getY()); + + Gesture continueGesture(kGestureFourFingerSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, + /* dx= */ 5, /* dy= */ 0); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture); + ASSERT_EQ(1u, args.size()); + arg = std::get<NotifyMotionArgs>(args.front()); + ASSERT_THAT(arg, + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithGestureOffset(0.005, 0, EPSILON), + WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(4u), WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER))); + EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX() + 15); + EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX() + 15); + EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX() + 15); + EXPECT_EQ(arg.pointerCoords[3].getX(), finger3Start.getX() + 15); + EXPECT_EQ(arg.pointerCoords[0].getY(), finger0Start.getY()); + EXPECT_EQ(arg.pointerCoords[1].getY(), finger1Start.getY()); + EXPECT_EQ(arg.pointerCoords[2].getY(), finger2Start.getY()); + EXPECT_EQ(arg.pointerCoords[3].getY(), finger3Start.getY()); + + Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, liftGesture); + ASSERT_EQ(4u, args.size()); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | + 3 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), + WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(4u), WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER))); + args.pop_front(); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | + 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), + WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(3u), WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER))); + args.pop_front(); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), + WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(2u), WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER))); + args.pop_front(); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithGestureOffset(0, 0, EPSILON), + WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(1u), WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER))); +} + +TEST_F(GestureConverterTest, Pinch_Inwards) { + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + + Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1, + GESTURES_ZOOM_START); + std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + ASSERT_EQ(2u, args.size()); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.0f, EPSILON), + WithCoords(POINTER_X - 100, POINTER_Y), WithPointerCount(1u), + WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER))); + args.pop_front(); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.0f, EPSILON), + WithPointerCoords(1, POINTER_X + 100, POINTER_Y), WithPointerCount(2u), + WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER))); + + Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, + /* dz= */ 0.8, GESTURES_ZOOM_UPDATE); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, updateGesture); + ASSERT_EQ(1u, args.size()); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(0.8f, EPSILON), + WithPointerCoords(0, POINTER_X - 80, POINTER_Y), + WithPointerCoords(1, POINTER_X + 80, POINTER_Y), WithPointerCount(2u), + WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER))); + + Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1, + GESTURES_ZOOM_END); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, endGesture); + ASSERT_EQ(2u, args.size()); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(2u), + WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER))); + args.pop_front(); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(1u), + WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER))); +} + +TEST_F(GestureConverterTest, Pinch_Outwards) { + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + + Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1, + GESTURES_ZOOM_START); + std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + ASSERT_EQ(2u, args.size()); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.0f, EPSILON), + WithCoords(POINTER_X - 100, POINTER_Y), WithPointerCount(1u), + WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER))); + args.pop_front(); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.0f, EPSILON), + WithPointerCoords(1, POINTER_X + 100, POINTER_Y), WithPointerCount(2u), + WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER))); + + Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, + /* dz= */ 1.2, GESTURES_ZOOM_UPDATE); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, updateGesture); + ASSERT_EQ(1u, args.size()); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.2f, EPSILON), + WithPointerCoords(0, POINTER_X - 120, POINTER_Y), + WithPointerCoords(1, POINTER_X + 120, POINTER_Y), WithPointerCount(2u), + WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER))); + + Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1, + GESTURES_ZOOM_END); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, endGesture); + ASSERT_EQ(2u, args.size()); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(2u), + WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER))); + args.pop_front(); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(1u), + WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER))); +} + +TEST_F(GestureConverterTest, Pinch_ClearsClassificationAndScaleFactorAfterGesture) { + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + + Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1, + GESTURES_ZOOM_START); + std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + + Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, + /* dz= */ 1.2, GESTURES_ZOOM_UPDATE); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, updateGesture); + + Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1, + GESTURES_ZOOM_END); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, endGesture); + + Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture); + ASSERT_EQ(1u, args.size()); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionClassification(MotionClassification::NONE), + WithGesturePinchScaleFactor(0, EPSILON))); +} + } // namespace android diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 96d27b8f11..95d35f421e 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -48,12 +48,9 @@ #include "InputMapperTest.h" #include "InstrumentedInputReader.h" #include "TestConstants.h" -#include "android/hardware/input/InputDeviceCountryCode.h" #include "input/DisplayViewport.h" #include "input/Input.h" -using android::hardware::input::InputDeviceCountryCode; - namespace android { using namespace ftl::flag_operators; @@ -2023,6 +2020,56 @@ TYPED_TEST(StylusButtonIntegrationTest, StylusButtonsWithinTouchGesture) { WithDeviceId(touchscreenId)))); } +TYPED_TEST(StylusButtonIntegrationTest, StylusButtonMotionEventsDisabled) { + TestFixture::mFakePolicy->setStylusButtonMotionEventsEnabled(false); + TestFixture::mReader->requestRefreshConfiguration( + InputReaderConfiguration::CHANGE_STYLUS_BUTTON_REPORTING); + + const Point centerPoint = TestFixture::mTouchscreen->getCenterPoint(); + const auto touchscreenId = TestFixture::mTouchscreenInfo.getId(); + const auto stylusId = TestFixture::mStylusInfo.getId(); + + // Start a stylus gesture. By the time this event is processed, the configuration change that + // was requested is guaranteed to be completed. + TestFixture::mTouchscreen->sendSlot(FIRST_SLOT); + TestFixture::mTouchscreen->sendTrackingId(FIRST_TRACKING_ID); + TestFixture::mTouchscreen->sendToolType(MT_TOOL_PEN); + TestFixture::mTouchscreen->sendDown(centerPoint); + TestFixture::mTouchscreen->sendSync(); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0), + WithDeviceId(touchscreenId)))); + + // Press and release a stylus button. Each change only generates a MOVE motion event. + // Key events are unaffected. + TestFixture::mStylus->pressKey(BTN_STYLUS); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyKeyWasCalled( + AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN), WithSource(AINPUT_SOURCE_KEYBOARD), + WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY), WithDeviceId(stylusId)))); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0), + WithDeviceId(touchscreenId)))); + + TestFixture::mStylus->releaseKey(BTN_STYLUS); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyKeyWasCalled( + AllOf(WithKeyAction(AKEY_EVENT_ACTION_UP), WithSource(AINPUT_SOURCE_KEYBOARD), + WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY), WithDeviceId(stylusId)))); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0), + WithDeviceId(touchscreenId)))); + + // Finish the stylus gesture. + TestFixture::mTouchscreen->sendTrackingId(INVALID_TRACKING_ID); + TestFixture::mTouchscreen->sendSync(); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0), + WithDeviceId(touchscreenId)))); +} + // --- ExternalStylusIntegrationTest --- // Verify the behavior of an external stylus. An external stylus can report pressure or button @@ -2240,17 +2287,6 @@ TEST_F(InputDeviceTest, ImmutableProperties) { ASSERT_EQ(ftl::Flags<InputDeviceClass>(0), mDevice->getClasses()); } -TEST_F(InputDeviceTest, CountryCodeCorrectlyMapped) { - mFakeEventHub->setCountryCode(EVENTHUB_ID, InputDeviceCountryCode::INTERNATIONAL); - - // Configuration - mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD); - InputReaderConfiguration config; - std::list<NotifyArgs> unused = mDevice->configure(ARBITRARY_TIME, &config, 0); - - ASSERT_EQ(InputDeviceCountryCode::INTERNATIONAL, mDevice->getDeviceInfo().getCountryCode()); -} - TEST_F(InputDeviceTest, WhenDeviceCreated_EnabledIsFalse) { ASSERT_EQ(mDevice->isEnabled(), false); } @@ -3600,6 +3636,20 @@ TEST_F(KeyboardInputMapperTest, Configure_AssignKeyboardLayoutInfo) { deviceInfo.getKeyboardLayoutInfo()->layoutType); } +TEST_F(KeyboardInputMapperTest, LayoutInfoCorrectlyMapped) { + mFakeEventHub->setRawLayoutInfo(EVENTHUB_ID, + RawLayoutInfo{.languageTag = "en", .layoutType = "extended"}); + + // Configuration + addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD, + AINPUT_KEYBOARD_TYPE_ALPHABETIC); + InputReaderConfiguration config; + std::list<NotifyArgs> unused = mDevice->configure(ARBITRARY_TIME, &config, 0); + + ASSERT_EQ("en", mDevice->getDeviceInfo().getKeyboardLayoutInfo()->languageTag); + ASSERT_EQ("extended", mDevice->getDeviceInfo().getKeyboardLayoutInfo()->layoutType); +} + // --- KeyboardInputMapperTest_ExternalDevice --- class KeyboardInputMapperTest_ExternalDevice : public InputMapperTest { @@ -6577,6 +6627,46 @@ TEST_F(SingleTouchInputMapperTest, ButtonIsReleasedOnTouchUp) { WithCoords(toDisplayX(100), toDisplayY(200)), WithButtonState(0)))); } +TEST_F(SingleTouchInputMapperTest, StylusButtonMotionEventsDisabled) { + addConfigurationProperty("touch.deviceType", "touchScreen"); + prepareDisplay(ui::ROTATION_0); + prepareButtons(); + prepareAxes(POSITION); + + mFakePolicy->setStylusButtonMotionEventsEnabled(false); + + SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>(); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled()); + + // Press a stylus button. + processKey(mapper, BTN_STYLUS, 1); + processSync(mapper); + + // Start a touch gesture and ensure that the stylus button is not reported. + processDown(mapper, 100, 200); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithButtonState(0)))); + + // Release and press the stylus button again. + processKey(mapper, BTN_STYLUS, 0); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithButtonState(0)))); + processKey(mapper, BTN_STYLUS, 1); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithButtonState(0)))); + + // Release the touch gesture. + processUp(mapper); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0)))); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); +} + TEST_F(SingleTouchInputMapperTest, WhenDeviceTypeIsSetToTouchNavigation_setsCorrectType) { mFakePolicy->addDeviceTypeAssociation(DEVICE_LOCATION, "touchNavigation"); prepareDisplay(ui::ROTATION_0); diff --git a/services/inputflinger/tests/TestInputListenerMatchers.h b/services/inputflinger/tests/TestInputListenerMatchers.h index e5a4b14f00..b9d96076f1 100644 --- a/services/inputflinger/tests/TestInputListenerMatchers.h +++ b/services/inputflinger/tests/TestInputListenerMatchers.h @@ -16,6 +16,8 @@ #pragma once +#include <cmath> + #include <android/input.h> #include <gmock/gmock.h> #include <gtest/gtest.h> @@ -66,6 +68,11 @@ MATCHER_P(WithKeyCode, keyCode, "KeyEvent with specified key code") { return arg.keyCode == keyCode; } +MATCHER_P(WithPointerCount, count, "MotionEvent with specified number of pointers") { + *result_listener << "expected " << count << " pointer(s), but got " << arg.pointerCount; + return arg.pointerCount == count; +} + MATCHER_P2(WithCoords, x, y, "InputEvent with specified coords") { const auto argX = arg.pointerCoords[0].getX(); const auto argY = arg.pointerCoords[0].getY(); @@ -74,6 +81,14 @@ MATCHER_P2(WithCoords, x, y, "InputEvent with specified coords") { return argX == x && argY == y; } +MATCHER_P3(WithPointerCoords, pointer, x, y, "InputEvent with specified coords for pointer") { + const auto argX = arg.pointerCoords[pointer].getX(); + const auto argY = arg.pointerCoords[pointer].getY(); + *result_listener << "expected pointer " << pointer << " to have coords (" << x << ", " << y + << "), but got (" << argX << ", " << argY << ")"; + return argX == x && argY == y; +} + MATCHER_P2(WithRelativeMotion, x, y, "InputEvent with specified relative motion") { const auto argX = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X); const auto argY = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y); @@ -82,6 +97,39 @@ MATCHER_P2(WithRelativeMotion, x, y, "InputEvent with specified relative motion" 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); + const auto argGestureY = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET); + const double xDiff = fabs(argGestureX - dx); + const double yDiff = fabs(argGestureY - dy); + *result_listener << "expected gesture offset (" << dx << ", " << dy << ") within " << epsilon + << ", but got (" << argGestureX << ", " << argGestureY << ")"; + return xDiff <= epsilon && yDiff <= epsilon; +} + +MATCHER_P3(WithGestureScrollDistance, x, y, epsilon, + "InputEvent with specified touchpad gesture scroll distance") { + const auto argXDistance = + arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_X_DISTANCE); + const auto argYDistance = + arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_Y_DISTANCE); + const double xDiff = fabs(argXDistance - x); + const double yDiff = fabs(argYDistance - y); + *result_listener << "expected gesture offset (" << x << ", " << y << ") within " << epsilon + << ", but got (" << argXDistance << ", " << argYDistance << ")"; + return xDiff <= epsilon && yDiff <= epsilon; +} + +MATCHER_P2(WithGesturePinchScaleFactor, factor, epsilon, + "InputEvent with specified touchpad pinch gesture scale factor") { + const auto argScaleFactor = + arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_PINCH_SCALE_FACTOR); + *result_listener << "expected gesture scale factor " << factor << " within " << epsilon + << " but got " << argScaleFactor; + return fabs(argScaleFactor - factor) <= epsilon; +} + MATCHER_P(WithPressure, pressure, "InputEvent with specified pressure") { const auto argPressure = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE); *result_listener << "expected pressure " << pressure << ", but got " << argPressure; @@ -100,6 +148,13 @@ MATCHER_P(WithFlags, flags, "InputEvent with specified flags") { return arg.flags == flags; } +MATCHER_P(WithMotionClassification, classification, + "InputEvent with specified MotionClassification") { + *result_listener << "expected classification " << motionClassificationToString(classification) + << ", but got " << motionClassificationToString(arg.classification); + return arg.classification == classification; +} + MATCHER_P(WithButtonState, buttons, "InputEvent with specified button state") { *result_listener << "expected button state " << buttons << ", but got " << arg.buttonState; return arg.buttonState == buttons; @@ -116,4 +171,9 @@ MATCHER_P(WithEventTime, eventTime, "InputEvent with specified eventTime") { return arg.eventTime == eventTime; } +MATCHER_P(WithDownTime, downTime, "InputEvent with specified downTime") { + *result_listener << "expected down time " << downTime << ", but got " << arg.downTime; + return arg.downTime == downTime; +} + } // namespace android diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h index a0910ea847..7c9be5c8d4 100644 --- a/services/inputflinger/tests/fuzzers/MapperHelpers.h +++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h @@ -19,9 +19,6 @@ #include <InputMapper.h> #include <InputReader.h> #include <ThreadSafeFuzzedDataProvider.h> -#include "android/hardware/input/InputDeviceCountryCode.h" - -using android::hardware::input::InputDeviceCountryCode; constexpr size_t kValidTypes[] = {EV_SW, EV_SYN, @@ -65,46 +62,6 @@ constexpr size_t kValidCodes[] = { BTN_TASK, }; -constexpr InputDeviceCountryCode kCountryCodes[] = { - InputDeviceCountryCode::INVALID, - InputDeviceCountryCode::NOT_SUPPORTED, - InputDeviceCountryCode::ARABIC, - InputDeviceCountryCode::BELGIAN, - InputDeviceCountryCode::CANADIAN_BILINGUAL, - InputDeviceCountryCode::CANADIAN_FRENCH, - InputDeviceCountryCode::CZECH_REPUBLIC, - InputDeviceCountryCode::DANISH, - InputDeviceCountryCode::FINNISH, - InputDeviceCountryCode::FRENCH, - InputDeviceCountryCode::GERMAN, - InputDeviceCountryCode::GREEK, - InputDeviceCountryCode::HEBREW, - InputDeviceCountryCode::HUNGARY, - InputDeviceCountryCode::INTERNATIONAL, - InputDeviceCountryCode::ITALIAN, - InputDeviceCountryCode::JAPAN, - InputDeviceCountryCode::KOREAN, - InputDeviceCountryCode::LATIN_AMERICAN, - InputDeviceCountryCode::DUTCH, - InputDeviceCountryCode::NORWEGIAN, - InputDeviceCountryCode::PERSIAN, - InputDeviceCountryCode::POLAND, - InputDeviceCountryCode::PORTUGUESE, - InputDeviceCountryCode::RUSSIA, - InputDeviceCountryCode::SLOVAKIA, - InputDeviceCountryCode::SPANISH, - InputDeviceCountryCode::SWEDISH, - InputDeviceCountryCode::SWISS_FRENCH, - InputDeviceCountryCode::SWISS_GERMAN, - InputDeviceCountryCode::SWITZERLAND, - InputDeviceCountryCode::TAIWAN, - InputDeviceCountryCode::TURKISH_Q, - InputDeviceCountryCode::UK, - InputDeviceCountryCode::US, - InputDeviceCountryCode::YUGOSLAVIA, - InputDeviceCountryCode::TURKISH_F, -}; - constexpr size_t kMaxSize = 256; namespace android { @@ -197,8 +154,8 @@ public: void setLightIntensities(int32_t deviceId, int32_t lightId, std::unordered_map<LightColor, int32_t> intensities) override{}; - InputDeviceCountryCode getCountryCode(int32_t deviceId) const override { - return mFdp->PickValueInArray<InputDeviceCountryCode>(kCountryCodes); + std::optional<RawLayoutInfo> getRawLayoutInfo(int32_t deviceId) const override { + return std::nullopt; }; int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const override { diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp index 82d0295a7b..b94b1c0a1a 100644 --- a/services/sensorservice/SensorEventConnection.cpp +++ b/services/sensorservice/SensorEventConnection.cpp @@ -82,7 +82,7 @@ void SensorService::SensorEventConnection::resetWakeLockRefCount() { void SensorService::SensorEventConnection::dump(String8& result) { Mutex::Autolock _l(mConnectionLock); result.appendFormat("\tOperating Mode: "); - if (!mService->isWhiteListedPackage(getPackageName())) { + if (!mService->isAllowListedPackage(getPackageName())) { result.append("RESTRICTED\n"); } else if (mDataInjectionMode) { result.append("DATA_INJECTION\n"); @@ -124,7 +124,7 @@ void SensorService::SensorEventConnection::dump(util::ProtoOutputStream* proto) using namespace service::SensorEventConnectionProto; Mutex::Autolock _l(mConnectionLock); - if (!mService->isWhiteListedPackage(getPackageName())) { + if (!mService->isAllowListedPackage(getPackageName())) { proto->write(OPERATING_MODE, OP_MODE_RESTRICTED); } else if (mDataInjectionMode) { proto->write(OPERATING_MODE, OP_MODE_DATA_INJECTION); @@ -850,6 +850,11 @@ int SensorService::SensorEventConnection::handleEvent(int fd, int events, void* // Unregister call backs. return 0; } + if (!mService->isAllowListedPackage(mPackageName)) { + ALOGE("App not allowed to inject data, dropping event" + "package=%s uid=%d", mPackageName.string(), mUid); + return 0; + } sensors_event_t sensor_event; memcpy(&sensor_event, buf, sizeof(sensors_event_t)); std::shared_ptr<SensorInterface> si = diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp index 6504b79527..5c98614f1a 100644 --- a/services/sensorservice/SensorService.cpp +++ b/services/sensorservice/SensorService.cpp @@ -52,6 +52,7 @@ #include "SensorEventConnection.h" #include "SensorRecord.h" #include "SensorRegistrationInfo.h" +#include "SensorServiceUtils.h" #include <inttypes.h> #include <math.h> @@ -64,6 +65,7 @@ #include <ctime> #include <future> +#include <string> #include <private/android_filesystem_config.h> @@ -547,55 +549,22 @@ status_t SensorService::dump(int fd, const Vector<String16>& args) { if (args.size() > 2) { return INVALID_OPERATION; } - ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock); - SensorDevice& dev(SensorDevice::getInstance()); - if (args.size() == 2 && args[0] == String16("restrict")) { - // If already in restricted mode. Ignore. - if (mCurrentOperatingMode == RESTRICTED) { - return status_t(NO_ERROR); - } - // If in any mode other than normal, ignore. - if (mCurrentOperatingMode != NORMAL) { - return INVALID_OPERATION; + if (args.size() > 0) { + Mode targetOperatingMode = NORMAL; + std::string inputStringMode = String8(args[0]).string(); + if (getTargetOperatingMode(inputStringMode, &targetOperatingMode)) { + status_t error = changeOperatingMode(args, targetOperatingMode); + // Dump the latest state only if no error was encountered. + if (error != NO_ERROR) { + return error; + } } + } - mCurrentOperatingMode = RESTRICTED; - // temporarily stop all sensor direct report and disable sensors - disableAllSensorsLocked(&connLock); - mWhiteListedPackage.setTo(String8(args[1])); - return status_t(NO_ERROR); - } else if (args.size() == 1 && args[0] == String16("enable")) { - // If currently in restricted mode, reset back to NORMAL mode else ignore. - if (mCurrentOperatingMode == RESTRICTED) { - mCurrentOperatingMode = NORMAL; - // enable sensors and recover all sensor direct report - enableAllSensorsLocked(&connLock); - } - if (mCurrentOperatingMode == DATA_INJECTION) { - resetToNormalModeLocked(); - } - mWhiteListedPackage.clear(); - return status_t(NO_ERROR); - } else if (args.size() == 2 && args[0] == String16("data_injection")) { - if (mCurrentOperatingMode == NORMAL) { - dev.disableAllSensors(); - status_t err = dev.setMode(DATA_INJECTION); - if (err == NO_ERROR) { - mCurrentOperatingMode = DATA_INJECTION; - } else { - // Re-enable sensors. - dev.enableAllSensors(); - } - mWhiteListedPackage.setTo(String8(args[1])); - return NO_ERROR; - } else if (mCurrentOperatingMode == DATA_INJECTION) { - // Already in DATA_INJECTION mode. Treat this as a no_op. - return NO_ERROR; - } else { - // Transition to data injection mode supported only from NORMAL mode. - return INVALID_OPERATION; - } - } else if (args.size() == 1 && args[0] == String16("--proto")) { + ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock); + // Run the following logic if a transition isn't requested above based on the input + // argument parsing. + if (args.size() == 1 && args[0] == String16("--proto")) { return dumpProtoLocked(fd, &connLock); } else if (!mSensors.hasAnySensor()) { result.append("No Sensors on the device\n"); @@ -654,10 +623,18 @@ status_t SensorService::dump(int fd, const Vector<String16>& args) { result.appendFormat(" NORMAL\n"); break; case RESTRICTED: - result.appendFormat(" RESTRICTED : %s\n", mWhiteListedPackage.string()); + result.appendFormat(" RESTRICTED : %s\n", mAllowListedPackage.string()); break; case DATA_INJECTION: - result.appendFormat(" DATA_INJECTION : %s\n", mWhiteListedPackage.string()); + result.appendFormat(" DATA_INJECTION : %s\n", mAllowListedPackage.string()); + break; + case REPLAY_DATA_INJECTION: + result.appendFormat(" REPLAY_DATA_INJECTION : %s\n", + mAllowListedPackage.string()); + break; + default: + result.appendFormat(" UNKNOWN\n"); + break; } result.appendFormat("Sensor Privacy: %s\n", mSensorPrivacyPolicy->isSensorPrivacyEnabled() ? "enabled" : "disabled"); @@ -775,11 +752,11 @@ status_t SensorService::dumpProtoLocked(int fd, ConnectionSafeAutolock* connLock break; case RESTRICTED: proto.write(OPERATING_MODE, OP_MODE_RESTRICTED); - proto.write(WHITELISTED_PACKAGE, std::string(mWhiteListedPackage.string())); + proto.write(WHITELISTED_PACKAGE, std::string(mAllowListedPackage.string())); break; case DATA_INJECTION: proto.write(OPERATING_MODE, OP_MODE_DATA_INJECTION); - proto.write(WHITELISTED_PACKAGE, std::string(mWhiteListedPackage.string())); + proto.write(WHITELISTED_PACKAGE, std::string(mAllowListedPackage.string())); break; default: proto.write(OPERATING_MODE, OP_MODE_UNKNOWN); @@ -1498,8 +1475,10 @@ Vector<Sensor> SensorService::getRuntimeSensorList(const String16& opPackageName sp<ISensorEventConnection> SensorService::createSensorEventConnection(const String8& packageName, int requestedMode, const String16& opPackageName, const String16& attributionTag) { - // Only 2 modes supported for a SensorEventConnection ... NORMAL and DATA_INJECTION. - if (requestedMode != NORMAL && requestedMode != DATA_INJECTION) { + // Only 3 modes supported for a SensorEventConnection ... NORMAL, DATA_INJECTION and + // REPLAY_DATA_INJECTION. + if (requestedMode != NORMAL && requestedMode != DATA_INJECTION && + requestedMode != REPLAY_DATA_INJECTION) { return nullptr; } resetTargetSdkVersionCache(opPackageName); @@ -1509,7 +1488,7 @@ sp<ISensorEventConnection> SensorService::createSensorEventConnection(const Stri // operating in DI mode. if (requestedMode == DATA_INJECTION) { if (mCurrentOperatingMode != DATA_INJECTION) return nullptr; - if (!isWhiteListedPackage(packageName)) return nullptr; + if (!isAllowListedPackage(packageName)) return nullptr; } uid_t uid = IPCThreadState::self()->getCallingUid(); @@ -1520,8 +1499,9 @@ sp<ISensorEventConnection> SensorService::createSensorEventConnection(const Stri String16 connOpPackageName = (opPackageName == String16("")) ? String16(connPackageName) : opPackageName; sp<SensorEventConnection> result(new SensorEventConnection(this, uid, connPackageName, - requestedMode == DATA_INJECTION, connOpPackageName, attributionTag)); - if (requestedMode == DATA_INJECTION) { + requestedMode == DATA_INJECTION || requestedMode == REPLAY_DATA_INJECTION, + connOpPackageName, attributionTag)); + if (requestedMode == DATA_INJECTION || requestedMode == REPLAY_DATA_INJECTION) { mConnectionHolder.addEventConnectionIfNotPresent(result); // Add the associated file descriptor to the Looper for polling whenever there is data to // be injected. @@ -1880,8 +1860,8 @@ status_t SensorService::enable(const sp<SensorEventConnection>& connection, } ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock); - if (mCurrentOperatingMode != NORMAL - && !isWhiteListedPackage(connection->getPackageName())) { + if (mCurrentOperatingMode != NORMAL && mCurrentOperatingMode != REPLAY_DATA_INJECTION && + !isAllowListedPackage(connection->getPackageName())) { return INVALID_OPERATION; } @@ -2229,6 +2209,95 @@ void SensorService::resetTargetSdkVersionCache(const String16& opPackageName) { } } +bool SensorService::getTargetOperatingMode(const std::string &inputString, Mode *targetModeOut) { + if (inputString == std::string("restrict")) { + *targetModeOut = RESTRICTED; + return true; + } + if (inputString == std::string("enable")) { + *targetModeOut = NORMAL; + return true; + } + if (inputString == std::string("data_injection")) { + *targetModeOut = DATA_INJECTION; + return true; + } + if (inputString == std::string("replay_data_injection")) { + *targetModeOut = REPLAY_DATA_INJECTION; + return true; + } + return false; +} + +status_t SensorService::changeOperatingMode(const Vector<String16>& args, + Mode targetOperatingMode) { + ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock); + SensorDevice& dev(SensorDevice::getInstance()); + if (mCurrentOperatingMode == targetOperatingMode) { + return NO_ERROR; + } + if (targetOperatingMode != NORMAL && args.size() < 2) { + return INVALID_OPERATION; + } + switch (targetOperatingMode) { + case NORMAL: + // If currently in restricted mode, reset back to NORMAL mode else ignore. + if (mCurrentOperatingMode == RESTRICTED) { + mCurrentOperatingMode = NORMAL; + // enable sensors and recover all sensor direct report + enableAllSensorsLocked(&connLock); + } + if (mCurrentOperatingMode == REPLAY_DATA_INJECTION) { + dev.disableAllSensors(); + } + if (mCurrentOperatingMode == DATA_INJECTION || + mCurrentOperatingMode == REPLAY_DATA_INJECTION) { + resetToNormalModeLocked(); + } + mAllowListedPackage.clear(); + return status_t(NO_ERROR); + case RESTRICTED: + // If in any mode other than normal, ignore. + if (mCurrentOperatingMode != NORMAL) { + return INVALID_OPERATION; + } + + mCurrentOperatingMode = RESTRICTED; + // temporarily stop all sensor direct report and disable sensors + disableAllSensorsLocked(&connLock); + mAllowListedPackage.setTo(String8(args[1])); + return status_t(NO_ERROR); + case REPLAY_DATA_INJECTION: + if (SensorServiceUtil::isUserBuild()) { + return INVALID_OPERATION; + } + FALLTHROUGH_INTENDED; + case DATA_INJECTION: + if (mCurrentOperatingMode == NORMAL) { + dev.disableAllSensors(); + // Always use DATA_INJECTION here since this value goes to the HAL and the HAL + // doesn't have an understanding of replay vs. normal data injection. + status_t err = dev.setMode(DATA_INJECTION); + if (err == NO_ERROR) { + mCurrentOperatingMode = targetOperatingMode; + } + if (err != NO_ERROR || targetOperatingMode == REPLAY_DATA_INJECTION) { + // Re-enable sensors. + dev.enableAllSensors(); + } + mAllowListedPackage.setTo(String8(args[1])); + return NO_ERROR; + } else { + // Transition to data injection mode supported only from NORMAL mode. + return INVALID_OPERATION; + } + break; + default: + break; + } + return NO_ERROR; +} + void SensorService::checkWakeLockState() { ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock); checkWakeLockStateLocked(&connLock); @@ -2258,14 +2327,14 @@ void SensorService::sendEventsFromCache(const sp<SensorEventConnection>& connect } } -bool SensorService::isWhiteListedPackage(const String8& packageName) { - return (packageName.contains(mWhiteListedPackage.string())); +bool SensorService::isAllowListedPackage(const String8& packageName) { + return (packageName.contains(mAllowListedPackage.string())); } bool SensorService::isOperationRestrictedLocked(const String16& opPackageName) { if (mCurrentOperatingMode == RESTRICTED) { String8 package(opPackageName); - return !isWhiteListedPackage(package); + return !isAllowListedPackage(package); } return false; } diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h index 78df501699..0798279b6b 100644 --- a/services/sensorservice/SensorService.h +++ b/services/sensorservice/SensorService.h @@ -102,8 +102,7 @@ public: // Step Detector etc. Typically in this mode, there will be a client (a // SensorEventConnection) which will be injecting sensor data into the HAL. Normal apps can // unregister and register for any sensor that supports injection. Registering to sensors - // that do not support injection will give an error. TODO: Allow exactly one - // client to inject sensor data at a time. + // that do not support injection will give an error. DATA_INJECTION = 1, // This mode is used only for testing sensors. Each sensor can be tested in isolation with // the required sampling_rate and maxReportLatency parameters without having to think about @@ -116,10 +115,14 @@ public: // corresponding parameters if the application hasn't unregistered for sensors in the mean // time. NOTE: Non allowlisted app whose sensors were previously deactivated may still // receive events if a allowlisted app requests data from the same sensor. - RESTRICTED = 2 + RESTRICTED = 2, + // Mostly equivalent to DATA_INJECTION with the difference being that the injected data is + // delivered to all requesting apps rather than just the package allowed to inject data. + // This mode is only allowed to be used on development builds. + REPLAY_DATA_INJECTION = 3, // State Transitions supported. - // RESTRICTED <--- NORMAL ---> DATA_INJECTION + // RESTRICTED <--- NORMAL ---> DATA_INJECTION/REPLAY_DATA_INJECTION // ---> <--- // Shell commands to switch modes in SensorService. @@ -395,6 +398,9 @@ private: static bool hasPermissionForSensor(const Sensor& sensor); static int getTargetSdkVersion(const String16& opPackageName); static void resetTargetSdkVersionCache(const String16& opPackageName); + // Checks if the provided target operating mode is valid and returns the enum if it is. + static bool getTargetOperatingMode(const std::string &inputString, Mode *targetModeOut); + status_t changeOperatingMode(const Vector<String16>& args, Mode targetOperatingMode); // SensorService acquires a partial wakelock for delivering events from wake up sensors. This // method checks whether all the events from these wake up sensors have been delivered to the // corresponding applications, if yes the wakelock is released. @@ -420,7 +426,7 @@ private: // If SensorService is operating in RESTRICTED mode, only select whitelisted packages are // allowed to register for or call flush on sensors. Typically only cts test packages are // allowed. - bool isWhiteListedPackage(const String8& packageName); + bool isAllowListedPackage(const String8& packageName); // Returns true if a connection with the specified opPackageName has no access to sensors // in the RESTRICTED mode (i.e. the service is in RESTRICTED mode, and the package is not @@ -519,7 +525,7 @@ private: // applications with this packageName are allowed to activate/deactivate or call flush on // sensors. To run CTS this is can be set to ".cts." and only CTS tests will get access to // sensors. - String8 mWhiteListedPackage; + String8 mAllowListedPackage; int mNextSensorRegIndex; Vector<SensorRegistrationInfo> mLastNSensorRegistrations; diff --git a/services/sensorservice/SensorServiceUtils.cpp b/services/sensorservice/SensorServiceUtils.cpp index 6bad962521..46b4b5bff6 100644 --- a/services/sensorservice/SensorServiceUtils.cpp +++ b/services/sensorservice/SensorServiceUtils.cpp @@ -16,6 +16,7 @@ #include "SensorServiceUtils.h" +#include <android-base/properties.h> #include <hardware/sensors.h> namespace android { @@ -76,5 +77,10 @@ size_t eventSizeBySensorType(int type) { } } +bool isUserBuild() { + std::string buildType = android::base::GetProperty("ro.build.type", "user"); + return "user" == buildType; +} + } // namespace SensorServiceUtil } // namespace android; diff --git a/services/sensorservice/SensorServiceUtils.h b/services/sensorservice/SensorServiceUtils.h index 49457cf287..a6e0d6b401 100644 --- a/services/sensorservice/SensorServiceUtils.h +++ b/services/sensorservice/SensorServiceUtils.h @@ -38,6 +38,11 @@ public: size_t eventSizeBySensorType(int type); +/** + * Returns true if on a user (production) build. + */ +bool isUserBuild(); + } // namespace SensorServiceUtil } // namespace android; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h index 2a5bfaea6c..b86782f417 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h @@ -137,6 +137,7 @@ struct OutputLayerCompositionState { HwcBufferCache hwcBufferCache; // The previously-active buffer for this layer. + uint64_t activeBufferId; uint32_t activeBufferSlot; // Set to true when overridden info has been sent to HW composer diff --git a/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp b/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp index 34ed214079..f0105b2782 100644 --- a/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp +++ b/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp @@ -28,11 +28,6 @@ HwcBufferCache::HwcBufferCache() { } HwcSlotAndBuffer HwcBufferCache::getHwcSlotAndBuffer(const sp<GraphicBuffer>& buffer) { - // TODO(b/261930578): This is for unit tests which don't mock GraphicBuffers but instead send - // in nullptrs. - if (buffer == nullptr) { - return {0, nullptr}; - } if (auto i = mCacheByBufferId.find(buffer->getId()); i != mCacheByBufferId.end()) { Cache& cache = i->second; // mark this cache slot as more recently used so it won't get evicted anytime soon diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp index d513731fa3..3ec681668b 100644 --- a/services/surfaceflinger/CompositionEngine/src/Output.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp @@ -118,8 +118,9 @@ const std::string& Output::getName() const { void Output::setName(const std::string& name) { mName = name; auto displayIdOpt = getDisplayId(); - mNamePlusId = base::StringPrintf("%s (%s)", mName.c_str(), - displayIdOpt ? to_string(*displayIdOpt).c_str() : "NA"); + mNamePlusId = displayIdOpt ? base::StringPrintf("%s (%s)", mName.c_str(), + to_string(*displayIdOpt).c_str()) + : mName; } void Output::setCompositionEnabled(bool enabled) { @@ -1224,8 +1225,9 @@ std::optional<base::unique_fd> Output::composeSurfaces( ALOGV(__FUNCTION__); const auto& outputState = getState(); - const TracedOrdinal<bool> hasClientComposition = {"hasClientComposition", - outputState.usesClientComposition}; + const TracedOrdinal<bool> hasClientComposition = { + base::StringPrintf("hasClientComposition %s", mNamePlusId.c_str()), + outputState.usesClientComposition}; if (!hasClientComposition) { setExpensiveRenderingExpected(false); return base::unique_fd(); diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp index 766d7ea4c3..60a2c83194 100644 --- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp +++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp @@ -617,13 +617,24 @@ void OutputLayer::uncacheBuffers(const std::vector<uint64_t>& bufferIdsToUncache return; } + // Uncache the active buffer last so that it's the first buffer to be purged from the cache + // next time a buffer is sent to this layer. + bool uncacheActiveBuffer = false; + std::vector<uint32_t> slotsToClear; for (uint64_t bufferId : bufferIdsToUncache) { - uint32_t slot = state.hwc->hwcBufferCache.uncache(bufferId); - if (slot != UINT32_MAX) { - slotsToClear.push_back(slot); + if (bufferId == state.hwc->activeBufferId) { + uncacheActiveBuffer = true; + } else { + uint32_t slot = state.hwc->hwcBufferCache.uncache(bufferId); + if (slot != UINT32_MAX) { + slotsToClear.push_back(slot); + } } } + if (uncacheActiveBuffer) { + slotsToClear.push_back(state.hwc->hwcBufferCache.uncache(state.hwc->activeBufferId)); + } hal::Error error = state.hwc->hwcLayer->setBufferSlotsToClear(slotsToClear, state.hwc->activeBufferSlot); @@ -655,17 +666,20 @@ void OutputLayer::writeBufferStateToHWC(HWC2::Layer* hwcLayer, hwcSlotAndBuffer = state.hwc->hwcBufferCache.getOverrideHwcSlotAndBuffer( state.overrideInfo.buffer->getBuffer()); hwcFence = state.overrideInfo.acquireFence; + // Keep track of the active buffer ID so when it's discarded we uncache it last so its + // slot will be used first, allowing the memory to be freed as soon as possible. + state.hwc->activeBufferId = state.overrideInfo.buffer->getBuffer()->getId(); } else { hwcSlotAndBuffer = state.hwc->hwcBufferCache.getHwcSlotAndBuffer(outputIndependentState.buffer); hwcFence = outputIndependentState.acquireFence; + // Keep track of the active buffer ID so when it's discarded we uncache it last so its + // slot will be used first, allowing the memory to be freed as soon as possible. + state.hwc->activeBufferId = outputIndependentState.buffer->getId(); } - // Keep track of the active buffer slot, so we can restore it after clearing other buffer // slots. - if (hwcSlotAndBuffer.buffer) { - state.hwc->activeBufferSlot = hwcSlotAndBuffer.slot; - } + state.hwc->activeBufferSlot = hwcSlotAndBuffer.slot; } if (auto error = hwcLayer->setBuffer(hwcSlotAndBuffer.slot, hwcSlotAndBuffer.buffer, hwcFence); diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp index 0edc22681f..9ad2edb983 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp @@ -1318,6 +1318,7 @@ TEST_F(OutputLayerWriteStateToHWCTest, setBlockingRegion) { struct OutputLayerUncacheBufferTest : public OutputLayerTest { static const sp<GraphicBuffer> kBuffer1; static const sp<GraphicBuffer> kBuffer2; + static const sp<GraphicBuffer> kBuffer3; static const sp<Fence> kFence; OutputLayerUncacheBufferTest() { @@ -1343,6 +1344,10 @@ const sp<GraphicBuffer> OutputLayerUncacheBufferTest::kBuffer2 = sp<GraphicBuffer>::make(2, 3, PIXEL_FORMAT_RGBA_8888, AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN | AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN); +const sp<GraphicBuffer> OutputLayerUncacheBufferTest::kBuffer3 = + sp<GraphicBuffer>::make(4, 5, PIXEL_FORMAT_RGBA_8888, + AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN | + AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN); const sp<Fence> OutputLayerUncacheBufferTest::kFence = sp<Fence>::make(); TEST_F(OutputLayerUncacheBufferTest, canUncacheAndReuseSlot) { @@ -1360,26 +1365,38 @@ TEST_F(OutputLayerUncacheBufferTest, canUncacheAndReuseSlot) { /*zIsOverridden*/ false, /*isPeekingThrough*/ false); Mock::VerifyAndClearExpectations(&mHwcLayer); - // buffer slots are cleared in HWC - std::vector<uint32_t> slotsToClear = {0, 1}; - EXPECT_CALL(mHwcLayer, - setBufferSlotsToClear(/*slotsToClear*/ slotsToClear, /*activeBufferSlot*/ 1)); - mOutputLayer.uncacheBuffers({kBuffer1->getId(), kBuffer2->getId()}); + // Buffer3 is stored in slot 2 + mLayerFEState.buffer = kBuffer3; + EXPECT_CALL(mHwcLayer, setBuffer(/*slot*/ 2, kBuffer3, kFence)); + mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0, + /*zIsOverridden*/ false, /*isPeekingThrough*/ false); Mock::VerifyAndClearExpectations(&mHwcLayer); - // rather than allocating a new slot, the active buffer slot (slot 1) is reused first to free - // the memory as soon as possible - mLayerFEState.buffer = kBuffer1; - EXPECT_CALL(mHwcLayer, setBuffer(/*slot*/ 1, kBuffer1, kFence)); + // Buffer2 becomes the active buffer again (with a nullptr) and reuses slot 1 + mLayerFEState.buffer = kBuffer2; + sp<GraphicBuffer> nullBuffer = nullptr; + EXPECT_CALL(mHwcLayer, setBuffer(/*slot*/ 1, nullBuffer, kFence)); mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0, /*zIsOverridden*/ false, /*isPeekingThrough*/ false); Mock::VerifyAndClearExpectations(&mHwcLayer); - // rather than allocating a new slot, slot 0 is reused - mLayerFEState.buffer = kBuffer2; - EXPECT_CALL(mHwcLayer, setBuffer(/*slot*/ 0, kBuffer2, kFence)); + // Buffer slots are cleared + std::vector<uint32_t> slotsToClear = {0, 2, 1}; // order doesn't matter + EXPECT_CALL(mHwcLayer, setBufferSlotsToClear(slotsToClear, /*activeBufferSlot*/ 1)); + // Uncache the active buffer in between other buffers to exercise correct algorithmic behavior. + mOutputLayer.uncacheBuffers({kBuffer1->getId(), kBuffer2->getId(), kBuffer3->getId()}); + Mock::VerifyAndClearExpectations(&mHwcLayer); + + // Buffer1 becomes active again, and rather than allocating a new slot, or re-using slot 0, + // the active buffer slot (slot 1 for Buffer2) is reused first, which allows HWC to free the + // memory for the active buffer. Note: slot 1 is different from the first and last buffer slot + // requested to be cleared in slotsToClear (slot 1), above, indicating that the algorithm + // correctly identifies the active buffer as the buffer in slot 1, despite ping-ponging. + mLayerFEState.buffer = kBuffer1; + EXPECT_CALL(mHwcLayer, setBuffer(/*slot*/ 1, kBuffer1, kFence)); mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0, /*zIsOverridden*/ false, /*isPeekingThrough*/ false); + Mock::VerifyAndClearExpectations(&mHwcLayer); } /* diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp index 96ae77fbff..098207709a 100644 --- a/services/surfaceflinger/DisplayDevice.cpp +++ b/services/surfaceflinger/DisplayDevice.cpp @@ -171,7 +171,7 @@ auto DisplayDevice::getFrontEndInfo() const -> frontend::DisplayInfo { void DisplayDevice::setPowerMode(hal::PowerMode mode) { if (mode == hal::PowerMode::OFF || mode == hal::PowerMode::ON) { - if (mStagedBrightness && mBrightness != *mStagedBrightness) { + if (mStagedBrightness && mBrightness != mStagedBrightness) { getCompositionDisplay()->setNextBrightness(*mStagedBrightness); mBrightness = *mStagedBrightness; } @@ -298,7 +298,7 @@ void DisplayDevice::stageBrightness(float brightness) { } void DisplayDevice::persistBrightness(bool needsComposite) { - if (mStagedBrightness && mBrightness != *mStagedBrightness) { + if (mStagedBrightness && mBrightness != mStagedBrightness) { if (needsComposite) { getCompositionDisplay()->setNextBrightness(*mStagedBrightness); } @@ -410,7 +410,8 @@ HdrCapabilities DisplayDevice::getHdrCapabilities() const { capabilities.getDesiredMinLuminance()); } -void DisplayDevice::enableRefreshRateOverlay(bool enable, bool showSpinner, bool showRenderRate) { +void DisplayDevice::enableRefreshRateOverlay(bool enable, bool showSpinner, bool showRenderRate, + bool showInMiddle) { if (!enable) { mRefreshRateOverlay.reset(); return; @@ -425,6 +426,10 @@ void DisplayDevice::enableRefreshRateOverlay(bool enable, bool showSpinner, bool features |= RefreshRateOverlay::Features::RenderRate; } + if (showInMiddle) { + features |= RefreshRateOverlay::Features::ShowInMiddle; + } + const auto fpsRange = mRefreshRateSelector->getSupportedRefreshRateRange(); mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(fpsRange, features); mRefreshRateOverlay->setLayerStack(getLayerStack()); diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h index d757673176..370bd66b9e 100644 --- a/services/surfaceflinger/DisplayDevice.h +++ b/services/surfaceflinger/DisplayDevice.h @@ -237,8 +237,8 @@ public: } // Enables an overlay to be displayed with the current refresh rate - void enableRefreshRateOverlay(bool enable, bool showSpinner, bool showRenderRate) - REQUIRES(kMainThreadContext); + void enableRefreshRateOverlay(bool enable, bool showSpinner, bool showRenderRate, + bool showInMiddle) REQUIRES(kMainThreadContext); bool isRefreshRateOverlayEnabled() const { return mRefreshRateOverlay != nullptr; } bool onKernelTimerChanged(std::optional<DisplayModeId>, bool timerExpired); void animateRefreshRateOverlay(); @@ -272,7 +272,7 @@ private: std::optional<hardware::graphics::composer::hal::PowerMode> mPowerMode; std::optional<float> mStagedBrightness; - float mBrightness = -1.f; + std::optional<float> mBrightness; // TODO(b/182939859): Remove special cases for primary display. const bool mIsPrimary; diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp index 6738f00d86..aaf2523338 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.cpp +++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp @@ -842,25 +842,24 @@ Error Layer::setPerFrameMetadata(const int32_t supportedPerFrameMetadata, mHdrMetadata.cta8613.maxFrameAverageLightLevel}}); } - Error error = static_cast<Error>( - mComposer.setLayerPerFrameMetadata(mDisplay->getId(), mId, perFrameMetadatas)); + const Error error = static_cast<Error>( + mComposer.setLayerPerFrameMetadata(mDisplay->getId(), mId, perFrameMetadatas)); + if (error != Error::NONE) { + return error; + } + std::vector<Hwc2::PerFrameMetadataBlob> perFrameMetadataBlobs; if (validTypes & HdrMetadata::HDR10PLUS) { if (CC_UNLIKELY(mHdrMetadata.hdr10plus.size() == 0)) { return Error::BAD_PARAMETER; } - std::vector<Hwc2::PerFrameMetadataBlob> perFrameMetadataBlobs; perFrameMetadataBlobs.push_back( {Hwc2::PerFrameMetadataKey::HDR10_PLUS_SEI, mHdrMetadata.hdr10plus}); - Error setMetadataBlobsError = - static_cast<Error>(mComposer.setLayerPerFrameMetadataBlobs(mDisplay->getId(), mId, - perFrameMetadataBlobs)); - if (error == Error::NONE) { - return setMetadataBlobsError; - } } - return error; + + return static_cast<Error>( + mComposer.setLayerPerFrameMetadataBlobs(mDisplay->getId(), mId, perFrameMetadataBlobs)); } Error Layer::setDisplayFrame(const Rect& frame) diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp index cd1ba70d84..27a099cd1f 100644 --- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp +++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp @@ -1175,7 +1175,7 @@ float FrameTimeline::computeFps(const std::unordered_set<int32_t>& layerIds) { std::optional<size_t> FrameTimeline::getFirstSignalFenceIndex() const { for (size_t i = 0; i < mPendingPresentFences.size(); i++) { const auto& [fence, _] = mPendingPresentFences[i]; - if (fence && fence->isValid() && fence->getSignalTime() != Fence::SIGNAL_TIME_PENDING) { + if (fence && fence->getSignalTime() != Fence::SIGNAL_TIME_PENDING) { return i; } } diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp index 7aa7e17b3b..0ade4679a3 100644 --- a/services/surfaceflinger/RefreshRateOverlay.cpp +++ b/services/surfaceflinger/RefreshRateOverlay.cpp @@ -320,7 +320,12 @@ void RefreshRateOverlay::setViewport(ui::Size viewport) { const auto width = std::min({kMaxWidth, viewport.width, viewport.height}); const auto height = 2 * width; Rect frame((5 * width) >> 4, height >> 5); - frame.offsetBy(width >> 5, height >> 4); + + if (!mFeatures.test(Features::ShowInMiddle)) { + frame.offsetBy(width >> 5, height >> 4); + } else { + frame.offsetBy(width >> 1, height >> 4); + } createTransaction(mSurfaceControl->get()) .setMatrix(mSurfaceControl->get(), frame.getWidth() / static_cast<float>(kBufferWidth), diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h index d6f828f7f1..b68a88c928 100644 --- a/services/surfaceflinger/RefreshRateOverlay.h +++ b/services/surfaceflinger/RefreshRateOverlay.h @@ -54,6 +54,7 @@ public: enum class Features { Spinner = 1 << 0, RenderRate = 1 << 1, + ShowInMiddle = 1 << 2, }; RefreshRateOverlay(FpsRange, ftl::Flags<Features>); diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp index 7c9cedfab8..55fa402fa6 100644 --- a/services/surfaceflinger/Scheduler/LayerHistory.cpp +++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp @@ -22,9 +22,9 @@ #include <android-base/stringprintf.h> #include <cutils/properties.h> +#include <gui/TraceUtils.h> #include <utils/Log.h> #include <utils/Timers.h> -#include <utils/Trace.h> #include <algorithm> #include <cmath> @@ -165,6 +165,7 @@ void LayerHistory::setDefaultFrameRateCompatibility(Layer* layer, bool contentDe } auto LayerHistory::summarize(const RefreshRateSelector& selector, nsecs_t now) -> Summary { + ATRACE_CALL(); Summary summary; std::lock_guard lock(mLock); @@ -178,6 +179,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()); const auto vote = info->getRefreshRateVote(selector, now); // Skip NoVote layer as those don't have any requirements if (vote.type == LayerVoteType::NoVote) { @@ -192,6 +194,8 @@ auto LayerHistory::summarize(const RefreshRateSelector& selector, nsecs_t now) - const float layerArea = transformed.getWidth() * transformed.getHeight(); float weight = mDisplayArea ? layerArea / mDisplayArea : 0.0f; + ATRACE_FORMAT_INSTANT("%s %s (%d%)", ftl::enum_string(vote.type).c_str(), + to_string(vote.fps).c_str(), weight * 100); summary.push_back({info->getName(), info->getOwnerUid(), vote.type, vote.fps, vote.seamlessness, weight, layerFocused}); @@ -204,6 +208,7 @@ auto LayerHistory::summarize(const RefreshRateSelector& selector, nsecs_t now) - } void LayerHistory::partitionLayers(nsecs_t now) { + ATRACE_CALL(); const nsecs_t threshold = getActiveLayerThreshold(now); // iterate over inactive map diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp index 7247e4b260..0142ccd93f 100644 --- a/services/surfaceflinger/Scheduler/LayerInfo.cpp +++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp @@ -29,6 +29,7 @@ #include <cutils/compiler.h> #include <cutils/trace.h> #include <ftl/enum.h> +#include <gui/TraceUtils.h> #undef LOG_TAG #define LOG_TAG "LayerInfo" @@ -76,12 +77,43 @@ bool LayerInfo::isFrameTimeValid(const FrameTimeData& frameTime) const { bool LayerInfo::isFrequent(nsecs_t now) const { using fps_approx_ops::operator>=; - // If we know nothing about this layer we consider it as frequent as it might be the start - // of an animation. + // If we know nothing about this layer (e.g. after touch event), + // we consider it as frequent as it might be the start of an animation. if (mFrameTimes.size() < kFrequentLayerWindowSize) { return true; } - return getFps(now) >= kMinFpsForFrequentLayer; + + // Non-active layers are also infrequent + if (mLastUpdatedTime < getActiveLayerThreshold(now)) { + return false; + } + + // We check whether we can classify this layer as frequent or infrequent: + // - frequent: a layer posted kFrequentLayerWindowSize within + // kMaxPeriodForFrequentLayerNs of each other. + // - infrequent: a layer posted kFrequentLayerWindowSize with longer + // gaps than kFrequentLayerWindowSize. + // If we can't determine the layer classification yet, we return the last + // classification. + bool isFrequent = true; + bool isInfrequent = true; + const auto n = mFrameTimes.size() - 1; + for (size_t i = 0; i < kFrequentLayerWindowSize - 1; i++) { + if (mFrameTimes[n - i].queueTime - mFrameTimes[n - i - 1].queueTime < + kMaxPeriodForFrequentLayerNs.count()) { + isInfrequent = false; + } else { + isFrequent = false; + } + } + + if (isFrequent || isInfrequent) { + return isFrequent; + } + + // If we can't determine whether the layer is frequent or not, we return + // the last known classification. + return !mLastRefreshRate.infrequent; } Fps LayerInfo::getFps(nsecs_t now) const { @@ -189,6 +221,7 @@ std::optional<nsecs_t> LayerInfo::calculateAverageFrameTime() const { std::optional<Fps> LayerInfo::calculateRefreshRateIfPossible(const RefreshRateSelector& selector, nsecs_t now) { + ATRACE_CALL(); static constexpr float MARGIN = 1.0f; // 1Hz if (!hasEnoughDataForHeuristic()) { ALOGV("Not enough data"); @@ -224,20 +257,23 @@ std::optional<Fps> LayerInfo::calculateRefreshRateIfPossible(const RefreshRateSe LayerInfo::LayerVote LayerInfo::getRefreshRateVote(const RefreshRateSelector& selector, nsecs_t now) { + ATRACE_CALL(); if (mLayerVote.type != LayerHistory::LayerVoteType::Heuristic) { ALOGV("%s voted %d ", mName.c_str(), static_cast<int>(mLayerVote.type)); return mLayerVote; } if (isAnimating(now)) { + ATRACE_FORMAT_INSTANT("animating"); ALOGV("%s is animating", mName.c_str()); - mLastRefreshRate.animatingOrInfrequent = true; + mLastRefreshRate.animating = true; return {LayerHistory::LayerVoteType::Max, Fps()}; } if (!isFrequent(now)) { + ATRACE_FORMAT_INSTANT("infrequent"); ALOGV("%s is infrequent", mName.c_str()); - mLastRefreshRate.animatingOrInfrequent = true; + mLastRefreshRate.infrequent = true; // Infrequent layers vote for mininal refresh rate for // battery saving purposes and also to prevent b/135718869. return {LayerHistory::LayerVoteType::Min, Fps()}; @@ -246,7 +282,7 @@ LayerInfo::LayerVote LayerInfo::getRefreshRateVote(const RefreshRateSelector& se // If the layer was previously tagged as animating or infrequent, we clear // the history as it is likely the layer just changed its behavior // and we should not look at stale data - if (mLastRefreshRate.animatingOrInfrequent) { + if (mLastRefreshRate.animating || mLastRefreshRate.infrequent) { clearHistory(now); } diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h index a5ffbbecb5..93485be0a4 100644 --- a/services/surfaceflinger/Scheduler/LayerInfo.h +++ b/services/surfaceflinger/Scheduler/LayerInfo.h @@ -53,7 +53,7 @@ class LayerInfo { // Layer is considered frequent if the earliest value in the window of most recent present times // is within a threshold. If a layer is infrequent, its average refresh rate is disregarded in // favor of a low refresh rate. - static constexpr size_t kFrequentLayerWindowSize = 3; + static constexpr size_t kFrequentLayerWindowSize = 4; static constexpr Fps kMinFpsForFrequentLayer = 10_Hz; static constexpr auto kMaxPeriodForFrequentLayerNs = std::chrono::nanoseconds(kMinFpsForFrequentLayer.getPeriodNsecs()) + 1ms; @@ -214,7 +214,10 @@ private: Fps reported; // Whether the last reported rate for LayerInfo::getRefreshRate() // was due to animation or infrequent updates - bool animatingOrInfrequent = false; + bool animating = false; + // Whether the last reported rate for LayerInfo::getRefreshRate() + // was due to infrequent updates + bool infrequent = false; }; // Class to store past calculated refresh rate and determine whether diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp index a05d3df2de..04c2d4177f 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp +++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp @@ -180,7 +180,7 @@ auto RefreshRateSelector::createFrameRateModes( for (auto divisor = start; divisor <= end; divisor++) { const auto fps = mode->getFps() / divisor; using fps_approx_ops::operator<; - if (fps < kMinSupportedFrameRate) { + if (divisor > 1 && fps < kMinSupportedFrameRate) { break; } @@ -354,6 +354,7 @@ float RefreshRateSelector::calculateDistanceScoreFromMax(Fps refreshRate) const float RefreshRateSelector::calculateLayerScoreLocked(const LayerRequirement& layer, Fps refreshRate, bool isSeamlessSwitch) const { + ATRACE_CALL(); // Slightly prefer seamless switches. constexpr float kSeamedSwitchPenalty = 0.95f; const float seamlessness = isSeamlessSwitch ? 1.0f : kSeamedSwitchPenalty; diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h index 14d08f85e2..4f5842a67a 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateSelector.h +++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h @@ -61,7 +61,7 @@ public: std::chrono::nanoseconds(800us).count(); // The lowest Render Frame Rate that will ever be selected - static constexpr Fps kMinSupportedFrameRate = 1_Hz; + static constexpr Fps kMinSupportedFrameRate = 20_Hz; class Policy { static constexpr int kAllowGroupSwitchingDefault = false; diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index 856fda0f6c..74a81b7258 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -661,6 +661,7 @@ void Scheduler::demoteLeaderDisplay() { template <typename S, typename T> auto Scheduler::applyPolicy(S Policy::*statePtr, T&& newState) -> GlobalSignals { + ATRACE_CALL(); std::vector<display::DisplayModeRequest> modeRequests; GlobalSignals consideredSignals; diff --git a/services/surfaceflinger/ScreenCaptureOutput.cpp b/services/surfaceflinger/ScreenCaptureOutput.cpp index 37b3218138..6d195b9f7b 100644 --- a/services/surfaceflinger/ScreenCaptureOutput.cpp +++ b/services/surfaceflinger/ScreenCaptureOutput.cpp @@ -53,6 +53,13 @@ std::shared_ptr<ScreenCaptureOutput> createScreenCaptureOutput(ScreenCaptureOutp Rect sourceCrop = args.renderArea.getSourceCrop(); output->setDisplaySize({sourceCrop.getWidth(), sourceCrop.getHeight()}); + { + std::string name = args.regionSampling ? "RegionSampling" : "ScreenCaptureOutput"; + if (auto displayDevice = args.renderArea.getDisplayDevice()) { + base::StringAppendF(&name, " for %" PRIu64, displayDevice->getId().value); + } + output->setName(name); + } return output; } diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 809eb35817..8ec07092d9 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -461,6 +461,8 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipI mRefreshRateOverlaySpinner = property_get_bool("debug.sf.show_refresh_rate_overlay_spinner", 0); mRefreshRateOverlayRenderRate = property_get_bool("debug.sf.show_refresh_rate_overlay_render_rate", 0); + mRefreshRateOverlayShowInMiddle = + property_get_bool("debug.sf.show_refresh_rate_overlay_in_middle", 0); if (!mIsUserBuild && base::GetBoolProperty("debug.sf.enable_transaction_tracing"s, true)) { mTransactionTracing.emplace(); @@ -1492,6 +1494,7 @@ status_t SurfaceFlinger::getOverlaySupport(gui::OverlayProperties* outProperties outCombination.dataspaces = std::move(dataspaces); outProperties->combinations.emplace_back(outCombination); } + outProperties->supportMixedColorSpaces = aidlProperties.supportMixedColorSpaces; return NO_ERROR; } @@ -2587,7 +2590,9 @@ void SurfaceFlinger::postComposition() { const TimePoint compositeTime = TimePoint::fromNs(mCompositionEngine->getLastFrameRefreshTimestamp()); const Duration presentLatency = - mPresentLatencyTracker.trackPendingFrame(compositeTime, presentFenceTime); + !getHwComposer().hasCapability(Capability::PRESENT_FENCE_IS_NOT_RELIABLE) + ? mPresentLatencyTracker.trackPendingFrame(compositeTime, presentFenceTime) + : Duration::zero(); const auto& schedule = mScheduler->getVsyncSchedule(); const TimePoint vsyncDeadline = schedule.vsyncDeadlineAfter(presentTime); @@ -2962,15 +2967,15 @@ sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal( const auto enableFrameRateOverride = [&] { using Config = scheduler::RefreshRateSelector::Config; - if (!sysprop::enable_frame_rate_override(false)) { + if (!sysprop::enable_frame_rate_override(true)) { return Config::FrameRateOverride::Disabled; } - if (sysprop::frame_rate_override_for_native_rates(true)) { + if (sysprop::frame_rate_override_for_native_rates(false)) { return Config::FrameRateOverride::AppOverrideNativeRefreshRates; } - if (!sysprop::frame_rate_override_global(false)) { + if (!sysprop::frame_rate_override_global(true)) { return Config::FrameRateOverride::AppOverride; } @@ -6587,7 +6592,7 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenCommon( [=, renderAreaFuture = std::move(renderAreaFuture)]() FTL_FAKE_GUARD( kMainThreadContext) mutable -> ftl::SharedFuture<FenceResult> { ScreenCaptureResults captureResults; - std::unique_ptr<RenderArea> renderArea = renderAreaFuture.get(); + std::shared_ptr<RenderArea> renderArea = renderAreaFuture.get(); if (!renderArea) { ALOGW("Skipping screen capture because of invalid render area."); if (captureListener) { @@ -6599,7 +6604,7 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenCommon( ftl::SharedFuture<FenceResult> renderFuture; renderArea->render([&]() FTL_FAKE_GUARD(kMainThreadContext) { - renderFuture = renderScreenImpl(std::move(renderArea), traverseLayers, buffer, + renderFuture = renderScreenImpl(renderArea, traverseLayers, buffer, canCaptureBlackoutContent, regionSampling, grayscale, captureResults); }); @@ -6627,7 +6632,7 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenCommon( } ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl( - std::unique_ptr<RenderArea> renderArea, TraverseLayersFunction traverseLayers, + std::shared_ptr<const RenderArea> renderArea, TraverseLayersFunction traverseLayers, const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool canCaptureBlackoutContent, bool regionSampling, bool grayscale, ScreenCaptureResults& captureResults) { @@ -7066,7 +7071,8 @@ void SurfaceFlinger::enableRefreshRateOverlay(bool enable) { if (display.snapshot().connectionType() == ui::DisplayConnectionType::Internal) { if (const auto device = getDisplayDeviceLocked(id)) { device->enableRefreshRateOverlay(enable, mRefreshRateOverlaySpinner, - mRefreshRateOverlayRenderRate); + mRefreshRateOverlayRenderRate, + mRefreshRateOverlayShowInMiddle); } } } diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 8e96b28f69..554ee8c474 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -657,6 +657,8 @@ private: bool mRefreshRateOverlaySpinner = false; // Show render rate with refresh rate overlay bool mRefreshRateOverlayRenderRate = false; + // Show render rate overlay offseted to the middle of the screen (e.g. for circular displays) + bool mRefreshRateOverlayShowInMiddle = false; void setDesiredActiveMode(display::DisplayModeRequest&&, bool force = false) REQUIRES(mStateLock); @@ -806,7 +808,7 @@ private: const std::shared_ptr<renderengine::ExternalTexture>&, bool regionSampling, bool grayscale, const sp<IScreenCaptureListener>&); ftl::SharedFuture<FenceResult> renderScreenImpl( - std::unique_ptr<RenderArea>, TraverseLayersFunction, + std::shared_ptr<const RenderArea>, TraverseLayersFunction, const std::shared_ptr<renderengine::ExternalTexture>&, bool canCaptureBlackoutContent, bool regionSampling, bool grayscale, ScreenCaptureResults&) EXCLUDES(mStateLock) REQUIRES(kMainThreadContext); diff --git a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp index f1a6c0e2fa..ab98dbfe2f 100644 --- a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp +++ b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp @@ -27,7 +27,6 @@ #include <log/log.h> #include <mock/MockEventThread.h> #include <renderengine/ExternalTexture.h> -#include <renderengine/mock/FakeExternalTexture.h> #include <renderengine/mock/RenderEngine.h> #include <utils/String16.h> #include <string> @@ -95,6 +94,30 @@ public: } }; +class FakeExternalTexture : public renderengine::ExternalTexture { + const sp<GraphicBuffer> mNullBuffer = nullptr; + uint32_t mWidth; + uint32_t mHeight; + uint64_t mId; + PixelFormat mPixelFormat; + uint64_t mUsage; + +public: + FakeExternalTexture(uint32_t width, uint32_t height, uint64_t id, PixelFormat pixelFormat, + uint64_t usage) + : mWidth(width), mHeight(height), mId(id), mPixelFormat(pixelFormat), mUsage(usage) {} + const sp<GraphicBuffer>& getBuffer() const { return mNullBuffer; } + bool hasSameBuffer(const renderengine::ExternalTexture& other) const override { + return getId() == other.getId(); + } + uint32_t getWidth() const override { return mWidth; } + uint32_t getHeight() const override { return mHeight; } + uint64_t getId() const override { return mId; } + PixelFormat getPixelFormat() const override { return mPixelFormat; } + uint64_t getUsage() const override { return mUsage; } + ~FakeExternalTexture() = default; +}; + class MockSurfaceFlinger : public SurfaceFlinger { public: MockSurfaceFlinger(Factory& factory) @@ -102,12 +125,10 @@ public: std::shared_ptr<renderengine::ExternalTexture> getExternalTextureFromBufferData( BufferData& bufferData, const char* /* layerName */, uint64_t /* transactionId */) override { - return std::make_shared<renderengine::mock::FakeExternalTexture>(bufferData.getWidth(), - bufferData.getHeight(), - bufferData.getId(), - bufferData - .getPixelFormat(), - bufferData.getUsage()); + return std::make_shared<FakeExternalTexture>(bufferData.getWidth(), bufferData.getHeight(), + bufferData.getId(), + bufferData.getPixelFormat(), + bufferData.getUsage()); }; // b/220017192 migrate from transact codes to ISurfaceComposer apis diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp index 06b9caa7cb..ba77600cda 100644 --- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp +++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp @@ -182,7 +182,9 @@ public: sp<mock::NativeWindow> mNativeWindow = sp<mock::NativeWindow>::make(); std::vector<sp<Layer>> mAuxiliaryLayers; - sp<GraphicBuffer> mBuffer = sp<GraphicBuffer>::make(); + sp<GraphicBuffer> mBuffer = + sp<GraphicBuffer>::make(1u, 1u, PIXEL_FORMAT_RGBA_8888, + GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_SW_READ_OFTEN); ANativeWindowBuffer* mNativeWindowBuffer = mBuffer->getNativeBuffer(); Hwc2::mock::Composer* mComposer = nullptr; diff --git a/services/surfaceflinger/tests/unittests/DisplayDevice_SetDisplayBrightnessTest.cpp b/services/surfaceflinger/tests/unittests/DisplayDevice_SetDisplayBrightnessTest.cpp index 225ad163d9..ac5e9274bb 100644 --- a/services/surfaceflinger/tests/unittests/DisplayDevice_SetDisplayBrightnessTest.cpp +++ b/services/surfaceflinger/tests/unittests/DisplayDevice_SetDisplayBrightnessTest.cpp @@ -96,5 +96,23 @@ TEST_F(SetDisplayBrightnessTest, persistDisplayBrightnessWithCompositeShortCircu EXPECT_EQ(std::nullopt, displayDevice->getCompositionDisplay()->getState().displayBrightness); } +TEST_F(SetDisplayBrightnessTest, firstDisplayBrightnessWithComposite) { + ftl::FakeGuard guard(kMainThreadContext); + sp<DisplayDevice> displayDevice = getDisplayDevice(); + + EXPECT_EQ(std::nullopt, displayDevice->getStagedBrightness()); + + constexpr float kDisplayBrightness = -1.0f; + displayDevice->stageBrightness(kDisplayBrightness); + + EXPECT_EQ(-1.0f, displayDevice->getStagedBrightness()); + + displayDevice->persistBrightness(true); + + EXPECT_EQ(std::nullopt, displayDevice->getStagedBrightness()); + EXPECT_EQ(kDisplayBrightness, + displayDevice->getCompositionDisplay()->getState().displayBrightness); +} + } // namespace } // namespace android diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h index d58e644506..223f4db889 100644 --- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h +++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h @@ -115,7 +115,9 @@ public: TestableSurfaceFlinger mFlinger; sp<mock::NativeWindow> mNativeWindow = sp<mock::NativeWindow>::make(); - sp<GraphicBuffer> mBuffer = sp<GraphicBuffer>::make(); + sp<GraphicBuffer> mBuffer = + sp<GraphicBuffer>::make(1u, 1u, PIXEL_FORMAT_RGBA_8888, + GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_SW_READ_OFTEN); Hwc2::mock::PowerAdvisor mPowerAdvisor; FakeDisplayInjector mFakeDisplayInjector{mFlinger, mPowerAdvisor, mNativeWindow}; diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp index 979924af58..8397f8d0ce 100644 --- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp @@ -51,8 +51,6 @@ protected: 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 auto REFRESH_RATE_AVERAGE_HISTORY_DURATION = - LayerInfo::RefreshRateHistory::HISTORY_DURATION; static constexpr Fps LO_FPS = 30_Hz; static constexpr auto LO_FPS_PERIOD = LO_FPS.getPeriodNsecs(); @@ -607,7 +605,7 @@ TEST_F(LayerHistoryTest, inactiveLayers) { // advance the time for the previous frame to be inactive time += MAX_ACTIVE_LAYER_PERIOD_NS.count(); - // Now event if we post a quick few frame we should stay infrequent + // 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.get(), time, time, LayerHistory::LayerUpdateType::Buffer); time += HI_FPS_PERIOD; @@ -706,6 +704,88 @@ TEST_F(LayerHistoryTest, infrequentAnimatingLayer) { EXPECT_EQ(1, 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.get(), 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.get(), time, time, LayerHistory::LayerUpdateType::Buffer); + 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.get(), time, time, LayerHistory::LayerUpdateType::Buffer); + time += (MAX_FREQUENT_LAYER_PERIOD_NS + 1ms).count(); + history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); + 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.get(), time, time, LayerHistory::LayerUpdateType::Buffer); + 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.get(), time, time, LayerHistory::LayerUpdateType::Buffer); + history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); + 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.get(), time, time, LayerHistory::LayerUpdateType::Buffer); + 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.get(), time, time, LayerHistory::LayerUpdateType::Buffer); + 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(); diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp index 79d02dd512..06f45f9197 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp @@ -141,12 +141,6 @@ protected: RefreshRateSelectorTest(); ~RefreshRateSelectorTest(); - // Represents the number of refresh rates possible - // from 1_Hz to 90_hz, including fractional rates. - static constexpr size_t kTotalRefreshRates120 = 120; - // Represents the number of refresh rates possible - // from 1_Hz to 120_hz, including fractional rates. - static constexpr size_t kTotalRefreshRates216 = 216; static constexpr DisplayModeId kModeId60{0}; static constexpr DisplayModeId kModeId90{1}; static constexpr DisplayModeId kModeId72{2}; @@ -159,6 +153,9 @@ protected: static constexpr DisplayModeId kModeId30Frac{9}; static constexpr DisplayModeId kModeId60Frac{10}; static constexpr DisplayModeId kModeId35{11}; + static constexpr DisplayModeId kModeId1{12}; + static constexpr DisplayModeId kModeId5{13}; + static constexpr DisplayModeId kModeId10{14}; static inline const ftl::NonNull<DisplayModePtr> kMode60 = ftl::as_non_null(createDisplayMode(kModeId60, 60_Hz)); @@ -196,6 +193,12 @@ protected: ftl::as_non_null(createDisplayMode(kModeId24, 24_Hz)); static inline const ftl::NonNull<DisplayModePtr> kMode24Frac = ftl::as_non_null(createDisplayMode(kModeId24Frac, 23.976_Hz)); + static inline const ftl::NonNull<DisplayModePtr> kMode1 = + ftl::as_non_null(createDisplayMode(kModeId1, 1_Hz)); + static inline const ftl::NonNull<DisplayModePtr> kMode5 = + ftl::as_non_null(createDisplayMode(kModeId5, 5_Hz)); + static inline const ftl::NonNull<DisplayModePtr> kMode10 = + ftl::as_non_null(createDisplayMode(kModeId10, 10_Hz)); // Test configurations. static inline const DisplayModes kModes_60 = makeModes(kMode60); @@ -218,6 +221,7 @@ protected: static inline const DisplayModes kModes_25_30_50_60 = makeModes(kMode60, kMode90, kMode72_G1, kMode120_G1, kMode30_G1, kMode25_G1, kMode50); static inline const DisplayModes kModes_60_120 = makeModes(kMode60, kMode120); + static inline const DisplayModes kModes_1_5_10 = makeModes(kMode1, kMode5, kMode10); // This is a typical TV configuration. static inline const DisplayModes kModes_24_25_30_50_60_Frac = @@ -1135,12 +1139,7 @@ TEST_P(RefreshRateSelectorTest, getMaxRefreshRatesByPolicy) { return {{90_Hz, kMode90}, {60_Hz, kMode60}, {45_Hz, kMode90}, {30_Hz, kMode30}}; } }(); - - if (GetParam() == Config::FrameRateOverride::Enabled) { - ASSERT_EQ(kTotalRefreshRates120, refreshRates.size()); - } else { - ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size()); - } + ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size()); for (size_t i = 0; i < expectedRefreshRates.size(); ++i) { EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode) @@ -1166,18 +1165,10 @@ TEST_P(RefreshRateSelectorTest, getMinRefreshRatesByPolicy) { case Config::FrameRateOverride::AppOverride: return {{30_Hz, kMode30}, {60_Hz, kMode60}, {90_Hz, kMode90}}; case Config::FrameRateOverride::Enabled: - return {{1_Hz, kMode30}, - {1.011_Hz, kMode90}, - {1.016_Hz, kMode60}, - {1.022_Hz, kMode90}}; + return {{30_Hz, kMode30}, {45_Hz, kMode90}, {60_Hz, kMode60}, {90_Hz, kMode90}}; } }(); - - if (GetParam() == Config::FrameRateOverride::Enabled) { - ASSERT_EQ(kTotalRefreshRates120, refreshRates.size()); - } else { - ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size()); - } + ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size()); for (size_t i = 0; i < expectedRefreshRates.size(); ++i) { EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode) @@ -1269,12 +1260,7 @@ TEST_P(RefreshRateSelectorTest, powerOnImminentConsidered) { {30_Hz, kMode60}, {22.5_Hz, kMode90}, {20_Hz, kMode60}}; } }(); - - if (GetParam() == Config::FrameRateOverride::Enabled) { - ASSERT_EQ(kTotalRefreshRates120, refreshRates.size()); - } else { - ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size()); - } + ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size()); for (size_t i = 0; i < expectedRefreshRates.size(); ++i) { EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode) @@ -1288,11 +1274,7 @@ TEST_P(RefreshRateSelectorTest, powerOnImminentConsidered) { selector.getRankedRefreshRatesAsPair({}, {.powerOnImminent = true}); EXPECT_TRUE(signals.powerOnImminent); - if (GetParam() == Config::FrameRateOverride::Enabled) { - ASSERT_EQ(kTotalRefreshRates120, refreshRates.size()); - } else { - ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size()); - } + ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size()); for (size_t i = 0; i < expectedRefreshRates.size(); ++i) { EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode) @@ -1312,11 +1294,7 @@ TEST_P(RefreshRateSelectorTest, powerOnImminentConsidered) { selector.getRankedRefreshRatesAsPair(layers, {.powerOnImminent = true}); EXPECT_TRUE(signals.powerOnImminent); - if (GetParam() == Config::FrameRateOverride::Enabled) { - ASSERT_EQ(kTotalRefreshRates120, refreshRates.size()); - } else { - ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size()); - } + ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size()); for (size_t i = 0; i < expectedRefreshRates.size(); ++i) { EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode) @@ -1341,12 +1319,7 @@ TEST_P(RefreshRateSelectorTest, powerOnImminentConsidered) { {30_Hz, kMode60}, {22.5_Hz, kMode90}, {20_Hz, kMode60}}; } }(); - - if (GetParam() == Config::FrameRateOverride::Enabled) { - ASSERT_EQ(kTotalRefreshRates120, refreshRates.size()); - } else { - ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size()); - } + ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size()); for (size_t i = 0; i < expectedRefreshRates.size(); ++i) { EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode) @@ -1599,11 +1572,7 @@ TEST_P(RefreshRateSelectorTest, testDisplayModeOrdering) { }(); auto actualRanking = selector.getRankedFrameRates(layers, {}).ranking; - if (GetParam() == Config::FrameRateOverride::Enabled) { - ASSERT_EQ(kTotalRefreshRates216, actualRanking.size()); - } else { - ASSERT_EQ(expectedRanking.size(), actualRanking.size()); - } + ASSERT_EQ(expectedRanking.size(), actualRanking.size()); for (size_t i = 0; i < expectedRanking.size(); ++i) { EXPECT_EQ(expectedRanking[i], actualRanking[i].frameRateMode) @@ -1645,11 +1614,7 @@ TEST_P(RefreshRateSelectorTest, testDisplayModeOrdering) { }(); actualRanking = selector.getRankedFrameRates(layers, {}).ranking; - if (GetParam() == Config::FrameRateOverride::Enabled) { - ASSERT_EQ(kTotalRefreshRates216, actualRanking.size()); - } else { - ASSERT_EQ(expectedRanking.size(), actualRanking.size()); - } + ASSERT_EQ(expectedRanking.size(), actualRanking.size()); for (size_t i = 0; i < expectedRanking.size(); ++i) { EXPECT_EQ(expectedRanking[i], actualRanking[i].frameRateMode) @@ -1689,11 +1654,7 @@ TEST_P(RefreshRateSelectorTest, testDisplayModeOrdering) { }(); actualRanking = selector.getRankedFrameRates(layers, {}).ranking; - if (GetParam() == Config::FrameRateOverride::Enabled) { - ASSERT_EQ(kTotalRefreshRates216, actualRanking.size()); - } else { - ASSERT_EQ(expectedRanking.size(), actualRanking.size()); - } + ASSERT_EQ(expectedRanking.size(), actualRanking.size()); for (size_t i = 0; i < expectedRanking.size(); ++i) { EXPECT_EQ(expectedRanking[i], actualRanking[i].frameRateMode) @@ -1736,11 +1697,7 @@ TEST_P(RefreshRateSelectorTest, testDisplayModeOrdering) { }(); actualRanking = selector.getRankedFrameRates(layers, {}).ranking; - if (GetParam() == Config::FrameRateOverride::Enabled) { - ASSERT_EQ(kTotalRefreshRates216, actualRanking.size()); - } else { - ASSERT_EQ(expectedRanking.size(), actualRanking.size()); - } + ASSERT_EQ(expectedRanking.size(), actualRanking.size()); for (size_t i = 0; i < expectedRanking.size(); ++i) { EXPECT_EQ(expectedRanking[i], actualRanking[i].frameRateMode) @@ -2370,8 +2327,7 @@ TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_FractionalRefreshRates_Exac } // b/190578904 -TEST_P(RefreshRateSelectorTest, - getBestFrameRateMode_withCloseRefreshRates_LayerVoteType_Heuristic) { +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withCloseRefreshRates) { if (g_noSlowTests) { GTEST_SKIP(); } @@ -2400,101 +2356,8 @@ TEST_P(RefreshRateSelectorTest, for (int fps = kMinRefreshRate; fps < kMaxRefreshRate; fps++) { const auto refreshRate = Fps::fromValue(static_cast<float>(fps)); testRefreshRate(refreshRate, LayerVoteType::Heuristic); - } -} -TEST_P(RefreshRateSelectorTest, - getBestFrameRateMode_withCloseRefreshRates_LayerVoteType_ExplicitDefault) { - if (g_noSlowTests) { - GTEST_SKIP(); - } - - const int kMinRefreshRate = RefreshRateSelector::kMinSupportedFrameRate.getIntValue(); - constexpr int kMaxRefreshRate = 240; - - DisplayModes displayModes; - for (int fps = kMinRefreshRate; fps < kMaxRefreshRate; fps++) { - const DisplayModeId modeId(fps); - displayModes.try_emplace(modeId, - createDisplayMode(modeId, - Fps::fromValue(static_cast<float>(fps)))); - } - - const auto selector = createSelector(std::move(displayModes), DisplayModeId(kMinRefreshRate)); - - std::vector<LayerRequirement> layers = {{.weight = 1.f}}; - const auto testRefreshRate = [&](Fps fps, LayerVoteType vote) { - layers[0].desiredRefreshRate = fps; - layers[0].vote = vote; - EXPECT_EQ(fps.getIntValue(), selector.getBestFrameRateMode(layers)->getFps().getIntValue()) - << "Failed for " << ftl::enum_string(vote); - }; - - for (int fps = kMinRefreshRate; fps < kMaxRefreshRate; fps++) { - const auto refreshRate = Fps::fromValue(static_cast<float>(fps)); testRefreshRate(refreshRate, LayerVoteType::ExplicitDefault); - } -} -TEST_P(RefreshRateSelectorTest, - getBestFrameRateMode_withCloseRefreshRates_LayerVoteType_ExplicitExactOrMultiple) { - if (g_noSlowTests) { - GTEST_SKIP(); - } - - const int kMinRefreshRate = RefreshRateSelector::kMinSupportedFrameRate.getIntValue(); - constexpr int kMaxRefreshRate = 240; - - DisplayModes displayModes; - for (int fps = kMinRefreshRate; fps < kMaxRefreshRate; fps++) { - const DisplayModeId modeId(fps); - displayModes.try_emplace(modeId, - createDisplayMode(modeId, - Fps::fromValue(static_cast<float>(fps)))); - } - - const auto selector = createSelector(std::move(displayModes), DisplayModeId(kMinRefreshRate)); - - std::vector<LayerRequirement> layers = {{.weight = 1.f}}; - const auto testRefreshRate = [&](Fps fps, LayerVoteType vote) { - layers[0].desiredRefreshRate = fps; - layers[0].vote = vote; - EXPECT_EQ(fps.getIntValue(), selector.getBestFrameRateMode(layers)->getFps().getIntValue()) - << "Failed for " << ftl::enum_string(vote); - }; - - for (int fps = kMinRefreshRate; fps < kMaxRefreshRate; fps++) { - const auto refreshRate = Fps::fromValue(static_cast<float>(fps)); testRefreshRate(refreshRate, LayerVoteType::ExplicitExactOrMultiple); - } -} -TEST_P(RefreshRateSelectorTest, - getBestFrameRateMode_withCloseRefreshRates_LayerVoteType_ExplicitExact) { - if (g_noSlowTests) { - GTEST_SKIP(); - } - - const int kMinRefreshRate = RefreshRateSelector::kMinSupportedFrameRate.getIntValue(); - constexpr int kMaxRefreshRate = 240; - - DisplayModes displayModes; - for (int fps = kMinRefreshRate; fps < kMaxRefreshRate; fps++) { - const DisplayModeId modeId(fps); - displayModes.try_emplace(modeId, - createDisplayMode(modeId, - Fps::fromValue(static_cast<float>(fps)))); - } - - const auto selector = createSelector(std::move(displayModes), DisplayModeId(kMinRefreshRate)); - - std::vector<LayerRequirement> layers = {{.weight = 1.f}}; - const auto testRefreshRate = [&](Fps fps, LayerVoteType vote) { - layers[0].desiredRefreshRate = fps; - layers[0].vote = vote; - EXPECT_EQ(fps.getIntValue(), selector.getBestFrameRateMode(layers)->getFps().getIntValue()) - << "Failed for " << ftl::enum_string(vote); - }; - - for (int fps = kMinRefreshRate; fps < kMaxRefreshRate; fps++) { - const auto refreshRate = Fps::fromValue(static_cast<float>(fps)); testRefreshRate(refreshRate, LayerVoteType::ExplicitExact); } } @@ -2954,18 +2817,13 @@ TEST_P(RefreshRateSelectorTest, renderFrameRates) { {90_Hz, 90_Hz}, {120_Hz, 120_Hz}}; case Config::FrameRateOverride::Enabled: - return {{1_Hz, 30_Hz}, {1.008_Hz, 120_Hz}, {1.011_Hz, 90_Hz}, - {1.014_Hz, 72_Hz}, {1.016_Hz, 60_Hz}, {1.022_Hz, 90_Hz}, - {1.0256_Hz, 120_Hz}, {1.028_Hz, 72_Hz}}; + return {{30_Hz, 30_Hz}, {36_Hz, 72_Hz}, {40_Hz, 120_Hz}, {45_Hz, 90_Hz}, + {60_Hz, 60_Hz}, {72_Hz, 72_Hz}, {90_Hz, 90_Hz}, {120_Hz, 120_Hz}}; } }(); const auto& primaryRefreshRates = selector.getPrimaryFrameRates(); - if (GetParam() == Config::FrameRateOverride::Enabled) { - ASSERT_EQ(kTotalRefreshRates216, primaryRefreshRates.size()); - } else { - ASSERT_EQ(expected.size(), primaryRefreshRates.size()); - } + ASSERT_EQ(expected.size(), primaryRefreshRates.size()); for (size_t i = 0; i < expected.size(); i++) { const auto [expectedRenderRate, expectedRefreshRate] = expected[i]; @@ -3108,5 +2966,12 @@ TEST_P(RefreshRateSelectorTest, policyCanBeInfinity) { EXPECT_EQ(kMode60, selector.getMinRefreshRateByPolicy()); } +TEST_P(RefreshRateSelectorTest, SupportsLowPhysicalRefreshRates) { + auto selector = createSelector(kModes_1_5_10, kModeId10); + + EXPECT_EQ(kMode10, selector.getMaxRefreshRateByPolicy()); + EXPECT_EQ(kMode1, selector.getMinRefreshRateByPolicy()); +} + } // namespace } // namespace android::scheduler diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp index 6e500593fa..ab732ed485 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp @@ -404,11 +404,13 @@ TEST_F(SetPowerModeInternalTest, setPowerModeInternalDoesNothingIfVirtualDisplay EXPECT_EQ(PowerMode::ON, display.mutableDisplayDevice()->getPowerMode()); } -TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToOnPrimaryDisplay) { +// TODO(b/262417075) +TEST_F(SetPowerModeInternalTest, DISABLED_transitionsDisplayFromOffToOnPrimaryDisplay) { transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOffToOnVariant>>(); } -TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToDozeSuspendPrimaryDisplay) { +// TODO(b/262417075) +TEST_F(SetPowerModeInternalTest, DISABLED_transitionsDisplayFromOffToDozeSuspendPrimaryDisplay) { transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOffToDozeSuspendVariant>>(); } @@ -444,11 +446,13 @@ TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToUnknownPrimaryDisplay transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToUnknownVariant>>(); } -TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToOnExternalDisplay) { +// TODO(b/262417075) +TEST_F(SetPowerModeInternalTest, DISABLED_transitionsDisplayFromOffToOnExternalDisplay) { transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOffToOnVariant>>(); } -TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToDozeSuspendExternalDisplay) { +// TODO(b/262417075) +TEST_F(SetPowerModeInternalTest, DISABLED_transitionsDisplayFromOffToDozeSuspendExternalDisplay) { transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOffToDozeSuspendVariant>>(); } diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index 2117084bbf..113c518c4b 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -401,7 +401,7 @@ public: return mFlinger->setPowerModeInternal(display, mode); } - auto renderScreenImpl(std::unique_ptr<RenderArea> renderArea, + auto renderScreenImpl(std::shared_ptr<const RenderArea> renderArea, SurfaceFlinger::TraverseLayersFunction traverseLayers, const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool forSystem, bool regionSampling) { diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp index 9ed992b230..273cdd547e 100644 --- a/vulkan/libvulkan/driver.cpp +++ b/vulkan/libvulkan/driver.cpp @@ -636,6 +636,7 @@ void CreateInfoWrapper::FilterExtension(const char* name) { case ProcHook::EXT_swapchain_colorspace: case ProcHook::KHR_get_surface_capabilities2: case ProcHook::GOOGLE_surfaceless_query: + case ProcHook::EXT_surface_maintenance1: hook_extensions_.set(ext_bit); // return now as these extensions do not require HAL support return; @@ -657,9 +658,11 @@ void CreateInfoWrapper::FilterExtension(const char* name) { case ProcHook::KHR_shared_presentable_image: case ProcHook::KHR_swapchain: case ProcHook::EXT_hdr_metadata: + case ProcHook::EXT_swapchain_maintenance1: case ProcHook::ANDROID_external_memory_android_hardware_buffer: case ProcHook::ANDROID_native_buffer: case ProcHook::GOOGLE_display_timing: + case ProcHook::KHR_external_fence_fd: case ProcHook::EXTENSION_CORE_1_0: case ProcHook::EXTENSION_CORE_1_1: case ProcHook::EXTENSION_CORE_1_2: @@ -690,16 +693,22 @@ void CreateInfoWrapper::FilterExtension(const char* name) { ext_bit = ProcHook::ANDROID_native_buffer; break; case ProcHook::KHR_incremental_present: - case ProcHook::GOOGLE_display_timing: case ProcHook::KHR_shared_presentable_image: + case ProcHook::GOOGLE_display_timing: hook_extensions_.set(ext_bit); // return now as these extensions do not require HAL support return; + case ProcHook::EXT_swapchain_maintenance1: + // map VK_KHR_swapchain_maintenance1 to KHR_external_fence_fd + name = VK_KHR_EXTERNAL_FENCE_FD_EXTENSION_NAME; + ext_bit = ProcHook::KHR_external_fence_fd; + break; case ProcHook::EXT_hdr_metadata: case ProcHook::KHR_bind_memory2: hook_extensions_.set(ext_bit); break; case ProcHook::ANDROID_external_memory_android_hardware_buffer: + case ProcHook::KHR_external_fence_fd: case ProcHook::EXTENSION_UNKNOWN: // Extensions we don't need to do anything about at this level break; @@ -715,6 +724,7 @@ void CreateInfoWrapper::FilterExtension(const char* name) { case ProcHook::KHR_surface_protected_capabilities: case ProcHook::EXT_debug_report: case ProcHook::EXT_swapchain_colorspace: + case ProcHook::EXT_surface_maintenance1: case ProcHook::GOOGLE_surfaceless_query: case ProcHook::ANDROID_native_buffer: case ProcHook::EXTENSION_CORE_1_0: @@ -747,10 +757,18 @@ void CreateInfoWrapper::FilterExtension(const char* name) { if (strcmp(name, props.extensionName) != 0) continue; + if (ext_bit != ProcHook::EXTENSION_UNKNOWN && + hal_extensions_.test(ext_bit)) { + ALOGI("CreateInfoWrapper::FilterExtension: already have '%s'.", name); + continue; + } + filter.names[filter.name_count++] = name; if (ext_bit != ProcHook::EXTENSION_UNKNOWN) { if (ext_bit == ProcHook::ANDROID_native_buffer) hook_extensions_.set(ProcHook::KHR_swapchain); + if (ext_bit == ProcHook::KHR_external_fence_fd) + hook_extensions_.set(ProcHook::EXT_swapchain_maintenance1); hal_extensions_.set(ext_bit); } @@ -940,6 +958,9 @@ VkResult EnumerateInstanceExtensionProperties( VK_KHR_GET_SURFACE_CAPABILITIES_2_SPEC_VERSION}); loader_extensions.push_back({VK_GOOGLE_SURFACELESS_QUERY_EXTENSION_NAME, VK_GOOGLE_SURFACELESS_QUERY_SPEC_VERSION}); + loader_extensions.push_back({ + VK_EXT_SURFACE_MAINTENANCE_1_EXTENSION_NAME, + VK_EXT_SURFACE_MAINTENANCE_1_SPEC_VERSION}); static const VkExtensionProperties loader_debug_report_extension = { VK_EXT_DEBUG_REPORT_EXTENSION_NAME, VK_EXT_DEBUG_REPORT_SPEC_VERSION, @@ -1072,6 +1093,33 @@ VkResult GetAndroidNativeBufferSpecVersion9Support( return result; } +bool CanSupportSwapchainMaintenance1Extension(VkPhysicalDevice physicalDevice) { + const auto& driver = GetData(physicalDevice).driver; + if (!driver.GetPhysicalDeviceExternalFenceProperties) + return false; + + // Requires support for external fences imported from sync fds. + // This is _almost_ universal on Android, but may be missing on + // some extremely old drivers, or on strange implementations like + // cuttlefish. + VkPhysicalDeviceExternalFenceInfo fenceInfo = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_FENCE_INFO, + nullptr, + VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT + }; + VkExternalFenceProperties fenceProperties = { + VK_STRUCTURE_TYPE_EXTERNAL_FENCE_PROPERTIES, + nullptr, + 0, 0, 0 + }; + + GetPhysicalDeviceExternalFenceProperties(physicalDevice, &fenceInfo, &fenceProperties); + if (fenceProperties.externalFenceFeatures & VK_EXTERNAL_FENCE_FEATURE_IMPORTABLE_BIT) + return true; + + return false; +} + VkResult EnumerateDeviceExtensionProperties( VkPhysicalDevice physicalDevice, const char* pLayerName, @@ -1149,6 +1197,12 @@ VkResult EnumerateDeviceExtensionProperties( VK_EXT_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_SPEC_VERSION}); } + if (CanSupportSwapchainMaintenance1Extension(physicalDevice)) { + loader_extensions.push_back({ + VK_EXT_SWAPCHAIN_MAINTENANCE_1_EXTENSION_NAME, + VK_EXT_SWAPCHAIN_MAINTENANCE_1_SPEC_VERSION}); + } + // enumerate our extensions first if (!pLayerName && pProperties) { uint32_t count = std::min( @@ -1644,6 +1698,12 @@ void GetPhysicalDeviceFeatures2(VkPhysicalDevice physicalDevice, imageCompressionControlSwapchainInChain = true; } break; + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SWAPCHAIN_MAINTENANCE_1_FEATURES_EXT: { + auto smf = reinterpret_cast<VkPhysicalDeviceSwapchainMaintenance1FeaturesEXT *>( + pFeats); + smf->swapchainMaintenance1 = true; + } break; + default: break; } diff --git a/vulkan/libvulkan/driver_gen.cpp b/vulkan/libvulkan/driver_gen.cpp index de98aa7e61..798af5c6a6 100644 --- a/vulkan/libvulkan/driver_gen.cpp +++ b/vulkan/libvulkan/driver_gen.cpp @@ -162,6 +162,15 @@ VKAPI_ATTR void checkedGetDeviceQueue2(VkDevice device, const VkDeviceQueueInfo2 } } +VKAPI_ATTR VkResult checkedReleaseSwapchainImagesEXT(VkDevice device, const VkReleaseSwapchainImagesInfoEXT* pReleaseInfo) { + if (GetData(device).hook_extensions[ProcHook::EXT_swapchain_maintenance1]) { + return ReleaseSwapchainImagesEXT(device, pReleaseInfo); + } else { + Logger(device).Err(device, "VK_EXT_swapchain_maintenance1 not enabled. vkReleaseSwapchainImagesEXT not executed."); + return VK_SUCCESS; + } +} + // clang-format on const ProcHook g_proc_hooks[] = { @@ -545,6 +554,13 @@ const ProcHook g_proc_hooks[] = { nullptr, }, { + "vkReleaseSwapchainImagesEXT", + ProcHook::DEVICE, + ProcHook::EXT_swapchain_maintenance1, + reinterpret_cast<PFN_vkVoidFunction>(ReleaseSwapchainImagesEXT), + reinterpret_cast<PFN_vkVoidFunction>(checkedReleaseSwapchainImagesEXT), + }, + { "vkSetHdrMetadataEXT", ProcHook::DEVICE, ProcHook::EXT_hdr_metadata, @@ -580,6 +596,8 @@ ProcHook::Extension GetProcHookExtension(const char* name) { if (strcmp(name, "VK_KHR_surface") == 0) return ProcHook::KHR_surface; if (strcmp(name, "VK_KHR_surface_protected_capabilities") == 0) return ProcHook::KHR_surface_protected_capabilities; if (strcmp(name, "VK_KHR_swapchain") == 0) return ProcHook::KHR_swapchain; + if (strcmp(name, "VK_EXT_swapchain_maintenance1") == 0) return ProcHook::EXT_swapchain_maintenance1; + if (strcmp(name, "VK_EXT_surface_maintenance1") == 0) return ProcHook::EXT_surface_maintenance1; if (strcmp(name, "VK_ANDROID_external_memory_android_hardware_buffer") == 0) return ProcHook::ANDROID_external_memory_android_hardware_buffer; if (strcmp(name, "VK_KHR_bind_memory2") == 0) return ProcHook::KHR_bind_memory2; if (strcmp(name, "VK_KHR_get_physical_device_properties2") == 0) return ProcHook::KHR_get_physical_device_properties2; @@ -587,6 +605,7 @@ ProcHook::Extension GetProcHookExtension(const char* name) { if (strcmp(name, "VK_KHR_external_memory_capabilities") == 0) return ProcHook::KHR_external_memory_capabilities; if (strcmp(name, "VK_KHR_external_semaphore_capabilities") == 0) return ProcHook::KHR_external_semaphore_capabilities; if (strcmp(name, "VK_KHR_external_fence_capabilities") == 0) return ProcHook::KHR_external_fence_capabilities; + if (strcmp(name, "VK_KHR_external_fence_fd") == 0) return ProcHook::KHR_external_fence_fd; // clang-format on return ProcHook::EXTENSION_UNKNOWN; } @@ -666,6 +685,7 @@ bool InitDriverTable(VkDevice dev, INIT_PROC(true, dev, CreateImage); INIT_PROC(true, dev, DestroyImage); INIT_PROC(true, dev, AllocateCommandBuffers); + INIT_PROC_EXT(KHR_external_fence_fd, true, dev, ImportFenceFdKHR); INIT_PROC(false, dev, BindImageMemory2); INIT_PROC_EXT(KHR_bind_memory2, true, dev, BindImageMemory2KHR); INIT_PROC(false, dev, GetDeviceQueue2); diff --git a/vulkan/libvulkan/driver_gen.h b/vulkan/libvulkan/driver_gen.h index 2f60086a27..31ba04ba1f 100644 --- a/vulkan/libvulkan/driver_gen.h +++ b/vulkan/libvulkan/driver_gen.h @@ -49,6 +49,8 @@ struct ProcHook { KHR_surface, KHR_surface_protected_capabilities, KHR_swapchain, + EXT_swapchain_maintenance1, + EXT_surface_maintenance1, ANDROID_external_memory_android_hardware_buffer, KHR_bind_memory2, KHR_get_physical_device_properties2, @@ -56,6 +58,7 @@ struct ProcHook { KHR_external_memory_capabilities, KHR_external_semaphore_capabilities, KHR_external_fence_capabilities, + KHR_external_fence_fd, EXTENSION_CORE_1_0, EXTENSION_CORE_1_1, @@ -118,6 +121,7 @@ struct DeviceDriverTable { PFN_vkCreateImage CreateImage; PFN_vkDestroyImage DestroyImage; PFN_vkAllocateCommandBuffers AllocateCommandBuffers; + PFN_vkImportFenceFdKHR ImportFenceFdKHR; PFN_vkBindImageMemory2 BindImageMemory2; PFN_vkBindImageMemory2KHR BindImageMemory2KHR; PFN_vkGetDeviceQueue2 GetDeviceQueue2; diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp index b03200e4a7..1bff50dd7f 100644 --- a/vulkan/libvulkan/swapchain.cpp +++ b/vulkan/libvulkan/swapchain.cpp @@ -291,6 +291,9 @@ struct Swapchain { release_fence(-1), dequeued(false) {} VkImage image; + // If the image is bound to memory, an sp to the underlying gralloc buffer. + // Otherwise, nullptr; the image will be bound to memory as part of + // AcquireNextImage. android::sp<ANativeWindowBuffer> buffer; // The fence is only valid when the buffer is dequeued, and should be // -1 any other time. When valid, we own the fd, and must ensure it is @@ -656,100 +659,40 @@ VkResult GetPhysicalDeviceSurfaceCapabilitiesKHR( VkSurfaceCapabilitiesKHR* capabilities) { ATRACE_CALL(); - int err; - int width, height; - int transform_hint; - int max_buffer_count; - if (surface == VK_NULL_HANDLE) { - const InstanceData& instance_data = GetData(pdev); - ProcHook::Extension surfaceless = ProcHook::GOOGLE_surfaceless_query; - bool surfaceless_enabled = - instance_data.hook_extensions.test(surfaceless); - if (!surfaceless_enabled) { - // It is an error to pass a surface==VK_NULL_HANDLE unless the - // VK_GOOGLE_surfaceless_query extension is enabled - return VK_ERROR_SURFACE_LOST_KHR; - } - // Support for VK_GOOGLE_surfaceless_query. The primary purpose of this - // extension for this function is for - // VkSurfaceProtectedCapabilitiesKHR::supportsProtected. The following - // four values cannot be known without a surface. Default values will - // be supplied anyway, but cannot be relied upon. - width = 0xFFFFFFFF; - height = 0xFFFFFFFF; - transform_hint = VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR; - capabilities->minImageCount = 0xFFFFFFFF; - capabilities->maxImageCount = 0xFFFFFFFF; - } else { - ANativeWindow* window = SurfaceFromHandle(surface)->window.get(); + // Implement in terms of GetPhysicalDeviceSurfaceCapabilities2KHR - err = window->query(window, NATIVE_WINDOW_DEFAULT_WIDTH, &width); - if (err != android::OK) { - ALOGE("NATIVE_WINDOW_DEFAULT_WIDTH query failed: %s (%d)", - strerror(-err), err); - return VK_ERROR_SURFACE_LOST_KHR; - } - err = window->query(window, NATIVE_WINDOW_DEFAULT_HEIGHT, &height); - if (err != android::OK) { - ALOGE("NATIVE_WINDOW_DEFAULT_WIDTH query failed: %s (%d)", - strerror(-err), err); - return VK_ERROR_SURFACE_LOST_KHR; - } - - err = window->query(window, NATIVE_WINDOW_TRANSFORM_HINT, - &transform_hint); - if (err != android::OK) { - ALOGE("NATIVE_WINDOW_TRANSFORM_HINT query failed: %s (%d)", - strerror(-err), err); - return VK_ERROR_SURFACE_LOST_KHR; - } - - err = window->query(window, NATIVE_WINDOW_MAX_BUFFER_COUNT, - &max_buffer_count); - if (err != android::OK) { - ALOGE("NATIVE_WINDOW_MAX_BUFFER_COUNT query failed: %s (%d)", - strerror(-err), err); - return VK_ERROR_SURFACE_LOST_KHR; - } - capabilities->minImageCount = std::min(max_buffer_count, 3); - capabilities->maxImageCount = static_cast<uint32_t>(max_buffer_count); - } - - capabilities->currentExtent = - VkExtent2D{static_cast<uint32_t>(width), static_cast<uint32_t>(height)}; + VkPhysicalDeviceSurfaceInfo2KHR info2 = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR, + nullptr, + surface + }; - // TODO(http://b/134182502): Figure out what the max extent should be. - capabilities->minImageExtent = VkExtent2D{1, 1}; - capabilities->maxImageExtent = VkExtent2D{4096, 4096}; + VkSurfaceCapabilities2KHR caps2 = { + VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR, + nullptr, + {}, + }; - if (capabilities->maxImageExtent.height < - capabilities->currentExtent.height) { - capabilities->maxImageExtent.height = - capabilities->currentExtent.height; - } + VkResult result = GetPhysicalDeviceSurfaceCapabilities2KHR(pdev, &info2, &caps2); + *capabilities = caps2.surfaceCapabilities; + return result; +} - if (capabilities->maxImageExtent.width < - capabilities->currentExtent.width) { - capabilities->maxImageExtent.width = capabilities->currentExtent.width; +// Does the call-twice and VK_INCOMPLETE handling for querying lists +// of things, where we already have the full set built in a vector. +template <typename T> +VkResult CopyWithIncomplete(std::vector<T> const& things, + T* callerPtr, uint32_t* callerCount) { + VkResult result = VK_SUCCESS; + if (callerPtr) { + if (things.size() > *callerCount) + result = VK_INCOMPLETE; + *callerCount = std::min(uint32_t(things.size()), *callerCount); + std::copy(things.begin(), things.begin() + *callerCount, callerPtr); + } else { + *callerCount = things.size(); } - - capabilities->maxImageArrayLayers = 1; - - capabilities->supportedTransforms = kSupportedTransforms; - capabilities->currentTransform = - TranslateNativeToVulkanTransform(transform_hint); - - // On Android, window composition is a WindowManager property, not something - // associated with the bufferqueue. It can't be changed from here. - capabilities->supportedCompositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR; - - capabilities->supportedUsageFlags = - VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | - VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT | - VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | - VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT; - - return VK_SUCCESS; + return result; } VKAPI_ATTR @@ -882,21 +825,7 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, // Android users. This includes the ANGLE team (a layered implementation of // OpenGL-ES). - VkResult result = VK_SUCCESS; - if (formats) { - uint32_t transfer_count = all_formats.size(); - if (transfer_count > *count) { - transfer_count = *count; - result = VK_INCOMPLETE; - } - std::copy(all_formats.begin(), all_formats.begin() + transfer_count, - formats); - *count = transfer_count; - } else { - *count = all_formats.size(); - } - - return result; + return CopyWithIncomplete(all_formats, formats, count); } VKAPI_ATTR @@ -906,19 +835,134 @@ VkResult GetPhysicalDeviceSurfaceCapabilities2KHR( VkSurfaceCapabilities2KHR* pSurfaceCapabilities) { ATRACE_CALL(); - VkResult result = GetPhysicalDeviceSurfaceCapabilitiesKHR( - physicalDevice, pSurfaceInfo->surface, - &pSurfaceCapabilities->surfaceCapabilities); + auto surface = pSurfaceInfo->surface; + auto capabilities = &pSurfaceCapabilities->surfaceCapabilities; + + VkSurfacePresentModeEXT const *pPresentMode = nullptr; + for (auto pNext = reinterpret_cast<VkBaseInStructure const *>(pSurfaceInfo->pNext); + pNext; pNext = reinterpret_cast<VkBaseInStructure const *>(pNext->pNext)) { + switch (pNext->sType) { + case VK_STRUCTURE_TYPE_SURFACE_PRESENT_MODE_EXT: + pPresentMode = reinterpret_cast<VkSurfacePresentModeEXT const *>(pNext); + break; + + default: + break; + } + } - VkSurfaceCapabilities2KHR* caps = pSurfaceCapabilities; - while (caps->pNext) { - caps = reinterpret_cast<VkSurfaceCapabilities2KHR*>(caps->pNext); + int err; + int width, height; + int transform_hint; + int max_buffer_count; + if (surface == VK_NULL_HANDLE) { + const InstanceData& instance_data = GetData(physicalDevice); + ProcHook::Extension surfaceless = ProcHook::GOOGLE_surfaceless_query; + bool surfaceless_enabled = + instance_data.hook_extensions.test(surfaceless); + if (!surfaceless_enabled) { + // It is an error to pass a surface==VK_NULL_HANDLE unless the + // VK_GOOGLE_surfaceless_query extension is enabled + return VK_ERROR_SURFACE_LOST_KHR; + } + // Support for VK_GOOGLE_surfaceless_query. The primary purpose of this + // extension for this function is for + // VkSurfaceProtectedCapabilitiesKHR::supportsProtected. The following + // four values cannot be known without a surface. Default values will + // be supplied anyway, but cannot be relied upon. + width = 0xFFFFFFFF; + height = 0xFFFFFFFF; + transform_hint = VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR; + capabilities->minImageCount = 0xFFFFFFFF; + capabilities->maxImageCount = 0xFFFFFFFF; + } else { + ANativeWindow* window = SurfaceFromHandle(surface)->window.get(); - switch (caps->sType) { + err = window->query(window, NATIVE_WINDOW_DEFAULT_WIDTH, &width); + if (err != android::OK) { + ALOGE("NATIVE_WINDOW_DEFAULT_WIDTH query failed: %s (%d)", + strerror(-err), err); + return VK_ERROR_SURFACE_LOST_KHR; + } + err = window->query(window, NATIVE_WINDOW_DEFAULT_HEIGHT, &height); + if (err != android::OK) { + ALOGE("NATIVE_WINDOW_DEFAULT_WIDTH query failed: %s (%d)", + strerror(-err), err); + return VK_ERROR_SURFACE_LOST_KHR; + } + + err = window->query(window, NATIVE_WINDOW_TRANSFORM_HINT, + &transform_hint); + if (err != android::OK) { + ALOGE("NATIVE_WINDOW_TRANSFORM_HINT query failed: %s (%d)", + strerror(-err), err); + return VK_ERROR_SURFACE_LOST_KHR; + } + + err = window->query(window, NATIVE_WINDOW_MAX_BUFFER_COUNT, + &max_buffer_count); + if (err != android::OK) { + ALOGE("NATIVE_WINDOW_MAX_BUFFER_COUNT query failed: %s (%d)", + strerror(-err), err); + return VK_ERROR_SURFACE_LOST_KHR; + } + + if (pPresentMode && IsSharedPresentMode(pPresentMode->presentMode)) { + capabilities->minImageCount = 1; + capabilities->maxImageCount = 1; + } else if (pPresentMode && pPresentMode->presentMode == VK_PRESENT_MODE_MAILBOX_KHR) { + // TODO: use undequeued buffer requirement for more precise bound + capabilities->minImageCount = std::min(max_buffer_count, 4); + capabilities->maxImageCount = static_cast<uint32_t>(max_buffer_count); + } else { + // TODO: if we're able to, provide better bounds on the number of buffers + // for other modes as well. + capabilities->minImageCount = std::min(max_buffer_count, 3); + capabilities->maxImageCount = static_cast<uint32_t>(max_buffer_count); + } + } + + capabilities->currentExtent = + VkExtent2D{static_cast<uint32_t>(width), static_cast<uint32_t>(height)}; + + // TODO(http://b/134182502): Figure out what the max extent should be. + capabilities->minImageExtent = VkExtent2D{1, 1}; + capabilities->maxImageExtent = VkExtent2D{4096, 4096}; + + if (capabilities->maxImageExtent.height < + capabilities->currentExtent.height) { + capabilities->maxImageExtent.height = + capabilities->currentExtent.height; + } + + if (capabilities->maxImageExtent.width < + capabilities->currentExtent.width) { + capabilities->maxImageExtent.width = capabilities->currentExtent.width; + } + + capabilities->maxImageArrayLayers = 1; + + capabilities->supportedTransforms = kSupportedTransforms; + capabilities->currentTransform = + TranslateNativeToVulkanTransform(transform_hint); + + // On Android, window composition is a WindowManager property, not something + // associated with the bufferqueue. It can't be changed from here. + capabilities->supportedCompositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR; + + capabilities->supportedUsageFlags = + VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | + VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT | + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | + VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT; + + for (auto pNext = reinterpret_cast<VkBaseOutStructure*>(pSurfaceCapabilities->pNext); + pNext; pNext = reinterpret_cast<VkBaseOutStructure*>(pNext->pNext)) { + + switch (pNext->sType) { case VK_STRUCTURE_TYPE_SHARED_PRESENT_SURFACE_CAPABILITIES_KHR: { VkSharedPresentSurfaceCapabilitiesKHR* shared_caps = - reinterpret_cast<VkSharedPresentSurfaceCapabilitiesKHR*>( - caps); + reinterpret_cast<VkSharedPresentSurfaceCapabilitiesKHR*>(pNext); // Claim same set of usage flags are supported for // shared present modes as for other modes. shared_caps->sharedPresentSupportedUsageFlags = @@ -928,17 +972,64 @@ VkResult GetPhysicalDeviceSurfaceCapabilities2KHR( case VK_STRUCTURE_TYPE_SURFACE_PROTECTED_CAPABILITIES_KHR: { VkSurfaceProtectedCapabilitiesKHR* protected_caps = - reinterpret_cast<VkSurfaceProtectedCapabilitiesKHR*>(caps); + reinterpret_cast<VkSurfaceProtectedCapabilitiesKHR*>(pNext); protected_caps->supportsProtected = VK_TRUE; } break; + case VK_STRUCTURE_TYPE_SURFACE_PRESENT_SCALING_CAPABILITIES_EXT: { + VkSurfacePresentScalingCapabilitiesEXT* scaling_caps = + reinterpret_cast<VkSurfacePresentScalingCapabilitiesEXT*>(pNext); + // By default, Android stretches the buffer to fit the window, + // without preserving aspect ratio. Other modes are technically possible + // but consult with CoGS team before exposing them here! + scaling_caps->supportedPresentScaling = VK_PRESENT_SCALING_STRETCH_BIT_EXT; + + // Since we always scale, we don't support any gravity. + scaling_caps->supportedPresentGravityX = 0; + scaling_caps->supportedPresentGravityY = 0; + + // Scaled image limits are just the basic image limits + scaling_caps->minScaledImageExtent = capabilities->minImageExtent; + scaling_caps->maxScaledImageExtent = capabilities->maxImageExtent; + } break; + + case VK_STRUCTURE_TYPE_SURFACE_PRESENT_MODE_COMPATIBILITY_EXT: { + VkSurfacePresentModeCompatibilityEXT* mode_caps = + reinterpret_cast<VkSurfacePresentModeCompatibilityEXT*>(pNext); + + ALOG_ASSERT(pPresentMode, + "querying VkSurfacePresentModeCompatibilityEXT " + "requires VkSurfacePresentModeEXT to be provided"); + std::vector<VkPresentModeKHR> compatibleModes; + compatibleModes.push_back(pPresentMode->presentMode); + + switch (pPresentMode->presentMode) { + // Shared modes are both compatible with each other. + case VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR: + compatibleModes.push_back(VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR); + break; + case VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR: + compatibleModes.push_back(VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR); + break; + default: + // Other modes are only compatible with themselves. + // TODO: consider whether switching between FIFO and MAILBOX is reasonable + break; + } + + // Note: this does not generate VK_INCOMPLETE since we're nested inside + // a larger query and there would be no way to determine exactly where it came from. + CopyWithIncomplete(compatibleModes, mode_caps->pPresentModes, + &mode_caps->presentModeCount); + } break; + default: // Ignore all other extension structs break; } } - return result; + return VK_SUCCESS; } VKAPI_ATTR @@ -1097,18 +1188,7 @@ VkResult GetPhysicalDeviceSurfacePresentModesKHR(VkPhysicalDevice pdev, present_modes.push_back(VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR); } - uint32_t num_modes = uint32_t(present_modes.size()); - - VkResult result = VK_SUCCESS; - if (modes) { - if (*count < num_modes) - result = VK_INCOMPLETE; - *count = std::min(*count, num_modes); - std::copy_n(present_modes.data(), *count, modes); - } else { - *count = num_modes; - } - return result; + return CopyWithIncomplete(present_modes, modes, count); } VKAPI_ATTR @@ -1394,8 +1474,7 @@ VkResult CreateSwapchainKHR(VkDevice device, } VkSwapchainImageUsageFlagsANDROID swapchain_image_usage = 0; - if (create_info->presentMode == VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR || - create_info->presentMode == VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR) { + if (IsSharedPresentMode(create_info->presentMode)) { swapchain_image_usage |= VK_SWAPCHAIN_IMAGE_USAGE_SHARED_BIT_ANDROID; err = native_window_set_shared_buffer_mode(window, true); if (err != android::OK) { @@ -1545,8 +1624,6 @@ VkResult CreateSwapchainKHR(VkDevice device, Swapchain* swapchain = new (mem) Swapchain(surface, num_images, create_info->presentMode, TranslateVulkanToNativeTransform(create_info->preTransform)); - // -- Dequeue all buffers and create a VkImage for each -- - // Any failures during or after this must cancel the dequeued buffers. VkSwapchainImageCreateInfoANDROID swapchain_image_create = { #pragma clang diagnostic push @@ -1563,13 +1640,18 @@ VkResult CreateSwapchainKHR(VkDevice device, #pragma clang diagnostic pop .pNext = &swapchain_image_create, }; + VkImageCreateInfo image_create = { .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, - .pNext = &image_native_buffer, + .pNext = nullptr, .flags = createProtectedSwapchain ? VK_IMAGE_CREATE_PROTECTED_BIT : 0u, .imageType = VK_IMAGE_TYPE_2D, .format = create_info->imageFormat, - .extent = {0, 0, 1}, + .extent = { + create_info->imageExtent.width, + create_info->imageExtent.height, + 1 + }, .mipLevels = 1, .arrayLayers = 1, .samples = VK_SAMPLE_COUNT_1_BIT, @@ -1580,60 +1662,87 @@ VkResult CreateSwapchainKHR(VkDevice device, .pQueueFamilyIndices = create_info->pQueueFamilyIndices, }; - for (uint32_t i = 0; i < num_images; i++) { - Swapchain::Image& img = swapchain->images[i]; + // Note: don't do deferred allocation for shared present modes. There's only one buffer + // involved so very little benefit. + if ((create_info->flags & VK_SWAPCHAIN_CREATE_DEFERRED_MEMORY_ALLOCATION_BIT_EXT) && + !IsSharedPresentMode(create_info->presentMode)) { + // Don't want to touch the underlying gralloc buffers yet; + // instead just create unbound VkImages which will later be bound to memory inside + // AcquireNextImage. + VkImageSwapchainCreateInfoKHR image_swapchain_create = { + .sType = VK_STRUCTURE_TYPE_IMAGE_SWAPCHAIN_CREATE_INFO_KHR, + .pNext = nullptr, + .swapchain = HandleFromSwapchain(swapchain), + }; + image_create.pNext = &image_swapchain_create; - ANativeWindowBuffer* buffer; - err = window->dequeueBuffer(window, &buffer, &img.dequeue_fence); - if (err != android::OK) { - ALOGE("dequeueBuffer[%u] failed: %s (%d)", i, strerror(-err), err); - switch (-err) { - case ENOMEM: - result = VK_ERROR_OUT_OF_DEVICE_MEMORY; - break; - default: - result = VK_ERROR_SURFACE_LOST_KHR; - break; + for (uint32_t i = 0; i < num_images; i++) { + Swapchain::Image& img = swapchain->images[i]; + img.buffer = nullptr; + img.dequeued = false; + + result = dispatch.CreateImage(device, &image_create, nullptr, &img.image); + if (result != VK_SUCCESS) { + ALOGD("vkCreateImage w/ for deferred swapchain image failed: %u", result); + break; } - break; } - img.buffer = buffer; - img.dequeued = true; - - image_create.extent = - VkExtent3D{static_cast<uint32_t>(img.buffer->width), - static_cast<uint32_t>(img.buffer->height), - 1}; - image_native_buffer.handle = img.buffer->handle; - image_native_buffer.stride = img.buffer->stride; - image_native_buffer.format = img.buffer->format; - image_native_buffer.usage = int(img.buffer->usage); - android_convertGralloc0To1Usage(int(img.buffer->usage), - &image_native_buffer.usage2.producer, - &image_native_buffer.usage2.consumer); - image_native_buffer.usage3 = img.buffer->usage; - - ATRACE_BEGIN("CreateImage"); - result = - dispatch.CreateImage(device, &image_create, nullptr, &img.image); - ATRACE_END(); - if (result != VK_SUCCESS) { - ALOGD("vkCreateImage w/ native buffer failed: %u", result); - break; + } else { + // -- Dequeue all buffers and create a VkImage for each -- + // Any failures during or after this must cancel the dequeued buffers. + + for (uint32_t i = 0; i < num_images; i++) { + Swapchain::Image& img = swapchain->images[i]; + + ANativeWindowBuffer* buffer; + err = window->dequeueBuffer(window, &buffer, &img.dequeue_fence); + if (err != android::OK) { + ALOGE("dequeueBuffer[%u] failed: %s (%d)", i, strerror(-err), err); + switch (-err) { + case ENOMEM: + result = VK_ERROR_OUT_OF_DEVICE_MEMORY; + break; + default: + result = VK_ERROR_SURFACE_LOST_KHR; + break; + } + break; + } + img.buffer = buffer; + img.dequeued = true; + + image_native_buffer.handle = img.buffer->handle; + image_native_buffer.stride = img.buffer->stride; + image_native_buffer.format = img.buffer->format; + image_native_buffer.usage = int(img.buffer->usage); + android_convertGralloc0To1Usage(int(img.buffer->usage), + &image_native_buffer.usage2.producer, + &image_native_buffer.usage2.consumer); + image_native_buffer.usage3 = img.buffer->usage; + image_create.pNext = &image_native_buffer; + + ATRACE_BEGIN("CreateImage"); + result = + dispatch.CreateImage(device, &image_create, nullptr, &img.image); + ATRACE_END(); + if (result != VK_SUCCESS) { + ALOGD("vkCreateImage w/ native buffer failed: %u", result); + break; + } } - } - // -- Cancel all buffers, returning them to the queue -- - // If an error occurred before, also destroy the VkImage and release the - // buffer reference. Otherwise, we retain a strong reference to the buffer. - for (uint32_t i = 0; i < num_images; i++) { - Swapchain::Image& img = swapchain->images[i]; - if (img.dequeued) { - if (!swapchain->shared) { - window->cancelBuffer(window, img.buffer.get(), - img.dequeue_fence); - img.dequeue_fence = -1; - img.dequeued = false; + // -- Cancel all buffers, returning them to the queue -- + // If an error occurred before, also destroy the VkImage and release the + // buffer reference. Otherwise, we retain a strong reference to the buffer. + for (uint32_t i = 0; i < num_images; i++) { + Swapchain::Image& img = swapchain->images[i]; + if (img.dequeued) { + if (!swapchain->shared) { + window->cancelBuffer(window, img.buffer.get(), + img.dequeue_fence); + img.dequeue_fence = -1; + img.dequeued = false; + } } } } @@ -1756,6 +1865,64 @@ VkResult AcquireNextImageKHR(VkDevice device, break; } } + + // If this is a deferred alloc swapchain, this may be the first time we've + // seen a particular buffer. If so, there should be an empty slot. Find it, + // and bind the gralloc buffer to the VkImage for that slot. If there is no + // empty slot, then we dequeued an unexpected buffer. Non-deferred swapchains + // will also take this path, but will never have an empty slot since we + // populated them all upfront. + if (idx == swapchain.num_images) { + for (idx = 0; idx < swapchain.num_images; idx++) { + if (!swapchain.images[idx].buffer) { + // Note: this structure is technically required for + // Vulkan correctness, even though the driver is probably going + // to use everything from the VkNativeBufferANDROID below. + // This is kindof silly, but it's how we did the ANB + // side of VK_KHR_swapchain v69, so we're stuck with it unless + // we want to go tinkering with the ANB spec some more. + VkBindImageMemorySwapchainInfoKHR bimsi = { + .sType = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_SWAPCHAIN_INFO_KHR, + .pNext = nullptr, + .swapchain = swapchain_handle, + .imageIndex = idx, + }; + VkNativeBufferANDROID nb = { + .sType = VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID, + .pNext = &bimsi, + .handle = buffer->handle, + .stride = buffer->stride, + .format = buffer->format, + .usage = int(buffer->usage), + }; + VkBindImageMemoryInfo bimi = { + .sType = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO, + .pNext = &nb, + .image = swapchain.images[idx].image, + .memory = VK_NULL_HANDLE, + .memoryOffset = 0, + }; + result = GetData(device).driver.BindImageMemory2(device, 1, &bimi); + if (result != VK_SUCCESS) { + // This shouldn't really happen. If it does, something is probably + // unrecoverably wrong with the swapchain and its images. Cancel + // the buffer and declare the swapchain broken. + ALOGE("failed to do deferred gralloc buffer bind"); + window->cancelBuffer(window, buffer, fence_fd); + return VK_ERROR_OUT_OF_DATE_KHR; + } + + swapchain.images[idx].dequeued = true; + swapchain.images[idx].dequeue_fence = fence_fd; + swapchain.images[idx].buffer = buffer; + break; + } + } + } + + // The buffer doesn't match any slot. This shouldn't normally happen, but is + // possible if the bufferqueue is reconfigured behind libvulkan's back. If this + // happens, just declare the swapchain to be broken and the app will recreate it. if (idx == swapchain.num_images) { ALOGE("dequeueBuffer returned unrecognized buffer"); window->cancelBuffer(window, buffer, fence_fd); @@ -1881,12 +2048,32 @@ static void SetSwapchainFrameTimestamp(Swapchain &swapchain, const VkPresentTime } } +// EXT_swapchain_maintenance1 present mode change +static bool SetSwapchainPresentMode(ANativeWindow *window, VkPresentModeKHR mode) { + // There is no dynamic switching between non-shared present modes. + // All we support is switching between demand and continuous refresh. + if (!IsSharedPresentMode(mode)) + return true; + + int err = native_window_set_auto_refresh(window, + mode == VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR); + if (err != android::OK) { + ALOGE("native_window_set_auto_refresh() failed: %s (%d)", + strerror(-err), err); + return false; + } + + return true; +} + static VkResult PresentOneSwapchain( VkQueue queue, Swapchain& swapchain, uint32_t imageIndex, const VkPresentRegionKHR *pRegion, const VkPresentTimeGOOGLE *pTime, + VkFence presentFence, + const VkPresentModeKHR *pPresentMode, uint32_t waitSemaphoreCount, const VkSemaphore *pWaitSemaphores) { @@ -1917,12 +2104,33 @@ static VkResult PresentOneSwapchain( ANativeWindow* window = swapchain.surface.window.get(); if (swapchain_result == VK_SUCCESS) { + if (presentFence != VK_NULL_HANDLE) { + int fence_copy = fence < 0 ? -1 : dup(fence); + VkImportFenceFdInfoKHR iffi = { + VK_STRUCTURE_TYPE_IMPORT_FENCE_FD_INFO_KHR, + nullptr, + presentFence, + VK_FENCE_IMPORT_TEMPORARY_BIT, + VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT, + fence_copy, + }; + if (VK_SUCCESS != dispatch.ImportFenceFdKHR(device, &iffi) && fence_copy >= 0) { + // ImportFenceFdKHR takes ownership only if it succeeds + close(fence_copy); + } + } + if (pRegion) { SetSwapchainSurfaceDamage(window, pRegion); } if (pTime) { SetSwapchainFrameTimestamp(swapchain, pTime); } + if (pPresentMode) { + if (!SetSwapchainPresentMode(window, *pPresentMode)) + swapchain_result = WorstPresentResult(swapchain_result, + VK_ERROR_SURFACE_LOST_KHR); + } err = window->queueBuffer(window, img.buffer.get(), fence); // queueBuffer always closes fence, even on error @@ -2003,6 +2211,9 @@ VkResult QueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* present_info) { // Look at the pNext chain for supported extension structs: const VkPresentRegionsKHR* present_regions = nullptr; const VkPresentTimesInfoGOOGLE* present_times = nullptr; + const VkSwapchainPresentFenceInfoEXT* present_fences = nullptr; + const VkSwapchainPresentModeInfoEXT* present_modes = nullptr; + const VkPresentRegionsKHR* next = reinterpret_cast<const VkPresentRegionsKHR*>(present_info->pNext); while (next) { @@ -2014,6 +2225,14 @@ VkResult QueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* present_info) { present_times = reinterpret_cast<const VkPresentTimesInfoGOOGLE*>(next); break; + case VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_FENCE_INFO_EXT: + present_fences = + reinterpret_cast<const VkSwapchainPresentFenceInfoEXT*>(next); + break; + case VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_MODE_INFO_EXT: + present_modes = + reinterpret_cast<const VkSwapchainPresentModeInfoEXT*>(next); + break; default: ALOGV("QueuePresentKHR ignoring unrecognized pNext->sType = %x", next->sType); @@ -2029,6 +2248,15 @@ VkResult QueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* present_info) { present_times->swapchainCount != present_info->swapchainCount, "VkPresentTimesInfoGOOGLE::swapchainCount != " "VkPresentInfo::swapchainCount"); + ALOGV_IF(present_fences && + present_fences->swapchainCount != present_info->swapchainCount, + "VkSwapchainPresentFenceInfoEXT::swapchainCount != " + "VkPresentInfo::swapchainCount"); + ALOGV_IF(present_modes && + present_modes->swapchainCount != present_info->swapchainCount, + "VkSwapchainPresentModeInfoEXT::swapchainCount != " + "VkPresentInfo::swapchainCount"); + const VkPresentRegionKHR* regions = (present_regions) ? present_regions->pRegions : nullptr; const VkPresentTimeGOOGLE* times = @@ -2044,6 +2272,8 @@ VkResult QueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* present_info) { present_info->pImageIndices[sc], (regions && !swapchain.mailbox_mode) ? ®ions[sc] : nullptr, times ? ×[sc] : nullptr, + present_fences ? present_fences->pFences[sc] : VK_NULL_HANDLE, + present_modes ? &present_modes->pPresentModes[sc] : nullptr, present_info->waitSemaphoreCount, present_info->pWaitSemaphores); @@ -2268,5 +2498,35 @@ VkResult BindImageMemory2KHR(VkDevice device, out_bind_infos.empty() ? pBindInfos : out_bind_infos.data()); } +VKAPI_ATTR +VkResult ReleaseSwapchainImagesEXT(VkDevice /*device*/, + const VkReleaseSwapchainImagesInfoEXT* pReleaseInfo) { + ATRACE_CALL(); + + Swapchain& swapchain = *SwapchainFromHandle(pReleaseInfo->swapchain); + ANativeWindow* window = swapchain.surface.window.get(); + + // If in shared present mode, don't actually release the image back to the BQ. + // Both sides share it forever. + if (swapchain.shared) + return VK_SUCCESS; + + for (uint32_t i = 0; i < pReleaseInfo->imageIndexCount; i++) { + Swapchain::Image& img = swapchain.images[pReleaseInfo->pImageIndices[i]]; + window->cancelBuffer(window, img.buffer.get(), img.dequeue_fence); + + // cancelBuffer has taken ownership of the dequeue fence + img.dequeue_fence = -1; + // if we're still holding a release fence, get rid of it now + if (img.release_fence >= 0) { + close(img.release_fence); + img.release_fence = -1; + } + img.dequeued = false; + } + + return VK_SUCCESS; +} + } // namespace driver } // namespace vulkan diff --git a/vulkan/libvulkan/swapchain.h b/vulkan/libvulkan/swapchain.h index 4912ef1a33..280fe9b5a3 100644 --- a/vulkan/libvulkan/swapchain.h +++ b/vulkan/libvulkan/swapchain.h @@ -46,6 +46,7 @@ VKAPI_ATTR VkResult GetPhysicalDeviceSurfaceCapabilities2KHR(VkPhysicalDevice ph VKAPI_ATTR VkResult GetPhysicalDeviceSurfaceFormats2KHR(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo, uint32_t* pSurfaceFormatCount, VkSurfaceFormat2KHR* pSurfaceFormats); VKAPI_ATTR VkResult BindImageMemory2(VkDevice device, uint32_t bindInfoCount, const VkBindImageMemoryInfo* pBindInfos); VKAPI_ATTR VkResult BindImageMemory2KHR(VkDevice device, uint32_t bindInfoCount, const VkBindImageMemoryInfo* pBindInfos); +VKAPI_ATTR VkResult ReleaseSwapchainImagesEXT(VkDevice device, const VkReleaseSwapchainImagesInfoEXT* pReleaseInfo); // clang-format on } // namespace driver diff --git a/vulkan/scripts/driver_generator.py b/vulkan/scripts/driver_generator.py index af56764a21..78b550c202 100644 --- a/vulkan/scripts/driver_generator.py +++ b/vulkan/scripts/driver_generator.py @@ -35,6 +35,8 @@ _INTERCEPTED_EXTENSIONS = [ 'VK_KHR_surface', 'VK_KHR_surface_protected_capabilities', 'VK_KHR_swapchain', + 'VK_EXT_swapchain_maintenance1', + 'VK_EXT_surface_maintenance1', ] # Extensions known to vulkan::driver level. @@ -46,6 +48,7 @@ _KNOWN_EXTENSIONS = _INTERCEPTED_EXTENSIONS + [ 'VK_KHR_external_memory_capabilities', 'VK_KHR_external_semaphore_capabilities', 'VK_KHR_external_fence_capabilities', + 'VK_KHR_external_fence_fd', ] # Functions needed at vulkan::driver level. @@ -112,6 +115,9 @@ _NEEDED_COMMANDS = [ # For promoted VK_KHR_external_fence_capabilities 'vkGetPhysicalDeviceExternalFenceProperties', 'vkGetPhysicalDeviceExternalFencePropertiesKHR', + + # VK_KHR_swapchain_maintenance1 requirement + 'vkImportFenceFdKHR', ] # Functions intercepted at vulkan::driver level. |