diff options
145 files changed, 2827 insertions, 1886 deletions
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp index f6e4ec3c0d..408151479e 100644 --- a/cmds/servicemanager/ServiceManager.cpp +++ b/cmds/servicemanager/ServiceManager.cpp @@ -291,6 +291,8 @@ sp<IBinder> ServiceManager::tryGetService(const std::string& name, bool startIfN service = &(it->second); if (!service->allowIsolated && is_multiuser_uid_isolated(ctx.uid)) { + LOG(WARNING) << "Isolated app with UID " << ctx.uid << " requested '" << name + << "', but the service is not allowed for isolated apps."; return nullptr; } out = service->binder; diff --git a/data/etc/Android.bp b/data/etc/Android.bp index 226cae12aa..2143d93632 100644 --- a/data/etc/Android.bp +++ b/data/etc/Android.bp @@ -263,6 +263,12 @@ prebuilt_etc { } prebuilt_etc { + name: "android.hardware.threadnetwork.prebuilt.xml", + src: "android.hardware.threadnetwork.xml", + defaults: ["frameworks_native_data_etc_defaults"], +} + +prebuilt_etc { name: "android.hardware.usb.accessory.prebuilt.xml", src: "android.hardware.usb.accessory.xml", defaults: ["frameworks_native_data_etc_defaults"], diff --git a/data/etc/android.hardware.telephony.satellite.xml b/data/etc/android.hardware.telephony.satellite.xml index d36c958763..5966cba277 100644 --- a/data/etc/android.hardware.telephony.satellite.xml +++ b/data/etc/android.hardware.telephony.satellite.xml @@ -14,7 +14,7 @@ limitations under the License. --> -<!-- Feature for devices that support satellite communication via satellite vendor service APIs. --> +<!-- Feature for devices that support Satellite communication via Satellite HAL APIs. --> <permissions> <feature name="android.hardware.telephony.satellite" /> </permissions> diff --git a/data/etc/android.hardware.threadnetwork.xml b/data/etc/android.hardware.threadnetwork.xml new file mode 100644 index 0000000000..9cbdc905fb --- /dev/null +++ b/data/etc/android.hardware.threadnetwork.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2023 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<!-- Adds the feature indicating support for the ThreadNetwork API --> +<permissions> + <feature name="android.hardware.threadnetwork" /> +</permissions> diff --git a/include/input/Input.h b/include/input/Input.h index 527a47741c..ea856c8f43 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -282,6 +282,7 @@ enum { // Indicates that the key represents a special gesture that has been detected by // the touch firmware or driver. Causes touch events from the same device to be canceled. + // This policy flag prevents key events from changing touch mode state. POLICY_FLAG_GESTURE = 0x00000008, POLICY_FLAG_RAW_MASK = 0x0000ffff, diff --git a/include/input/KeyLayoutMap.h b/include/input/KeyLayoutMap.h index 8c3c74af20..b126abecea 100644 --- a/include/input/KeyLayoutMap.h +++ b/include/input/KeyLayoutMap.h @@ -17,13 +17,13 @@ #pragma once #include <android-base/result.h> +#include <input/InputDevice.h> + #include <stdint.h> #include <utils/Errors.h> #include <utils/Tokenizer.h> #include <set> -#include <input/InputDevice.h> - namespace android { struct AxisInfo { diff --git a/include/input/RingBuffer.h b/include/input/RingBuffer.h index 37fe5afeea..d2747d6fad 100644 --- a/include/input/RingBuffer.h +++ b/include/input/RingBuffer.h @@ -24,7 +24,6 @@ #include <type_traits> #include <utility> -#include <android-base/logging.h> #include <android-base/stringprintf.h> namespace android { @@ -277,15 +276,16 @@ private: // Converts the index of an element in [0, size()] to its corresponding index in mBuffer. size_type bufferIndex(size_type elementIndex) const { - CHECK_LE(elementIndex, size()); + if (elementIndex > size()) { + abort(); + } size_type index = mBegin + elementIndex; if (index >= capacity()) { index -= capacity(); } - CHECK_LT(index, capacity()) - << android::base::StringPrintf("Invalid index calculated for element (%zu) " - "in buffer of size %zu", - elementIndex, size()); + if (index >= capacity()) { + abort(); + } return index; } diff --git a/include/input/VelocityControl.h b/include/input/VelocityControl.h index f3c201e7c4..b78f63e1ae 100644 --- a/include/input/VelocityControl.h +++ b/include/input/VelocityControl.h @@ -88,7 +88,7 @@ public: VelocityControl(); /* Gets the various parameters. */ - VelocityControlParameters& getParameters(); + const VelocityControlParameters& getParameters() const; /* Sets the various parameters. */ void setParameters(const VelocityControlParameters& parameters); diff --git a/include/input/VelocityTracker.h b/include/input/VelocityTracker.h index da97c3e855..b58feac444 100644 --- a/include/input/VelocityTracker.h +++ b/include/input/VelocityTracker.h @@ -17,6 +17,7 @@ #pragma once #include <input/Input.h> +#include <input/RingBuffer.h> #include <utils/BitSet.h> #include <utils/Timers.h> #include <map> @@ -31,6 +32,8 @@ class VelocityTrackerStrategy; */ class VelocityTracker { public: + static const size_t MAX_DEGREE = 4; + enum class Strategy : int32_t { DEFAULT = -1, MIN = 0, @@ -47,23 +50,6 @@ public: MAX = LEGACY, }; - struct Estimator { - static const size_t MAX_DEGREE = 4; - - // Estimator time base. - nsecs_t time = 0; - - // Polynomial coefficients describing motion. - std::array<float, MAX_DEGREE + 1> coeff{}; - - // Polynomial degree (number of coefficients), or zero if no information is - // available. - uint32_t degree = 0; - - // Confidence (coefficient of determination), between 0 (no fit) and 1 (perfect fit). - float confidence = 0; - }; - /* * Contains all available velocity data from a VelocityTracker. */ @@ -124,11 +110,6 @@ public: // [-maxVelocity, maxVelocity], inclusive. ComputedVelocity getComputedVelocity(int32_t units, float maxVelocity); - // 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. - 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.value_or(-1); } @@ -169,14 +150,48 @@ public: 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; + virtual std::optional<float> getVelocity(int32_t pointerId) const = 0; }; +/** + * A `VelocityTrackerStrategy` that accumulates added data points and processes the accumulated data + * points when getting velocity. + */ +class AccumulatingVelocityTrackerStrategy : public VelocityTrackerStrategy { +public: + AccumulatingVelocityTrackerStrategy(nsecs_t horizonNanos, bool maintainHorizonDuringAdd); + + void addMovement(nsecs_t eventTime, int32_t pointerId, float position) override; + void clearPointer(int32_t pointerId) override; + +protected: + struct Movement { + nsecs_t eventTime; + float position; + }; + + // Number of samples to keep. + // If different strategies would like to maintain different history size, we can make this a + // protected const field. + static constexpr uint32_t HISTORY_SIZE = 20; + + /** + * Duration, in nanoseconds, since the latest movement where a movement may be considered for + * velocity calculation. + */ + const nsecs_t mHorizonNanos; + /** + * If true, data points outside of horizon (see `mHorizonNanos`) will be cleared after each + * addition of a new movement. + */ + const bool mMaintainHorizonDuringAdd; + std::map<int32_t /*pointerId*/, RingBuffer<Movement>> mMovements; +}; /* * Velocity tracker algorithm based on least-squares linear regression. */ -class LeastSquaresVelocityTrackerStrategy : public VelocityTrackerStrategy { +class LeastSquaresVelocityTrackerStrategy : public AccumulatingVelocityTrackerStrategy { public: enum class Weighting { // No weights applied. All data points are equally reliable. @@ -193,13 +208,11 @@ public: RECENT, }; - // Degree must be no greater than Estimator::MAX_DEGREE. + // Degree must be no greater than VelocityTracker::MAX_DEGREE. LeastSquaresVelocityTrackerStrategy(uint32_t degree, Weighting weighting = Weighting::NONE); ~LeastSquaresVelocityTrackerStrategy() override; - 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; + std::optional<float> getVelocity(int32_t pointerId) const override; private: // Sample horizon. @@ -207,23 +220,19 @@ private: // changes in direction. static const nsecs_t HORIZON = 100 * 1000000; // 100 ms - // Number of samples to keep. - static const uint32_t HISTORY_SIZE = 20; - - struct Movement { - nsecs_t eventTime; - float position; - }; - float chooseWeight(int32_t pointerId, uint32_t index) const; + /** + * An optimized least-squares solver for degree 2 and no weight (i.e. `Weighting.NONE`). + * The provided container of movements shall NOT be empty, and shall have the movements in + * chronological order. + */ + std::optional<float> solveUnweightedLeastSquaresDeg2( + const RingBuffer<Movement>& movements) const; const uint32_t mDegree; const Weighting mWeighting; - std::map<int32_t /*pointerId*/, size_t /*positionInArray*/> mIndex; - std::map<int32_t /*pointerId*/, std::array<Movement, HISTORY_SIZE>> mMovements; }; - /* * Velocity tracker algorithm that uses an IIR filter. */ @@ -235,7 +244,7 @@ public: 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; + std::optional<float> getVelocity(int32_t pointerId) const override; private: // Current state estimate for a particular pointer. @@ -252,49 +261,33 @@ private: void initState(State& state, nsecs_t eventTime, float pos) const; void updateState(State& state, nsecs_t eventTime, float pos) const; - void populateEstimator(const State& state, VelocityTracker::Estimator* outEstimator) const; }; /* * Velocity tracker strategy used prior to ICS. */ -class LegacyVelocityTrackerStrategy : public VelocityTrackerStrategy { +class LegacyVelocityTrackerStrategy : public AccumulatingVelocityTrackerStrategy { public: LegacyVelocityTrackerStrategy(); ~LegacyVelocityTrackerStrategy() override; - 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; + std::optional<float> getVelocity(int32_t pointerId) const override; private: // Oldest sample to consider when calculating the velocity. static const nsecs_t HORIZON = 200 * 1000000; // 100 ms - // Number of samples to keep. - static const uint32_t HISTORY_SIZE = 20; - // The minimum duration between samples when estimating velocity. static const nsecs_t MIN_DURATION = 10 * 1000000; // 10 ms - - struct Movement { - nsecs_t eventTime; - float position; - }; - - std::map<int32_t /*pointerId*/, size_t /*positionInArray*/> mIndex; - std::map<int32_t /*pointerId*/, std::array<Movement, HISTORY_SIZE>> mMovements; }; -class ImpulseVelocityTrackerStrategy : public VelocityTrackerStrategy { +class ImpulseVelocityTrackerStrategy : public AccumulatingVelocityTrackerStrategy { public: ImpulseVelocityTrackerStrategy(bool deltaValues); ~ImpulseVelocityTrackerStrategy() override; - 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; + std::optional<float> getVelocity(int32_t pointerId) const override; private: // Sample horizon. @@ -302,21 +295,10 @@ private: // changes in direction. static constexpr nsecs_t HORIZON = 100 * 1000000; // 100 ms - // Number of samples to keep. - static constexpr size_t HISTORY_SIZE = 20; - - struct Movement { - nsecs_t eventTime; - float position; - }; - // Whether or not the input movement values for the strategy come in the form of delta values. // If the input values are not deltas, the strategy needs to calculate deltas as part of its // velocity calculation. const bool mDeltaValues; - - 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/include/powermanager/PowerHalController.h b/include/powermanager/PowerHalController.h index 82e4551c3c..9e426d3ea3 100644 --- a/include/powermanager/PowerHalController.h +++ b/include/powermanager/PowerHalController.h @@ -17,11 +17,11 @@ #ifndef ANDROID_POWERHALCONTROLLER_H #define ANDROID_POWERHALCONTROLLER_H +#include <aidl/android/hardware/power/Boost.h> +#include <aidl/android/hardware/power/IPower.h> +#include <aidl/android/hardware/power/IPowerHintSession.h> +#include <aidl/android/hardware/power/Mode.h> #include <android-base/thread_annotations.h> -#include <android/hardware/power/Boost.h> -#include <android/hardware/power/IPower.h> -#include <android/hardware/power/IPowerHintSession.h> -#include <android/hardware/power/Mode.h> #include <powermanager/PowerHalWrapper.h> namespace android { @@ -55,11 +55,13 @@ public: virtual void init(); - virtual HalResult<void> setBoost(hardware::power::Boost boost, int32_t durationMs) override; - virtual HalResult<void> setMode(hardware::power::Mode mode, bool enabled) override; - virtual HalResult<sp<hardware::power::IPowerHintSession>> createHintSession( - int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, - int64_t durationNanos) override; + virtual HalResult<void> setBoost(aidl::android::hardware::power::Boost boost, + int32_t durationMs) override; + virtual HalResult<void> setMode(aidl::android::hardware::power::Mode mode, + bool enabled) override; + virtual HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>> + createHintSession(int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, + int64_t durationNanos) override; virtual HalResult<int64_t> getHintSessionPreferredRate() override; private: diff --git a/include/powermanager/PowerHalLoader.h b/include/powermanager/PowerHalLoader.h index e0384f31db..cbbfa597ba 100644 --- a/include/powermanager/PowerHalLoader.h +++ b/include/powermanager/PowerHalLoader.h @@ -17,11 +17,11 @@ #ifndef ANDROID_POWERHALLOADER_H #define ANDROID_POWERHALLOADER_H +#include <aidl/android/hardware/power/IPower.h> #include <android-base/thread_annotations.h> #include <android/hardware/power/1.1/IPower.h> #include <android/hardware/power/1.2/IPower.h> #include <android/hardware/power/1.3/IPower.h> -#include <android/hardware/power/IPower.h> namespace android { @@ -31,7 +31,7 @@ namespace power { class PowerHalLoader { public: static void unloadAll(); - static sp<hardware::power::IPower> loadAidl(); + static std::shared_ptr<aidl::android::hardware::power::IPower> loadAidl(); static sp<hardware::power::V1_0::IPower> loadHidlV1_0(); static sp<hardware::power::V1_1::IPower> loadHidlV1_1(); static sp<hardware::power::V1_2::IPower> loadHidlV1_2(); @@ -39,7 +39,7 @@ public: private: static std::mutex gHalMutex; - static sp<hardware::power::IPower> gHalAidl GUARDED_BY(gHalMutex); + static std::shared_ptr<aidl::android::hardware::power::IPower> gHalAidl GUARDED_BY(gHalMutex); static sp<hardware::power::V1_0::IPower> gHalHidlV1_0 GUARDED_BY(gHalMutex); static sp<hardware::power::V1_1::IPower> gHalHidlV1_1 GUARDED_BY(gHalMutex); static sp<hardware::power::V1_2::IPower> gHalHidlV1_2 GUARDED_BY(gHalMutex); diff --git a/include/powermanager/PowerHalWrapper.h b/include/powermanager/PowerHalWrapper.h index 8028aa86e1..4e4a1b000d 100644 --- a/include/powermanager/PowerHalWrapper.h +++ b/include/powermanager/PowerHalWrapper.h @@ -17,14 +17,15 @@ #ifndef ANDROID_POWERHALWRAPPER_H #define ANDROID_POWERHALWRAPPER_H +#include <aidl/android/hardware/power/Boost.h> +#include <aidl/android/hardware/power/IPower.h> +#include <aidl/android/hardware/power/IPowerHintSession.h> +#include <aidl/android/hardware/power/Mode.h> #include <android-base/thread_annotations.h> #include <android/hardware/power/1.1/IPower.h> #include <android/hardware/power/1.2/IPower.h> #include <android/hardware/power/1.3/IPower.h> -#include <android/hardware/power/Boost.h> -#include <android/hardware/power/IPower.h> -#include <android/hardware/power/IPowerHintSession.h> -#include <android/hardware/power/Mode.h> +#include <binder/Status.h> namespace android { @@ -47,7 +48,7 @@ public: } static HalResult<T> unsupported() { return HalResult("", /* unsupported= */ true); } - static HalResult<T> fromStatus(binder::Status status, T data) { + static HalResult<T> fromStatus(const binder::Status& status, T data) { if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) { return HalResult<T>::unsupported(); } @@ -56,14 +57,28 @@ public: } return HalResult<T>::failed(std::string(status.toString8().c_str())); } - static HalResult<T> fromStatus(hardware::power::V1_0::Status status, T data); + + static HalResult<T> fromStatus(const ndk::ScopedAStatus& status, T data) { + if (status.getExceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) { + return HalResult<T>::unsupported(); + } + if (status.isOk()) { + return HalResult<T>::ok(data); + } + return HalResult<T>::failed(std::string(status.getDescription())); + } template <typename R> - static HalResult<T> fromReturn(hardware::Return<R>& ret, T data); + static HalResult<T> fromReturn(hardware::Return<R>& ret, T data) { + return ret.isOk() ? HalResult<T>::ok(data) : HalResult<T>::failed(ret.description()); + } template <typename R> static HalResult<T> fromReturn(hardware::Return<R>& ret, hardware::power::V1_0::Status status, - T data); + T data) { + return ret.isOk() ? HalResult<T>::fromStatus(status, data) + : HalResult<T>::failed(ret.description()); + } // This will throw std::bad_optional_access if this result is not ok. const T& value() const { return mValue.value(); } @@ -91,12 +106,30 @@ public: static HalResult<void> failed(std::string msg) { return HalResult(std::move(msg)); } static HalResult<void> unsupported() { return HalResult(/* unsupported= */ true); } - static HalResult<void> fromStatus(status_t status); - static HalResult<void> fromStatus(binder::Status status); - static HalResult<void> fromStatus(hardware::power::V1_0::Status status); + static HalResult<void> fromStatus(const binder::Status& status) { + if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) { + return HalResult<void>::unsupported(); + } + if (status.isOk()) { + return HalResult<void>::ok(); + } + return HalResult<void>::failed(std::string(status.toString8().c_str())); + } + + static HalResult<void> fromStatus(const ndk::ScopedAStatus& status) { + if (status.getExceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) { + return HalResult<void>::unsupported(); + } + if (status.isOk()) { + return HalResult<void>::ok(); + } + return HalResult<void>::failed(std::string(status.getDescription())); + } template <typename R> - static HalResult<void> fromReturn(hardware::Return<R>& ret); + static HalResult<void> fromReturn(hardware::Return<R>& ret) { + return ret.isOk() ? HalResult<void>::ok() : HalResult<void>::failed(ret.description()); + } bool isOk() const { return !mUnsupported && !mFailed; } bool isFailed() const { return !mUnsupported && mFailed; } @@ -119,11 +152,12 @@ class HalWrapper { public: virtual ~HalWrapper() = default; - virtual HalResult<void> setBoost(hardware::power::Boost boost, int32_t durationMs) = 0; - virtual HalResult<void> setMode(hardware::power::Mode mode, bool enabled) = 0; - virtual HalResult<sp<hardware::power::IPowerHintSession>> createHintSession( - int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, - int64_t durationNanos) = 0; + virtual HalResult<void> setBoost(aidl::android::hardware::power::Boost boost, + int32_t durationMs) = 0; + virtual HalResult<void> setMode(aidl::android::hardware::power::Mode mode, bool enabled) = 0; + virtual HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>> + createHintSession(int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, + int64_t durationNanos) = 0; virtual HalResult<int64_t> getHintSessionPreferredRate() = 0; }; @@ -131,14 +165,15 @@ public: class EmptyHalWrapper : public HalWrapper { public: EmptyHalWrapper() = default; - ~EmptyHalWrapper() = default; + ~EmptyHalWrapper() override = default; - virtual HalResult<void> setBoost(hardware::power::Boost boost, int32_t durationMs) override; - virtual HalResult<void> setMode(hardware::power::Mode mode, bool enabled) override; - virtual HalResult<sp<hardware::power::IPowerHintSession>> createHintSession( + HalResult<void> setBoost(aidl::android::hardware::power::Boost boost, + int32_t durationMs) override; + HalResult<void> setMode(aidl::android::hardware::power::Mode mode, bool enabled) override; + HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>> createHintSession( int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos) override; - virtual HalResult<int64_t> getHintSessionPreferredRate() override; + HalResult<int64_t> getHintSessionPreferredRate() override; }; // Wrapper for the HIDL Power HAL v1.0. @@ -146,14 +181,15 @@ class HidlHalWrapperV1_0 : public HalWrapper { public: explicit HidlHalWrapperV1_0(sp<hardware::power::V1_0::IPower> handleV1_0) : mHandleV1_0(std::move(handleV1_0)) {} - virtual ~HidlHalWrapperV1_0() = default; + ~HidlHalWrapperV1_0() override = default; - virtual HalResult<void> setBoost(hardware::power::Boost boost, int32_t durationMs) override; - virtual HalResult<void> setMode(hardware::power::Mode mode, bool enabled) override; - virtual HalResult<sp<hardware::power::IPowerHintSession>> createHintSession( + HalResult<void> setBoost(aidl::android::hardware::power::Boost boost, + int32_t durationMs) override; + HalResult<void> setMode(aidl::android::hardware::power::Mode mode, bool enabled) override; + HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>> createHintSession( int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos) override; - virtual HalResult<int64_t> getHintSessionPreferredRate() override; + HalResult<int64_t> getHintSessionPreferredRate() override; protected: const sp<hardware::power::V1_0::IPower> mHandleV1_0; @@ -167,67 +203,71 @@ private: // Wrapper for the HIDL Power HAL v1.1. class HidlHalWrapperV1_1 : public HidlHalWrapperV1_0 { public: - HidlHalWrapperV1_1(sp<hardware::power::V1_1::IPower> handleV1_1) + explicit HidlHalWrapperV1_1(sp<hardware::power::V1_1::IPower> handleV1_1) : HidlHalWrapperV1_0(std::move(handleV1_1)) {} - virtual ~HidlHalWrapperV1_1() = default; + ~HidlHalWrapperV1_1() override = default; protected: - virtual HalResult<void> sendPowerHint(hardware::power::V1_3::PowerHint hintId, - uint32_t data) override; + HalResult<void> sendPowerHint(hardware::power::V1_3::PowerHint hintId, uint32_t data) override; }; // Wrapper for the HIDL Power HAL v1.2. class HidlHalWrapperV1_2 : public HidlHalWrapperV1_1 { public: - virtual HalResult<void> setBoost(hardware::power::Boost boost, int32_t durationMs) override; - virtual HalResult<void> setMode(hardware::power::Mode mode, bool enabled) override; - HidlHalWrapperV1_2(sp<hardware::power::V1_2::IPower> handleV1_2) + HalResult<void> setBoost(aidl::android::hardware::power::Boost boost, + int32_t durationMs) override; + HalResult<void> setMode(aidl::android::hardware::power::Mode mode, bool enabled) override; + explicit HidlHalWrapperV1_2(sp<hardware::power::V1_2::IPower> handleV1_2) : HidlHalWrapperV1_1(std::move(handleV1_2)) {} - virtual ~HidlHalWrapperV1_2() = default; + ~HidlHalWrapperV1_2() override = default; protected: - virtual HalResult<void> sendPowerHint(hardware::power::V1_3::PowerHint hintId, - uint32_t data) override; + HalResult<void> sendPowerHint(hardware::power::V1_3::PowerHint hintId, uint32_t data) override; }; // Wrapper for the HIDL Power HAL v1.3. class HidlHalWrapperV1_3 : public HidlHalWrapperV1_2 { public: - virtual HalResult<void> setMode(hardware::power::Mode mode, bool enabled) override; - HidlHalWrapperV1_3(sp<hardware::power::V1_3::IPower> handleV1_3) + HalResult<void> setMode(aidl::android::hardware::power::Mode mode, bool enabled) override; + explicit HidlHalWrapperV1_3(sp<hardware::power::V1_3::IPower> handleV1_3) : HidlHalWrapperV1_2(std::move(handleV1_3)) {} - virtual ~HidlHalWrapperV1_3() = default; + ~HidlHalWrapperV1_3() override = default; protected: - virtual HalResult<void> sendPowerHint(hardware::power::V1_3::PowerHint hintId, - uint32_t data) override; + HalResult<void> sendPowerHint(hardware::power::V1_3::PowerHint hintId, uint32_t data) override; }; // Wrapper for the AIDL Power HAL. class AidlHalWrapper : public HalWrapper { public: - explicit AidlHalWrapper(sp<hardware::power::IPower> handle) : mHandle(std::move(handle)) {} - virtual ~AidlHalWrapper() = default; - - virtual HalResult<void> setBoost(hardware::power::Boost boost, int32_t durationMs) override; - virtual HalResult<void> setMode(hardware::power::Mode mode, bool enabled) override; - virtual HalResult<sp<hardware::power::IPowerHintSession>> createHintSession( + explicit AidlHalWrapper(std::shared_ptr<aidl::android::hardware::power::IPower> handle) + : mHandle(std::move(handle)) {} + ~AidlHalWrapper() override = default; + + HalResult<void> setBoost(aidl::android::hardware::power::Boost boost, + int32_t durationMs) override; + HalResult<void> setMode(aidl::android::hardware::power::Mode mode, bool enabled) override; + HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>> createHintSession( int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos) override; - virtual HalResult<int64_t> getHintSessionPreferredRate() override; + HalResult<int64_t> getHintSessionPreferredRate() override; private: // Control access to the boost and mode supported arrays. std::mutex mBoostMutex; std::mutex mModeMutex; - sp<hardware::power::IPower> mHandle; + std::shared_ptr<aidl::android::hardware::power::IPower> mHandle; // Android framework only sends boost upto DISPLAY_UPDATE_IMMINENT. // Need to increase the array size if more boost supported. - std::array<std::atomic<HalSupport>, - static_cast<int32_t>(hardware::power::Boost::DISPLAY_UPDATE_IMMINENT) + 1> + std::array< + std::atomic<HalSupport>, + static_cast<int32_t>(aidl::android::hardware::power::Boost::DISPLAY_UPDATE_IMMINENT) + + 1> mBoostSupportedArray GUARDED_BY(mBoostMutex) = {HalSupport::UNKNOWN}; std::array<std::atomic<HalSupport>, - static_cast<int32_t>(*(android::enum_range<hardware::power::Mode>().end() - 1)) + 1> + static_cast<int32_t>( + *(ndk::enum_range<aidl::android::hardware::power::Mode>().end() - 1)) + + 1> mModeSupportedArray GUARDED_BY(mModeMutex) = {HalSupport::UNKNOWN}; }; diff --git a/libs/binder/ActivityManager.cpp b/libs/binder/ActivityManager.cpp index aca5009148..526427663b 100644 --- a/libs/binder/ActivityManager.cpp +++ b/libs/binder/ActivityManager.cpp @@ -21,6 +21,7 @@ #include <binder/ActivityManager.h> #include <binder/Binder.h> #include <binder/IServiceManager.h> +#include <binder/ProcessState.h> #include <utils/SystemClock.h> @@ -33,27 +34,36 @@ ActivityManager::ActivityManager() sp<IActivityManager> ActivityManager::getService() { std::lock_guard<Mutex> scoped_lock(mLock); - int64_t startTime = 0; sp<IActivityManager> service = mService; - while (service == nullptr || !IInterface::asBinder(service)->isBinderAlive()) { - sp<IBinder> binder = defaultServiceManager()->checkService(String16("activity")); - if (binder == nullptr) { - // Wait for the activity service to come back... - if (startTime == 0) { - startTime = uptimeMillis(); - ALOGI("Waiting for activity service"); - } else if ((uptimeMillis() - startTime) > 1000000) { - ALOGW("Waiting too long for activity service, giving up"); - service = nullptr; - break; - } - usleep(25000); - } else { + if (ProcessState::self()->isThreadPoolStarted()) { + if (service == nullptr || !IInterface::asBinder(service)->isBinderAlive()) { + sp<IBinder> binder = defaultServiceManager()->waitForService(String16("activity")); service = interface_cast<IActivityManager>(binder); mService = service; } + } else { + ALOGI("Thread pool not started. Polling for activity service."); + int64_t startTime = 0; + while (service == nullptr || !IInterface::asBinder(service)->isBinderAlive()) { + sp<IBinder> binder = defaultServiceManager()->checkService(String16("activity")); + if (binder == nullptr) { + // Wait for the activity service to come back... + if (startTime == 0) { + startTime = uptimeMillis(); + ALOGI("Waiting for activity service"); + } else if ((uptimeMillis() - startTime) > 1000000) { + ALOGW("Waiting too long for activity service, giving up"); + service = nullptr; + break; + } + usleep(25000); + } else { + service = interface_cast<IActivityManager>(binder); + mService = service; + } + } } - return service; + return mService; } int ActivityManager::openContentUri(const String16& stringUri) diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index 9b685f9145..bbaa419cf0 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -69,6 +69,10 @@ typedef uintptr_t binder_uintptr_t; #endif // BINDER_WITH_KERNEL_IPC +#ifdef __BIONIC__ +#include <android/fdsan.h> +#endif + #define LOG_REFS(...) // #define LOG_REFS(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__) #define LOG_ALLOC(...) @@ -109,6 +113,37 @@ constexpr size_t kMaxFds = 1024; // Maximum size of a blob to transfer in-place. static const size_t BLOB_INPLACE_LIMIT = 16 * 1024; +#if defined(__BIONIC__) +static void FdTag(int fd, const void* old_addr, const void* new_addr) { + if (android_fdsan_exchange_owner_tag) { + uint64_t old_tag = android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_PARCEL, + reinterpret_cast<uint64_t>(old_addr)); + uint64_t new_tag = android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_PARCEL, + reinterpret_cast<uint64_t>(new_addr)); + android_fdsan_exchange_owner_tag(fd, old_tag, new_tag); + } +} +static void FdTagClose(int fd, const void* addr) { + if (android_fdsan_close_with_tag) { + uint64_t tag = android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_PARCEL, + reinterpret_cast<uint64_t>(addr)); + android_fdsan_close_with_tag(fd, tag); + } else { + close(fd); + } +} +#else +static void FdTag(int fd, const void* old_addr, const void* new_addr) { + (void)fd; + (void)old_addr; + (void)new_addr; +} +static void FdTagClose(int fd, const void* addr) { + (void)addr; + close(fd); +} +#endif + enum { BLOB_INPLACE = 0, BLOB_ASHMEM_IMMUTABLE = 1, @@ -134,6 +169,9 @@ static void acquire_object(const sp<ProcessState>& proc, const flat_binder_objec return; } case BINDER_TYPE_FD: { + if (obj.cookie != 0) { // owned + FdTag(obj.handle, nullptr, who); + } return; } } @@ -159,8 +197,10 @@ static void release_object(const sp<ProcessState>& proc, const flat_binder_objec return; } case BINDER_TYPE_FD: { + // note: this path is not used when mOwner, so the tag is also released + // in 'closeFileDescriptors' if (obj.cookie != 0) { // owned - close(obj.handle); + FdTagClose(obj.handle, who); } return; } @@ -554,7 +594,6 @@ status_t Parcel::appendFrom(const Parcel* parcel, size_t offset, size_t len) { kernelFields->mObjectsSize++; flat_binder_object* flat = reinterpret_cast<flat_binder_object*>(mData + off); - acquire_object(proc, *flat, this); if (flat->hdr.type == BINDER_TYPE_FD) { // If this is a file descriptor, we need to dup it so the @@ -567,6 +606,8 @@ status_t Parcel::appendFrom(const Parcel* parcel, size_t offset, size_t len) { err = FDS_NOT_ALLOWED; } } + + acquire_object(proc, *flat, this); } } #else @@ -2553,7 +2594,8 @@ void Parcel::closeFileDescriptors() { reinterpret_cast<flat_binder_object*>(mData + kernelFields->mObjects[i]); if (flat->hdr.type == BINDER_TYPE_FD) { // ALOGI("Closing fd: %ld", flat->handle); - close(flat->handle); + // FDs from the kernel are always owned + FdTagClose(flat->handle, this); } } #else // BINDER_WITH_KERNEL_IPC @@ -2634,6 +2676,10 @@ void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize, const bin kernelFields->mObjectsSize = 0; break; } + if (type == BINDER_TYPE_FD) { + // FDs from the kernel are always owned + FdTag(flat->handle, 0, this); + } minOffset = offset + sizeof(flat_binder_object); } scanForFds(); diff --git a/libs/bufferstreams/Android.bp b/libs/bufferstreams/Android.bp new file mode 100644 index 0000000000..e1dc9bafca --- /dev/null +++ b/libs/bufferstreams/Android.bp @@ -0,0 +1,17 @@ +// Copyright (C) 2023 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + default_applicable_licenses: ["frameworks_native_license"], +} diff --git a/libs/bufferstreams/OWNERS b/libs/bufferstreams/OWNERS new file mode 100644 index 0000000000..32b72b8592 --- /dev/null +++ b/libs/bufferstreams/OWNERS @@ -0,0 +1,7 @@ +carlosmr@google.com +hibrian@google.com +jreck@google.com +jshargo@google.com + +file:/services/surfaceflinger/OWNERS + diff --git a/libs/gui/DisplayEventReceiver.cpp b/libs/gui/DisplayEventReceiver.cpp index 6849a95d1e..67cbc7b111 100644 --- a/libs/gui/DisplayEventReceiver.cpp +++ b/libs/gui/DisplayEventReceiver.cpp @@ -99,7 +99,7 @@ status_t DisplayEventReceiver::getLatestVsyncEventData( if (mEventConnection != nullptr) { auto status = mEventConnection->getLatestVsyncEventData(outVsyncEventData); if (!status.isOk()) { - ALOGE("Failed to get latest vsync event data: %s", status.exceptionMessage().c_str()); + ALOGE("Failed to get latest vsync event data: %s", status.toString8().c_str()); return status.transactionError(); } return NO_ERROR; diff --git a/libs/gui/fuzzer/Android.bp b/libs/gui/fuzzer/Android.bp index 82e1b5ae4d..75bae7650f 100644 --- a/libs/gui/fuzzer/Android.bp +++ b/libs/gui/fuzzer/Android.bp @@ -46,7 +46,7 @@ cc_defaults { "android.hardware.configstore-utils", "android.hardware.graphics.bufferqueue@1.0", "android.hardware.graphics.bufferqueue@2.0", - "android.hardware.power-V4-cpp", + "android.hardware.power-V4-ndk", "android.hidl.token@1.0", "libSurfaceFlingerProp", "libgui", @@ -72,6 +72,14 @@ cc_defaults { "android-media-fuzzing-reports@google.com", ], componentid: 155276, + hotlists: [ + "4593311", + ], + description: "The fuzzer targets the APIs of libgui library", + vector: "local_no_privileges_required", + service_privilege: "privileged", + users: "multi_user", + fuzzed_code_usage: "shipped", }, } diff --git a/libs/gui/fuzzer/libgui_surfaceComposerClient_fuzzer.cpp b/libs/gui/fuzzer/libgui_surfaceComposerClient_fuzzer.cpp index 95b7f39c11..3e37e4850e 100644 --- a/libs/gui/fuzzer/libgui_surfaceComposerClient_fuzzer.cpp +++ b/libs/gui/fuzzer/libgui_surfaceComposerClient_fuzzer.cpp @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include <android/hardware/power/Boost.h> +#include <aidl/android/hardware/power/Boost.h> #include <fuzzbinder/libbinder_driver.h> #include <gui/Surface.h> #include <gui/SurfaceComposerClient.h> @@ -39,10 +39,13 @@ constexpr ui::ColorMode kColormodes[] = {ui::ColorMode::NATIVE, ui::ColorMode::BT2100_HLG, ui::ColorMode::DISPLAY_BT2020}; -constexpr hardware::power::Boost kBoost[] = { - hardware::power::Boost::INTERACTION, hardware::power::Boost::DISPLAY_UPDATE_IMMINENT, - hardware::power::Boost::ML_ACC, hardware::power::Boost::AUDIO_LAUNCH, - hardware::power::Boost::CAMERA_LAUNCH, hardware::power::Boost::CAMERA_SHOT, +constexpr aidl::android::hardware::power::Boost kBoost[] = { + aidl::android::hardware::power::Boost::INTERACTION, + aidl::android::hardware::power::Boost::DISPLAY_UPDATE_IMMINENT, + aidl::android::hardware::power::Boost::ML_ACC, + aidl::android::hardware::power::Boost::AUDIO_LAUNCH, + aidl::android::hardware::power::Boost::CAMERA_LAUNCH, + aidl::android::hardware::power::Boost::CAMERA_SHOT, }; constexpr gui::TouchOcclusionMode kMode[] = { @@ -284,7 +287,7 @@ void SurfaceComposerClientFuzzer::invokeSurfaceComposerClient() { SurfaceComposerClient::doUncacheBufferTransaction(mFdp.ConsumeIntegral<uint64_t>()); SurfaceComposerClient::setDisplayBrightness(displayToken, getBrightness(&mFdp)); - hardware::power::Boost boostId = mFdp.PickValueInArray(kBoost); + aidl::android::hardware::power::Boost boostId = mFdp.PickValueInArray(kBoost); SurfaceComposerClient::notifyPowerBoost((int32_t)boostId); String8 surfaceName((mFdp.ConsumeRandomLengthString(kRandomStringMaxBytes)).c_str()); diff --git a/libs/gui/include/gui/JankInfo.h b/libs/gui/include/gui/JankInfo.h index 1dddeba616..bf354e7bb4 100644 --- a/libs/gui/include/gui/JankInfo.h +++ b/libs/gui/include/gui/JankInfo.h @@ -46,6 +46,8 @@ enum JankType { // where the previous frame was presented in the current frame's expected vsync. This pushes the // current frame to the next vsync. The behavior is similar to BufferStuffing. SurfaceFlingerStuffing = 0x100, + // Frame was dropped, as a newer frame was ready and replaced this frame. + Dropped = 0x200, }; } // namespace android diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index 62e5f89d21..7aa7068538 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -267,7 +267,8 @@ struct layer_state_t { layer_state_t::HIERARCHY_CHANGES | layer_state_t::eAlphaChanged | layer_state_t::eColorTransformChanged | layer_state_t::eCornerRadiusChanged | layer_state_t::eFlagsChanged | layer_state_t::eTrustedOverlayChanged | - layer_state_t::eFrameRateChanged | layer_state_t::eFixedTransformHintChanged; + layer_state_t::eFrameRateChanged | layer_state_t::eFrameRateSelectionPriority | + layer_state_t::eFixedTransformHintChanged; // Changes affecting data sent to input. static constexpr uint64_t INPUT_CHANGES = layer_state_t::eInputInfoChanged | diff --git a/libs/input/Android.bp b/libs/input/Android.bp index 8a17d8a831..769677c6ff 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -109,62 +109,6 @@ cc_library_static { ], } -genrule { - name: "libinput_cxx_bridge_code", - tools: ["cxxbridge"], - cmd: "$(location cxxbridge) $(in) >> $(out)", - srcs: ["input_verifier.rs"], - out: ["inputverifier_generated.cpp"], -} - -genrule { - name: "libinput_cxx_bridge_header", - tools: ["cxxbridge"], - cmd: "$(location cxxbridge) $(in) --header >> $(out)", - srcs: ["input_verifier.rs"], - out: ["input_verifier.rs.h"], -} - -rust_defaults { - name: "libinput_rust_defaults", - srcs: ["input_verifier.rs"], - host_supported: true, - rustlibs: [ - "libbitflags", - "libcxx", - "libinput_bindgen", - "liblogger", - "liblog_rust", - "inputconstants-rust", - ], - - shared_libs: [ - "libbase", - "liblog", - ], -} - -rust_ffi_static { - name: "libinput_rust", - crate_name: "input", - defaults: ["libinput_rust_defaults"], -} - -rust_test { - name: "libinput_rust_test", - defaults: ["libinput_rust_defaults"], - whole_static_libs: [ - "libinput_from_rust_to_cpp", - ], - test_options: { - unit_test: true, - }, - test_suites: ["device_tests"], - sanitize: { - hwaddress: true, - }, -} - cc_library { name: "libinput", cpp_std: "c++20", @@ -174,12 +118,18 @@ cc_library { "-Wextra", "-Werror", "-Wno-unused-parameter", + "-Wthread-safety", + "-Wshadow", + "-Wshadow-field-in-constructor-modified", + "-Wshadow-uncaptured-local", + "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION", ], srcs: [ - "FromRustToCpp.cpp", + "android/os/IInputFlinger.aidl", "Input.cpp", "InputDevice.cpp", "InputEventLabels.cpp", + "InputTransport.cpp", "InputVerifier.cpp", "Keyboard.cpp", "KeyCharacterMap.cpp", @@ -208,14 +158,14 @@ cc_library { "toolbox_input_labels", ], - generated_sources: ["libinput_cxx_bridge_code"], - shared_libs: [ "libbase", + "libbinder", "libcutils", "liblog", "libPlatformProperties", "libtinyxml2", + "libutils", "libvintf", ], @@ -231,15 +181,17 @@ cc_library { static_libs: [ "inputconstants-cpp", + "libgui_window_info_static", "libui-types", "libtflite_static", ], whole_static_libs: [ - "libinput_rust", + "libinput_rust_ffi", ], export_static_lib_headers: [ + "libgui_window_info_static", "libui-types", ], @@ -250,54 +202,15 @@ cc_library { target: { android: { - srcs: [ - "InputTransport.cpp", - "android/os/IInputFlinger.aidl", - ], - - export_shared_lib_headers: ["libbinder"], - - shared_libs: [ - "libutils", - "libbinder", - ], - - static_libs: [ - "libgui_window_info_static", - ], - - export_static_lib_headers: [ - "libgui_window_info_static", - ], - required: [ "motion_predictor_model_prebuilt", "motion_predictor_model_config", ], }, host: { - shared: { - enabled: false, - }, include_dirs: [ "bionic/libc/kernel/android/uapi/", "bionic/libc/kernel/uapi", - "frameworks/native/libs/arect/include", - ], - }, - host_linux: { - srcs: [ - "InputTransport.cpp", - ], - static_libs: [ - "libgui_window_info_static", - ], - shared_libs: [ - "libbinder", - ], - - export_static_lib_headers: [ - "libgui_window_info_static", ], }, }, diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index 4d3d8bc31c..0b0309e27b 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -409,7 +409,7 @@ status_t InputChannel::openInputChannelPair(const std::string& name, setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize)); setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize)); - sp<IBinder> token = new BBinder(); + sp<IBinder> token = sp<BBinder>::make(); std::string serverChannelName = name + " (server)"; android::base::unique_fd serverFd(sockets[0]); diff --git a/libs/input/InputVerifier.cpp b/libs/input/InputVerifier.cpp index 32b4ca0fc1..b0546a5243 100644 --- a/libs/input/InputVerifier.cpp +++ b/libs/input/InputVerifier.cpp @@ -18,7 +18,7 @@ #include <android-base/logging.h> #include <input/InputVerifier.h> -#include "input_verifier.rs.h" +#include "input_cxx_bridge.rs.h" using android::base::Error; using android::base::Result; diff --git a/libs/input/MotionPredictor.cpp b/libs/input/MotionPredictor.cpp index 68e688817b..80a8d03ab9 100644 --- a/libs/input/MotionPredictor.cpp +++ b/libs/input/MotionPredictor.cpp @@ -25,9 +25,9 @@ #include <string> #include <vector> +#include <android-base/logging.h> #include <android-base/strings.h> #include <android/input.h> -#include <log/log.h> #include <attestation/HmacKeyManager.h> #include <ftl/enum.h> diff --git a/libs/input/VelocityControl.cpp b/libs/input/VelocityControl.cpp index 5720099033..c835a081a5 100644 --- a/libs/input/VelocityControl.cpp +++ b/libs/input/VelocityControl.cpp @@ -37,7 +37,7 @@ VelocityControl::VelocityControl() { reset(); } -VelocityControlParameters& VelocityControl::getParameters() { +const VelocityControlParameters& VelocityControl::getParameters() const{ return mParameters; } diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp index 8551e5fa1c..87c7768f25 100644 --- a/libs/input/VelocityTracker.cpp +++ b/libs/input/VelocityTracker.cpp @@ -22,7 +22,6 @@ #include <math.h> #include <optional> -#include <android-base/stringprintf.h> #include <input/PrintTools.h> #include <input/VelocityTracker.h> #include <utils/BitSet.h> @@ -56,6 +55,9 @@ const bool DEBUG_IMPULSE = // Nanoseconds per milliseconds. static const nsecs_t NANOS_PER_MS = 1000000; +// Seconds per nanosecond. +static const float SECONDS_PER_NANO = 1E-9; + // All axes supported for velocity tracking, mapped to their default strategies. // Although other strategies are available for testing and comparison purposes, // the default strategy is the one that applications will actually use. Be very careful @@ -268,12 +270,8 @@ void VelocityTracker::addMovement(nsecs_t eventTime, int32_t pointerId, int32_t ", 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); + ALOGD(" %d: axis=%d, position=%0.3f, velocity=%s", pointerId, axis, position, + toString(getVelocity(axis, pointerId)).c_str()); } } @@ -349,9 +347,9 @@ void VelocityTracker::addMovement(const MotionEvent* event) { } 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]; + const auto& it = mConfiguredStrategies.find(axis); + if (it != mConfiguredStrategies.end()) { + return it->second->getVelocity(pointerId); } return {}; } @@ -374,57 +372,53 @@ VelocityTracker::ComputedVelocity VelocityTracker::getComputedVelocity(int32_t u return computedVelocity; } -std::optional<VelocityTracker::Estimator> VelocityTracker::getEstimator(int32_t axis, - int32_t pointerId) const { - const auto& it = mConfiguredStrategies.find(axis); - if (it == mConfiguredStrategies.end()) { - return std::nullopt; - } - return it->second->getEstimator(pointerId); -} - -// --- LeastSquaresVelocityTrackerStrategy --- +AccumulatingVelocityTrackerStrategy::AccumulatingVelocityTrackerStrategy( + nsecs_t horizonNanos, bool maintainHorizonDuringAdd) + : mHorizonNanos(horizonNanos), mMaintainHorizonDuringAdd(maintainHorizonDuringAdd) {} -LeastSquaresVelocityTrackerStrategy::LeastSquaresVelocityTrackerStrategy(uint32_t degree, - Weighting weighting) - : mDegree(degree), mWeighting(weighting) {} - -LeastSquaresVelocityTrackerStrategy::~LeastSquaresVelocityTrackerStrategy() { -} - -void LeastSquaresVelocityTrackerStrategy::clearPointer(int32_t pointerId) { - mIndex.erase(pointerId); +void AccumulatingVelocityTrackerStrategy::clearPointer(int32_t pointerId) { mMovements.erase(pointerId); } -void LeastSquaresVelocityTrackerStrategy::addMovement(nsecs_t eventTime, int32_t pointerId, +void AccumulatingVelocityTrackerStrategy::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) { + auto [ringBufferIt, _] = mMovements.try_emplace(pointerId, HISTORY_SIZE); + RingBuffer<Movement>& movements = ringBufferIt->second; + const size_t size = movements.size(); + + if (size != 0 && movements[size - 1].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. + // for this time (i.e. pop out the last element, and insert the updated movement). // 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; + movements.popBack(); } - Movement& movement = movementIt->second[index]; - movement.eventTime = eventTime; - movement.position = position; + movements.pushBack({eventTime, position}); + + // Clear movements that do not fall within `mHorizonNanos` of the latest movement. + // Note that, if in the future we decide to use more movements (i.e. increase HISTORY_SIZE), + // we can consider making this step binary-search based, which will give us some improvement. + if (mMaintainHorizonDuringAdd) { + while (eventTime - movements[0].eventTime > mHorizonNanos) { + movements.popFront(); + } + } } +// --- LeastSquaresVelocityTrackerStrategy --- + +LeastSquaresVelocityTrackerStrategy::LeastSquaresVelocityTrackerStrategy(uint32_t degree, + Weighting weighting) + : AccumulatingVelocityTrackerStrategy(HORIZON /*horizonNanos*/, + true /*maintainHorizonDuringAdd*/), + mDegree(degree), + mWeighting(weighting) {} + +LeastSquaresVelocityTrackerStrategy::~LeastSquaresVelocityTrackerStrategy() {} + /** * Solves a linear least squares problem to obtain a N degree polynomial that fits * the specified input data as nearly as possible. @@ -474,10 +468,9 @@ void LeastSquaresVelocityTrackerStrategy::addMovement(nsecs_t eventTime, int32_t * http://en.wikipedia.org/wiki/Numerical_methods_for_linear_least_squares * 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, - std::array<float, VelocityTracker::Estimator::MAX_DEGREE + 1>& outB, - float* outDet) { +static std::optional<float> solveLeastSquares(const std::vector<float>& x, + const std::vector<float>& y, + const std::vector<float>& w, uint32_t n) { 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), @@ -515,7 +508,7 @@ static bool solveLeastSquares(const std::vector<float>& x, const std::vector<flo if (norm < 0.000001f) { // vectors are linearly dependent or zero so no solution ALOGD_IF(DEBUG_STRATEGY, " - no solution, norm=%f", norm); - return false; + return {}; } float invNorm = 1.0f / norm; @@ -549,6 +542,7 @@ static bool solveLeastSquares(const std::vector<float>& x, const std::vector<flo for (uint32_t h = 0; h < m; h++) { wy[h] = y[h] * w[h]; } + std::array<float, VelocityTracker::MAX_DEGREE + 1> outB; for (uint32_t i = n; i != 0; ) { i--; outB[i] = vectorDot(&q[i][0], wy, m); @@ -570,42 +564,46 @@ static bool solveLeastSquares(const std::vector<float>& x, const std::vector<flo } ymean /= m; - float sserr = 0; - float sstot = 0; - for (uint32_t h = 0; h < m; h++) { - float err = y[h] - outB[0]; - float term = 1; - for (uint32_t i = 1; i < n; i++) { - term *= x[h]; - err -= term * outB[i]; + if (DEBUG_STRATEGY) { + float sserr = 0; + float sstot = 0; + for (uint32_t h = 0; h < m; h++) { + float err = y[h] - outB[0]; + float term = 1; + for (uint32_t i = 1; i < n; i++) { + term *= x[h]; + err -= term * outB[i]; + } + sserr += w[h] * w[h] * err * err; + float var = y[h] - ymean; + sstot += w[h] * w[h] * var * var; } - sserr += w[h] * w[h] * err * err; - float var = y[h] - ymean; - sstot += w[h] * w[h] * var * var; + ALOGD(" - sserr=%f", sserr); + ALOGD(" - sstot=%f", sstot); } - *outDet = sstot > 0.000001f ? 1.0f - (sserr / sstot) : 1; - ALOGD_IF(DEBUG_STRATEGY, " - sserr=%f", sserr); - ALOGD_IF(DEBUG_STRATEGY, " - sstot=%f", sstot); - ALOGD_IF(DEBUG_STRATEGY, " - det=%f", *outDet); - - return true; + return outB[1]; } /* * Optimized unweighted second-order least squares fit. About 2x speed improvement compared to * the default implementation */ -static std::optional<std::array<float, 3>> solveUnweightedLeastSquaresDeg2( - const std::vector<float>& x, const std::vector<float>& y) { - const size_t count = x.size(); - LOG_ALWAYS_FATAL_IF(count != y.size(), "Mismatching array sizes"); - // Solving y = a*x^2 + b*x + c +std::optional<float> LeastSquaresVelocityTrackerStrategy::solveUnweightedLeastSquaresDeg2( + const RingBuffer<Movement>& movements) const { + // Solving y = a*x^2 + b*x + c, where + // - "x" is age (i.e. duration since latest movement) of the movemnets + // - "y" is positions of the movements. float sxi = 0, sxiyi = 0, syi = 0, sxi2 = 0, sxi3 = 0, sxi2yi = 0, sxi4 = 0; + const size_t count = movements.size(); + const Movement& newestMovement = movements[count - 1]; for (size_t i = 0; i < count; i++) { - float xi = x[i]; - float yi = y[i]; + const Movement& movement = movements[i]; + nsecs_t age = newestMovement.eventTime - movement.eventTime; + float xi = -age * SECONDS_PER_NANO; + float yi = movement.position; + float xi2 = xi*xi; float xi3 = xi2*xi; float xi4 = xi3*xi; @@ -632,124 +630,68 @@ static std::optional<std::array<float, 3>> solveUnweightedLeastSquaresDeg2( ALOGW("division by 0 when computing velocity, Sxx=%f, Sx2x2=%f, Sxx2=%f", Sxx, Sx2x2, Sxx2); return std::nullopt; } - // Compute a - float numerator = Sx2y*Sxx - Sxy*Sxx2; - float a = numerator / denominator; - - // Compute b - numerator = Sxy*Sx2x2 - Sx2y*Sxx2; - float b = numerator / denominator; - // Compute c - float c = syi/count - b * sxi/count - a * sxi2/count; - - return std::make_optional(std::array<float, 3>({c, b, a})); + return (Sxy * Sx2x2 - Sx2y * Sxx2) / denominator; } -std::optional<VelocityTracker::Estimator> LeastSquaresVelocityTrackerStrategy::getEstimator( - int32_t pointerId) const { +std::optional<float> LeastSquaresVelocityTrackerStrategy::getVelocity(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.at(pointerId); - const Movement& newestMovement = movementIt->second[index]; - do { - 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. 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) { + const RingBuffer<Movement>& movements = movementIt->second; + const size_t size = movements.size(); + if (size == 0) { return std::nullopt; // no data } - // Calculate a least squares polynomial fit. uint32_t degree = mDegree; - if (degree > m - 1) { - degree = m - 1; + if (degree > size - 1) { + degree = size - 1; + } + + if (degree <= 0) { + return std::nullopt; } if (degree == 2 && mWeighting == Weighting::NONE) { // Optimize unweighted, quadratic polynomial fit - std::optional<std::array<float, 3>> coeff = - solveUnweightedLeastSquaresDeg2(time, positions); - if (coeff) { - 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 estimator; - } - } else if (degree >= 1) { - // General case for an Nth degree polynomial fit - float det; - uint32_t n = degree + 1; - 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(estimator.degree), vectorToString(estimator.coeff.data(), n).c_str(), - estimator.confidence); - - return estimator; - } + return solveUnweightedLeastSquaresDeg2(movements); + } + + // Iterate over movement samples in reverse time order and collect samples. + std::vector<float> positions; + std::vector<float> w; + std::vector<float> time; + + const Movement& newestMovement = movements[size - 1]; + for (ssize_t i = size - 1; i >= 0; i--) { + const Movement& movement = movements[i]; + nsecs_t age = newestMovement.eventTime - movement.eventTime; + positions.push_back(movement.position); + w.push_back(chooseWeight(pointerId, i)); + time.push_back(-age * 0.000000001f); } - // No velocity data available for this pointer, but we do have its current position. - VelocityTracker::Estimator estimator; - estimator.coeff[0] = positions[0]; - estimator.time = newestMovement.eventTime; - estimator.degree = 0; - estimator.confidence = 1; - return estimator; + // General case for an Nth degree polynomial fit + return solveLeastSquares(time, positions, w, degree + 1); } float LeastSquaresVelocityTrackerStrategy::chooseWeight(int32_t pointerId, uint32_t index) const { - const std::array<Movement, HISTORY_SIZE>& movements = mMovements.at(pointerId); + const RingBuffer<Movement>& movements = mMovements.at(pointerId); + const size_t size = movements.size(); 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.at(pointerId)) { + if (index == size - 1) { return 1.0f; } - uint32_t nextIndex = (index + 1) % HISTORY_SIZE; float deltaMillis = - (movements[nextIndex].eventTime - movements[index].eventTime) * 0.000001f; + (movements[index + 1].eventTime - movements[index].eventTime) * 0.000001f; if (deltaMillis < 0) { return 0.5f; } @@ -766,8 +708,7 @@ float LeastSquaresVelocityTrackerStrategy::chooseWeight(int32_t pointerId, uint3 // age 50ms: 1.0 // age 60ms: 0.5 float ageMillis = - (movements[mIndex.at(pointerId)].eventTime - movements[index].eventTime) * - 0.000001f; + (movements[size - 1].eventTime - movements[index].eventTime) * 0.000001f; if (ageMillis < 0) { return 0.5f; } @@ -789,8 +730,7 @@ float LeastSquaresVelocityTrackerStrategy::chooseWeight(int32_t pointerId, uint3 // age 50ms: 1.0 // age 100ms: 0.5 float ageMillis = - (movements[mIndex.at(pointerId)].eventTime - movements[index].eventTime) * - 0.000001f; + (movements[size - 1].eventTime - movements[index].eventTime) * 0.000001f; if (ageMillis < 50) { return 1.0f; } @@ -830,13 +770,9 @@ void IntegratingVelocityTrackerStrategy::addMovement(nsecs_t eventTime, int32_t mPointerIdBits.markBit(pointerId); } -std::optional<VelocityTracker::Estimator> IntegratingVelocityTrackerStrategy::getEstimator( - int32_t pointerId) const { +std::optional<float> IntegratingVelocityTrackerStrategy::getVelocity(int32_t pointerId) const { if (mPointerIdBits.hasBit(pointerId)) { - const State& state = mPointerState[pointerId]; - VelocityTracker::Estimator estimator; - populateEstimator(state, &estimator); - return estimator; + return mPointerState[pointerId].vel; } return std::nullopt; @@ -886,77 +822,39 @@ void IntegratingVelocityTrackerStrategy::updateState(State& state, nsecs_t event state.pos = pos; } -void IntegratingVelocityTrackerStrategy::populateEstimator(const State& state, - VelocityTracker::Estimator* outEstimator) const { - outEstimator->time = state.updateTime; - outEstimator->confidence = 1.0f; - outEstimator->degree = state.degree; - outEstimator->coeff[0] = state.pos; - outEstimator->coeff[1] = state.vel; - outEstimator->coeff[2] = state.accel / 2; -} - - // --- LegacyVelocityTrackerStrategy --- -LegacyVelocityTrackerStrategy::LegacyVelocityTrackerStrategy() {} +LegacyVelocityTrackerStrategy::LegacyVelocityTrackerStrategy() + : AccumulatingVelocityTrackerStrategy(HORIZON /*horizonNanos*/, + false /*maintainHorizonDuringAdd*/) {} LegacyVelocityTrackerStrategy::~LegacyVelocityTrackerStrategy() { } -void LegacyVelocityTrackerStrategy::clearPointer(int32_t pointerId) { - mIndex.erase(pointerId); - mMovements.erase(pointerId); -} - -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 = movementIt->second[index]; - movement.eventTime = eventTime; - movement.position = position; -} - -std::optional<VelocityTracker::Estimator> LegacyVelocityTrackerStrategy::getEstimator( - int32_t pointerId) const { +std::optional<float> LegacyVelocityTrackerStrategy::getVelocity(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)]; + + const RingBuffer<Movement>& movements = movementIt->second; + const size_t size = movements.size(); + if (size == 0) { + return std::nullopt; // no data + } + + const Movement& newestMovement = movements[size - 1]; // 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.at(pointerId); - uint32_t numTouches = 1; - do { - uint32_t nextOldestIndex = (oldestIndex == 0 ? HISTORY_SIZE : oldestIndex) - 1; - const Movement& nextOldestMovement = mMovements.at(pointerId)[nextOldestIndex]; + uint32_t oldestIndex = size - 1; + for (ssize_t i = size - 1; i >= 0; i--) { + const Movement& nextOldestMovement = movements[i]; if (nextOldestMovement.eventTime < minTime) { break; } - oldestIndex = nextOldestIndex; - } while (++numTouches < HISTORY_SIZE); + oldestIndex = i; + } // Calculate an exponentially weighted moving average of the velocity estimate // at different points in time measured relative to the oldest sample. @@ -970,17 +868,13 @@ std::optional<VelocityTracker::Estimator> LegacyVelocityTrackerStrategy::getEsti // 16ms apart but some consecutive samples could be only 0.5sm apart because // the hardware or driver reports them irregularly or in bursts. float accumV = 0; - uint32_t index = oldestIndex; uint32_t samplesUsed = 0; - const Movement& oldestMovement = mMovements.at(pointerId)[oldestIndex]; + const Movement& oldestMovement = movements[oldestIndex]; float oldestPosition = oldestMovement.position; nsecs_t lastDuration = 0; - while (numTouches-- > 1) { - if (++index == HISTORY_SIZE) { - index = 0; - } - const Movement& movement = mMovements.at(pointerId)[index]; + for (size_t i = oldestIndex; i < size; i++) { + const Movement& movement = movements[i]; nsecs_t duration = movement.eventTime - oldestMovement.eventTime; // If the duration between samples is small, we may significantly overestimate @@ -996,62 +890,22 @@ std::optional<VelocityTracker::Estimator> LegacyVelocityTrackerStrategy::getEsti } } - // Report velocity. - float newestPosition = newestMovement.position; - VelocityTracker::Estimator estimator; - estimator.time = newestMovement.eventTime; - estimator.confidence = 1; - estimator.coeff[0] = newestPosition; if (samplesUsed) { - estimator.coeff[1] = accumV; - estimator.degree = 1; - } else { - estimator.degree = 0; + return accumV; } - return estimator; + return std::nullopt; } // --- ImpulseVelocityTrackerStrategy --- ImpulseVelocityTrackerStrategy::ImpulseVelocityTrackerStrategy(bool deltaValues) - : mDeltaValues(deltaValues) {} + : AccumulatingVelocityTrackerStrategy(HORIZON /*horizonNanos*/, + true /*maintainHorizonDuringAdd*/), + mDeltaValues(deltaValues) {} ImpulseVelocityTrackerStrategy::~ImpulseVelocityTrackerStrategy() { } -void ImpulseVelocityTrackerStrategy::clearPointer(int32_t pointerId) { - mIndex.erase(pointerId); - mMovements.erase(pointerId); -} - -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. - index++; - } - if (index == HISTORY_SIZE) { - index = 0; - } - - Movement& movement = movementIt->second[index]; - movement.eventTime = eventTime; - movement.position = position; -} - /** * Calculate the total impulse provided to the screen and the resulting velocity. * @@ -1126,112 +980,44 @@ static float kineticEnergyToVelocity(float work) { return (work < 0 ? -1.0 : 1.0) * sqrtf(fabsf(work)) * sqrt2; } -static float calculateImpulseVelocity(const nsecs_t* t, const float* x, size_t count, - bool deltaValues) { - // The input should be in reversed time order (most recent sample at index i=0) - // t[i] is in nanoseconds, but due to FP arithmetic, convert to seconds inside this function - static constexpr float SECONDS_PER_NANO = 1E-9; - - if (count < 2) { - return 0; // if 0 or 1 points, velocity is zero - } - if (t[1] > t[0]) { // Algorithm will still work, but not perfectly - ALOGE("Samples provided to calculateImpulseVelocity in the wrong order"); - } - - // If the data values are delta values, we do not have to calculate deltas here. - // We can use the delta values directly, along with the calculated time deltas. - // Since the data value input is in reversed time order: - // [a] for non-delta inputs, instantenous velocity = (x[i] - x[i-1])/(t[i] - t[i-1]) - // [b] for delta inputs, instantenous velocity = -x[i-1]/(t[i] - t[i - 1]) - // e.g., let the non-delta values are: V = [2, 3, 7], the equivalent deltas are D = [2, 1, 4]. - // Since the input is in reversed time order, the input values for this function would be - // V'=[7, 3, 2] and D'=[4, 1, 2] for the non-delta and delta values, respectively. - // - // The equivalent of {(V'[2] - V'[1]) = 2 - 3 = -1} would be {-D'[1] = -1} - // Similarly, the equivalent of {(V'[1] - V'[0]) = 3 - 7 = -4} would be {-D'[0] = -4} - - if (count == 2) { // if 2 points, basic linear calculation - if (t[1] == t[0]) { - ALOGE("Events have identical time stamps t=%" PRId64 ", setting velocity = 0", t[0]); - return 0; - } - const float deltaX = deltaValues ? -x[0] : x[1] - x[0]; - return deltaX / (SECONDS_PER_NANO * (t[1] - t[0])); - } - // Guaranteed to have at least 3 points here - float work = 0; - for (size_t i = count - 1; i > 0 ; i--) { // start with the oldest sample and go forward in time - if (t[i] == t[i-1]) { - ALOGE("Events have identical time stamps t=%" PRId64 ", skipping sample", t[i]); - continue; - } - float vprev = kineticEnergyToVelocity(work); // v[i-1] - const float deltaX = deltaValues ? -x[i-1] : x[i] - x[i-1]; - float vcurr = deltaX / (SECONDS_PER_NANO * (t[i] - t[i-1])); // v[i] - work += (vcurr - vprev) * fabsf(vcurr); - if (i == count - 1) { - work *= 0.5; // initial condition, case 2) above - } - } - return kineticEnergyToVelocity(work); -} - -std::optional<VelocityTracker::Estimator> ImpulseVelocityTrackerStrategy::getEstimator( - int32_t pointerId) const { +std::optional<float> ImpulseVelocityTrackerStrategy::getVelocity(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.at(pointerId); - const Movement& newestMovement = movementIt->second[index]; - do { - const Movement& movement = movementIt->second[index]; + const RingBuffer<Movement>& movements = movementIt->second; + const size_t size = movements.size(); + if (size == 0) { + return std::nullopt; // no data + } - 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; - } + float work = 0; + for (size_t i = 0; i < size - 1; i++) { + const Movement& mvt = movements[i]; + const Movement& nextMvt = movements[i + 1]; - positions[m] = movement.position; - time[m] = movement.eventTime; - index = (index == 0 ? HISTORY_SIZE : index) - 1; - } while (++m < HISTORY_SIZE); + float vprev = kineticEnergyToVelocity(work); + float delta = mDeltaValues ? nextMvt.position : nextMvt.position - mvt.position; + float vcurr = delta / (SECONDS_PER_NANO * (nextMvt.eventTime - mvt.eventTime)); + work += (vcurr - vprev) * fabsf(vcurr); - if (m == 0) { - return std::nullopt; // no data + if (i == 0) { + work *= 0.5; // initial condition, case 2) above + } } - VelocityTracker::Estimator estimator; - estimator.coeff[0] = 0; - estimator.coeff[1] = calculateImpulseVelocity(time, positions, m, mDeltaValues); - estimator.coeff[2] = 0; - - estimator.time = newestMovement.eventTime; - estimator.degree = 2; // similar results to 2nd degree fit - estimator.confidence = 1; - ALOGD_IF(DEBUG_STRATEGY, "velocity: %.1f", estimator.coeff[1]); + const float velocity = kineticEnergyToVelocity(work); + ALOGD_IF(DEBUG_STRATEGY, "velocity: %.1f", velocity); 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); - for (ssize_t i = m - 1; i >= 0; i--) { - lsq2.addMovement(time[i], pointerId, AMOTION_EVENT_AXIS_X, positions[i]); + for (size_t i = 0; i < size; i++) { + const Movement& mvt = movements[i]; + lsq2.addMovement(mvt.eventTime, pointerId, AMOTION_EVENT_AXIS_X, mvt.position); } std::optional<float> v = lsq2.getVelocity(AMOTION_EVENT_AXIS_X, pointerId); if (v) { @@ -1240,7 +1026,7 @@ std::optional<VelocityTracker::Estimator> ImpulseVelocityTrackerStrategy::getEst ALOGD("lsq2 velocity: could not compute velocity"); } } - return estimator; + return velocity; } } // namespace android diff --git a/libs/input/rust/Android.bp b/libs/input/rust/Android.bp new file mode 100644 index 0000000000..018d199ce2 --- /dev/null +++ b/libs/input/rust/Android.bp @@ -0,0 +1,72 @@ +// Copyright 2023 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +rust_defaults { + name: "libinput_rust_defaults", + crate_name: "input", + srcs: ["lib.rs"], + host_supported: true, + rustlibs: [ + "libbitflags", + "libcxx", + "libinput_bindgen", + "liblogger", + "liblog_rust", + "inputconstants-rust", + ], + whole_static_libs: [ + "libinput_from_rust_to_cpp", + ], + shared_libs: [ + "libbase", + ], +} + +rust_library { + name: "libinput_rust", + defaults: ["libinput_rust_defaults"], +} + +rust_ffi_static { + name: "libinput_rust_ffi", + defaults: ["libinput_rust_defaults"], +} + +rust_test { + name: "libinput_rust_test", + defaults: ["libinput_rust_defaults"], + test_options: { + unit_test: true, + }, + test_suites: ["device_tests"], + sanitize: { + hwaddress: true, + }, +} + +genrule { + name: "libinput_cxx_bridge_code", + tools: ["cxxbridge"], + cmd: "$(location cxxbridge) $(in) >> $(out)", + srcs: ["lib.rs"], + out: ["input_cxx_bridge_generated.cpp"], +} + +genrule { + name: "libinput_cxx_bridge_header", + tools: ["cxxbridge"], + cmd: "$(location cxxbridge) $(in) --header >> $(out)", + srcs: ["lib.rs"], + out: ["input_cxx_bridge.rs.h"], +} diff --git a/libs/input/rust/input.rs b/libs/input/rust/input.rs new file mode 100644 index 0000000000..a308c26b2e --- /dev/null +++ b/libs/input/rust/input.rs @@ -0,0 +1,126 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//! Common definitions of the Android Input Framework in rust. + +use bitflags::bitflags; +use std::fmt; + +/// The InputDevice ID. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub struct DeviceId(pub i32); + +/// A rust enum representation of a MotionEvent action. +#[repr(u32)] +pub enum MotionAction { + /// ACTION_DOWN + Down = input_bindgen::AMOTION_EVENT_ACTION_DOWN, + /// ACTION_UP + Up = input_bindgen::AMOTION_EVENT_ACTION_UP, + /// ACTION_MOVE + Move = input_bindgen::AMOTION_EVENT_ACTION_MOVE, + /// ACTION_CANCEL + Cancel = input_bindgen::AMOTION_EVENT_ACTION_CANCEL, + /// ACTION_OUTSIDE + Outside = input_bindgen::AMOTION_EVENT_ACTION_OUTSIDE, + /// ACTION_POINTER_DOWN + PointerDown { + /// The index of the affected pointer. + action_index: usize, + } = input_bindgen::AMOTION_EVENT_ACTION_POINTER_DOWN, + /// ACTION_POINTER_UP + PointerUp { + /// The index of the affected pointer. + action_index: usize, + } = input_bindgen::AMOTION_EVENT_ACTION_POINTER_UP, + /// ACTION_HOVER_ENTER + HoverEnter = input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER, + /// ACTION_HOVER_MOVE + HoverMove = input_bindgen::AMOTION_EVENT_ACTION_HOVER_MOVE, + /// ACTION_HOVER_EXIT + HoverExit = input_bindgen::AMOTION_EVENT_ACTION_HOVER_EXIT, + /// ACTION_SCROLL + Scroll = input_bindgen::AMOTION_EVENT_ACTION_SCROLL, + /// ACTION_BUTTON_PRESS + ButtonPress = input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS, + /// ACTION_BUTTON_RELEASE + ButtonRelease = input_bindgen::AMOTION_EVENT_ACTION_BUTTON_RELEASE, +} + +impl fmt::Display for MotionAction { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + MotionAction::Down => write!(f, "DOWN"), + MotionAction::Up => write!(f, "UP"), + MotionAction::Move => write!(f, "MOVE"), + MotionAction::Cancel => write!(f, "CANCEL"), + MotionAction::Outside => write!(f, "OUTSIDE"), + MotionAction::PointerDown { action_index } => { + write!(f, "POINTER_DOWN({})", action_index) + } + MotionAction::PointerUp { action_index } => write!(f, "POINTER_UP({})", action_index), + MotionAction::HoverMove => write!(f, "HOVER_MOVE"), + MotionAction::Scroll => write!(f, "SCROLL"), + MotionAction::HoverEnter => write!(f, "HOVER_ENTER"), + MotionAction::HoverExit => write!(f, "HOVER_EXIT"), + MotionAction::ButtonPress => write!(f, "BUTTON_PRESS"), + MotionAction::ButtonRelease => write!(f, "BUTTON_RELEASE"), + } + } +} + +impl From<u32> for MotionAction { + fn from(action: u32) -> Self { + let (action_masked, action_index) = MotionAction::breakdown_action(action); + match action_masked { + input_bindgen::AMOTION_EVENT_ACTION_DOWN => MotionAction::Down, + input_bindgen::AMOTION_EVENT_ACTION_UP => MotionAction::Up, + input_bindgen::AMOTION_EVENT_ACTION_MOVE => MotionAction::Move, + input_bindgen::AMOTION_EVENT_ACTION_CANCEL => MotionAction::Cancel, + input_bindgen::AMOTION_EVENT_ACTION_OUTSIDE => MotionAction::Outside, + input_bindgen::AMOTION_EVENT_ACTION_POINTER_DOWN => { + MotionAction::PointerDown { action_index } + } + input_bindgen::AMOTION_EVENT_ACTION_POINTER_UP => { + MotionAction::PointerUp { action_index } + } + input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER => MotionAction::HoverEnter, + input_bindgen::AMOTION_EVENT_ACTION_HOVER_MOVE => MotionAction::HoverMove, + input_bindgen::AMOTION_EVENT_ACTION_HOVER_EXIT => MotionAction::HoverExit, + input_bindgen::AMOTION_EVENT_ACTION_SCROLL => MotionAction::Scroll, + input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS => MotionAction::ButtonPress, + input_bindgen::AMOTION_EVENT_ACTION_BUTTON_RELEASE => MotionAction::ButtonRelease, + _ => panic!("Unknown action: {}", action), + } + } +} + +impl MotionAction { + fn breakdown_action(action: u32) -> (u32, usize) { + let action_masked = action & input_bindgen::AMOTION_EVENT_ACTION_MASK; + let index = (action & input_bindgen::AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) + >> input_bindgen::AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; + (action_masked, index.try_into().unwrap()) + } +} + +bitflags! { + /// MotionEvent flags. + pub struct MotionFlags: i32 { + /// FLAG_CANCELED + const CANCELED = input_bindgen::AMOTION_EVENT_FLAG_CANCELED; + } +} diff --git a/libs/input/input_verifier.rs b/libs/input/rust/input_verifier.rs index 2e05a63149..1cc11297b6 100644 --- a/libs/input/input_verifier.rs +++ b/libs/input/rust/input_verifier.rs @@ -14,191 +14,45 @@ * limitations under the License. */ -//! Validate the incoming motion stream. -//! This class is not thread-safe. -//! State is stored in the "InputVerifier" object -//! that can be created via the 'create' method. -//! Usage: -//! Box<InputVerifier> verifier = create("inputChannel name"); -//! result = process_movement(verifier, ...); -//! if (result) { -//! crash(result.error_message()); -//! } +//! Contains the InputVerifier, used to validate a stream of input events. +use crate::ffi::RustPointerProperties; +use crate::input::{DeviceId, MotionAction, MotionFlags}; +use log::info; use std::collections::HashMap; use std::collections::HashSet; -use bitflags::bitflags; -use log::info; - -#[cxx::bridge(namespace = "android::input")] -mod ffi { - #[namespace = "android"] - unsafe extern "C++" { - include!("ffi/FromRustToCpp.h"); - fn shouldLog(tag: &str) -> bool; - } - #[namespace = "android::input::verifier"] - extern "Rust" { - type InputVerifier; - - fn create(name: String) -> Box<InputVerifier>; - fn process_movement( - verifier: &mut InputVerifier, - device_id: i32, - action: u32, - pointer_properties: &[RustPointerProperties], - flags: i32, - ) -> String; - } - - pub struct RustPointerProperties { - id: i32, - } -} - -use crate::ffi::shouldLog; -use crate::ffi::RustPointerProperties; - -#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] -struct DeviceId(i32); - -fn process_movement( - verifier: &mut InputVerifier, - device_id: i32, - action: u32, - pointer_properties: &[RustPointerProperties], - flags: i32, -) -> String { - let result = verifier.process_movement( - DeviceId(device_id), - action, - pointer_properties, - Flags::from_bits(flags).unwrap(), - ); - match result { - Ok(()) => "".to_string(), - Err(e) => e, - } -} - -fn create(name: String) -> Box<InputVerifier> { - Box::new(InputVerifier::new(&name)) -} - -#[repr(u32)] -enum MotionAction { - Down = input_bindgen::AMOTION_EVENT_ACTION_DOWN, - Up = input_bindgen::AMOTION_EVENT_ACTION_UP, - Move = input_bindgen::AMOTION_EVENT_ACTION_MOVE, - Cancel = input_bindgen::AMOTION_EVENT_ACTION_CANCEL, - Outside = input_bindgen::AMOTION_EVENT_ACTION_OUTSIDE, - PointerDown { action_index: usize } = input_bindgen::AMOTION_EVENT_ACTION_POINTER_DOWN, - PointerUp { action_index: usize } = input_bindgen::AMOTION_EVENT_ACTION_POINTER_UP, - HoverEnter = input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER, - HoverMove = input_bindgen::AMOTION_EVENT_ACTION_HOVER_MOVE, - HoverExit = input_bindgen::AMOTION_EVENT_ACTION_HOVER_EXIT, - Scroll = input_bindgen::AMOTION_EVENT_ACTION_SCROLL, - ButtonPress = input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS, - ButtonRelease = input_bindgen::AMOTION_EVENT_ACTION_BUTTON_RELEASE, -} - -fn get_action_index(action: u32) -> usize { - let index = (action & input_bindgen::AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) - >> input_bindgen::AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; - index.try_into().unwrap() -} - -impl From<u32> for MotionAction { - fn from(action: u32) -> Self { - let action_masked = action & input_bindgen::AMOTION_EVENT_ACTION_MASK; - let action_index = get_action_index(action); - match action_masked { - input_bindgen::AMOTION_EVENT_ACTION_DOWN => MotionAction::Down, - input_bindgen::AMOTION_EVENT_ACTION_UP => MotionAction::Up, - input_bindgen::AMOTION_EVENT_ACTION_MOVE => MotionAction::Move, - input_bindgen::AMOTION_EVENT_ACTION_CANCEL => MotionAction::Cancel, - input_bindgen::AMOTION_EVENT_ACTION_OUTSIDE => MotionAction::Outside, - input_bindgen::AMOTION_EVENT_ACTION_POINTER_DOWN => { - MotionAction::PointerDown { action_index } - } - input_bindgen::AMOTION_EVENT_ACTION_POINTER_UP => { - MotionAction::PointerUp { action_index } - } - input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER => MotionAction::HoverEnter, - input_bindgen::AMOTION_EVENT_ACTION_HOVER_MOVE => MotionAction::HoverMove, - input_bindgen::AMOTION_EVENT_ACTION_HOVER_EXIT => MotionAction::HoverExit, - input_bindgen::AMOTION_EVENT_ACTION_SCROLL => MotionAction::Scroll, - input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS => MotionAction::ButtonPress, - input_bindgen::AMOTION_EVENT_ACTION_BUTTON_RELEASE => MotionAction::ButtonRelease, - _ => panic!("Unknown action: {}", action), - } - } -} - -bitflags! { - struct Flags: i32 { - const CANCELED = input_bindgen::AMOTION_EVENT_FLAG_CANCELED; - } -} - -fn motion_action_to_string(action: u32) -> String { - match action.into() { - MotionAction::Down => "DOWN".to_string(), - MotionAction::Up => "UP".to_string(), - MotionAction::Move => "MOVE".to_string(), - MotionAction::Cancel => "CANCEL".to_string(), - MotionAction::Outside => "OUTSIDE".to_string(), - MotionAction::PointerDown { action_index } => { - format!("POINTER_DOWN({})", action_index) - } - MotionAction::PointerUp { action_index } => { - format!("POINTER_UP({})", action_index) - } - MotionAction::HoverMove => "HOVER_MOVE".to_string(), - MotionAction::Scroll => "SCROLL".to_string(), - MotionAction::HoverEnter => "HOVER_ENTER".to_string(), - MotionAction::HoverExit => "HOVER_EXIT".to_string(), - MotionAction::ButtonPress => "BUTTON_PRESS".to_string(), - MotionAction::ButtonRelease => "BUTTON_RELEASE".to_string(), - } -} - -/** - * Log all of the movements that are sent to this verifier. Helps to identify the streams that lead - * to inconsistent events. - * Enable this via "adb shell setprop log.tag.InputVerifierLogEvents DEBUG" - */ -fn log_events() -> bool { - shouldLog("InputVerifierLogEvents") -} - -struct InputVerifier { +/// The InputVerifier is used to validate a stream of input events. +pub struct InputVerifier { name: String, + should_log: bool, touching_pointer_ids_by_device: HashMap<DeviceId, HashSet<i32>>, } impl InputVerifier { - fn new(name: &str) -> Self { + /// Create a new InputVerifier. + pub fn new(name: &str, should_log: bool) -> Self { logger::init( logger::Config::default() .with_tag_on_device("InputVerifier") .with_min_level(log::Level::Trace), ); - Self { name: name.to_owned(), touching_pointer_ids_by_device: HashMap::new() } + Self { name: name.to_owned(), should_log, touching_pointer_ids_by_device: HashMap::new() } } - fn process_movement( + /// Process a pointer movement event from an InputDevice. + /// If the event is not valid, we return an error string that describes the issue. + pub fn process_movement( &mut self, device_id: DeviceId, action: u32, pointer_properties: &[RustPointerProperties], - flags: Flags, + flags: MotionFlags, ) -> Result<(), String> { - if log_events() { + if self.should_log { info!( "Processing {} for device {:?} ({} pointer{}) on {}", - motion_action_to_string(action), + MotionAction::from(action).to_string(), device_id, pointer_properties.len(), if pointer_properties.len() == 1 { "" } else { "s" }, @@ -284,7 +138,7 @@ impl InputVerifier { it.clear(); } MotionAction::Cancel => { - if flags.contains(Flags::CANCELED) { + if flags.contains(MotionFlags::CANCELED) { return Err(format!( "{}: For ACTION_CANCEL, must set FLAG_CANCELED", self.name @@ -325,20 +179,20 @@ impl InputVerifier { #[cfg(test)] mod tests { + use crate::input_verifier::InputVerifier; use crate::DeviceId; - use crate::Flags; - use crate::InputVerifier; + use crate::MotionFlags; use crate::RustPointerProperties; #[test] fn single_pointer_stream() { - let mut verifier = InputVerifier::new("Test"); + let mut verifier = InputVerifier::new("Test", /*should_log*/ false); let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]); assert!(verifier .process_movement( DeviceId(1), input_bindgen::AMOTION_EVENT_ACTION_DOWN, &pointer_properties, - Flags::empty(), + MotionFlags::empty(), ) .is_ok()); assert!(verifier @@ -346,7 +200,7 @@ mod tests { DeviceId(1), input_bindgen::AMOTION_EVENT_ACTION_MOVE, &pointer_properties, - Flags::empty(), + MotionFlags::empty(), ) .is_ok()); assert!(verifier @@ -354,21 +208,21 @@ mod tests { DeviceId(1), input_bindgen::AMOTION_EVENT_ACTION_UP, &pointer_properties, - Flags::empty(), + MotionFlags::empty(), ) .is_ok()); } #[test] fn multi_device_stream() { - let mut verifier = InputVerifier::new("Test"); + let mut verifier = InputVerifier::new("Test", /*should_log*/ false); let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]); assert!(verifier .process_movement( DeviceId(1), input_bindgen::AMOTION_EVENT_ACTION_DOWN, &pointer_properties, - Flags::empty(), + MotionFlags::empty(), ) .is_ok()); assert!(verifier @@ -376,7 +230,7 @@ mod tests { DeviceId(1), input_bindgen::AMOTION_EVENT_ACTION_MOVE, &pointer_properties, - Flags::empty(), + MotionFlags::empty(), ) .is_ok()); assert!(verifier @@ -384,7 +238,7 @@ mod tests { DeviceId(2), input_bindgen::AMOTION_EVENT_ACTION_DOWN, &pointer_properties, - Flags::empty(), + MotionFlags::empty(), ) .is_ok()); assert!(verifier @@ -392,7 +246,7 @@ mod tests { DeviceId(2), input_bindgen::AMOTION_EVENT_ACTION_MOVE, &pointer_properties, - Flags::empty(), + MotionFlags::empty(), ) .is_ok()); assert!(verifier @@ -400,21 +254,21 @@ mod tests { DeviceId(1), input_bindgen::AMOTION_EVENT_ACTION_UP, &pointer_properties, - Flags::empty(), + MotionFlags::empty(), ) .is_ok()); } #[test] fn test_invalid_up() { - let mut verifier = InputVerifier::new("Test"); + let mut verifier = InputVerifier::new("Test", /*should_log*/ false); let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]); assert!(verifier .process_movement( DeviceId(1), input_bindgen::AMOTION_EVENT_ACTION_UP, &pointer_properties, - Flags::empty(), + MotionFlags::empty(), ) .is_err()); } diff --git a/libs/input/rust/lib.rs b/libs/input/rust/lib.rs new file mode 100644 index 0000000000..25b2ecbcda --- /dev/null +++ b/libs/input/rust/lib.rs @@ -0,0 +1,88 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//! The rust component of libinput. + +mod input; +mod input_verifier; + +pub use input::{DeviceId, MotionAction, MotionFlags}; +pub use input_verifier::InputVerifier; + +#[cxx::bridge(namespace = "android::input")] +mod ffi { + #[namespace = "android"] + unsafe extern "C++" { + include!("ffi/FromRustToCpp.h"); + fn shouldLog(tag: &str) -> bool; + } + + #[namespace = "android::input::verifier"] + extern "Rust" { + /// Used to validate the incoming motion stream. + /// This class is not thread-safe. + /// State is stored in the "InputVerifier" object + /// that can be created via the 'create' method. + /// Usage: + /// + /// ```ignore + /// Box<InputVerifier> verifier = create("inputChannel name"); + /// result = process_movement(verifier, ...); + /// if (result) { + /// crash(result.error_message()); + /// } + /// ``` + type InputVerifier; + fn create(name: String) -> Box<InputVerifier>; + fn process_movement( + verifier: &mut InputVerifier, + device_id: i32, + action: u32, + pointer_properties: &[RustPointerProperties], + flags: i32, + ) -> String; + } + + #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] + pub struct RustPointerProperties { + pub id: i32, + } +} + +use crate::ffi::RustPointerProperties; + +fn process_movement( + verifier: &mut InputVerifier, + device_id: i32, + action: u32, + pointer_properties: &[RustPointerProperties], + flags: i32, +) -> String { + let result = verifier.process_movement( + DeviceId(device_id), + action, + pointer_properties, + MotionFlags::from_bits(flags).unwrap(), + ); + match result { + Ok(()) => "".to_string(), + Err(e) => e, + } +} + +fn create(name: String) -> Box<InputVerifier> { + Box::new(InputVerifier::new(&name, ffi::shouldLog("InputVerifierLogEvents"))) +} diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp index ae721093a0..ffebff1e65 100644 --- a/libs/input/tests/VelocityTracker_test.cpp +++ b/libs/input/tests/VelocityTracker_test.cpp @@ -42,8 +42,8 @@ constexpr int32_t DEFAULT_POINTER_ID = 0; // pointer ID used for manually define // here EV = expected value, tol = VELOCITY_TOLERANCE constexpr float VELOCITY_TOLERANCE = 0.2; -// estimate coefficients must be within 0.001% of the target value -constexpr float COEFFICIENT_TOLERANCE = 0.00001; +// quadratic velocity must be within 0.001% of the target value +constexpr float QUADRATIC_VELOCITY_TOLERANCE = 0.00001; // --- VelocityTrackerTest --- class VelocityTrackerTest : public testing::Test { }; @@ -76,10 +76,6 @@ static void checkVelocity(std::optional<float> Vactual, std::optional<float> Vta } } -static void checkCoefficient(float actual, float target) { - EXPECT_NEAR_BY_FRACTION(actual, target, COEFFICIENT_TOLERANCE); -} - struct Position { float x; float y; @@ -284,21 +280,20 @@ static void computeAndCheckAxisScrollVelocity( checkVelocity(computeVelocity(strategy, motions, AMOTION_EVENT_AXIS_SCROLL), targetVelocity); } -static void computeAndCheckQuadraticEstimate(const std::vector<PlanarMotionEventEntry>& motions, - const std::array<float, 3>& coefficients) { +static void computeAndCheckQuadraticVelocity(const std::vector<PlanarMotionEventEntry>& motions, + float velocity) { VelocityTracker vt(VelocityTracker::Strategy::LSQ2); std::vector<MotionEvent> events = createTouchMotionEventStream(motions); for (MotionEvent event : events) { vt.addMovement(&event); } - 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]); - } + std::optional<float> velocityX = vt.getVelocity(AMOTION_EVENT_AXIS_X, 0); + std::optional<float> velocityY = vt.getVelocity(AMOTION_EVENT_AXIS_Y, 0); + ASSERT_TRUE(velocityX); + ASSERT_TRUE(velocityY); + + EXPECT_NEAR_BY_FRACTION(*velocityX, velocity, QUADRATIC_VELOCITY_TOLERANCE); + EXPECT_NEAR_BY_FRACTION(*velocityY, velocity, QUADRATIC_VELOCITY_TOLERANCE); } /* @@ -461,8 +456,6 @@ TEST_F(VelocityTrackerTest, TestApiInteractionsWithNoMotionEvents) { EXPECT_FALSE(vt.getVelocity(AMOTION_EVENT_AXIS_X, DEFAULT_POINTER_ID)); - 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++) { EXPECT_FALSE(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, id)); @@ -1074,7 +1067,7 @@ TEST_F(VelocityTrackerTest, SailfishFlingDownFast3) { * If the events with POINTER_UP or POINTER_DOWN are not handled correctly (these should not be * part of the fitted data), this can cause large velocity values to be reported instead. */ -TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_ThreeFingerTap) { +TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategy_ThreeFingerTap) { std::vector<PlanarMotionEventEntry> motions = { { 0us, {{1063, 1128}, {NAN, NAN}, {NAN, NAN}} }, { 10800us, {{1063, 1128}, {682, 1318}, {NAN, NAN}} }, // POINTER_DOWN @@ -1162,7 +1155,7 @@ TEST_F(VelocityTrackerTest, LongDelayBeforeActionPointerUp) { * ================== Tests for least squares fitting ============================================== * * Special care must be taken when constructing tests for LeastSquaresVelocityTrackerStrategy - * getEstimator function. In particular: + * getVelocity function. In particular: * - inside the function, time gets converted from nanoseconds to seconds * before being used in the fit. * - any values that are older than 100 ms are being discarded. @@ -1183,7 +1176,7 @@ TEST_F(VelocityTrackerTest, LongDelayBeforeActionPointerUp) { * The coefficients are (0, 0, 1). * In the test, we would convert these coefficients to (0*(1E3)^0, 0*(1E3)^1, 1*(1E3)^2). */ -TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Constant) { +TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategy_Constant) { std::vector<PlanarMotionEventEntry> motions = { { 0ms, {{1, 1}} }, // 0 s { 1ms, {{1, 1}} }, // 0.001 s @@ -1195,13 +1188,13 @@ TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Constan // -0.002, 1 // -0.001, 1 // -0.ms, 1 - computeAndCheckQuadraticEstimate(motions, std::array<float, 3>({1, 0, 0})); + computeAndCheckQuadraticVelocity(motions, 0); } /* * Straight line y = x :: the constant and quadratic coefficients are zero. */ -TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Linear) { +TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategy_Linear) { std::vector<PlanarMotionEventEntry> motions = { { 0ms, {{-2, -2}} }, { 1ms, {{-1, -1}} }, @@ -1213,13 +1206,13 @@ TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Linear) // -0.002, -2 // -0.001, -1 // -0.000, 0 - computeAndCheckQuadraticEstimate(motions, std::array<float, 3>({0, 1E3, 0})); + computeAndCheckQuadraticVelocity(motions, 1E3); } /* * Parabola */ -TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Parabolic) { +TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategy_Parabolic) { std::vector<PlanarMotionEventEntry> motions = { { 0ms, {{1, 1}} }, { 1ms, {{4, 4}} }, @@ -1231,13 +1224,13 @@ TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Parabol // -0.002, 1 // -0.001, 4 // -0.000, 8 - computeAndCheckQuadraticEstimate(motions, std::array<float, 3>({8, 4.5E3, 0.5E6})); + computeAndCheckQuadraticVelocity(motions, 4.5E3); } /* * Parabola */ -TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Parabolic2) { +TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategy_Parabolic2) { std::vector<PlanarMotionEventEntry> motions = { { 0ms, {{1, 1}} }, { 1ms, {{4, 4}} }, @@ -1249,13 +1242,13 @@ TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Parabol // -0.002, 1 // -0.001, 4 // -0.000, 9 - computeAndCheckQuadraticEstimate(motions, std::array<float, 3>({9, 6E3, 1E6})); + computeAndCheckQuadraticVelocity(motions, 6E3); } /* * Parabola :: y = x^2 :: the constant and linear coefficients are zero. */ -TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Parabolic3) { +TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategy_Parabolic3) { std::vector<PlanarMotionEventEntry> motions = { { 0ms, {{4, 4}} }, { 1ms, {{1, 1}} }, @@ -1267,7 +1260,7 @@ TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Parabol // -0.002, 4 // -0.001, 1 // -0.000, 0 - computeAndCheckQuadraticEstimate(motions, std::array<float, 3>({0, 0E3, 1E6})); + computeAndCheckQuadraticVelocity(motions, 0E3); } // Recorded by hand on sailfish, but only the diffs are taken to test cumulative axis velocity. diff --git a/libs/renderengine/skia/AutoBackendTexture.cpp b/libs/renderengine/skia/AutoBackendTexture.cpp index 23c99b0796..dad3c1993b 100644 --- a/libs/renderengine/skia/AutoBackendTexture.cpp +++ b/libs/renderengine/skia/AutoBackendTexture.cpp @@ -20,6 +20,11 @@ #define LOG_TAG "RenderEngine" #define ATRACE_TAG ATRACE_TAG_GRAPHICS +#include <SkImage.h> +#include <include/gpu/ganesh/SkImageGanesh.h> +#include <include/gpu/ganesh/SkSurfaceGanesh.h> + +#include <android/hardware_buffer.h> #include "ColorSpaces.h" #include "log/log_main.h" #include "utils/Trace.h" @@ -79,7 +84,7 @@ void AutoBackendTexture::releaseSurfaceProc(SkSurface::ReleaseContext releaseCon // releaseImageProc is invoked by SkImage, when the texture is no longer in use. // "releaseContext" contains an "AutoBackendTexture*". -void AutoBackendTexture::releaseImageProc(SkImage::ReleaseContext releaseContext) { +void AutoBackendTexture::releaseImageProc(SkImages::ReleaseContext releaseContext) { AutoBackendTexture* textureRelease = reinterpret_cast<AutoBackendTexture*>(releaseContext); textureRelease->unref(false); } @@ -136,8 +141,9 @@ sk_sp<SkImage> AutoBackendTexture::makeImage(ui::Dataspace dataspace, SkAlphaTyp } sk_sp<SkImage> image = - SkImage::MakeFromTexture(context, mBackendTexture, kTopLeft_GrSurfaceOrigin, colorType, - alphaType, toSkColorSpace(dataspace), releaseImageProc, this); + SkImages::BorrowTextureFrom(context, mBackendTexture, kTopLeft_GrSurfaceOrigin, + colorType, alphaType, toSkColorSpace(dataspace), + releaseImageProc, this); if (image.get()) { // The following ref will be counteracted by releaseProc, when SkImage is discarded. ref(); @@ -157,10 +163,10 @@ sk_sp<SkSurface> AutoBackendTexture::getOrCreateSurface(ui::Dataspace dataspace, LOG_ALWAYS_FATAL_IF(!mIsOutputBuffer, "You can't generate a SkSurface for a read-only texture"); if (!mSurface.get() || mDataspace != dataspace) { sk_sp<SkSurface> surface = - SkSurface::MakeFromBackendTexture(context, mBackendTexture, - kTopLeft_GrSurfaceOrigin, 0, mColorType, - toSkColorSpace(dataspace), nullptr, - releaseSurfaceProc, this); + SkSurfaces::WrapBackendTexture(context, mBackendTexture, + kTopLeft_GrSurfaceOrigin, 0, mColorType, + toSkColorSpace(dataspace), nullptr, + releaseSurfaceProc, this); if (surface.get()) { // The following ref will be counteracted by releaseProc, when SkSurface is discarded. ref(); diff --git a/libs/renderengine/skia/AutoBackendTexture.h b/libs/renderengine/skia/AutoBackendTexture.h index 00b901be11..509ac40f77 100644 --- a/libs/renderengine/skia/AutoBackendTexture.h +++ b/libs/renderengine/skia/AutoBackendTexture.h @@ -144,7 +144,7 @@ private: CleanupManager& mCleanupMgr; static void releaseSurfaceProc(SkSurface::ReleaseContext releaseContext); - static void releaseImageProc(SkImage::ReleaseContext releaseContext); + static void releaseImageProc(SkImages::ReleaseContext releaseContext); int mUsageCount = 0; diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp index 18d9864f35..871d258ce7 100644 --- a/libs/renderengine/skia/SkiaRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaRenderEngine.cpp @@ -20,6 +20,7 @@ #include "SkiaRenderEngine.h" +#include <include/gpu/ganesh/SkSurfaceGanesh.h> #include <GrBackendSemaphore.h> #include <GrContextOptions.h> #include <SkBlendMode.h> @@ -1109,7 +1110,7 @@ void SkiaRenderEngine::drawLayersInternal( } if (kFlushAfterEveryLayer) { ATRACE_NAME("flush surface"); - activeSurface->flush(); + skgpu::ganesh::Flush(activeSurface); } } for (const auto& borderRenderInfo : display.borderInfoList) { @@ -1137,7 +1138,7 @@ void SkiaRenderEngine::drawLayersInternal( { ATRACE_NAME("flush surface"); LOG_ALWAYS_FATAL_IF(activeSurface != dstSurface); - activeSurface->flush(); + skgpu::ganesh::Flush(activeSurface); } auto drawFence = sp<Fence>::make(flushAndSubmit(grContext)); diff --git a/libs/renderengine/skia/filters/BlurFilter.cpp b/libs/renderengine/skia/filters/BlurFilter.cpp index 2557ac9770..1e0c4cf9d0 100644 --- a/libs/renderengine/skia/filters/BlurFilter.cpp +++ b/libs/renderengine/skia/filters/BlurFilter.cpp @@ -16,6 +16,7 @@ #define ATRACE_TAG ATRACE_TAG_GRAPHICS #include "BlurFilter.h" +#include <SkBlendMode.h> #include <SkCanvas.h> #include <SkPaint.h> #include <SkRRect.h> @@ -23,6 +24,7 @@ #include <SkSize.h> #include <SkString.h> #include <SkSurface.h> +#include <SkTileMode.h> #include <log/log.h> #include <utils/Trace.h> diff --git a/libs/renderengine/skia/filters/GaussianBlurFilter.cpp b/libs/renderengine/skia/filters/GaussianBlurFilter.cpp index 511d7c9350..e72c501336 100644 --- a/libs/renderengine/skia/filters/GaussianBlurFilter.cpp +++ b/libs/renderengine/skia/filters/GaussianBlurFilter.cpp @@ -17,6 +17,7 @@ #define ATRACE_TAG ATRACE_TAG_GRAPHICS #include "GaussianBlurFilter.h" +#include <SkBlendMode.h> #include <SkCanvas.h> #include <SkPaint.h> #include <SkRRect.h> @@ -25,6 +26,8 @@ #include <SkSize.h> #include <SkString.h> #include <SkSurface.h> +#include <SkTileMode.h> +#include <include/gpu/ganesh/SkSurfaceGanesh.h> #include "include/gpu/GpuTypes.h" // from Skia #include <log/log.h> #include <utils/Trace.h> @@ -45,8 +48,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, - skgpu::Budgeted::kNo, scaledInfo); + sk_sp<SkSurface> surface = SkSurfaces::RenderTarget(context, + skgpu::Budgeted::kNo, scaledInfo); SkPaint paint; paint.setBlendMode(SkBlendMode::kSrc); diff --git a/libs/renderengine/skia/filters/KawaseBlurFilter.cpp b/libs/renderengine/skia/filters/KawaseBlurFilter.cpp index e370c39a94..0c7335c05d 100644 --- a/libs/renderengine/skia/filters/KawaseBlurFilter.cpp +++ b/libs/renderengine/skia/filters/KawaseBlurFilter.cpp @@ -17,13 +17,20 @@ #define ATRACE_TAG ATRACE_TAG_GRAPHICS #include "KawaseBlurFilter.h" +#include <SkAlphaType.h> +#include <SkBlendMode.h> #include <SkCanvas.h> +#include <SkImageInfo.h> #include <SkPaint.h> #include <SkRRect.h> #include <SkRuntimeEffect.h> +#include <SkShader.h> #include <SkSize.h> #include <SkString.h> #include <SkSurface.h> +#include <SkTileMode.h> +#include <include/gpu/GpuTypes.h> +#include <include/gpu/ganesh/SkSurfaceGanesh.h> #include <log/log.h> #include <utils/Trace.h> @@ -32,19 +39,18 @@ namespace renderengine { namespace skia { KawaseBlurFilter::KawaseBlurFilter(): BlurFilter() { - SkString blurString(R"( - uniform shader child; - uniform float in_blurOffset; - - half4 main(float2 xy) { - half4 c = child.eval(xy); - c += child.eval(xy + float2(+in_blurOffset, +in_blurOffset)); - c += child.eval(xy + float2(+in_blurOffset, -in_blurOffset)); - c += child.eval(xy + float2(-in_blurOffset, -in_blurOffset)); - c += child.eval(xy + float2(-in_blurOffset, +in_blurOffset)); - return half4(c.rgb * 0.2, 1.0); - } - )"); + SkString blurString( + "uniform shader child;" + "uniform float in_blurOffset;" + + "half4 main(float2 xy) {" + "half4 c = child.eval(xy);" + "c += child.eval(xy + float2(+in_blurOffset, +in_blurOffset));" + "c += child.eval(xy + float2(+in_blurOffset, -in_blurOffset));" + "c += child.eval(xy + float2(-in_blurOffset, -in_blurOffset));" + "c += child.eval(xy + float2(-in_blurOffset, +in_blurOffset));" + "return half4(c.rgb * 0.2, 1.0);" + "}"); auto [blurEffect, error] = SkRuntimeEffect::MakeForShader(blurString); if (!blurEffect) { @@ -53,9 +59,44 @@ KawaseBlurFilter::KawaseBlurFilter(): BlurFilter() { mBlurEffect = std::move(blurEffect); } -sk_sp<SkImage> KawaseBlurFilter::generate(GrRecordingContext* context, const uint32_t blurRadius, - const sk_sp<SkImage> input, const SkRect& blurRect) - const { +// Draws the given runtime shader on a GPU (Ganesh) surface and returns the result as an +// SkImage. +static sk_sp<SkImage> makeImage(GrRecordingContext* context, SkRuntimeShaderBuilder* builder, + const SkImageInfo& resultInfo) { + if (resultInfo.alphaType() == kUnpremul_SkAlphaType || + resultInfo.alphaType() == kUnknown_SkAlphaType) { + return nullptr; + } + constexpr int kSampleCount = 1; + constexpr bool kMipmapped = false; + + sk_sp<SkSurface> surface = SkSurfaces::RenderTarget(context, + skgpu::Budgeted::kYes, + resultInfo, + kSampleCount, + kTopLeft_GrSurfaceOrigin, + nullptr, + kMipmapped); + if (!surface) { + return nullptr; + } + sk_sp<SkShader> shader = builder->makeShader(nullptr); + if (!shader) { + return nullptr; + } + SkPaint paint; + paint.setShader(std::move(shader)); + paint.setBlendMode(SkBlendMode::kSrc); + surface->getCanvas()->drawPaint(paint); + return surface->makeImageSnapshot(); +} + +sk_sp<SkImage> KawaseBlurFilter::generate(GrRecordingContext* context, + const uint32_t blurRadius, + const sk_sp<SkImage> input, + const SkRect& blurRect) const { + LOG_ALWAYS_FATAL_IF(context == nullptr, "%s: Needs GPU context", __func__); + LOG_ALWAYS_FATAL_IF(input == nullptr, "%s: Invalid input image", __func__); // Kawase is an approximation of Gaussian, but it behaves differently from it. // A radius transformation is required for approximating them, and also to introduce // non-integer steps, necessary to smoothly interpolate large radii. @@ -80,14 +121,15 @@ sk_sp<SkImage> KawaseBlurFilter::generate(GrRecordingContext* context, const uin input->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear, blurMatrix); blurBuilder.uniform("in_blurOffset") = radiusByPasses * kInputScale; - sk_sp<SkImage> tmpBlur(blurBuilder.makeImage(context, nullptr, scaledInfo, false)); + sk_sp<SkImage> tmpBlur = makeImage(context, &blurBuilder, scaledInfo); // And now we'll build our chain of scaled blur stages for (auto i = 1; i < numberOfPasses; i++) { + LOG_ALWAYS_FATAL_IF(tmpBlur == nullptr, "%s: tmpBlur is null for pass %d", __func__, i); blurBuilder.child("child") = tmpBlur->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear); blurBuilder.uniform("in_blurOffset") = (float) i * radiusByPasses * kInputScale; - tmpBlur = blurBuilder.makeImage(context, nullptr, scaledInfo, false); + tmpBlur = makeImage(context, &blurBuilder, scaledInfo); } return tmpBlur; diff --git a/libs/sensor/ISensorServer.cpp b/libs/sensor/ISensorServer.cpp index 019d6cb070..634d35a5b8 100644 --- a/libs/sensor/ISensorServer.cpp +++ b/libs/sensor/ISensorServer.cpp @@ -64,6 +64,14 @@ public: Sensor s; Vector<Sensor> v; uint32_t n = reply.readUint32(); + // The size of the n Sensor elements on the wire is what we really want, but + // this is better than nothing. + if (n > reply.dataAvail()) { + ALOGE("Failed to get a reasonable size of the sensor list. This is likely a " + "malformed reply parcel. Number of elements: %d, data available in reply: %zu", + n, reply.dataAvail()); + return v; + } v.setCapacity(n); while (n) { n--; @@ -86,6 +94,14 @@ public: Sensor s; Vector<Sensor> v; uint32_t n = reply.readUint32(); + // The size of the n Sensor elements on the wire is what we really want, but + // this is better than nothing. + if (n > reply.dataAvail()) { + ALOGE("Failed to get a reasonable size of the sensor list. This is likely a " + "malformed reply parcel. Number of elements: %d, data available in reply: %zu", + n, reply.dataAvail()); + return v; + } v.setCapacity(n); while (n) { n--; @@ -109,6 +125,14 @@ public: Sensor s; Vector<Sensor> v; uint32_t n = reply.readUint32(); + // The size of the n Sensor elements on the wire is what we really want, but + // this is better than nothing. + if (n > reply.dataAvail()) { + ALOGE("Failed to get a reasonable size of the sensor list. This is likely a " + "malformed reply parcel. Number of elements: %d, data available in reply: %zu", + n, reply.dataAvail()); + return v; + } v.setCapacity(n); while (n) { n--; diff --git a/libs/sensorprivacy/SensorPrivacyManager.cpp b/libs/sensorprivacy/SensorPrivacyManager.cpp index 2be98e7281..57c74ee565 100644 --- a/libs/sensorprivacy/SensorPrivacyManager.cpp +++ b/libs/sensorprivacy/SensorPrivacyManager.cpp @@ -32,27 +32,12 @@ SensorPrivacyManager::SensorPrivacyManager() sp<hardware::ISensorPrivacyManager> SensorPrivacyManager::getService() { std::lock_guard<Mutex> scoped_lock(mLock); - int64_t startTime = 0; sp<hardware::ISensorPrivacyManager> service = mService; - while (service == nullptr || !IInterface::asBinder(service)->isBinderAlive()) { - sp<IBinder> binder = defaultServiceManager()->checkService(String16("sensor_privacy")); - if (binder == nullptr) { - // Wait for the sensor privacy service to come back... - if (startTime == 0) { - startTime = uptimeMillis(); - ALOGI("Waiting for sensor privacy service"); - } else if ((uptimeMillis() - startTime) > 1000000) { - ALOGW("Waiting too long for sensor privacy service, giving up"); - service = nullptr; - break; - } - usleep(25000); - } else { - service = interface_cast<hardware::ISensorPrivacyManager>(binder); - mService = service; - } + if (service == nullptr || !IInterface::asBinder(service)->isBinderAlive()) { + sp<IBinder> binder = defaultServiceManager()->waitForService(String16("sensor_privacy")); + mService = interface_cast<hardware::ISensorPrivacyManager>(binder); } - return service; + return mService; } bool SensorPrivacyManager::supportsSensorToggle(int toggleType, int sensor) { diff --git a/libs/ui/include/ui/GraphicBuffer.h b/libs/ui/include/ui/GraphicBuffer.h index dbe475b805..f859848b0c 100644 --- a/libs/ui/include/ui/GraphicBuffer.h +++ b/libs/ui/include/ui/GraphicBuffer.h @@ -36,6 +36,15 @@ #include <hardware/gralloc.h> +#if defined(__ANDROID_APEX__) || defined(__ANDROID_VNDK__) +// TODO: Provide alternatives that aren't broken +#define AHB_CONVERSION \ + [[deprecated("WARNING: VNDK casts beteween GraphicBuffer & AHardwareBuffer are UNSAFE and " \ + "will be removed in the future")]] +#else +#define AHB_CONVERSION +#endif + namespace android { class GraphicBufferMapper; @@ -80,10 +89,10 @@ public: static sp<GraphicBuffer> from(ANativeWindowBuffer *); - static GraphicBuffer* fromAHardwareBuffer(AHardwareBuffer*); - static GraphicBuffer const* fromAHardwareBuffer(AHardwareBuffer const*); - AHardwareBuffer* toAHardwareBuffer(); - AHardwareBuffer const* toAHardwareBuffer() const; + AHB_CONVERSION static GraphicBuffer* fromAHardwareBuffer(AHardwareBuffer*); + AHB_CONVERSION static GraphicBuffer const* fromAHardwareBuffer(AHardwareBuffer const*); + AHB_CONVERSION AHardwareBuffer* toAHardwareBuffer(); + AHB_CONVERSION AHardwareBuffer const* toAHardwareBuffer() const; // Create a GraphicBuffer to be unflatten'ed into or be reallocated. GraphicBuffer(); diff --git a/libs/ultrahdr/icc.cpp b/libs/ultrahdr/icc.cpp index 1ab3c7c793..e41b645cce 100644 --- a/libs/ultrahdr/icc.cpp +++ b/libs/ultrahdr/icc.cpp @@ -19,7 +19,6 @@ #endif #include <ultrahdr/icc.h> -#include <ultrahdr/gainmapmath.h> #include <vector> #include <utils/Log.h> @@ -218,8 +217,8 @@ sp<DataStruct> IccHelper::write_trc_tag(const int table_entries, const void* tab total_length = (((total_length + 2) >> 2) << 2); // 4 aligned sp<DataStruct> dataStruct = sp<DataStruct>::make(total_length); dataStruct->write32(Endian_SwapBE32(kTAG_CurveType)); // Type - dataStruct->write32(0); // Reserved - dataStruct->write32(Endian_SwapBE32(table_entries)); // Value count + dataStruct->write32(0); // Reserved + dataStruct->write32(Endian_SwapBE32(table_entries)); // Value count for (size_t i = 0; i < table_entries; ++i) { uint16_t value = reinterpret_cast<const uint16_t*>(table_16)[i]; dataStruct->write16(value); @@ -227,14 +226,30 @@ sp<DataStruct> IccHelper::write_trc_tag(const int table_entries, const void* tab return dataStruct; } -sp<DataStruct> IccHelper::write_trc_tag_for_linear() { - int total_length = 16; - sp<DataStruct> dataStruct = sp<DataStruct>::make(total_length); - dataStruct->write32(Endian_SwapBE32(kTAG_ParaCurveType)); // Type - dataStruct->write32(0); // Reserved - dataStruct->write32(Endian_SwapBE16(kExponential_ParaCurveType)); - dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(1.0))); +sp<DataStruct> IccHelper::write_trc_tag(const TransferFunction& fn) { + if (fn.a == 1.f && fn.b == 0.f && fn.c == 0.f + && fn.d == 0.f && fn.e == 0.f && fn.f == 0.f) { + int total_length = 16; + sp<DataStruct> dataStruct = new DataStruct(total_length); + dataStruct->write32(Endian_SwapBE32(kTAG_ParaCurveType)); // Type + dataStruct->write32(0); // Reserved + dataStruct->write32(Endian_SwapBE16(kExponential_ParaCurveType)); + dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.g))); + return dataStruct; + } + int total_length = 40; + sp<DataStruct> dataStruct = new DataStruct(total_length); + dataStruct->write32(Endian_SwapBE32(kTAG_ParaCurveType)); // Type + dataStruct->write32(0); // Reserved + dataStruct->write32(Endian_SwapBE16(kGABCDEF_ParaCurveType)); + dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.g))); + dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.a))); + dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.b))); + dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.c))); + dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.d))); + dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.e))); + dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.f))); return dataStruct; } @@ -349,7 +364,7 @@ sp<DataStruct> IccHelper::write_mAB_or_mBA_tag(uint32_t type, // The "B" curve is required. for (size_t i = 0; i < kNumChannels; ++i) { - b_curves_data[i] = write_trc_tag_for_linear(); + b_curves_data[i] = write_trc_tag(kLinear_TransFun); } // The "A" curve and CLUT are optional. @@ -362,7 +377,7 @@ sp<DataStruct> IccHelper::write_mAB_or_mBA_tag(uint32_t type, a_curves_offset = clut_offset + clut->getLength(); for (size_t i = 0; i < kNumChannels; ++i) { - a_curves_data[i] = write_trc_tag_for_linear(); + a_curves_data[i] = write_trc_tag(kLinear_TransFun); } } @@ -460,9 +475,9 @@ sp<DataStruct> IccHelper::writeIccProfile(ultrahdr_transfer_function tf, tags.emplace_back(kTAG_bTRC, write_trc_tag(kTrcTableSize, reinterpret_cast<uint8_t*>(trc_table.data()))); } else { - tags.emplace_back(kTAG_rTRC, write_trc_tag_for_linear()); - tags.emplace_back(kTAG_gTRC, write_trc_tag_for_linear()); - tags.emplace_back(kTAG_bTRC, write_trc_tag_for_linear()); + tags.emplace_back(kTAG_rTRC, write_trc_tag(kSRGB_TransFun)); + tags.emplace_back(kTAG_gTRC, write_trc_tag(kSRGB_TransFun)); + tags.emplace_back(kTAG_bTRC, write_trc_tag(kSRGB_TransFun)); } } diff --git a/libs/ultrahdr/include/ultrahdr/gainmapmath.h b/libs/ultrahdr/include/ultrahdr/gainmapmath.h index edf152d8ed..50b4d2fab1 100644 --- a/libs/ultrahdr/include/ultrahdr/gainmapmath.h +++ b/libs/ultrahdr/include/ultrahdr/gainmapmath.h @@ -51,6 +51,23 @@ struct Color { typedef Color (*ColorTransformFn)(Color); typedef float (*ColorCalculationFn)(Color); +// A transfer function mapping encoded values to linear values, +// represented by this 7-parameter piecewise function: +// +// linear = sign(encoded) * (c*|encoded| + f) , 0 <= |encoded| < d +// = sign(encoded) * ((a*|encoded| + b)^g + e), d <= |encoded| +// +// (A simple gamma transfer function sets g to gamma and a to 1.) +typedef struct TransferFunction { + float g, a,b,c,d,e,f; +} TransferFunction; + +static constexpr TransferFunction kSRGB_TransFun = + { 2.4f, (float)(1/1.055), (float)(0.055/1.055), (float)(1/12.92), 0.04045f, 0.0f, 0.0f }; + +static constexpr TransferFunction kLinear_TransFun = + { 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }; + inline Color operator+=(Color& lhs, const Color& rhs) { lhs.r += rhs.r; lhs.g += rhs.g; @@ -312,7 +329,7 @@ Color hlgOetf(Color e); float hlgOetfLUT(float e); Color hlgOetfLUT(Color e); -constexpr size_t kHlgOETFPrecision = 10; +constexpr size_t kHlgOETFPrecision = 16; constexpr size_t kHlgOETFNumEntries = 1 << kHlgOETFPrecision; /* @@ -325,7 +342,7 @@ Color hlgInvOetf(Color e_gamma); float hlgInvOetfLUT(float e_gamma); Color hlgInvOetfLUT(Color e_gamma); -constexpr size_t kHlgInvOETFPrecision = 10; +constexpr size_t kHlgInvOETFPrecision = 12; constexpr size_t kHlgInvOETFNumEntries = 1 << kHlgInvOETFPrecision; /* @@ -338,7 +355,7 @@ Color pqOetf(Color e); float pqOetfLUT(float e); Color pqOetfLUT(Color e); -constexpr size_t kPqOETFPrecision = 10; +constexpr size_t kPqOETFPrecision = 16; constexpr size_t kPqOETFNumEntries = 1 << kPqOETFPrecision; /* @@ -351,7 +368,7 @@ Color pqInvOetf(Color e_gamma); float pqInvOetfLUT(float e_gamma); Color pqInvOetfLUT(Color e_gamma); -constexpr size_t kPqInvOETFPrecision = 10; +constexpr size_t kPqInvOETFPrecision = 12; constexpr size_t kPqInvOETFNumEntries = 1 << kPqInvOETFPrecision; diff --git a/libs/ultrahdr/include/ultrahdr/icc.h b/libs/ultrahdr/include/ultrahdr/icc.h index 7f047f8f5b..971b267fe4 100644 --- a/libs/ultrahdr/include/ultrahdr/icc.h +++ b/libs/ultrahdr/include/ultrahdr/icc.h @@ -17,6 +17,7 @@ #ifndef ANDROID_ULTRAHDR_ICC_H #define ANDROID_ULTRAHDR_ICC_H +#include <ultrahdr/gainmapmath.h> #include <ultrahdr/jpegr.h> #include <ultrahdr/jpegrutils.h> #include <utils/RefBase.h> @@ -222,7 +223,7 @@ private: const ultrahdr_color_gamut gamut); static sp<DataStruct> write_xyz_tag(float x, float y, float z); static sp<DataStruct> write_trc_tag(const int table_entries, const void* table_16); - static sp<DataStruct> write_trc_tag_for_linear(); + static sp<DataStruct> write_trc_tag(const TransferFunction& fn); static float compute_tone_map_gain(const ultrahdr_transfer_function tf, float L); static sp<DataStruct> write_cicp_tag(uint32_t color_primaries, uint32_t transfer_characteristics); diff --git a/libs/ultrahdr/include/ultrahdr/jpegr.h b/libs/ultrahdr/include/ultrahdr/jpegr.h index a35fd30634..f80496a758 100644 --- a/libs/ultrahdr/include/ultrahdr/jpegr.h +++ b/libs/ultrahdr/include/ultrahdr/jpegr.h @@ -348,16 +348,6 @@ private: status_t extractPrimaryImageAndGainMap(jr_compressed_ptr compressed_jpegr_image, jr_compressed_ptr primary_image, jr_compressed_ptr gain_map); - /* - * This method is called in the decoding pipeline. It will read XMP metadata to find the start - * position of the compressed gain map, and will extract the compressed gain map. - * - * @param compressed_jpegr_image compressed JPEGR image - * @param dest destination of compressed gain map - * @return NO_ERROR if calculation succeeds, error code if error occurs. - */ - status_t extractGainMap(jr_compressed_ptr compressed_jpegr_image, - jr_compressed_ptr dest); /* * This method is called in the encoding pipeline. It will take the standard 8-bit JPEG image, diff --git a/libs/ultrahdr/include/ultrahdr/jpegrerrorcode.h b/libs/ultrahdr/include/ultrahdr/jpegrerrorcode.h index 064123210f..5420e1c9cf 100644 --- a/libs/ultrahdr/include/ultrahdr/jpegrerrorcode.h +++ b/libs/ultrahdr/include/ultrahdr/jpegrerrorcode.h @@ -44,6 +44,7 @@ enum { ERROR_JPEGR_INVALID_TRANS_FUNC = JPEGR_IO_ERROR_BASE - 6, ERROR_JPEGR_INVALID_METADATA = JPEGR_IO_ERROR_BASE - 7, ERROR_JPEGR_UNSUPPORTED_METADATA = JPEGR_IO_ERROR_BASE - 8, + ERROR_JPEGR_GAIN_MAP_IMAGE_NOT_FOUND = JPEGR_IO_ERROR_BASE - 9, JPEGR_RUNTIME_ERROR_BASE = -20000, ERROR_JPEGR_ENCODE_ERROR = JPEGR_RUNTIME_ERROR_BASE - 1, diff --git a/libs/ultrahdr/jpegr.cpp b/libs/ultrahdr/jpegr.cpp index 9c57f34c2a..5a601bd6b9 100644 --- a/libs/ultrahdr/jpegr.cpp +++ b/libs/ultrahdr/jpegr.cpp @@ -539,9 +539,12 @@ status_t JpegR::getJPEGRInfo(jr_compressed_ptr compressed_jpegr_image, jr_info_p return ERROR_JPEGR_INVALID_NULL_PTR; } - jpegr_compressed_struct primary_image, gain_map; - JPEGR_CHECK(extractPrimaryImageAndGainMap(compressed_jpegr_image, - &primary_image, &gain_map)); + jpegr_compressed_struct primary_image, gainmap_image; + status_t status = + extractPrimaryImageAndGainMap(compressed_jpegr_image, &primary_image, &gainmap_image); + if (status != NO_ERROR && status != ERROR_JPEGR_GAIN_MAP_IMAGE_NOT_FOUND) { + return status; + } JpegDecoderHelper jpeg_decoder; if (!jpeg_decoder.getCompressedImageParameters(primary_image.data, primary_image.length, @@ -550,7 +553,7 @@ status_t JpegR::getJPEGRInfo(jr_compressed_ptr compressed_jpegr_image, jr_info_p return ERROR_JPEGR_DECODE_ERROR; } - return NO_ERROR; + return status; } /* Decode API */ @@ -586,45 +589,56 @@ status_t JpegR::decodeJPEGR(jr_compressed_ptr compressed_jpegr_image, return ERROR_JPEGR_INVALID_INPUT_TYPE; } + jpegr_compressed_struct primary_image, gainmap_image; + status_t status = + extractPrimaryImageAndGainMap(compressed_jpegr_image, &primary_image, &gainmap_image); + if (status != NO_ERROR) { + if (output_format != ULTRAHDR_OUTPUT_SDR || status != ERROR_JPEGR_GAIN_MAP_IMAGE_NOT_FOUND) { + ALOGE("received invalid compressed jpegr image"); + return status; + } + } + + JpegDecoderHelper jpeg_decoder; + if (!jpeg_decoder.decompressImage(primary_image.data, primary_image.length, + (output_format == ULTRAHDR_OUTPUT_SDR))) { + return ERROR_JPEGR_DECODE_ERROR; + } + if (output_format == ULTRAHDR_OUTPUT_SDR) { - JpegDecoderHelper jpeg_decoder; - if (!jpeg_decoder.decompressImage(compressed_jpegr_image->data, compressed_jpegr_image->length, - true)) { - return ERROR_JPEGR_DECODE_ERROR; + if ((jpeg_decoder.getDecompressedImageWidth() * + jpeg_decoder.getDecompressedImageHeight() * 4) > + jpeg_decoder.getDecompressedImageSize()) { + return ERROR_JPEGR_CALCULATION_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; - - if (gain_map == nullptr && exif == nullptr) { - return NO_ERROR; + } else { + if ((jpeg_decoder.getDecompressedImageWidth() * + jpeg_decoder.getDecompressedImageHeight() * 3 / 2) > + jpeg_decoder.getDecompressedImageSize()) { + return ERROR_JPEGR_CALCULATION_ERROR; } + } - if (exif != nullptr) { - if (exif->data == nullptr) { - return ERROR_JPEGR_INVALID_NULL_PTR; - } - if (exif->length < jpeg_decoder.getEXIFSize()) { - return ERROR_JPEGR_BUFFER_TOO_SMALL; - } - memcpy(exif->data, jpeg_decoder.getEXIFPtr(), jpeg_decoder.getEXIFSize()); - exif->length = jpeg_decoder.getEXIFSize(); + if (exif != nullptr) { + if (exif->data == nullptr) { + return ERROR_JPEGR_INVALID_NULL_PTR; } - if (gain_map == nullptr) { - return NO_ERROR; + if (exif->length < jpeg_decoder.getEXIFSize()) { + return ERROR_JPEGR_BUFFER_TOO_SMALL; } + memcpy(exif->data, jpeg_decoder.getEXIFPtr(), jpeg_decoder.getEXIFSize()); + exif->length = jpeg_decoder.getEXIFSize(); } - jpegr_compressed_struct compressed_map; - JPEGR_CHECK(extractGainMap(compressed_jpegr_image, &compressed_map)); + if (output_format == ULTRAHDR_OUTPUT_SDR) { + dest->width = jpeg_decoder.getDecompressedImageWidth(); + dest->height = jpeg_decoder.getDecompressedImageHeight(); + memcpy(dest->data, jpeg_decoder.getDecompressedImagePtr(), dest->width * dest->height * 4); + return NO_ERROR; + } JpegDecoderHelper gain_map_decoder; - if (!gain_map_decoder.decompressImage(compressed_map.data, compressed_map.length)) { + if (!gain_map_decoder.decompressImage(gainmap_image.data, gainmap_image.length)) { return ERROR_JPEGR_DECODE_ERROR; } if ((gain_map_decoder.getDecompressedImageWidth() * @@ -633,12 +647,17 @@ status_t JpegR::decodeJPEGR(jr_compressed_ptr compressed_jpegr_image, return ERROR_JPEGR_CALCULATION_ERROR; } + jpegr_uncompressed_struct map; + map.data = gain_map_decoder.getDecompressedImagePtr(); + map.width = gain_map_decoder.getDecompressedImageWidth(); + map.height = gain_map_decoder.getDecompressedImageHeight(); + if (gain_map != nullptr) { - gain_map->width = gain_map_decoder.getDecompressedImageWidth(); - gain_map->height = gain_map_decoder.getDecompressedImageHeight(); + gain_map->width = map.width; + gain_map->height = map.height; int size = gain_map->width * gain_map->height; gain_map->data = malloc(size); - memcpy(gain_map->data, gain_map_decoder.getDecompressedImagePtr(), size); + memcpy(gain_map->data, map.data, size); } ultrahdr_metadata_struct uhdr_metadata; @@ -648,46 +667,16 @@ status_t JpegR::decodeJPEGR(jr_compressed_ptr compressed_jpegr_image, } if (metadata != nullptr) { - metadata->version = uhdr_metadata.version; - metadata->minContentBoost = uhdr_metadata.minContentBoost; - metadata->maxContentBoost = uhdr_metadata.maxContentBoost; - metadata->gamma = uhdr_metadata.gamma; - metadata->offsetSdr = uhdr_metadata.offsetSdr; - metadata->offsetHdr = uhdr_metadata.offsetHdr; - metadata->hdrCapacityMin = uhdr_metadata.hdrCapacityMin; - metadata->hdrCapacityMax = uhdr_metadata.hdrCapacityMax; - } - - if (output_format == ULTRAHDR_OUTPUT_SDR) { - return NO_ERROR; - } - - JpegDecoderHelper jpeg_decoder; - if (!jpeg_decoder.decompressImage(compressed_jpegr_image->data, compressed_jpegr_image->length)) { - return ERROR_JPEGR_DECODE_ERROR; - } - if ((jpeg_decoder.getDecompressedImageWidth() * - jpeg_decoder.getDecompressedImageHeight() * 3 / 2) > - jpeg_decoder.getDecompressedImageSize()) { - return ERROR_JPEGR_CALCULATION_ERROR; + metadata->version = uhdr_metadata.version; + metadata->minContentBoost = uhdr_metadata.minContentBoost; + metadata->maxContentBoost = uhdr_metadata.maxContentBoost; + metadata->gamma = uhdr_metadata.gamma; + metadata->offsetSdr = uhdr_metadata.offsetSdr; + metadata->offsetHdr = uhdr_metadata.offsetHdr; + metadata->hdrCapacityMin = uhdr_metadata.hdrCapacityMin; + metadata->hdrCapacityMax = uhdr_metadata.hdrCapacityMax; } - if (exif != nullptr) { - if (exif->data == nullptr) { - return ERROR_JPEGR_INVALID_NULL_PTR; - } - if (exif->length < jpeg_decoder.getEXIFSize()) { - return ERROR_JPEGR_BUFFER_TOO_SMALL; - } - memcpy(exif->data, jpeg_decoder.getEXIFPtr(), jpeg_decoder.getEXIFSize()); - exif->length = jpeg_decoder.getEXIFSize(); - } - - jpegr_uncompressed_struct map; - map.data = gain_map_decoder.getDecompressedImagePtr(); - map.width = gain_map_decoder.getDecompressedImageWidth(); - map.height = gain_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(); @@ -1070,7 +1059,7 @@ status_t JpegR::applyGainMap(jr_uncompressed_ptr uncompressed_yuv_420_image, } case ULTRAHDR_OUTPUT_HDR_PQ: { -#if USE_HLG_OETF_LUT +#if USE_PQ_OETF_LUT ColorTransformFn hdrOetf = pqOetfLUT; #else ColorTransformFn hdrOetf = pqOetf; @@ -1131,12 +1120,8 @@ status_t JpegR::extractPrimaryImageAndGainMap(jr_compressed_ptr compressed_jpegr const auto& jpeg_info = jpeg_info_builder.GetInfo(); const auto& image_ranges = jpeg_info.GetImageRanges(); - if (image_ranges.empty()) { - return ERROR_JPEGR_INVALID_INPUT_TYPE; - } - if (image_ranges.size() != 2) { - // Must be 2 JPEG Images + if (image_ranges.empty()) { return ERROR_JPEGR_INVALID_INPUT_TYPE; } @@ -1146,23 +1131,23 @@ status_t JpegR::extractPrimaryImageAndGainMap(jr_compressed_ptr compressed_jpegr primary_image->length = image_ranges[0].GetLength(); } + if (image_ranges.size() == 1) { + return ERROR_JPEGR_GAIN_MAP_IMAGE_NOT_FOUND; + } + if (gain_map != nullptr) { gain_map->data = static_cast<uint8_t*>(compressed_jpegr_image->data) + image_ranges[1].GetBegin(); gain_map->length = image_ranges[1].GetLength(); } - return NO_ERROR; -} - - -status_t JpegR::extractGainMap(jr_compressed_ptr compressed_jpegr_image, - jr_compressed_ptr dest) { - if (compressed_jpegr_image == nullptr || dest == nullptr) { - return ERROR_JPEGR_INVALID_NULL_PTR; + // TODO: choose primary image and gain map image carefully + if (image_ranges.size() > 2) { + ALOGW("Number of jpeg images present %d, primary, gain map images may not be correctly chosen", + (int)image_ranges.size()); } - return extractPrimaryImageAndGainMap(compressed_jpegr_image, nullptr, dest); + return NO_ERROR; } // JPEG/R structure: diff --git a/services/displayservice/Android.bp b/services/displayservice/Android.bp index 8681784405..c88f2fca83 100644 --- a/services/displayservice/Android.bp +++ b/services/displayservice/Android.bp @@ -23,7 +23,7 @@ package { default_applicable_licenses: ["frameworks_native_license"], } -cc_library_shared { +cc_library_static { name: "libdisplayservicehidl", srcs: [ @@ -37,18 +37,24 @@ cc_library_shared { "libgui", "libhidlbase", "libutils", + ], + + static_libs: [ "android.frameworks.displayservice@1.0", ], export_include_dirs: ["include"], export_shared_lib_headers: [ - "android.frameworks.displayservice@1.0", "libgui", "libutils", ], + export_static_lib_headers: [ + "android.frameworks.displayservice@1.0", + ], + cflags: [ "-Werror", "-Wall", - ] + ], } diff --git a/services/gpuservice/Android.bp b/services/gpuservice/Android.bp index fba64c7569..052efb6bbb 100644 --- a/services/gpuservice/Android.bp +++ b/services/gpuservice/Android.bp @@ -71,6 +71,7 @@ filegroup { cc_library_shared { name: "libgpuservice", defaults: ["libgpuservice_production_defaults"], + export_include_dirs: ["include"], srcs: [ ":libgpuservice_sources", ], diff --git a/services/gpuservice/GpuService.cpp b/services/gpuservice/GpuService.cpp index 5e7b2e8df8..4a08c11c14 100644 --- a/services/gpuservice/GpuService.cpp +++ b/services/gpuservice/GpuService.cpp @@ -16,7 +16,7 @@ #define ATRACE_TAG ATRACE_TAG_GRAPHICS -#include "GpuService.h" +#include "gpuservice/GpuService.h" #include <android-base/stringprintf.h> #include <android-base/properties.h> @@ -35,6 +35,7 @@ #include <vkjson.h> #include <thread> +#include <memory> namespace android { @@ -58,18 +59,21 @@ GpuService::GpuService() mGpuStats(std::make_unique<GpuStats>()), mGpuMemTracer(std::make_unique<GpuMemTracer>()) { - std::thread gpuMemAsyncInitThread([this]() { + mGpuMemAsyncInitThread = std::make_unique<std::thread>([this] (){ mGpuMem->initialize(); mGpuMemTracer->initialize(mGpuMem); }); - gpuMemAsyncInitThread.detach(); - std::thread gpuWorkAsyncInitThread([this]() { + mGpuWorkAsyncInitThread = std::make_unique<std::thread>([this]() { mGpuWork->initialize(); }); - gpuWorkAsyncInitThread.detach(); }; +GpuService::~GpuService() { + mGpuWorkAsyncInitThread->join(); + mGpuMemAsyncInitThread->join(); +} + void GpuService::setGpuStats(const std::string& driverPackageName, const std::string& driverVersionName, uint64_t driverVersionCode, int64_t driverBuildTime, const std::string& appPackageName, diff --git a/services/gpuservice/GpuService.h b/services/gpuservice/include/gpuservice/GpuService.h index 0e559f2c34..54f8f666bc 100644 --- a/services/gpuservice/GpuService.h +++ b/services/gpuservice/include/gpuservice/GpuService.h @@ -24,6 +24,7 @@ #include <serviceutils/PriorityDumper.h> #include <mutex> +#include <thread> #include <vector> namespace android { @@ -41,6 +42,7 @@ public: static const char* const SERVICE_NAME ANDROID_API; GpuService() ANDROID_API; + ~GpuService(); protected: status_t shellCommand(int in, int out, int err, std::vector<String16>& args) override; @@ -90,6 +92,8 @@ private: std::unique_ptr<GpuMemTracer> mGpuMemTracer; std::mutex mLock; std::string mDeveloperDriverPath; + std::unique_ptr<std::thread> mGpuMemAsyncInitThread; + std::unique_ptr<std::thread> mGpuWorkAsyncInitThread; }; } // namespace android diff --git a/services/gpuservice/main_gpuservice.cpp b/services/gpuservice/main_gpuservice.cpp index 64aafcab6a..200237219e 100644 --- a/services/gpuservice/main_gpuservice.cpp +++ b/services/gpuservice/main_gpuservice.cpp @@ -18,7 +18,7 @@ #include <binder/IServiceManager.h> #include <binder/ProcessState.h> #include <sys/resource.h> -#include "GpuService.h" +#include "gpuservice/GpuService.h" using namespace android; diff --git a/services/gpuservice/tests/fuzzers/GpuServiceFuzzer.cpp b/services/gpuservice/tests/fuzzers/GpuServiceFuzzer.cpp index c2574a3fd3..241b8646e1 100644 --- a/services/gpuservice/tests/fuzzers/GpuServiceFuzzer.cpp +++ b/services/gpuservice/tests/fuzzers/GpuServiceFuzzer.cpp @@ -16,7 +16,7 @@ #include <fuzzbinder/libbinder_driver.h> -#include "GpuService.h" +#include "gpuservice/GpuService.h" using ::android::fuzzService; using ::android::GpuService; diff --git a/services/gpuservice/tests/unittests/Android.bp b/services/gpuservice/tests/unittests/Android.bp index 51642f9472..c870b17b79 100644 --- a/services/gpuservice/tests/unittests/Android.bp +++ b/services/gpuservice/tests/unittests/Android.bp @@ -28,6 +28,7 @@ cc_test { "GpuMemTest.cpp", "GpuMemTracerTest.cpp", "GpuStatsTest.cpp", + "GpuServiceTest.cpp", ], header_libs: ["bpf_headers"], shared_libs: [ @@ -45,6 +46,7 @@ cc_test { "libstatslog", "libstatspull", "libutils", + "libgpuservice", ], static_libs: [ "libgmock", diff --git a/services/gpuservice/tests/unittests/GpuServiceTest.cpp b/services/gpuservice/tests/unittests/GpuServiceTest.cpp new file mode 100644 index 0000000000..62b3e53f53 --- /dev/null +++ b/services/gpuservice/tests/unittests/GpuServiceTest.cpp @@ -0,0 +1,52 @@ +#undef LOG_TAG +#define LOG_TAG "gpuservice_unittest" + +#include "gpuservice/GpuService.h" + +#include <gtest/gtest.h> +#include <log/log_main.h> + +#include <chrono> +#include <thread> + +namespace android { +namespace { + +class GpuServiceTest : public testing::Test { +public: + GpuServiceTest() { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); + } + + ~GpuServiceTest() { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name()); + } + +}; + + +/* +* The behaviour before this test + fixes was UB caused by threads accessing deallocated memory. +* +* This test creates the service (which initializes the culprit threads), +* deallocates it immediately and sleeps. +* +* GpuService's destructor gets called and joins the threads. +* If we haven't crashed by the time the sleep time has elapsed, we're good +* Let the test pass. +*/ +TEST_F(GpuServiceTest, onInitializeShouldNotCauseUseAfterFree) { + sp<GpuService> service = new GpuService(); + service.clear(); + std::this_thread::sleep_for(std::chrono::seconds(3)); + + // If we haven't crashed yet due to threads accessing freed up memory, let the test pass + EXPECT_TRUE(true); +} + +} // namespace +} // namespace android diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp index dc7c75a775..ee03d947db 100644 --- a/services/inputflinger/Android.bp +++ b/services/inputflinger/Android.bp @@ -28,6 +28,7 @@ inputflinger_tidy_checks = [ cc_defaults { name: "inputflinger_defaults", + host_supported: true, cpp_std: "c++20", cflags: [ "-Wall", @@ -50,6 +51,20 @@ cc_defaults { "-*", // Disable all checks not explicitly enabled for now ] + inputflinger_tidy_checks, tidy_checks_as_errors: inputflinger_tidy_checks, + target: { + host: { + sanitize: { + address: true, + }, + include_dirs: [ + "bionic/libc/kernel/android/uapi/", + "bionic/libc/kernel/uapi", + ], + cflags: [ + "-D__ANDROID_HOST__", + ], + }, + }, } ///////////////////////////////////////////////// @@ -79,6 +94,7 @@ cc_defaults { "libcrypto", "libcutils", "libhidlbase", + "libinput", "libkll", "liblog", "libprotobuf-cpp-lite", @@ -95,30 +111,22 @@ cc_defaults { android: { shared_libs: [ "libgui", - "libinput", "libstatspull", "libstatssocket", ], }, host: { static_libs: [ - "libinput", "libstatspull", "libstatssocket", ], - include_dirs: [ - "bionic/libc/kernel/android/uapi/", - "bionic/libc/kernel/uapi", - ], - cflags: [ - "-D__ANDROID_HOST__", - ], }, }, } cc_library_shared { name: "libinputflinger", + host_supported: true, defaults: [ "inputflinger_defaults", "libinputflinger_defaults", @@ -179,25 +187,13 @@ cc_defaults { "libbase", "libbinder", "libcutils", + "libinput", "liblog", "libutils", ], header_libs: [ "libinputflinger_headers", ], - target: { - android: { - shared_libs: [ - "libinput", - ], - }, - host: { - static_libs: [ - "libinput", - "libui-types", - ], - }, - }, } cc_library_shared { @@ -224,6 +220,7 @@ phony { // native targets "libgui_test", "libinput", + "libinputreader_static", "libinputflinger", "inputflinger_tests", "inputflinger_benchmarks", diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp index 1a9f47d934..09c88ba8dd 100644 --- a/services/inputflinger/InputManager.cpp +++ b/services/inputflinger/InputManager.cpp @@ -186,7 +186,7 @@ status_t InputManager::dump(int fd, const Vector<String16>& args) { dump += " InputFlinger dump\n"; - ::write(fd, dump.c_str(), dump.size()); + TEMP_FAILURE_RETRY(::write(fd, dump.c_str(), dump.size())); return NO_ERROR; } diff --git a/services/inputflinger/TEST_MAPPING b/services/inputflinger/TEST_MAPPING index cdc4c088b5..ca00e7dac2 100644 --- a/services/inputflinger/TEST_MAPPING +++ b/services/inputflinger/TEST_MAPPING @@ -116,6 +116,14 @@ ] }, { + "name": "CtsAppTestCases", + "options": [ + { + "include-filter": "android.app.cts.ToolbarActionBarTest" + } + ] + }, + { "name": "FrameworksServicesTests", "options": [ { @@ -246,6 +254,14 @@ ] }, { + "name": "CtsAppTestCases", + "options": [ + { + "include-filter": "android.app.cts.ToolbarActionBarTest" + } + ] + }, + { "name": "FrameworksServicesTests", "options": [ { diff --git a/services/inputflinger/benchmarks/Android.bp b/services/inputflinger/benchmarks/Android.bp index 4e2a6fbf87..e200f8b303 100644 --- a/services/inputflinger/benchmarks/Android.bp +++ b/services/inputflinger/benchmarks/Android.bp @@ -19,6 +19,7 @@ cc_benchmark { shared_libs: [ "libbase", "libbinder", + "libbinder_ndk", "libcrypto", "libcutils", "libinputflinger_base", diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp index b2e274d832..7a410831f6 100644 --- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp +++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp @@ -82,8 +82,6 @@ private: void notifyVibratorState(int32_t deviceId, bool isOn) override {} - InputDispatcherConfiguration getDispatcherConfiguration() override { return mConfig; } - bool filterInputEvent(const InputEvent& inputEvent, uint32_t policyFlags) override { return true; // dispatch event normally } diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp index 492551ec06..8b57730b92 100644 --- a/services/inputflinger/dispatcher/Android.bp +++ b/services/inputflinger/dispatcher/Android.bp @@ -59,6 +59,7 @@ cc_defaults { "libbase", "libcrypto", "libcutils", + "libinput", "libkll", "liblog", "libprotobuf-cpp-lite", @@ -74,14 +75,12 @@ cc_defaults { android: { shared_libs: [ "libgui", - "libinput", "libstatspull", "libstatssocket", ], }, host: { static_libs: [ - "libinput", "libstatspull", "libstatssocket", ], diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 65c2407bec..29c4e469ea 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -4357,7 +4357,7 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs& args) { validateMotionEvent(args.action, args.actionButton, args.getPointerCount(), args.pointerProperties.data()); if (!motionCheck.ok()) { - LOG(ERROR) << "Invalid event: " << args.dump() << "; reason: " << motionCheck.error(); + LOG(FATAL) << "Invalid event: " << args.dump() << "; reason: " << motionCheck.error(); return; } @@ -5133,13 +5133,6 @@ void InputDispatcher::setInputWindowsLocked( // Copy old handles for release if they are no longer present. const std::vector<sp<WindowInfoHandle>> oldWindowHandles = getWindowHandlesLocked(displayId); - // Save the old windows' orientation by ID before it gets updated. - std::unordered_map<int32_t, uint32_t> oldWindowOrientations; - for (const sp<WindowInfoHandle>& handle : oldWindowHandles) { - oldWindowOrientations.emplace(handle->getId(), - handle->getInfo()->transform.getOrientation()); - } - updateWindowHandlesForDisplayLocked(windowInfoHandles, displayId); const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesLocked(displayId); @@ -5193,23 +5186,6 @@ void InputDispatcher::setInputWindowsLocked( } } - // Determine if the orientation of any of the input windows have changed, and cancel all - // pointer events if necessary. - for (const sp<WindowInfoHandle>& oldWindowHandle : oldWindowHandles) { - const sp<WindowInfoHandle> newWindowHandle = getWindowHandleLocked(oldWindowHandle); - if (newWindowHandle != nullptr && - newWindowHandle->getInfo()->transform.getOrientation() != - oldWindowOrientations[oldWindowHandle->getId()]) { - std::shared_ptr<InputChannel> inputChannel = - getInputChannelLocked(newWindowHandle->getToken()); - if (inputChannel != nullptr) { - CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS, - "touched window's orientation changed"); - synthesizeCancelationEventsForInputChannelLocked(inputChannel, options); - } - } - } - // Release information for windows that are no longer present. // This ensures that unused input channels are released promptly. // Otherwise, they might stick around until the window handle is destroyed @@ -6817,13 +6793,6 @@ void InputDispatcher::cancelCurrentTouch() { mLooper->wake(); } -void InputDispatcher::requestRefreshConfiguration() { - InputDispatcherConfiguration config = mPolicy.getDispatcherConfiguration(); - - std::scoped_lock _l(mLock); - mConfig = config; -} - void InputDispatcher::setMonitorDispatchingTimeoutForTest(std::chrono::nanoseconds timeout) { std::scoped_lock _l(mLock); mMonitorDispatchingTimeout = timeout; @@ -6936,4 +6905,11 @@ sp<WindowInfoHandle> InputDispatcher::findWallpaperWindowBelow( return nullptr; } +void InputDispatcher::setKeyRepeatConfiguration(nsecs_t timeout, nsecs_t delay) { + std::scoped_lock _l(mLock); + + mConfig.keyRepeatTimeout = timeout; + mConfig.keyRepeatDelay = delay; +} + } // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 37f569ee97..9cd1666a80 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -148,11 +148,11 @@ public: void cancelCurrentTouch() override; - void requestRefreshConfiguration() override; - // Public to allow tests to verify that a Monitor can get ANR. void setMonitorDispatchingTimeoutForTest(std::chrono::nanoseconds timeout); + void setKeyRepeatConfiguration(nsecs_t timeout, nsecs_t delay) override; + private: enum class DropReason { NOT_DROPPED, diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h index 49597e2101..f13885a143 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h @@ -226,11 +226,10 @@ public: */ virtual void cancelCurrentTouch() = 0; - /** - * Request that the InputDispatcher's configuration, which can be obtained through the policy, - * be updated. + /* + * Updates key repeat configuration timeout and delay. */ - virtual void requestRefreshConfiguration() = 0; + virtual void setKeyRepeatConfiguration(nsecs_t timeout, nsecs_t delay) = 0; }; } // namespace android diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h index 729d01f363..af28e48121 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h @@ -73,9 +73,6 @@ public: InputDeviceSensorAccuracy accuracy) = 0; virtual void notifyVibratorState(int32_t deviceId, bool isOn) = 0; - /* Gets the input dispatcher configuration. */ - virtual InputDispatcherConfiguration getDispatcherConfiguration() = 0; - /* Filters an input event. * Return true to dispatch the event unmodified, false to consume the event. * A filter can also transform and inject events later by passing POLICY_FLAG_FILTERED diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp index ccb8773ed0..c0c6d1f7e1 100644 --- a/services/inputflinger/reader/Android.bp +++ b/services/inputflinger/reader/Android.bp @@ -52,6 +52,7 @@ filegroup { "mapper/RotaryEncoderInputMapper.cpp", "mapper/SensorInputMapper.cpp", "mapper/SingleTouchInputMapper.cpp", + "mapper/SlopController.cpp", "mapper/SwitchInputMapper.cpp", "mapper/TouchCursorInputMapperCommon.cpp", "mapper/TouchInputMapper.cpp", @@ -79,6 +80,7 @@ cc_defaults { "libcrypto", "libcutils", "libjsoncpp", + "libinput", "liblog", "libPlatformProperties", "libstatslog", @@ -97,13 +99,11 @@ cc_defaults { target: { android: { shared_libs: [ - "libinput", "libstatspull", ], }, host: { static_libs: [ - "libinput", "libbinder", "libstatspull", ], diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp index 0a64a1c4a8..2aaddf59bb 100644 --- a/services/inputflinger/reader/InputDevice.cpp +++ b/services/inputflinger/reader/InputDevice.cpp @@ -48,6 +48,7 @@ InputDevice::InputDevice(InputReaderContext* context, int32_t id, int32_t genera mIdentifier(identifier), mClasses(0), mSources(0), + mIsWaking(false), mIsExternal(false), mHasMic(false), mDropUntilNextSync(false) {} @@ -101,6 +102,7 @@ void InputDevice::dump(std::string& dump, const std::string& eventHubDevStr) { dump += StringPrintf(INDENT "%s", eventHubDevStr.c_str()); dump += StringPrintf(INDENT2 "Generation: %d\n", mGeneration); dump += StringPrintf(INDENT2 "IsExternal: %s\n", toString(mIsExternal)); + dump += StringPrintf(INDENT2 "IsWaking: %s\n", toString(mIsWaking)); dump += StringPrintf(INDENT2 "AssociatedDisplayPort: "); if (mAssociatedDisplayPort) { dump += StringPrintf("%" PRIu8 "\n", *mAssociatedDisplayPort); @@ -220,6 +222,7 @@ std::list<NotifyArgs> InputDevice::configure(nsecs_t when, mAssociatedDeviceType = getValueByKey(readerConfig.deviceTypeAssociations, mIdentifier.location); + mIsWaking = mConfiguration.getBool("device.wake").value_or(false); } if (!changes.any() || changes.test(Change::KEYBOARD_LAYOUTS)) { @@ -376,9 +379,25 @@ std::list<NotifyArgs> InputDevice::process(const RawEvent* rawEvents, size_t cou } --count; } + postProcess(out); return out; } +void InputDevice::postProcess(std::list<NotifyArgs>& args) const { + if (mIsWaking) { + // Update policy flags to request wake for the `NotifyArgs` that come from waking devices. + for (auto& arg : args) { + if (const auto notifyMotionArgs = std::get_if<NotifyMotionArgs>(&arg)) { + notifyMotionArgs->policyFlags |= POLICY_FLAG_WAKE; + } else if (const auto notifySwitchArgs = std::get_if<NotifySwitchArgs>(&arg)) { + notifySwitchArgs->policyFlags |= POLICY_FLAG_WAKE; + } else if (const auto notifyKeyArgs = std::get_if<NotifyKeyArgs>(&arg)) { + notifyKeyArgs->policyFlags |= POLICY_FLAG_WAKE; + } + } + } +} + std::list<NotifyArgs> InputDevice::timeoutExpired(nsecs_t when) { std::list<NotifyArgs> out; for_each_mapper([&](InputMapper& mapper) { out += mapper.timeoutExpired(when); }); diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h index 2f8e5bd6cf..aae3fe79e0 100644 --- a/services/inputflinger/reader/include/InputDevice.h +++ b/services/inputflinger/reader/include/InputDevice.h @@ -191,6 +191,7 @@ private: std::unique_ptr<PeripheralControllerInterface> mController; uint32_t mSources; + bool mIsWaking; bool mIsExternal; std::optional<uint8_t> mAssociatedDisplayPort; std::optional<std::string> mAssociatedDisplayUniqueId; @@ -207,6 +208,10 @@ private: PropertyMap mConfiguration; + // Runs logic post a `process` call. This can be used to update the generated `NotifyArgs` as + // per the properties of the InputDevice. + void postProcess(std::list<NotifyArgs>& args) const; + // helpers to interate over the devices collection // run a function against every mapper on every subdevice inline void for_each_mapper(std::function<void(InputMapper&)> f) { diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp index 7388752805..e03a7730ae 100644 --- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp +++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp @@ -205,6 +205,7 @@ std::list<NotifyArgs> KeyboardInputMapper::processKey(nsecs_t when, nsecs_t read int32_t keyCode; int32_t keyMetaState; uint32_t policyFlags; + int32_t flags = AKEY_EVENT_FLAG_FROM_SYSTEM; if (getDeviceContext().mapKey(scanCode, usageCode, mMetaState, &keyCode, &keyMetaState, &policyFlags)) { @@ -226,6 +227,7 @@ std::list<NotifyArgs> KeyboardInputMapper::processKey(nsecs_t when, nsecs_t read // key repeat, be sure to use same keycode as before in case of rotation keyCode = mKeyDowns[*keyDownIndex].keyCode; downTime = mKeyDowns[*keyDownIndex].downTime; + flags = mKeyDowns[*keyDownIndex].flags; } else { // key down if ((policyFlags & POLICY_FLAG_VIRTUAL) && @@ -234,12 +236,14 @@ std::list<NotifyArgs> KeyboardInputMapper::processKey(nsecs_t when, nsecs_t read } if (policyFlags & POLICY_FLAG_GESTURE) { out += getDeviceContext().cancelTouch(when, readTime); + flags |= AKEY_EVENT_FLAG_KEEP_TOUCH_MODE; } KeyDown keyDown; keyDown.keyCode = keyCode; keyDown.scanCode = scanCode; keyDown.downTime = when; + keyDown.flags = flags; mKeyDowns.push_back(keyDown); } } else { @@ -248,6 +252,7 @@ std::list<NotifyArgs> KeyboardInputMapper::processKey(nsecs_t when, nsecs_t read // key up, be sure to use same keycode as before in case of rotation keyCode = mKeyDowns[*keyDownIndex].keyCode; downTime = mKeyDowns[*keyDownIndex].downTime; + flags = mKeyDowns[*keyDownIndex].flags; mKeyDowns.erase(mKeyDowns.begin() + *keyDownIndex); } else { // key was not actually down @@ -281,9 +286,8 @@ std::list<NotifyArgs> KeyboardInputMapper::processKey(nsecs_t when, nsecs_t read out.emplace_back(NotifyKeyArgs(getContext()->getNextId(), when, readTime, getDeviceId(), mSource, getDisplayId(), policyFlags, - down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, - AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, - downTime)); + down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, flags, + keyCode, scanCode, keyMetaState, downTime)); return out; } @@ -410,7 +414,7 @@ std::list<NotifyArgs> KeyboardInputMapper::cancelAllDownKeys(nsecs_t when) { out.emplace_back(NotifyKeyArgs(getContext()->getNextId(), when, systemTime(SYSTEM_TIME_MONOTONIC), getDeviceId(), mSource, getDisplayId(), /*policyFlags=*/0, AKEY_EVENT_ACTION_UP, - AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_CANCELED, + mKeyDowns[i].flags | AKEY_EVENT_FLAG_CANCELED, mKeyDowns[i].keyCode, mKeyDowns[i].scanCode, AMETA_NONE, mKeyDowns[i].downTime)); } diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h index cd3d3c49e9..45fd68b8bb 100644 --- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h +++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h @@ -57,6 +57,7 @@ private: nsecs_t downTime{}; int32_t keyCode{}; int32_t scanCode{}; + int32_t flags{}; }; uint32_t mSource{}; diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp index 13f2e59db9..7251830fa9 100644 --- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp +++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp @@ -20,6 +20,7 @@ #include "RotaryEncoderInputMapper.h" +#include <utils/Timers.h> #include <optional> #include "CursorScrollAccumulator.h" @@ -62,6 +63,7 @@ void RotaryEncoderInputMapper::dump(std::string& dump) { dump += INDENT2 "Rotary Encoder Input Mapper:\n"; dump += StringPrintf(INDENT3 "HaveWheel: %s\n", toString(mRotaryEncoderScrollAccumulator.haveRelativeVWheel())); + dump += StringPrintf(INDENT3 "HaveSlopController: %s\n", toString(mSlopController != nullptr)); } std::list<NotifyArgs> RotaryEncoderInputMapper::reconfigure(nsecs_t when, @@ -70,6 +72,16 @@ std::list<NotifyArgs> RotaryEncoderInputMapper::reconfigure(nsecs_t when, std::list<NotifyArgs> out = InputMapper::reconfigure(when, config, changes); if (!changes.any()) { mRotaryEncoderScrollAccumulator.configure(getDeviceContext()); + + const PropertyMap& propertyMap = getDeviceContext().getConfiguration(); + float slopThreshold = propertyMap.getInt("rotary_encoder.slop_threshold").value_or(0); + int32_t slopDurationNs = milliseconds_to_nanoseconds( + propertyMap.getInt("rotary_encoder.slop_duration_ms").value_or(0)); + if (slopThreshold > 0 && slopDurationNs > 0) { + mSlopController = std::make_unique<SlopController>(slopThreshold, slopDurationNs); + } else { + mSlopController = nullptr; + } } if (!changes.any() || changes.test(InputReaderConfiguration::Change::DISPLAY_INFO)) { std::optional<DisplayViewport> internalViewport = @@ -103,6 +115,10 @@ std::list<NotifyArgs> RotaryEncoderInputMapper::sync(nsecs_t when, nsecs_t readT std::list<NotifyArgs> out; float scroll = mRotaryEncoderScrollAccumulator.getRelativeVWheel(); + if (mSlopController) { + scroll = mSlopController->consumeEvent(when, scroll); + } + bool scrolled = scroll != 0; // Send motion event. diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h index 9e2e8c4342..fe5d152cef 100644 --- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h +++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h @@ -20,6 +20,7 @@ #include "CursorScrollAccumulator.h" #include "InputMapper.h" +#include "SlopController.h" namespace android { @@ -46,6 +47,7 @@ private: int32_t mSource; float mScalingFactor; ui::Rotation mOrientation; + std::unique_ptr<SlopController> mSlopController; explicit RotaryEncoderInputMapper(InputDeviceContext& deviceContext, const InputReaderConfiguration& readerConfig); diff --git a/services/inputflinger/reader/mapper/SlopController.cpp b/services/inputflinger/reader/mapper/SlopController.cpp new file mode 100644 index 0000000000..f79219f151 --- /dev/null +++ b/services/inputflinger/reader/mapper/SlopController.cpp @@ -0,0 +1,79 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// clang-format off +#include "../Macros.h" +// clang-format on + +#include "SlopController.h" + +namespace { +int signOf(float value) { + if (value == 0) return 0; + if (value > 0) return 1; + return -1; +} +} // namespace + +namespace android { + +SlopController::SlopController(float slopThreshold, nsecs_t slopDurationNanos) + : mSlopThreshold(slopThreshold), mSlopDurationNanos(slopDurationNanos) {} + +float SlopController::consumeEvent(nsecs_t eventTimeNanos, float value) { + if (mSlopDurationNanos == 0) { + return value; + } + + if (shouldResetSlopTracking(eventTimeNanos, value)) { + mCumulativeValue = 0; + mHasSlopBeenMet = false; + } + + mLastEventTimeNanos = eventTimeNanos; + + if (mHasSlopBeenMet) { + // Since slop has already been met, we know that all of the current value would pass the + // slop threshold. So return that, without any further processing. + return value; + } + + mCumulativeValue += value; + + if (abs(mCumulativeValue) >= mSlopThreshold) { + mHasSlopBeenMet = true; + // Return the amount of value that exceeds the slop. + return signOf(value) * (abs(mCumulativeValue) - mSlopThreshold); + } + + return 0; +} + +bool SlopController::shouldResetSlopTracking(nsecs_t eventTimeNanos, float value) const { + const nsecs_t ageNanos = eventTimeNanos - mLastEventTimeNanos; + if (ageNanos >= mSlopDurationNanos) { + return true; + } + if (value == 0) { + return false; + } + if (signOf(mCumulativeValue) != signOf(value)) { + return true; + } + return false; +} + +} // namespace android diff --git a/services/inputflinger/reader/mapper/SlopController.h b/services/inputflinger/reader/mapper/SlopController.h new file mode 100644 index 0000000000..c1064102e8 --- /dev/null +++ b/services/inputflinger/reader/mapper/SlopController.h @@ -0,0 +1,54 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <utils/Timers.h> + +namespace android { + +/** + * Controls a slop logic. Slop here refers to an approach to try and drop insignificant input + * events. This is helpful in cases where unintentional input events may cause unintended outcomes, + * like scrolling a screen or keeping the screen awake. + * + * Current slop logic: + * "If time since last event > Xns, then discard the next N values." + */ +class SlopController final { +public: + SlopController(float slopThreshold, nsecs_t slopDurationNanos); + + /** + * Consumes an event with a given time and value for slop processing. + * Returns an amount <=value that should be consumed. + */ + float consumeEvent(nsecs_t eventTime, float value); + +private: + bool shouldResetSlopTracking(nsecs_t eventTimeNanos, float value) const; + + /** The amount of event values ignored after an inactivity of the slop duration. */ + const float mSlopThreshold; + /** The duration of inactivity that resets slop controlling. */ + const nsecs_t mSlopDurationNanos; + + nsecs_t mLastEventTimeNanos = 0; + float mCumulativeValue = 0; + bool mHasSlopBeenMet = false; +}; + +} // namespace android diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index f2b0a4b0a7..44538f1434 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -271,7 +271,7 @@ void TouchInputMapper::dump(std::string& dump) { toString(mFusedStylusPointerId).c_str()); dump += StringPrintf(INDENT4 "External Stylus Data Timeout: %" PRId64 "\n", mExternalStylusFusionTimeout); - dump += StringPrintf(INDENT4 " External Stylus Buttons Applied: 0x%08x", + dump += StringPrintf(INDENT4 "External Stylus Buttons Applied: 0x%08x\n", mExternalStylusButtonsApplied); dump += INDENT3 "External Stylus State:\n"; dumpStylusState(dump, mExternalStylusState); @@ -972,7 +972,18 @@ void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded) (rawXResolution > 0 && rawYResolution > 0) ? (rawXResolution + rawYResolution) / 2 : 0; const DisplayViewport& newViewport = newViewportOpt.value_or(kUninitializedViewport); - const bool viewportChanged = mViewport != newViewport; + bool viewportChanged; + if (mParameters.enableForInactiveViewport) { + // When touch is enabled for an inactive viewport, ignore the + // viewport active status when checking whether the viewport has + // changed. + DisplayViewport tempViewport = mViewport; + tempViewport.isActive = newViewport.isActive; + viewportChanged = tempViewport != newViewport; + } else { + viewportChanged = mViewport != newViewport; + } + bool skipViewportUpdate = false; if (viewportChanged) { const bool viewportOrientationChanged = mViewport.orientation != newViewport.orientation; diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h index d8b59ca39b..c5dfb00adb 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.h +++ b/services/inputflinger/reader/mapper/TouchInputMapper.h @@ -195,9 +195,9 @@ protected: enum class DeviceMode { DISABLED, // input is disabled DIRECT, // direct mapping (touchscreen) - UNSCALED, // unscaled mapping (touchpad) + UNSCALED, // unscaled mapping (e.g. captured touchpad) NAVIGATION, // unscaled mapping with assist gesture (touch navigation) - POINTER, // pointer mapping (pointer) + POINTER, // pointer mapping (e.g. uncaptured touchpad, drawing tablet) ftl_last = POINTER }; diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp index 300bb85630..38640b3f02 100644 --- a/services/inputflinger/tests/Android.bp +++ b/services/inputflinger/tests/Android.bp @@ -59,6 +59,7 @@ cc_test { "NotifyArgs_test.cpp", "PreferStylusOverTouch_test.cpp", "PropertyProvider_test.cpp", + "SlopController_test.cpp", "SyncQueue_test.cpp", "TestInputListener.cpp", "TouchpadInputMapper_test.cpp", @@ -74,25 +75,9 @@ cc_test { target: { android: { shared_libs: [ - "libinput", "libvintf", ], }, - host: { - sanitize: { - address: true, - }, - include_dirs: [ - "bionic/libc/kernel/android/uapi/", - "bionic/libc/kernel/uapi", - ], - cflags: [ - "-D__ANDROID_HOST__", - ], - static_libs: [ - "libinput", - ], - }, }, sanitize: { hwaddress: true, diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 5af9999e3e..295065610d 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -206,11 +206,9 @@ MATCHER_P(WithPointers, pointers, "MotionEvent with specified pointers") { // --- FakeInputDispatcherPolicy --- class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface { - InputDispatcherConfiguration mConfig; - struct AnrResult { sp<IBinder> token{}; - gui::Pid pid{-1}; + gui::Pid pid{gui::Pid::INVALID}; }; public: @@ -349,11 +347,6 @@ public: "signal"; } - void setKeyRepeatConfiguration(nsecs_t timeout, nsecs_t delay) { - mConfig.keyRepeatTimeout = timeout; - mConfig.keyRepeatDelay = delay; - } - PointerCaptureRequest assertSetPointerCaptureCalled(bool enabled) { std::unique_lock lock(mLock); base::ScopedLockAssertion assumeLocked(mLock); @@ -550,8 +543,6 @@ private: void notifyVibratorState(int32_t deviceId, bool isOn) override {} - InputDispatcherConfiguration getDispatcherConfiguration() override { return mConfig; } - bool filterInputEvent(const InputEvent& inputEvent, uint32_t policyFlags) override { std::scoped_lock lock(mLock); switch (inputEvent.getType()) { @@ -3941,9 +3932,7 @@ TEST_F(InputDispatcherTest, TouchpadThreeFingerSwipeNotSentToSingleWindow) { /** * Send a two-pointer gesture to a single window. The window's orientation changes in response to * the first pointer. - * Ensure that the second pointer is not sent to the window. - * - * The subsequent gesture should be correctly delivered to the window. + * Ensure that the second pointer and the subsequent gesture is correctly delivered to the window. */ TEST_F(InputDispatcherTest, MultiplePointersWithRotatingWindow) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); @@ -3973,8 +3962,6 @@ TEST_F(InputDispatcherTest, MultiplePointersWithRotatingWindow) { mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowDup}}}); - window->consumeMotionEvent(WithMotionAction(ACTION_CANCEL)); - mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN) .downTime(baseTime + 10) .eventTime(baseTime + 30) @@ -3982,19 +3969,26 @@ TEST_F(InputDispatcherTest, MultiplePointersWithRotatingWindow) { .pointer(PointerBuilder(1, ToolType::FINGER).x(200).y(200)) .build()); - // Finish the gesture and start a new one. Ensure the new gesture is sent to the window + window->consumeMotionEvent(WithMotionAction(POINTER_1_DOWN)); + + // Finish the gesture and start a new one. Ensure all events are sent to the window. mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN) .downTime(baseTime + 10) .eventTime(baseTime + 40) .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100)) .pointer(PointerBuilder(1, ToolType::FINGER).x(200).y(200)) .build()); + + window->consumeMotionEvent(WithMotionAction(POINTER_1_UP)); + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN) .downTime(baseTime + 10) .eventTime(baseTime + 50) .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100)) .build()); + window->consumeMotionEvent(WithMotionAction(ACTION_UP)); + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) .downTime(baseTime + 60) .eventTime(baseTime + 60) @@ -5705,10 +5699,9 @@ protected: virtual void SetUp() override { mFakePolicy = std::make_unique<FakeInputDispatcherPolicy>(); - mFakePolicy->setKeyRepeatConfiguration(KEY_REPEAT_TIMEOUT, KEY_REPEAT_DELAY); mDispatcher = std::make_unique<InputDispatcher>(*mFakePolicy); - mDispatcher->requestRefreshConfiguration(); mDispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false); + mDispatcher->setKeyRepeatConfiguration(KEY_REPEAT_TIMEOUT, KEY_REPEAT_DELAY); ASSERT_EQ(OK, mDispatcher->start()); setUpWindow(); @@ -6191,7 +6184,7 @@ protected: POLICY_FLAG_PASS_TO_USER | POLICY_FLAG_DISABLE_KEY_REPEAT; ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, mDispatcher->injectInputEvent(&event, /*targetUid=*/{}, - InputEventInjectionSync::WAIT_FOR_RESULT, 10ms, + InputEventInjectionSync::WAIT_FOR_RESULT, 100ms, policyFlags | additionalPolicyFlags)); InputEvent* received = mWindow->consume(); @@ -6226,7 +6219,7 @@ protected: const int32_t additionalPolicyFlags = POLICY_FLAG_PASS_TO_USER; ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, mDispatcher->injectInputEvent(&event, /*targetUid=*/{}, - InputEventInjectionSync::WAIT_FOR_RESULT, 10ms, + InputEventInjectionSync::WAIT_FOR_RESULT, 100ms, policyFlags | additionalPolicyFlags)); InputEvent* received = mWindow->consume(); diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index d1c3f7d156..e751c89c4e 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -161,6 +161,7 @@ class FakeInputMapper : public InputMapper { // fake mapping which would normally come from keyCharacterMap std::unordered_map<int32_t, int32_t> mKeyCodeMapping; std::vector<int32_t> mSupportedKeyCodes; + std::list<NotifyArgs> mProcessResult; std::mutex mLock; std::condition_variable mStateChangedCondition; @@ -191,6 +192,14 @@ public: mMetaState = metaState; } + // Sets the return value for the `process` call. + void setProcessResult(std::list<NotifyArgs> notifyArgs) { + mProcessResult.clear(); + for (auto notifyArg : notifyArgs) { + mProcessResult.push_back(notifyArg); + } + } + void assertConfigureWasCalled() { std::unique_lock<std::mutex> lock(mLock); base::ScopedLockAssertion assumeLocked(mLock); @@ -291,7 +300,7 @@ private: mLastEvent = *rawEvent; mProcessWasCalled = true; mStateChangedCondition.notify_all(); - return {}; + return mProcessResult; } int32_t getKeyCodeState(uint32_t, int32_t keyCode) override { @@ -2475,6 +2484,73 @@ TEST_F(InputDeviceTest, WhenMappersAreRegistered_DeviceIsNotIgnoredAndForwardsRe ASSERT_NO_FATAL_FAILURE(mapper2.assertProcessWasCalled()); } +TEST_F(InputDeviceTest, WakeDevice_AddsWakeFlagToProcessNotifyArgs) { + mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, "device.wake", "1"); + FakeInputMapper& mapper = + mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, mFakePolicy->getReaderConfiguration(), + AINPUT_SOURCE_KEYBOARD); + NotifyMotionArgs args1; + NotifySwitchArgs args2; + NotifyKeyArgs args3; + mapper.setProcessResult({args1, args2, args3}); + + InputReaderConfiguration config; + std::list<NotifyArgs> unused = mDevice->configure(ARBITRARY_TIME, config, /*changes=*/{}); + + RawEvent event; + event.deviceId = EVENTHUB_ID; + std::list<NotifyArgs> notifyArgs = mDevice->process(&event, 1); + + for (auto& arg : notifyArgs) { + if (const auto notifyMotionArgs = std::get_if<NotifyMotionArgs>(&arg)) { + ASSERT_EQ(POLICY_FLAG_WAKE, notifyMotionArgs->policyFlags); + } else if (const auto notifySwitchArgs = std::get_if<NotifySwitchArgs>(&arg)) { + ASSERT_EQ(POLICY_FLAG_WAKE, notifySwitchArgs->policyFlags); + } else if (const auto notifyKeyArgs = std::get_if<NotifyKeyArgs>(&arg)) { + ASSERT_EQ(POLICY_FLAG_WAKE, notifyKeyArgs->policyFlags); + } + } +} + +TEST_F(InputDeviceTest, NotWakeDevice_DoesNotAddWakeFlagToProcessNotifyArgs) { + mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, "device.wake", "0"); + FakeInputMapper& mapper = + mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, mFakePolicy->getReaderConfiguration(), + AINPUT_SOURCE_KEYBOARD); + NotifyMotionArgs args; + mapper.setProcessResult({args}); + + InputReaderConfiguration config; + std::list<NotifyArgs> unused = mDevice->configure(ARBITRARY_TIME, config, /*changes=*/{}); + + RawEvent event; + event.deviceId = EVENTHUB_ID; + std::list<NotifyArgs> notifyArgs = mDevice->process(&event, 1); + + // POLICY_FLAG_WAKE is not added to the NotifyArgs. + ASSERT_EQ(0u, std::get<NotifyMotionArgs>(notifyArgs.front()).policyFlags); +} + +TEST_F(InputDeviceTest, NotWakeDevice_DoesNotRemoveExistingWakeFlagFromProcessNotifyArgs) { + mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, "device.wake", "0"); + FakeInputMapper& mapper = + mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, mFakePolicy->getReaderConfiguration(), + AINPUT_SOURCE_KEYBOARD); + NotifyMotionArgs args; + args.policyFlags = POLICY_FLAG_WAKE; + mapper.setProcessResult({args}); + + InputReaderConfiguration config; + std::list<NotifyArgs> unused = mDevice->configure(ARBITRARY_TIME, config, /*changes=*/{}); + + RawEvent event; + event.deviceId = EVENTHUB_ID; + std::list<NotifyArgs> notifyArgs = mDevice->process(&event, 1); + + // The POLICY_FLAG_WAKE is preserved, despite the device being a non-wake device. + ASSERT_EQ(POLICY_FLAG_WAKE, std::get<NotifyMotionArgs>(notifyArgs.front()).policyFlags); +} + // A single input device is associated with a specific display. Check that: // 1. Device is disabled if the viewport corresponding to the associated display is not found // 2. Device is disabled when setEnabled API is called @@ -3727,6 +3803,19 @@ TEST_F(KeyboardInputMapperTest, LayoutInfoCorrectlyMapped) { ASSERT_EQ("extended", mDevice->getDeviceInfo().getKeyboardLayoutInfo()->layoutType); } +TEST_F(KeyboardInputMapperTest, Process_GesureEventToSetFlagKeepTouchMode) { + mFakeEventHub->addKey(EVENTHUB_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, POLICY_FLAG_GESTURE); + KeyboardInputMapper& mapper = + constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD, + AINPUT_KEYBOARD_TYPE_ALPHABETIC); + NotifyKeyArgs args; + + // Key down + process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_LEFT, 1); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); + ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_KEEP_TOUCH_MODE, args.flags); +} + // --- KeyboardInputMapperTest_ExternalDevice --- class KeyboardInputMapperTest_ExternalDevice : public InputMapperTest { @@ -9373,6 +9462,11 @@ TEST_F(MultiTouchInputMapperTest, WhenViewportIsNotActive_TouchesAreProcessed) { EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); } +/** + * When the viewport is deactivated (isActive transitions from true to false), + * and touch.enableForInactiveViewport is false, touches prior to the transition + * should be cancelled. + */ TEST_F(MultiTouchInputMapperTest, Process_DeactivateViewport_AbortTouches) { addConfigurationProperty("touch.deviceType", "touchScreen"); addConfigurationProperty("touch.enableForInactiveViewport", "0"); @@ -9424,6 +9518,60 @@ TEST_F(MultiTouchInputMapperTest, Process_DeactivateViewport_AbortTouches) { EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); } +/** + * When the viewport is deactivated (isActive transitions from true to false), + * and touch.enableForInactiveViewport is true, touches prior to the transition + * should not be cancelled. + */ +TEST_F(MultiTouchInputMapperTest, Process_DeactivateViewport_TouchesNotAborted) { + addConfigurationProperty("touch.deviceType", "touchScreen"); + addConfigurationProperty("touch.enableForInactiveViewport", "1"); + mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, + /*isActive=*/true, UNIQUE_ID, NO_PORT, ViewportType::INTERNAL); + std::optional<DisplayViewport> optionalDisplayViewport = + mFakePolicy->getDisplayViewportByUniqueId(UNIQUE_ID); + ASSERT_TRUE(optionalDisplayViewport.has_value()); + DisplayViewport displayViewport = *optionalDisplayViewport; + + configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO); + prepareAxes(POSITION); + MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>(); + + // Finger down + int32_t x = 100, y = 100; + processPosition(mapper, x, y); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + WithMotionAction(AMOTION_EVENT_ACTION_DOWN))); + + // Deactivate display viewport + displayViewport.isActive = false; + ASSERT_TRUE(mFakePolicy->updateViewport(displayViewport)); + configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO); + + // The ongoing touch should not be canceled + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); + + // Finger move is not ignored + x += 10, y += 10; + processPosition(mapper, x, y); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + WithMotionAction(AMOTION_EVENT_ACTION_MOVE))); + + // Reactivate display viewport + displayViewport.isActive = true; + ASSERT_TRUE(mFakePolicy->updateViewport(displayViewport)); + configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO); + + // Finger move continues and does not start new gesture + x += 10, y += 10; + processPosition(mapper, x, y); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + WithMotionAction(AMOTION_EVENT_ACTION_MOVE))); +} + TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShowTouches) { // Setup the first touch screen device. prepareAxes(POSITION | ID | SLOT); diff --git a/services/inputflinger/tests/SlopController_test.cpp b/services/inputflinger/tests/SlopController_test.cpp new file mode 100644 index 0000000000..f524acd025 --- /dev/null +++ b/services/inputflinger/tests/SlopController_test.cpp @@ -0,0 +1,99 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "../reader/mapper/SlopController.h" + +#include <gtest/gtest.h> + +namespace android { + +// --- SlopControllerTest --- + +TEST(SlopControllerTest, PositiveValues) { + SlopController controller = SlopController(/*slopThreshold=*/5, /*slopDurationNanos=*/100); + + ASSERT_EQ(0, controller.consumeEvent(1000, 1)); + ASSERT_EQ(0, controller.consumeEvent(1003, 3)); + ASSERT_EQ(2, controller.consumeEvent(1005, 3)); + ASSERT_EQ(4, controller.consumeEvent(1009, 4)); + + SlopController controller2 = SlopController(/*slopThreshold=*/5, /*slopDurationNanos=*/100); + + ASSERT_EQ(0, controller2.consumeEvent(1000, 5)); + ASSERT_EQ(3, controller2.consumeEvent(1003, 3)); + ASSERT_EQ(4, controller2.consumeEvent(1005, 4)); +} + +TEST(SlopControllerTest, NegativeValues) { + SlopController controller = SlopController(/*slopThreshold=*/5, /*slopDurationNanos=*/100); + + ASSERT_EQ(0, controller.consumeEvent(1000, -1)); + ASSERT_EQ(0, controller.consumeEvent(1003, -3)); + ASSERT_EQ(-2, controller.consumeEvent(1005, -3)); + ASSERT_EQ(-4, controller.consumeEvent(1009, -4)); + + SlopController controller2 = SlopController(/*slopThreshold=*/5, /*slopDurationNanos=*/100); + + ASSERT_EQ(0, controller2.consumeEvent(1000, -5)); + ASSERT_EQ(-3, controller2.consumeEvent(1003, -3)); + ASSERT_EQ(-4, controller2.consumeEvent(1005, -4)); +} + +TEST(SlopControllerTest, ZeroDoesNotResetSlop) { + SlopController controller = SlopController(/*slopThreshold=*/5, /*slopDurationNanos=*/100); + + ASSERT_EQ(1, controller.consumeEvent(1005, 6)); + ASSERT_EQ(0, controller.consumeEvent(1006, 0)); + ASSERT_EQ(2, controller.consumeEvent(1008, 2)); +} + +TEST(SlopControllerTest, SignChange_ResetsSlop) { + SlopController controller = SlopController(/*slopThreshold=*/5, /*slopDurationNanos=*/100); + + ASSERT_EQ(0, controller.consumeEvent(1000, 2)); + ASSERT_EQ(0, controller.consumeEvent(1001, -4)); + ASSERT_EQ(0, controller.consumeEvent(1002, 3)); + ASSERT_EQ(0, controller.consumeEvent(1003, -2)); + + ASSERT_EQ(1, controller.consumeEvent(1005, 6)); + ASSERT_EQ(0, controller.consumeEvent(1006, 0)); + ASSERT_EQ(2, controller.consumeEvent(1008, 2)); + + ASSERT_EQ(0, controller.consumeEvent(1010, -4)); + ASSERT_EQ(-1, controller.consumeEvent(1011, -2)); + + ASSERT_EQ(0, controller.consumeEvent(1015, 5)); + ASSERT_EQ(2, controller.consumeEvent(1016, 2)); + + ASSERT_EQ(0, controller.consumeEvent(1017, -5)); + ASSERT_EQ(-2, controller.consumeEvent(1018, -2)); +} + +TEST(SlopControllerTest, OldAge_ResetsSlop) { + SlopController controller = SlopController(/*slopThreshold=*/5, /*slopDurationNanos=*/100); + + ASSERT_EQ(1, controller.consumeEvent(1005, 6)); + ASSERT_EQ(0, controller.consumeEvent(1108, 2)); // age exceeds slop duration + + ASSERT_EQ(1, controller.consumeEvent(1110, 4)); + ASSERT_EQ(0, controller.consumeEvent(1210, 2)); // age equals slop duration + + ASSERT_EQ(0, controller.consumeEvent(1215, -3)); + ASSERT_EQ(-2, controller.consumeEvent(1216, -4)); + ASSERT_EQ(-5, controller.consumeEvent(1315, -5)); +} + +} // namespace android diff --git a/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp index 8098ef2edd..993f6a8e5c 100644 --- a/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp +++ b/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp @@ -16,6 +16,7 @@ #include <CursorInputMapper.h> #include <FuzzContainer.h> +#include <MapperHelpers.h> namespace android { @@ -65,22 +66,11 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) { mapper.populateDeviceInfo(info); }, [&]() -> void { - int32_t type, code; - type = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidTypes) - : fdp->ConsumeIntegral<int32_t>(); - code = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidCodes) - : fdp->ConsumeIntegral<int32_t>(); - // Need to reconfigure with 0 or you risk a NPE. std::list<NotifyArgs> unused = mapper.reconfigure(fdp->ConsumeIntegral<nsecs_t>(), policyConfig, InputReaderConfiguration::Change(0)); - RawEvent rawEvent{fdp->ConsumeIntegral<nsecs_t>(), - fdp->ConsumeIntegral<nsecs_t>(), - fdp->ConsumeIntegral<int32_t>(), - type, - code, - fdp->ConsumeIntegral<int32_t>()}; + RawEvent rawEvent = getFuzzedRawEvent(*fdp); unused += mapper.process(&rawEvent); }, [&]() -> void { diff --git a/services/inputflinger/tests/fuzzers/FuzzContainer.h b/services/inputflinger/tests/fuzzers/FuzzContainer.h index b9929289f1..fd14e024c6 100644 --- a/services/inputflinger/tests/fuzzers/FuzzContainer.h +++ b/services/inputflinger/tests/fuzzers/FuzzContainer.h @@ -38,7 +38,7 @@ public: std::string deviceName = mFdp->ConsumeRandomLengthString(16); std::string deviceLocation = mFdp->ConsumeRandomLengthString(12); int32_t deviceID = mFdp->ConsumeIntegralInRange<int32_t>(0, 5); - int32_t deviceGeneration = mFdp->ConsumeIntegralInRange<int32_t>(/*from*/ 0, /*to*/ 5); + int32_t deviceGeneration = mFdp->ConsumeIntegralInRange<int32_t>(/*from=*/0, /*to=*/5); // Create mocked objects. mFuzzEventHub = std::make_shared<FuzzEventHub>(mFdp); diff --git a/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp index 616e870811..d11e8ae24a 100644 --- a/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp +++ b/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp @@ -16,6 +16,7 @@ #include <FuzzContainer.h> #include <KeyboardInputMapper.h> +#include <MapperHelpers.h> namespace android { @@ -72,17 +73,7 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) { std::list<NotifyArgs> unused = mapper.reset(fdp->ConsumeIntegral<nsecs_t>()); }, [&]() -> void { - int32_t type, code; - type = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidTypes) - : fdp->ConsumeIntegral<int32_t>(); - code = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidCodes) - : fdp->ConsumeIntegral<int32_t>(); - RawEvent rawEvent{fdp->ConsumeIntegral<nsecs_t>(), - fdp->ConsumeIntegral<nsecs_t>(), - fdp->ConsumeIntegral<int32_t>(), - type, - code, - fdp->ConsumeIntegral<int32_t>()}; + RawEvent rawEvent = getFuzzedRawEvent(*fdp); std::list<NotifyArgs> unused = mapper.process(&rawEvent); }, [&]() -> void { diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h index 1e44e0fba0..4a2c98ccae 100644 --- a/services/inputflinger/tests/fuzzers/MapperHelpers.h +++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h @@ -22,7 +22,6 @@ constexpr size_t kValidTypes[] = {EV_SW, EV_SYN, - SYN_REPORT, EV_ABS, EV_KEY, EV_MSC, @@ -46,7 +45,6 @@ constexpr size_t kValidCodes[] = { ABS_MT_PRESSURE, ABS_MT_DISTANCE, ABS_MT_TOOL_TYPE, - SYN_MT_REPORT, MSC_SCAN, REL_X, REL_Y, @@ -74,6 +72,22 @@ ToolType getFuzzedToolType(Fdp& fdp) { return static_cast<ToolType>(toolType); } +template <class Fdp> +RawEvent getFuzzedRawEvent(Fdp& fdp) { + const int32_t type = fdp.ConsumeBool() ? fdp.PickValueInArray(kValidTypes) + : fdp.template ConsumeIntegral<int32_t>(); + const int32_t code = fdp.ConsumeBool() ? fdp.PickValueInArray(kValidCodes) + : fdp.template ConsumeIntegral<int32_t>(); + return RawEvent{ + .when = fdp.template ConsumeIntegral<nsecs_t>(), + .readTime = fdp.template ConsumeIntegral<nsecs_t>(), + .deviceId = fdp.template ConsumeIntegral<int32_t>(), + .type = type, + .code = code, + .value = fdp.template ConsumeIntegral<int32_t>(), + }; +} + class FuzzEventHub : public EventHubInterface { InputDeviceIdentifier mIdentifier; std::vector<TouchVideoFrame> mVideoFrames; @@ -118,18 +132,7 @@ public: std::vector<RawEvent> events; const size_t count = mFdp->ConsumeIntegralInRange<size_t>(0, kMaxSize); for (size_t i = 0; i < count; ++i) { - int32_t type = mFdp->ConsumeBool() ? mFdp->PickValueInArray(kValidTypes) - : mFdp->ConsumeIntegral<int32_t>(); - int32_t code = mFdp->ConsumeBool() ? mFdp->PickValueInArray(kValidCodes) - : mFdp->ConsumeIntegral<int32_t>(); - events.push_back({ - .when = mFdp->ConsumeIntegral<nsecs_t>(), - .readTime = mFdp->ConsumeIntegral<nsecs_t>(), - .deviceId = mFdp->ConsumeIntegral<int32_t>(), - .type = type, - .code = code, - .value = mFdp->ConsumeIntegral<int32_t>(), - }); + events.push_back(getFuzzedRawEvent(*mFdp)); } return events; } diff --git a/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp index 212462d700..494b0efe79 100644 --- a/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp +++ b/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp @@ -15,6 +15,7 @@ */ #include <FuzzContainer.h> +#include <MapperHelpers.h> #include <MultiTouchInputMapper.h> namespace android { @@ -87,16 +88,7 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) { std::list<NotifyArgs> unused = mapper.reset(fdp->ConsumeIntegral<nsecs_t>()); }, [&]() -> void { - int32_t type = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidTypes) - : fdp->ConsumeIntegral<int32_t>(); - int32_t code = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidCodes) - : fdp->ConsumeIntegral<int32_t>(); - RawEvent rawEvent{fdp->ConsumeIntegral<nsecs_t>(), - fdp->ConsumeIntegral<nsecs_t>(), - fdp->ConsumeIntegral<int32_t>(), - type, - code, - fdp->ConsumeIntegral<int32_t>()}; + RawEvent rawEvent = getFuzzedRawEvent(*fdp); std::list<NotifyArgs> unused = mapper.process(&rawEvent); }, [&]() -> void { diff --git a/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp index 590207ea22..381e7b5f8f 100644 --- a/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp +++ b/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp @@ -15,6 +15,7 @@ */ #include <FuzzContainer.h> +#include <MapperHelpers.h> #include <SwitchInputMapper.h> namespace android { @@ -36,16 +37,7 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) { }, [&]() -> void { mapper.getSources(); }, [&]() -> void { - int32_t type = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidTypes) - : fdp->ConsumeIntegral<int32_t>(); - int32_t code = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidCodes) - : fdp->ConsumeIntegral<int32_t>(); - RawEvent rawEvent{fdp->ConsumeIntegral<nsecs_t>(), - fdp->ConsumeIntegral<nsecs_t>(), - fdp->ConsumeIntegral<int32_t>(), - type, - code, - fdp->ConsumeIntegral<int32_t>()}; + RawEvent rawEvent = getFuzzedRawEvent(*fdp); std::list<NotifyArgs> unused = mapper.process(&rawEvent); }, [&]() -> void { diff --git a/services/powermanager/Android.bp b/services/powermanager/Android.bp index b34e54fd6b..2523f3b6a6 100644 --- a/services/powermanager/Android.bp +++ b/services/powermanager/Android.bp @@ -33,6 +33,7 @@ cc_library_shared { shared_libs: [ "libbinder", + "libbinder_ndk", "libhidlbase", "liblog", "libutils", @@ -40,7 +41,7 @@ cc_library_shared { "android.hardware.power@1.1", "android.hardware.power@1.2", "android.hardware.power@1.3", - "android.hardware.power-V4-cpp", + "android.hardware.power-V4-ndk", ], export_shared_lib_headers: [ @@ -48,7 +49,7 @@ cc_library_shared { "android.hardware.power@1.1", "android.hardware.power@1.2", "android.hardware.power@1.3", - "android.hardware.power-V4-cpp", + "android.hardware.power-V4-ndk", ], cflags: [ diff --git a/services/powermanager/PowerHalController.cpp b/services/powermanager/PowerHalController.cpp index f89035fd1c..9a23c848c9 100644 --- a/services/powermanager/PowerHalController.cpp +++ b/services/powermanager/PowerHalController.cpp @@ -15,11 +15,11 @@ */ #define LOG_TAG "PowerHalController" +#include <aidl/android/hardware/power/Boost.h> +#include <aidl/android/hardware/power/IPower.h> +#include <aidl/android/hardware/power/IPowerHintSession.h> +#include <aidl/android/hardware/power/Mode.h> #include <android/hardware/power/1.1/IPower.h> -#include <android/hardware/power/Boost.h> -#include <android/hardware/power/IPower.h> -#include <android/hardware/power/IPowerHintSession.h> -#include <android/hardware/power/Mode.h> #include <powermanager/PowerHalController.h> #include <powermanager/PowerHalLoader.h> #include <utils/Log.h> @@ -33,7 +33,8 @@ namespace power { // ------------------------------------------------------------------------------------------------- std::unique_ptr<HalWrapper> HalConnector::connect() { - if (sp<IPower> halAidl = PowerHalLoader::loadAidl()) { + if (std::shared_ptr<aidl::android::hardware::power::IPower> halAidl = + PowerHalLoader::loadAidl()) { return std::make_unique<AidlHalWrapper>(halAidl); } // If V1_0 isn't defined, none of them are @@ -90,20 +91,24 @@ HalResult<T> PowerHalController::processHalResult(HalResult<T> result, const cha return result; } -HalResult<void> PowerHalController::setBoost(Boost boost, int32_t durationMs) { +HalResult<void> PowerHalController::setBoost(aidl::android::hardware::power::Boost boost, + int32_t durationMs) { std::shared_ptr<HalWrapper> handle = initHal(); auto result = handle->setBoost(boost, durationMs); return processHalResult(result, "setBoost"); } -HalResult<void> PowerHalController::setMode(Mode mode, bool enabled) { +HalResult<void> PowerHalController::setMode(aidl::android::hardware::power::Mode mode, + bool enabled) { std::shared_ptr<HalWrapper> handle = initHal(); auto result = handle->setMode(mode, enabled); return processHalResult(result, "setMode"); } -HalResult<sp<IPowerHintSession>> PowerHalController::createHintSession( - int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos) { +HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>> +PowerHalController::createHintSession(int32_t tgid, int32_t uid, + const std::vector<int32_t>& threadIds, + int64_t durationNanos) { std::shared_ptr<HalWrapper> handle = initHal(); auto result = handle->createHintSession(tgid, uid, threadIds, durationNanos); return processHalResult(result, "createHintSession"); diff --git a/services/powermanager/PowerHalLoader.cpp b/services/powermanager/PowerHalLoader.cpp index 6bd40f8ff2..22144615da 100644 --- a/services/powermanager/PowerHalLoader.cpp +++ b/services/powermanager/PowerHalLoader.cpp @@ -16,10 +16,11 @@ #define LOG_TAG "PowerHalLoader" +#include <aidl/android/hardware/power/IPower.h> +#include <android/binder_manager.h> #include <android/hardware/power/1.1/IPower.h> #include <android/hardware/power/1.2/IPower.h> #include <android/hardware/power/1.3/IPower.h> -#include <android/hardware/power/IPower.h> #include <binder/IServiceManager.h> #include <hardware/power.h> #include <hardware_legacy/power.h> @@ -54,7 +55,7 @@ sp<T> loadHal(bool& exists, sp<T>& hal, F& loadFn, const char* halName) { // ------------------------------------------------------------------------------------------------- std::mutex PowerHalLoader::gHalMutex; -sp<IPower> PowerHalLoader::gHalAidl = nullptr; +std::shared_ptr<aidl::android::hardware::power::IPower> PowerHalLoader::gHalAidl = nullptr; sp<V1_0::IPower> PowerHalLoader::gHalHidlV1_0 = nullptr; sp<V1_1::IPower> PowerHalLoader::gHalHidlV1_1 = nullptr; sp<V1_2::IPower> PowerHalLoader::gHalHidlV1_2 = nullptr; @@ -69,11 +70,30 @@ void PowerHalLoader::unloadAll() { gHalHidlV1_3 = nullptr; } -sp<IPower> PowerHalLoader::loadAidl() { +std::shared_ptr<aidl::android::hardware::power::IPower> PowerHalLoader::loadAidl() { std::lock_guard<std::mutex> lock(gHalMutex); static bool gHalExists = true; - static auto loadFn = []() { return waitForVintfService<IPower>(); }; - return loadHal<IPower>(gHalExists, gHalAidl, loadFn, "AIDL"); + if (!gHalExists) { + return nullptr; + } + if (gHalAidl) { + return gHalAidl; + } + auto aidlServiceName = + std::string(aidl::android::hardware::power::IPower::descriptor) + "/default"; + if (!AServiceManager_isDeclared(aidlServiceName.c_str())) { + gHalExists = false; + return nullptr; + } + gHalAidl = aidl::android::hardware::power::IPower::fromBinder( + ndk::SpAIBinder(AServiceManager_waitForService(aidlServiceName.c_str()))); + if (gHalAidl) { + ALOGI("Successfully connected to Power HAL AIDL service."); + } else { + ALOGI("Power HAL AIDL service not available."); + gHalExists = false; + } + return gHalAidl; } sp<V1_0::IPower> PowerHalLoader::loadHidlV1_0() { diff --git a/services/powermanager/PowerHalWrapper.cpp b/services/powermanager/PowerHalWrapper.cpp index 9e7adf8e5c..76afbfc646 100644 --- a/services/powermanager/PowerHalWrapper.cpp +++ b/services/powermanager/PowerHalWrapper.cpp @@ -15,86 +15,49 @@ */ #define LOG_TAG "HalWrapper" -#include <android/hardware/power/Boost.h> -#include <android/hardware/power/IPowerHintSession.h> -#include <android/hardware/power/Mode.h> +#include <aidl/android/hardware/power/Boost.h> +#include <aidl/android/hardware/power/IPowerHintSession.h> +#include <aidl/android/hardware/power/Mode.h> #include <powermanager/PowerHalWrapper.h> #include <utils/Log.h> #include <cinttypes> using namespace android::hardware::power; -namespace Aidl = android::hardware::power; +namespace Aidl = aidl::android::hardware::power; namespace android { namespace power { // ------------------------------------------------------------------------------------------------- - -inline HalResult<void> toHalResult(const binder::Status& result) { +inline HalResult<void> toHalResult(const ndk::ScopedAStatus& result) { if (result.isOk()) { return HalResult<void>::ok(); } - ALOGE("Power HAL request failed: %s", result.toString8().c_str()); - return HalResult<void>::fromStatus(result); -} - -template <typename T> -template <typename R> -HalResult<T> HalResult<T>::fromReturn(hardware::Return<R>& ret, T data) { - return ret.isOk() ? HalResult<T>::ok(data) : HalResult<T>::failed(ret.description()); -} - -template <typename T> -template <typename R> -HalResult<T> HalResult<T>::fromReturn(hardware::Return<R>& ret, V1_0::Status status, T data) { - return ret.isOk() ? HalResult<T>::fromStatus(status, data) - : HalResult<T>::failed(ret.description()); -} - -// ------------------------------------------------------------------------------------------------- - -HalResult<void> HalResult<void>::fromStatus(status_t status) { - if (status == android::OK) { - return HalResult<void>::ok(); - } - return HalResult<void>::failed(statusToString(status)); + ALOGE("Power HAL request failed: %s", result.getDescription().c_str()); + return HalResult<void>::failed(result.getDescription()); } -HalResult<void> HalResult<void>::fromStatus(binder::Status status) { - if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) { - return HalResult<void>::unsupported(); - } - if (status.isOk()) { - return HalResult<void>::ok(); - } - return HalResult<void>::failed(std::string(status.toString8().c_str())); -} - -template <typename R> -HalResult<void> HalResult<void>::fromReturn(hardware::Return<R>& ret) { - return ret.isOk() ? HalResult<void>::ok() : HalResult<void>::failed(ret.description()); -} // ------------------------------------------------------------------------------------------------- -HalResult<void> EmptyHalWrapper::setBoost(Boost boost, int32_t durationMs) { +HalResult<void> EmptyHalWrapper::setBoost(Aidl::Boost boost, int32_t durationMs) { ALOGV("Skipped setBoost %s with duration %dms because Power HAL not available", toString(boost).c_str(), durationMs); return HalResult<void>::unsupported(); } -HalResult<void> EmptyHalWrapper::setMode(Mode mode, bool enabled) { +HalResult<void> EmptyHalWrapper::setMode(Aidl::Mode mode, bool enabled) { ALOGV("Skipped setMode %s to %s because Power HAL not available", toString(mode).c_str(), enabled ? "true" : "false"); return HalResult<void>::unsupported(); } -HalResult<sp<Aidl::IPowerHintSession>> EmptyHalWrapper::createHintSession( +HalResult<std::shared_ptr<Aidl::IPowerHintSession>> EmptyHalWrapper::createHintSession( int32_t, int32_t, const std::vector<int32_t>& threadIds, int64_t) { ALOGV("Skipped createHintSession(task num=%zu) because Power HAL not available", threadIds.size()); - return HalResult<sp<Aidl::IPowerHintSession>>::unsupported(); + return HalResult<std::shared_ptr<Aidl::IPowerHintSession>>::unsupported(); } HalResult<int64_t> EmptyHalWrapper::getHintSessionPreferredRate() { @@ -104,8 +67,8 @@ HalResult<int64_t> EmptyHalWrapper::getHintSessionPreferredRate() { // ------------------------------------------------------------------------------------------------- -HalResult<void> HidlHalWrapperV1_0::setBoost(Boost boost, int32_t durationMs) { - if (boost == Boost::INTERACTION) { +HalResult<void> HidlHalWrapperV1_0::setBoost(Aidl::Boost boost, int32_t durationMs) { + if (boost == Aidl::Boost::INTERACTION) { return sendPowerHint(V1_3::PowerHint::INTERACTION, durationMs); } else { ALOGV("Skipped setBoost %s because Power HAL AIDL not available", toString(boost).c_str()); @@ -113,20 +76,20 @@ HalResult<void> HidlHalWrapperV1_0::setBoost(Boost boost, int32_t durationMs) { } } -HalResult<void> HidlHalWrapperV1_0::setMode(Mode mode, bool enabled) { +HalResult<void> HidlHalWrapperV1_0::setMode(Aidl::Mode mode, bool enabled) { uint32_t data = enabled ? 1 : 0; switch (mode) { - case Mode::LAUNCH: + case Aidl::Mode::LAUNCH: return sendPowerHint(V1_3::PowerHint::LAUNCH, data); - case Mode::LOW_POWER: + case Aidl::Mode::LOW_POWER: return sendPowerHint(V1_3::PowerHint::LOW_POWER, data); - case Mode::SUSTAINED_PERFORMANCE: + case Aidl::Mode::SUSTAINED_PERFORMANCE: return sendPowerHint(V1_3::PowerHint::SUSTAINED_PERFORMANCE, data); - case Mode::VR: + case Aidl::Mode::VR: return sendPowerHint(V1_3::PowerHint::VR_MODE, data); - case Mode::INTERACTIVE: + case Aidl::Mode::INTERACTIVE: return setInteractive(enabled); - case Mode::DOUBLE_TAP_TO_WAKE: + case Aidl::Mode::DOUBLE_TAP_TO_WAKE: return setFeature(V1_0::Feature::POWER_FEATURE_DOUBLE_TAP_TO_WAKE, enabled); default: ALOGV("Skipped setMode %s because Power HAL AIDL not available", @@ -150,11 +113,11 @@ HalResult<void> HidlHalWrapperV1_0::setFeature(V1_0::Feature feature, bool enabl return HalResult<void>::fromReturn(ret); } -HalResult<sp<hardware::power::IPowerHintSession>> HidlHalWrapperV1_0::createHintSession( +HalResult<std::shared_ptr<Aidl::IPowerHintSession>> HidlHalWrapperV1_0::createHintSession( int32_t, int32_t, const std::vector<int32_t>& threadIds, int64_t) { ALOGV("Skipped createHintSession(task num=%zu) because Power HAL not available", threadIds.size()); - return HalResult<sp<Aidl::IPowerHintSession>>::unsupported(); + return HalResult<std::shared_ptr<Aidl::IPowerHintSession>>::unsupported(); } HalResult<int64_t> HidlHalWrapperV1_0::getHintSessionPreferredRate() { @@ -178,26 +141,26 @@ HalResult<void> HidlHalWrapperV1_2::sendPowerHint(V1_3::PowerHint hintId, uint32 return HalResult<void>::fromReturn(ret); } -HalResult<void> HidlHalWrapperV1_2::setBoost(Boost boost, int32_t durationMs) { +HalResult<void> HidlHalWrapperV1_2::setBoost(Aidl::Boost boost, int32_t durationMs) { switch (boost) { - case Boost::CAMERA_SHOT: + case Aidl::Boost::CAMERA_SHOT: return sendPowerHint(V1_3::PowerHint::CAMERA_SHOT, durationMs); - case Boost::CAMERA_LAUNCH: + case Aidl::Boost::CAMERA_LAUNCH: return sendPowerHint(V1_3::PowerHint::CAMERA_LAUNCH, durationMs); default: return HidlHalWrapperV1_1::setBoost(boost, durationMs); } } -HalResult<void> HidlHalWrapperV1_2::setMode(Mode mode, bool enabled) { +HalResult<void> HidlHalWrapperV1_2::setMode(Aidl::Mode mode, bool enabled) { uint32_t data = enabled ? 1 : 0; switch (mode) { - case Mode::CAMERA_STREAMING_SECURE: - case Mode::CAMERA_STREAMING_LOW: - case Mode::CAMERA_STREAMING_MID: - case Mode::CAMERA_STREAMING_HIGH: + case Aidl::Mode::CAMERA_STREAMING_SECURE: + case Aidl::Mode::CAMERA_STREAMING_LOW: + case Aidl::Mode::CAMERA_STREAMING_MID: + case Aidl::Mode::CAMERA_STREAMING_HIGH: return sendPowerHint(V1_3::PowerHint::CAMERA_STREAMING, data); - case Mode::AUDIO_STREAMING_LOW_LATENCY: + case Aidl::Mode::AUDIO_STREAMING_LOW_LATENCY: return sendPowerHint(V1_3::PowerHint::AUDIO_LOW_LATENCY, data); default: return HidlHalWrapperV1_1::setMode(mode, enabled); @@ -206,9 +169,9 @@ HalResult<void> HidlHalWrapperV1_2::setMode(Mode mode, bool enabled) { // ------------------------------------------------------------------------------------------------- -HalResult<void> HidlHalWrapperV1_3::setMode(Mode mode, bool enabled) { +HalResult<void> HidlHalWrapperV1_3::setMode(Aidl::Mode mode, bool enabled) { uint32_t data = enabled ? 1 : 0; - if (mode == Mode::EXPENSIVE_RENDERING) { + if (mode == Aidl::Mode::EXPENSIVE_RENDERING) { return sendPowerHint(V1_3::PowerHint::EXPENSIVE_RENDERING, data); } return HidlHalWrapperV1_2::setMode(mode, enabled); @@ -222,7 +185,7 @@ HalResult<void> HidlHalWrapperV1_3::sendPowerHint(V1_3::PowerHint hintId, uint32 // ------------------------------------------------------------------------------------------------- -HalResult<void> AidlHalWrapper::setBoost(Boost boost, int32_t durationMs) { +HalResult<void> AidlHalWrapper::setBoost(Aidl::Boost boost, int32_t durationMs) { std::unique_lock<std::mutex> lock(mBoostMutex); size_t idx = static_cast<size_t>(boost); @@ -237,7 +200,7 @@ HalResult<void> AidlHalWrapper::setBoost(Boost boost, int32_t durationMs) { auto isSupportedRet = mHandle->isBoostSupported(boost, &isSupported); if (!isSupportedRet.isOk()) { ALOGE("Skipped setBoost %s because check support failed with: %s", - toString(boost).c_str(), isSupportedRet.toString8().c_str()); + toString(boost).c_str(), isSupportedRet.getDescription().c_str()); // return HalResult::FAILED; return HalResult<void>::fromStatus(isSupportedRet); } @@ -254,7 +217,7 @@ HalResult<void> AidlHalWrapper::setBoost(Boost boost, int32_t durationMs) { return toHalResult(mHandle->setBoost(boost, durationMs)); } -HalResult<void> AidlHalWrapper::setMode(Mode mode, bool enabled) { +HalResult<void> AidlHalWrapper::setMode(Aidl::Mode mode, bool enabled) { std::unique_lock<std::mutex> lock(mModeMutex); size_t idx = static_cast<size_t>(mode); @@ -268,7 +231,7 @@ HalResult<void> AidlHalWrapper::setMode(Mode mode, bool enabled) { bool isSupported = false; auto isSupportedRet = mHandle->isModeSupported(mode, &isSupported); if (!isSupportedRet.isOk()) { - return HalResult<void>::failed(isSupportedRet.toString8().c_str()); + return HalResult<void>::failed(isSupportedRet.getDescription()); } mModeSupportedArray[idx] = isSupported ? HalSupport::ON : HalSupport::OFF; @@ -283,10 +246,10 @@ HalResult<void> AidlHalWrapper::setMode(Mode mode, bool enabled) { return toHalResult(mHandle->setMode(mode, enabled)); } -HalResult<sp<Aidl::IPowerHintSession>> AidlHalWrapper::createHintSession( +HalResult<std::shared_ptr<Aidl::IPowerHintSession>> AidlHalWrapper::createHintSession( int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos) { - sp<IPowerHintSession> appSession; - return HalResult<sp<Aidl::IPowerHintSession>>:: + std::shared_ptr<Aidl::IPowerHintSession> appSession; + return HalResult<std::shared_ptr<Aidl::IPowerHintSession>>:: fromStatus(mHandle->createHintSession(tgid, uid, threadIds, durationNanos, &appSession), appSession); } diff --git a/services/powermanager/benchmarks/Android.bp b/services/powermanager/benchmarks/Android.bp index 4343aec227..03fc38d304 100644 --- a/services/powermanager/benchmarks/Android.bp +++ b/services/powermanager/benchmarks/Android.bp @@ -32,6 +32,7 @@ cc_benchmark { shared_libs: [ "libbase", "libbinder", + "libbinder_ndk", "libhidlbase", "liblog", "libpowermanager", @@ -40,7 +41,7 @@ cc_benchmark { "android.hardware.power@1.1", "android.hardware.power@1.2", "android.hardware.power@1.3", - "android.hardware.power-V4-cpp", + "android.hardware.power-V4-ndk", ], static_libs: [ "libtestUtil", diff --git a/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp b/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp index 6e5e14de28..759485ffce 100644 --- a/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp +++ b/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp @@ -16,21 +16,24 @@ #define LOG_TAG "PowerHalAidlBenchmarks" -#include <android/hardware/power/Boost.h> -#include <android/hardware/power/IPower.h> -#include <android/hardware/power/IPowerHintSession.h> -#include <android/hardware/power/Mode.h> -#include <android/hardware/power/WorkDuration.h> +#include <aidl/android/hardware/power/Boost.h> +#include <aidl/android/hardware/power/IPower.h> +#include <aidl/android/hardware/power/IPowerHintSession.h> +#include <aidl/android/hardware/power/Mode.h> +#include <aidl/android/hardware/power/WorkDuration.h> #include <benchmark/benchmark.h> #include <binder/IServiceManager.h> +#include <binder/Status.h> +#include <powermanager/PowerHalLoader.h> #include <testUtil.h> #include <chrono> -using android::hardware::power::Boost; -using android::hardware::power::IPower; -using android::hardware::power::IPowerHintSession; -using android::hardware::power::Mode; -using android::hardware::power::WorkDuration; +using aidl::android::hardware::power::Boost; +using aidl::android::hardware::power::IPower; +using aidl::android::hardware::power::IPowerHintSession; +using aidl::android::hardware::power::Mode; +using aidl::android::hardware::power::WorkDuration; +using android::power::PowerHalLoader; using std::chrono::microseconds; using namespace android; @@ -63,15 +66,15 @@ static constexpr microseconds ONEWAY_API_DELAY = 100us; template <class R, class... Args0, class... Args1> static void runBenchmark(benchmark::State& state, microseconds delay, R (IPower::*fn)(Args0...), Args1&&... args1) { - sp<IPower> hal = waitForVintfService<IPower>(); + std::shared_ptr<IPower> hal = PowerHalLoader::loadAidl(); if (hal == nullptr) { ALOGV("Power HAL not available, skipping test..."); return; } - binder::Status ret = (*hal.*fn)(std::forward<Args1>(args1)...); - if (ret.exceptionCode() == binder::Status::Exception::EX_UNSUPPORTED_OPERATION) { + ndk::ScopedAStatus ret = (*hal.*fn)(std::forward<Args1>(args1)...); + if (ret.getExceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) { ALOGV("Power HAL does not support this operation, skipping test..."); return; } @@ -79,7 +82,7 @@ static void runBenchmark(benchmark::State& state, microseconds delay, R (IPower: while (state.KeepRunning()) { ret = (*hal.*fn)(std::forward<Args1>(args1)...); state.PauseTiming(); - if (!ret.isOk()) state.SkipWithError(ret.toString8().c_str()); + if (!ret.isOk()) state.SkipWithError(ret.getDescription().c_str()); if (delay > 0us) { testDelaySpin(std::chrono::duration_cast<std::chrono::duration<float>>(delay).count()); } @@ -90,9 +93,9 @@ static void runBenchmark(benchmark::State& state, microseconds delay, R (IPower: template <class R, class... Args0, class... Args1> static void runSessionBenchmark(benchmark::State& state, R (IPowerHintSession::*fn)(Args0...), Args1&&... args1) { - sp<IPower> pwHal = waitForVintfService<IPower>(); + std::shared_ptr<IPower> hal = PowerHalLoader::loadAidl(); - if (pwHal == nullptr) { + if (hal == nullptr) { ALOGV("Power HAL not available, skipping test..."); return; } @@ -100,32 +103,32 @@ static void runSessionBenchmark(benchmark::State& state, R (IPowerHintSession::* // do not use tid from the benchmark process, use 1 for init std::vector<int32_t> threadIds{1}; int64_t durationNanos = 16666666L; - sp<IPowerHintSession> hal; + std::shared_ptr<IPowerHintSession> session; - auto status = pwHal->createHintSession(1, 0, threadIds, durationNanos, &hal); + auto status = hal->createHintSession(1, 0, threadIds, durationNanos, &session); - if (hal == nullptr) { + if (session == nullptr) { ALOGV("Power HAL doesn't support session, skipping test..."); return; } - binder::Status ret = (*hal.*fn)(std::forward<Args1>(args1)...); - if (ret.exceptionCode() == binder::Status::Exception::EX_UNSUPPORTED_OPERATION) { + ndk::ScopedAStatus ret = (*session.*fn)(std::forward<Args1>(args1)...); + if (ret.getExceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) { ALOGV("Power HAL does not support this operation, skipping test..."); return; } while (state.KeepRunning()) { - ret = (*hal.*fn)(std::forward<Args1>(args1)...); + ret = (*session.*fn)(std::forward<Args1>(args1)...); state.PauseTiming(); - if (!ret.isOk()) state.SkipWithError(ret.toString8().c_str()); + if (!ret.isOk()) state.SkipWithError(ret.getDescription().c_str()); if (ONEWAY_API_DELAY > 0us) { testDelaySpin(std::chrono::duration_cast<std::chrono::duration<float>>(ONEWAY_API_DELAY) .count()); } state.ResumeTiming(); } - hal->close(); + session->close(); } static void BM_PowerHalAidlBenchmarks_isBoostSupported(benchmark::State& state) { @@ -155,16 +158,17 @@ static void BM_PowerHalAidlBenchmarks_createHintSession(benchmark::State& state) int64_t durationNanos = 16666666L; int32_t tgid = 999; int32_t uid = 1001; - sp<IPowerHintSession> appSession; - sp<IPower> hal = waitForVintfService<IPower>(); + std::shared_ptr<IPowerHintSession> appSession; + std::shared_ptr<IPower> hal = PowerHalLoader::loadAidl(); if (hal == nullptr) { ALOGV("Power HAL not available, skipping test..."); return; } - binder::Status ret = hal->createHintSession(tgid, uid, threadIds, durationNanos, &appSession); - if (ret.exceptionCode() == binder::Status::Exception::EX_UNSUPPORTED_OPERATION) { + ndk::ScopedAStatus ret = + hal->createHintSession(tgid, uid, threadIds, durationNanos, &appSession); + if (ret.getExceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) { ALOGV("Power HAL does not support this operation, skipping test..."); return; } @@ -172,7 +176,7 @@ static void BM_PowerHalAidlBenchmarks_createHintSession(benchmark::State& state) while (state.KeepRunning()) { ret = hal->createHintSession(tgid, uid, threadIds, durationNanos, &appSession); state.PauseTiming(); - if (!ret.isOk()) state.SkipWithError(ret.toString8().c_str()); + if (!ret.isOk()) state.SkipWithError(ret.getDescription().c_str()); appSession->close(); state.ResumeTiming(); } diff --git a/services/powermanager/benchmarks/PowerHalControllerBenchmarks.cpp b/services/powermanager/benchmarks/PowerHalControllerBenchmarks.cpp index f8abc7aba6..effddda6a4 100644 --- a/services/powermanager/benchmarks/PowerHalControllerBenchmarks.cpp +++ b/services/powermanager/benchmarks/PowerHalControllerBenchmarks.cpp @@ -16,15 +16,15 @@ #define LOG_TAG "PowerHalControllerBenchmarks" -#include <android/hardware/power/Boost.h> -#include <android/hardware/power/Mode.h> +#include <aidl/android/hardware/power/Boost.h> +#include <aidl/android/hardware/power/Mode.h> #include <benchmark/benchmark.h> #include <powermanager/PowerHalController.h> #include <testUtil.h> #include <chrono> -using android::hardware::power::Boost; -using android::hardware::power::Mode; +using aidl::android::hardware::power::Boost; +using aidl::android::hardware::power::Mode; using android::power::HalResult; using android::power::PowerHalController; diff --git a/services/powermanager/benchmarks/PowerHalHidlBenchmarks.cpp b/services/powermanager/benchmarks/PowerHalHidlBenchmarks.cpp index 167f3a64af..111b5d70bc 100644 --- a/services/powermanager/benchmarks/PowerHalHidlBenchmarks.cpp +++ b/services/powermanager/benchmarks/PowerHalHidlBenchmarks.cpp @@ -16,10 +16,10 @@ #define LOG_TAG "PowerHalHidlBenchmarks" +#include <aidl/android/hardware/power/Boost.h> +#include <aidl/android/hardware/power/IPower.h> +#include <aidl/android/hardware/power/Mode.h> #include <android/hardware/power/1.1/IPower.h> -#include <android/hardware/power/Boost.h> -#include <android/hardware/power/IPower.h> -#include <android/hardware/power/Mode.h> #include <benchmark/benchmark.h> #include <hardware/power.h> #include <hardware_legacy/power.h> @@ -27,8 +27,6 @@ #include <chrono> using android::hardware::Return; -using android::hardware::power::Boost; -using android::hardware::power::Mode; using android::hardware::power::V1_0::Feature; using android::hardware::power::V1_0::PowerHint; using std::chrono::microseconds; diff --git a/services/powermanager/tests/Android.bp b/services/powermanager/tests/Android.bp index 54dffcf817..08fcdc8275 100644 --- a/services/powermanager/tests/Android.bp +++ b/services/powermanager/tests/Android.bp @@ -43,6 +43,7 @@ cc_test { shared_libs: [ "libbase", "libbinder", + "libbinder_ndk", "libhidlbase", "liblog", "libpowermanager", @@ -51,9 +52,10 @@ cc_test { "android.hardware.power@1.1", "android.hardware.power@1.2", "android.hardware.power@1.3", - "android.hardware.power-V4-cpp", + "android.hardware.power-V4-ndk", ], static_libs: [ "libgmock", ], + require_root: true, } diff --git a/services/powermanager/tests/PowerHalControllerTest.cpp b/services/powermanager/tests/PowerHalControllerTest.cpp index 6cc7a6f3da..01270ce599 100644 --- a/services/powermanager/tests/PowerHalControllerTest.cpp +++ b/services/powermanager/tests/PowerHalControllerTest.cpp @@ -16,9 +16,9 @@ #define LOG_TAG "PowerHalControllerTest" -#include <android/hardware/power/Boost.h> -#include <android/hardware/power/IPower.h> -#include <android/hardware/power/Mode.h> +#include <aidl/android/hardware/power/Boost.h> +#include <aidl/android/hardware/power/IPower.h> +#include <aidl/android/hardware/power/Mode.h> #include <gmock/gmock.h> #include <gtest/gtest.h> #include <powermanager/PowerHalController.h> @@ -26,8 +26,8 @@ #include <thread> -using android::hardware::power::Boost; -using android::hardware::power::Mode; +using aidl::android::hardware::power::Boost; +using aidl::android::hardware::power::Mode; using android::hardware::power::V1_0::Feature; using android::hardware::power::V1_0::IPower; using android::hardware::power::V1_0::PowerHint; diff --git a/services/powermanager/tests/PowerHalLoaderTest.cpp b/services/powermanager/tests/PowerHalLoaderTest.cpp index e36deed042..7d9735421b 100644 --- a/services/powermanager/tests/PowerHalLoaderTest.cpp +++ b/services/powermanager/tests/PowerHalLoaderTest.cpp @@ -16,9 +16,8 @@ #define LOG_TAG "PowerHalLoaderTest" -#include <android-base/logging.h> +#include <aidl/android/hardware/power/IPower.h> #include <android/hardware/power/1.1/IPower.h> -#include <android/hardware/power/IPower.h> #include <gtest/gtest.h> #include <powermanager/PowerHalLoader.h> @@ -28,7 +27,7 @@ using IPowerV1_0 = android::hardware::power::V1_0::IPower; using IPowerV1_1 = android::hardware::power::V1_1::IPower; using IPowerV1_2 = android::hardware::power::V1_2::IPower; using IPowerV1_3 = android::hardware::power::V1_3::IPower; -using IPowerAidl = android::hardware::power::IPower; +using IPowerAidl = aidl::android::hardware::power::IPower; using namespace android; using namespace android::power; @@ -37,30 +36,30 @@ using namespace testing; // ------------------------------------------------------------------------------------------------- template <typename T> -sp<T> loadHal(); +T loadHal(); template <> -sp<IPowerAidl> loadHal<IPowerAidl>() { +std::shared_ptr<IPowerAidl> loadHal<std::shared_ptr<IPowerAidl>>() { return PowerHalLoader::loadAidl(); } template <> -sp<IPowerV1_0> loadHal<IPowerV1_0>() { +sp<IPowerV1_0> loadHal<sp<IPowerV1_0>>() { return PowerHalLoader::loadHidlV1_0(); } template <> -sp<IPowerV1_1> loadHal<IPowerV1_1>() { +sp<IPowerV1_1> loadHal<sp<IPowerV1_1>>() { return PowerHalLoader::loadHidlV1_1(); } template <> -sp<IPowerV1_2> loadHal<IPowerV1_2>() { +sp<IPowerV1_2> loadHal<sp<IPowerV1_2>>() { return PowerHalLoader::loadHidlV1_2(); } template <> -sp<IPowerV1_3> loadHal<IPowerV1_3>() { +sp<IPowerV1_3> loadHal<sp<IPowerV1_3>>() { return PowerHalLoader::loadHidlV1_3(); } @@ -69,46 +68,47 @@ sp<IPowerV1_3> loadHal<IPowerV1_3>() { template <typename T> class PowerHalLoaderTest : public Test { public: - sp<T> load() { return ::loadHal<T>(); } + T load() { return ::loadHal<T>(); } void unload() { PowerHalLoader::unloadAll(); } }; // ------------------------------------------------------------------------------------------------- - -typedef ::testing::Types<IPowerAidl, IPowerV1_0, IPowerV1_1, IPowerV1_2, IPowerV1_3> PowerHalTypes; +typedef ::testing::Types<std::shared_ptr<IPowerAidl>, sp<IPowerV1_0>, sp<IPowerV1_1>, + sp<IPowerV1_2>, sp<IPowerV1_3>> + PowerHalTypes; TYPED_TEST_SUITE(PowerHalLoaderTest, PowerHalTypes); TYPED_TEST(PowerHalLoaderTest, TestLoadsOnlyOnce) { - sp<TypeParam> firstHal = this->load(); + TypeParam firstHal = this->load(); if (firstHal == nullptr) { ALOGE("Power HAL not available. Skipping test."); return; } - sp<TypeParam> secondHal = this->load(); + TypeParam secondHal = this->load(); ASSERT_EQ(firstHal, secondHal); } TYPED_TEST(PowerHalLoaderTest, TestUnload) { - sp<TypeParam> firstHal = this->load(); + TypeParam firstHal = this->load(); if (firstHal == nullptr) { ALOGE("Power HAL not available. Skipping test."); return; } this->unload(); - sp<TypeParam> secondHal = this->load(); + TypeParam secondHal = this->load(); ASSERT_NE(secondHal, nullptr); ASSERT_NE(firstHal, secondHal); } TYPED_TEST(PowerHalLoaderTest, TestLoadMultiThreadLoadsOnlyOnce) { - std::vector<std::future<sp<TypeParam>>> futures; + std::vector<std::future<TypeParam>> futures; for (int i = 0; i < 10; i++) { futures.push_back( std::async(std::launch::async, &PowerHalLoaderTest<TypeParam>::load, this)); } futures[0].wait(); - sp<TypeParam> firstHal = futures[0].get(); + TypeParam firstHal = futures[0].get(); if (firstHal == nullptr) { ALOGE("Power HAL not available. Skipping test."); return; @@ -116,7 +116,7 @@ TYPED_TEST(PowerHalLoaderTest, TestLoadMultiThreadLoadsOnlyOnce) { for (int i = 1; i < 10; i++) { futures[i].wait(); - sp<TypeParam> currentHal = futures[i].get(); + TypeParam currentHal = futures[i].get(); ASSERT_EQ(firstHal, currentHal); } } diff --git a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp index cb1a77a45f..641ba9f44b 100644 --- a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp +++ b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp @@ -16,9 +16,9 @@ #define LOG_TAG "PowerHalWrapperAidlTest" -#include <android/hardware/power/Boost.h> -#include <android/hardware/power/IPowerHintSession.h> -#include <android/hardware/power/Mode.h> +#include <aidl/android/hardware/power/Boost.h> +#include <aidl/android/hardware/power/IPowerHintSession.h> +#include <aidl/android/hardware/power/Mode.h> #include <binder/IServiceManager.h> #include <gmock/gmock.h> #include <gtest/gtest.h> @@ -28,11 +28,11 @@ #include <unistd.h> #include <thread> +using aidl::android::hardware::power::Boost; +using aidl::android::hardware::power::IPower; +using aidl::android::hardware::power::IPowerHintSession; +using aidl::android::hardware::power::Mode; using android::binder::Status; -using android::hardware::power::Boost; -using android::hardware::power::IPower; -using android::hardware::power::IPowerHintSession; -using android::hardware::power::Mode; using namespace android; using namespace android::power; @@ -43,18 +43,21 @@ using namespace testing; class MockIPower : public IPower { public: - MOCK_METHOD(Status, isBoostSupported, (Boost boost, bool* ret), (override)); - MOCK_METHOD(Status, setBoost, (Boost boost, int32_t durationMs), (override)); - MOCK_METHOD(Status, isModeSupported, (Mode mode, bool* ret), (override)); - MOCK_METHOD(Status, setMode, (Mode mode, bool enabled), (override)); - MOCK_METHOD(Status, createHintSession, + MockIPower() = default; + + MOCK_METHOD(ndk::ScopedAStatus, isBoostSupported, (Boost boost, bool* ret), (override)); + MOCK_METHOD(ndk::ScopedAStatus, setBoost, (Boost boost, int32_t durationMs), (override)); + MOCK_METHOD(ndk::ScopedAStatus, isModeSupported, (Mode mode, bool* ret), (override)); + MOCK_METHOD(ndk::ScopedAStatus, setMode, (Mode mode, bool enabled), (override)); + MOCK_METHOD(ndk::ScopedAStatus, createHintSession, (int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, - int64_t durationNanos, sp<IPowerHintSession>* session), + int64_t durationNanos, std::shared_ptr<IPowerHintSession>* session), (override)); - MOCK_METHOD(Status, getHintSessionPreferredRate, (int64_t * rate), (override)); - MOCK_METHOD(int32_t, getInterfaceVersion, (), (override)); - MOCK_METHOD(std::string, getInterfaceHash, (), (override)); - MOCK_METHOD(IBinder*, onAsBinder, (), (override)); + MOCK_METHOD(ndk::ScopedAStatus, getHintSessionPreferredRate, (int64_t * rate), (override)); + MOCK_METHOD(ndk::ScopedAStatus, getInterfaceVersion, (int32_t * version), (override)); + MOCK_METHOD(ndk::ScopedAStatus, getInterfaceHash, (std::string * hash), (override)); + MOCK_METHOD(ndk::SpAIBinder, asBinder, (), (override)); + MOCK_METHOD(bool, isRemote, (), (override)); }; // ------------------------------------------------------------------------------------------------- @@ -65,13 +68,13 @@ public: protected: std::unique_ptr<HalWrapper> mWrapper = nullptr; - sp<StrictMock<MockIPower>> mMockHal = nullptr; + std::shared_ptr<StrictMock<MockIPower>> mMockHal = nullptr; }; // ------------------------------------------------------------------------------------------------- void PowerHalWrapperAidlTest::SetUp() { - mMockHal = new StrictMock<MockIPower>(); + mMockHal = ndk::SharedRefBase::make<StrictMock<MockIPower>>(); mWrapper = std::make_unique<AidlHalWrapper>(mMockHal); ASSERT_NE(nullptr, mWrapper); } @@ -83,9 +86,11 @@ TEST_F(PowerHalWrapperAidlTest, TestSetBoostSuccessful) { InSequence seq; EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::DISPLAY_UPDATE_IMMINENT), _)) .Times(Exactly(1)) - .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status()))); + .WillOnce(DoAll(SetArgPointee<1>(true), + Return(testing::ByMove(ndk::ScopedAStatus::ok())))); EXPECT_CALL(*mMockHal.get(), setBoost(Eq(Boost::DISPLAY_UPDATE_IMMINENT), Eq(100))) - .Times(Exactly(1)); + .Times(Exactly(1)) + .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::ok()))); } auto result = mWrapper->setBoost(Boost::DISPLAY_UPDATE_IMMINENT, 100); @@ -97,13 +102,14 @@ TEST_F(PowerHalWrapperAidlTest, TestSetBoostFailed) { InSequence seq; EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::INTERACTION), _)) .Times(Exactly(1)) - .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status()))); + .WillOnce(DoAll(SetArgPointee<1>(true), + Return(testing::ByMove(ndk::ScopedAStatus::ok())))); EXPECT_CALL(*mMockHal.get(), setBoost(Eq(Boost::INTERACTION), Eq(100))) .Times(Exactly(1)) - .WillRepeatedly(Return(Status::fromExceptionCode(-1))); + .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::fromExceptionCode(-1)))); EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::DISPLAY_UPDATE_IMMINENT), _)) .Times(Exactly(1)) - .WillRepeatedly(Return(Status::fromExceptionCode(-1))); + .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::fromExceptionCode(-1)))); } auto result = mWrapper->setBoost(Boost::INTERACTION, 100); @@ -115,7 +121,8 @@ TEST_F(PowerHalWrapperAidlTest, TestSetBoostFailed) { TEST_F(PowerHalWrapperAidlTest, TestSetBoostUnsupported) { EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::INTERACTION), _)) .Times(Exactly(1)) - .WillRepeatedly(DoAll(SetArgPointee<1>(false), Return(Status()))); + .WillOnce(DoAll(SetArgPointee<1>(false), + Return(testing::ByMove(ndk::ScopedAStatus::ok())))); auto result = mWrapper->setBoost(Boost::INTERACTION, 1000); ASSERT_TRUE(result.isUnsupported()); @@ -128,8 +135,13 @@ TEST_F(PowerHalWrapperAidlTest, TestSetBoostMultiThreadCheckSupportedOnlyOnce) { InSequence seq; EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::INTERACTION), _)) .Times(Exactly(1)) - .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status()))); - EXPECT_CALL(*mMockHal.get(), setBoost(Eq(Boost::INTERACTION), Eq(100))).Times(Exactly(10)); + .WillOnce(DoAll(SetArgPointee<1>(true), + Return(testing::ByMove(ndk::ScopedAStatus::ok())))); + auto& exp = EXPECT_CALL(*mMockHal.get(), setBoost(Eq(Boost::INTERACTION), Eq(100))) + .Times(Exactly(10)); + for (int i = 0; i < 10; i++) { + exp.WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::ok()))); + } } std::vector<std::thread> threads; @@ -147,9 +159,11 @@ TEST_F(PowerHalWrapperAidlTest, TestSetModeSuccessful) { InSequence seq; EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::DISPLAY_INACTIVE), _)) .Times(Exactly(1)) - .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status()))); + .WillOnce(DoAll(SetArgPointee<1>(true), + Return(testing::ByMove(ndk::ScopedAStatus::ok())))); EXPECT_CALL(*mMockHal.get(), setMode(Eq(Mode::DISPLAY_INACTIVE), Eq(false))) - .Times(Exactly(1)); + .Times(Exactly(1)) + .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::ok()))); } auto result = mWrapper->setMode(Mode::DISPLAY_INACTIVE, false); @@ -161,13 +175,14 @@ TEST_F(PowerHalWrapperAidlTest, TestSetModeFailed) { InSequence seq; EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::LAUNCH), _)) .Times(Exactly(1)) - .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status()))); + .WillOnce(DoAll(SetArgPointee<1>(true), + Return(testing::ByMove(ndk::ScopedAStatus::ok())))); EXPECT_CALL(*mMockHal.get(), setMode(Eq(Mode::LAUNCH), Eq(true))) .Times(Exactly(1)) - .WillRepeatedly(Return(Status::fromExceptionCode(-1))); + .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::fromExceptionCode(-1)))); EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::DISPLAY_INACTIVE), _)) .Times(Exactly(1)) - .WillRepeatedly(Return(Status::fromExceptionCode(-1))); + .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::fromExceptionCode(-1)))); } auto result = mWrapper->setMode(Mode::LAUNCH, true); @@ -179,14 +194,16 @@ TEST_F(PowerHalWrapperAidlTest, TestSetModeFailed) { TEST_F(PowerHalWrapperAidlTest, TestSetModeUnsupported) { EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::LAUNCH), _)) .Times(Exactly(1)) - .WillRepeatedly(DoAll(SetArgPointee<1>(false), Return(Status()))); + .WillOnce(DoAll(SetArgPointee<1>(false), + Return(testing::ByMove(ndk::ScopedAStatus::ok())))); auto result = mWrapper->setMode(Mode::LAUNCH, true); ASSERT_TRUE(result.isUnsupported()); EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::CAMERA_STREAMING_HIGH), _)) .Times(Exactly(1)) - .WillRepeatedly(DoAll(SetArgPointee<1>(false), Return(Status()))); + .WillOnce(DoAll(SetArgPointee<1>(false), + Return(testing::ByMove(ndk::ScopedAStatus::ok())))); result = mWrapper->setMode(Mode::CAMERA_STREAMING_HIGH, true); ASSERT_TRUE(result.isUnsupported()); } @@ -196,8 +213,13 @@ TEST_F(PowerHalWrapperAidlTest, TestSetModeMultiThreadCheckSupportedOnlyOnce) { InSequence seq; EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::LAUNCH), _)) .Times(Exactly(1)) - .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status()))); - EXPECT_CALL(*mMockHal.get(), setMode(Eq(Mode::LAUNCH), Eq(false))).Times(Exactly(10)); + .WillOnce(DoAll(SetArgPointee<1>(true), + Return(testing::ByMove(ndk::ScopedAStatus::ok())))); + auto& exp = EXPECT_CALL(*mMockHal.get(), setMode(Eq(Mode::LAUNCH), Eq(false))) + .Times(Exactly(10)); + for (int i = 0; i < 10; i++) { + exp.WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::ok()))); + } } std::vector<std::thread> threads; @@ -217,7 +239,8 @@ TEST_F(PowerHalWrapperAidlTest, TestCreateHintSessionSuccessful) { int64_t durationNanos = 16666666L; EXPECT_CALL(*mMockHal.get(), createHintSession(Eq(tgid), Eq(uid), Eq(threadIds), Eq(durationNanos), _)) - .Times(Exactly(1)); + .Times(Exactly(1)) + .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::ok()))); auto result = mWrapper->createHintSession(tgid, uid, threadIds, durationNanos); ASSERT_TRUE(result.isOk()); } @@ -230,13 +253,16 @@ TEST_F(PowerHalWrapperAidlTest, TestCreateHintSessionFailed) { EXPECT_CALL(*mMockHal.get(), createHintSession(Eq(tgid), Eq(uid), Eq(threadIds), Eq(durationNanos), _)) .Times(Exactly(1)) - .WillRepeatedly(Return(Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT))); + .WillOnce(Return(testing::ByMove( + ndk::ScopedAStatus::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT)))); auto result = mWrapper->createHintSession(tgid, uid, threadIds, durationNanos); ASSERT_TRUE(result.isFailed()); } TEST_F(PowerHalWrapperAidlTest, TestGetHintSessionPreferredRate) { - EXPECT_CALL(*mMockHal.get(), getHintSessionPreferredRate(_)).Times(Exactly(1)); + EXPECT_CALL(*mMockHal.get(), getHintSessionPreferredRate(_)) + .Times(Exactly(1)) + .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::ok()))); auto result = mWrapper->getHintSessionPreferredRate(); ASSERT_TRUE(result.isOk()); int64_t rate = result.value(); diff --git a/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp b/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp index 0cd2e22e7e..461143bfc6 100644 --- a/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp +++ b/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp @@ -16,17 +16,17 @@ #define LOG_TAG "PowerHalWrapperHidlV1_0Test" -#include <android/hardware/power/Boost.h> -#include <android/hardware/power/IPower.h> -#include <android/hardware/power/Mode.h> +#include <aidl/android/hardware/power/Boost.h> +#include <aidl/android/hardware/power/IPower.h> +#include <aidl/android/hardware/power/Mode.h> #include <binder/IServiceManager.h> #include <gmock/gmock.h> #include <gtest/gtest.h> #include <powermanager/PowerHalWrapper.h> #include <utils/Log.h> -using android::hardware::power::Boost; -using android::hardware::power::Mode; +using aidl::android::hardware::power::Boost; +using aidl::android::hardware::power::Mode; using android::hardware::power::V1_0::Feature; using android::hardware::power::V1_0::IPower; using android::hardware::power::V1_0::PowerHint; diff --git a/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp b/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp index 32f84e20b6..79dd996098 100644 --- a/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp +++ b/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp @@ -16,18 +16,18 @@ #define LOG_TAG "PowerHalWrapperHidlV1_1Test" +#include <aidl/android/hardware/power/Boost.h> +#include <aidl/android/hardware/power/IPower.h> +#include <aidl/android/hardware/power/Mode.h> #include <android/hardware/power/1.1/IPower.h> -#include <android/hardware/power/Boost.h> -#include <android/hardware/power/IPower.h> -#include <android/hardware/power/Mode.h> #include <binder/IServiceManager.h> #include <gmock/gmock.h> #include <gtest/gtest.h> #include <powermanager/PowerHalWrapper.h> #include <utils/Log.h> -using android::hardware::power::Boost; -using android::hardware::power::Mode; +using aidl::android::hardware::power::Boost; +using aidl::android::hardware::power::Mode; using android::hardware::power::V1_0::Feature; using android::hardware::power::V1_0::PowerHint; using IPowerV1_1 = android::hardware::power::V1_1::IPower; diff --git a/services/powermanager/tests/PowerHalWrapperHidlV1_2Test.cpp b/services/powermanager/tests/PowerHalWrapperHidlV1_2Test.cpp index cf48409f5f..aa6d6c7a3d 100644 --- a/services/powermanager/tests/PowerHalWrapperHidlV1_2Test.cpp +++ b/services/powermanager/tests/PowerHalWrapperHidlV1_2Test.cpp @@ -16,18 +16,18 @@ #define LOG_TAG "PowerHalWrapperHidlV1_2Test" +#include <aidl/android/hardware/power/Boost.h> +#include <aidl/android/hardware/power/IPower.h> +#include <aidl/android/hardware/power/Mode.h> #include <android/hardware/power/1.2/IPower.h> -#include <android/hardware/power/Boost.h> -#include <android/hardware/power/IPower.h> -#include <android/hardware/power/Mode.h> #include <binder/IServiceManager.h> #include <gmock/gmock.h> #include <gtest/gtest.h> #include <powermanager/PowerHalWrapper.h> #include <utils/Log.h> -using android::hardware::power::Boost; -using android::hardware::power::Mode; +using aidl::android::hardware::power::Boost; +using aidl::android::hardware::power::Mode; using android::hardware::power::V1_0::Feature; using PowerHintV1_0 = android::hardware::power::V1_0::PowerHint; using PowerHintV1_2 = android::hardware::power::V1_2::PowerHint; diff --git a/services/powermanager/tests/PowerHalWrapperHidlV1_3Test.cpp b/services/powermanager/tests/PowerHalWrapperHidlV1_3Test.cpp index 2c48537edc..a995afd750 100644 --- a/services/powermanager/tests/PowerHalWrapperHidlV1_3Test.cpp +++ b/services/powermanager/tests/PowerHalWrapperHidlV1_3Test.cpp @@ -16,18 +16,18 @@ #define LOG_TAG "PowerHalWrapperHidlV1_3Test" +#include <aidl/android/hardware/power/Boost.h> +#include <aidl/android/hardware/power/IPower.h> +#include <aidl/android/hardware/power/Mode.h> #include <android/hardware/power/1.3/IPower.h> -#include <android/hardware/power/Boost.h> -#include <android/hardware/power/IPower.h> -#include <android/hardware/power/Mode.h> #include <binder/IServiceManager.h> #include <gmock/gmock.h> #include <gtest/gtest.h> #include <powermanager/PowerHalWrapper.h> #include <utils/Log.h> -using android::hardware::power::Boost; -using android::hardware::power::Mode; +using aidl::android::hardware::power::Boost; +using aidl::android::hardware::power::Mode; using android::hardware::power::V1_0::Feature; using PowerHintV1_0 = android::hardware::power::V1_0::PowerHint; using PowerHintV1_2 = android::hardware::power::V1_2::PowerHint; diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp index 5683a9280f..89c80bc83a 100644 --- a/services/surfaceflinger/Android.bp +++ b/services/surfaceflinger/Android.bp @@ -27,6 +27,7 @@ cc_defaults { defaults: [ "android.hardware.graphics.composer3-ndk_shared", "librenderengine_deps", + "libtimestats_deps", "surfaceflinger_defaults", ], cflags: [ @@ -47,7 +48,7 @@ cc_defaults { "android.hardware.graphics.composer@2.2", "android.hardware.graphics.composer@2.3", "android.hardware.graphics.composer@2.4", - "android.hardware.power-V4-cpp", + "android.hardware.power-V4-ndk", "libbase", "libbinder", "libbinder_ndk", @@ -58,14 +59,12 @@ cc_defaults { "libGLESv2", "libgui", "libhidlbase", - "liblayers_proto", "liblog", "libnativewindow", "libpowermanager", "libprocessgroup", "libprotobuf-cpp-lite", "libsync", - "libtimestats", "libui", "libinput", "libutils", @@ -77,11 +76,13 @@ cc_defaults { "libcompositionengine", "libframetimeline", "libgui_aidl_static", + "liblayers_proto", "libperfetto_client_experimental", "librenderengine", "libscheduler", "libserviceutils", "libshaders", + "libtimestats", "libtonemap", ], header_libs: [ @@ -95,6 +96,7 @@ cc_defaults { "libcompositionengine", "librenderengine", "libserviceutils", + "libtimestats", ], export_shared_lib_headers: [ "android.hardware.graphics.allocator@2.0", @@ -106,7 +108,6 @@ cc_defaults { "android.hardware.graphics.composer@2.4", "libpowermanager", "libhidlbase", - "libtimestats", ], // TODO (marissaw): this library is not used by surfaceflinger. This is here so // the library compiled in a way that is accessible to system partition when running @@ -213,14 +214,12 @@ cc_defaults { "-DLOG_TAG=\"SurfaceFlinger\"", ], shared_libs: [ - "android.frameworks.displayservice@1.0", "android.hardware.configstore-utils", "android.hardware.configstore@1.0", "android.hardware.graphics.allocator@2.0", "android.hardware.graphics.allocator@3.0", "libbinder", "libcutils", - "libdisplayservicehidl", "libhidlbase", "liblog", "libprocessgroup", @@ -228,6 +227,8 @@ cc_defaults { "libutils", ], static_libs: [ + "android.frameworks.displayservice@1.0", + "libdisplayservicehidl", "libserviceutils", ], } diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp index f3a0186e3e..3426495297 100644 --- a/services/surfaceflinger/CompositionEngine/Android.bp +++ b/services/surfaceflinger/CompositionEngine/Android.bp @@ -12,6 +12,7 @@ cc_defaults { defaults: [ "android.hardware.graphics.composer3-ndk_shared", "librenderengine_deps", + "libtimestats_deps", "surfaceflinger_defaults", ], cflags: [ @@ -26,22 +27,22 @@ cc_defaults { "android.hardware.graphics.composer@2.4", "android.hardware.power@1.0", "android.hardware.power@1.3", - "android.hardware.power-V4-cpp", + "android.hardware.power-V4-ndk", "libbase", "libcutils", "libgui", - "liblayers_proto", "liblog", "libnativewindow", "libprotobuf-cpp-lite", "libSurfaceFlingerProp", - "libtimestats", "libui", "libutils", ], static_libs: [ + "liblayers_proto", "libmath", "librenderengine", + "libtimestats", "libtonemap", "libaidlcommonsupport", "libprocessgroup", diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp index 9c7576e76d..f00ef671ad 100644 --- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp +++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp @@ -31,9 +31,9 @@ #include <utils/Mutex.h> #include <utils/Trace.h> -#include <android/hardware/power/IPower.h> -#include <android/hardware/power/IPowerHintSession.h> -#include <android/hardware/power/WorkDuration.h> +#include <aidl/android/hardware/power/IPower.h> +#include <aidl/android/hardware/power/IPowerHintSession.h> +#include <aidl/android/hardware/power/WorkDuration.h> #include <binder/IServiceManager.h> @@ -49,11 +49,11 @@ PowerAdvisor::~PowerAdvisor() = default; namespace impl { -using android::hardware::power::Boost; -using android::hardware::power::IPowerHintSession; -using android::hardware::power::Mode; -using android::hardware::power::SessionHint; -using android::hardware::power::WorkDuration; +using aidl::android::hardware::power::Boost; +using aidl::android::hardware::power::IPowerHintSession; +using aidl::android::hardware::power::Mode; +using aidl::android::hardware::power::SessionHint; +using aidl::android::hardware::power::WorkDuration; PowerAdvisor::~PowerAdvisor() = default; @@ -230,7 +230,7 @@ void PowerAdvisor::updateTargetWorkDuration(Duration targetDuration) { auto ret = mHintSession->updateTargetWorkDuration(targetDuration.ns()); if (!ret.isOk()) { ALOGW("Failed to set power hint target work duration with error: %s", - ret.exceptionMessage().c_str()); + ret.getDescription().c_str()); mHintSessionRunning = false; } } @@ -274,7 +274,7 @@ void PowerAdvisor::reportActualWorkDuration() { auto ret = mHintSession->reportActualWorkDuration(mHintSessionQueue); if (!ret.isOk()) { ALOGW("Failed to report actual work durations with error: %s", - ret.exceptionMessage().c_str()); + ret.getDescription().c_str()); mHintSessionRunning = false; return; } diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h index cfaa135299..05e4c8b930 100644 --- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h +++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h @@ -25,7 +25,7 @@ #include <ui/FenceTime.h> #include <utils/Mutex.h> -#include <android/hardware/power/IPower.h> +#include <aidl/android/hardware/power/IPower.h> #include <compositionengine/impl/OutputCompositionState.h> #include <powermanager/PowerHalController.h> #include <scheduler/Time.h> @@ -247,13 +247,14 @@ private: bool mHintSessionRunning = false; std::mutex mHintSessionMutex; - sp<hardware::power::IPowerHintSession> mHintSession GUARDED_BY(mHintSessionMutex) = nullptr; + std::shared_ptr<aidl::android::hardware::power::IPowerHintSession> mHintSession + GUARDED_BY(mHintSessionMutex) = nullptr; // Initialize to true so we try to call, to check if it's supported bool mHasExpensiveRendering = true; bool mHasDisplayUpdateImminent = true; // Queue of actual durations saved to report - std::vector<hardware::power::WorkDuration> mHintSessionQueue; + std::vector<aidl::android::hardware::power::WorkDuration> mHintSessionQueue; // The latest values we have received for target and actual Duration mTargetDuration = kDefaultTargetDuration; std::optional<Duration> mActualDuration; diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp index ded734efad..dcc29b9913 100644 --- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp +++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp @@ -140,6 +140,10 @@ std::string jankTypeBitmaskToString(int32_t jankType) { janks.emplace_back("SurfaceFlinger Stuffing"); jankType &= ~JankType::SurfaceFlingerStuffing; } + if (jankType & JankType::Dropped) { + janks.emplace_back("Dropped Frame"); + jankType &= ~JankType::Dropped; + } // jankType should be 0 if all types of jank were checked for. LOG_ALWAYS_FATAL_IF(jankType != 0, "Unrecognized jank type value 0x%x", jankType); @@ -264,6 +268,11 @@ int32_t jankTypeBitmaskToProto(int32_t jankType) { protoJank |= FrameTimelineEvent::JANK_SF_STUFFING; jankType &= ~JankType::SurfaceFlingerStuffing; } + if (jankType & JankType::Dropped) { + // Jank dropped does not append to other janks, it fully overrides. + protoJank |= FrameTimelineEvent::JANK_DROPPED; + jankType &= ~JankType::Dropped; + } // jankType should be 0 if all types of jank were checked for. LOG_ALWAYS_FATAL_IF(jankType != 0, "Unrecognized jank type value 0x%x", jankType); @@ -365,8 +374,7 @@ void SurfaceFrame::setGpuComposition() { std::optional<int32_t> SurfaceFrame::getJankType() const { std::scoped_lock lock(mMutex); if (mPresentState == PresentState::Dropped) { - // Return no jank if it's a dropped frame since we cannot attribute a jank to a it. - return JankType::None; + return JankType::Dropped; } if (mActuals.presentTime == 0) { // Frame hasn't been presented yet. @@ -503,7 +511,8 @@ void SurfaceFrame::classifyJankLocked(int32_t displayFrameJankType, const Fps& r // We classify prediction expired as AppDeadlineMissed as the // TokenManager::kMaxTokens we store is large enough to account for a // reasonable app, so prediction expire would mean a huge scheduling delay. - mJankType = JankType::AppDeadlineMissed; + mJankType = mPresentState != PresentState::Presented ? JankType::Dropped + : JankType::AppDeadlineMissed; deadlineDelta = -1; return; } @@ -594,17 +603,17 @@ void SurfaceFrame::classifyJankLocked(int32_t displayFrameJankType, const Fps& r mJankType |= displayFrameJankType; } } + if (mPresentState != PresentState::Presented) { + mJankType = JankType::Dropped; + // Since frame was not presented, lets drop any present value + mActuals.presentTime = 0; + } } void SurfaceFrame::onPresent(nsecs_t presentTime, int32_t displayFrameJankType, Fps refreshRate, nsecs_t displayDeadlineDelta, nsecs_t displayPresentDelta) { std::scoped_lock lock(mMutex); - if (mPresentState != PresentState::Presented) { - // No need to update dropped buffers - return; - } - mActuals.presentTime = presentTime; nsecs_t deadlineDelta = 0; diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp index 163d34575c..ab4c15d670 100644 --- a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp +++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp @@ -149,13 +149,33 @@ std::string LayerHierarchy::getDebugStringShort() const { return debug + "}"; } -std::string LayerHierarchy::getDebugString(const char* prefix) const { - std::string debug = prefix + getDebugStringShort(); - for (auto& [child, childVariant] : mChildren) { - std::string childPrefix = " " + std::string(prefix) + " " + std::to_string(childVariant); - debug += "\n" + child->getDebugString(childPrefix.c_str()); +void LayerHierarchy::dump(std::ostream& out, const std::string& prefix, + LayerHierarchy::Variant variant, bool isLastChild) const { + if (!mLayer) { + out << " ROOT"; + } else { + out << prefix + (isLastChild ? "└─ " : "├─ "); + if (variant == LayerHierarchy::Variant::Relative) { + out << "(Relative) "; + } else if (variant == LayerHierarchy::Variant::Mirror) { + out << "(Mirroring) " << *mLayer << "\n" + prefix + " └─ ..."; + return; + } + out << *mLayer; } - return debug; + + for (size_t i = 0; i < mChildren.size(); i++) { + auto& [child, childVariant] = mChildren[i]; + if (childVariant == LayerHierarchy::Variant::Detached) continue; + const bool lastChild = i == (mChildren.size() - 1); + std::string childPrefix = prefix; + if (mLayer) { + childPrefix += (isLastChild ? " " : "│ "); + } + out << "\n"; + child->dump(out, childPrefix, childVariant, lastChild); + } + return; } bool LayerHierarchy::hasRelZLoop(uint32_t& outInvalidRelativeRoot) const { diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.h b/services/surfaceflinger/FrontEnd/LayerHierarchy.h index 5389adab6a..1e4838727b 100644 --- a/services/surfaceflinger/FrontEnd/LayerHierarchy.h +++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.h @@ -156,7 +156,12 @@ public: const RequestedLayerState* getLayer() const; const LayerHierarchy* getRelativeParent() const; const LayerHierarchy* getParent() const; - std::string getDebugString(const char* prefix = "") const; + friend std::ostream& operator<<(std::ostream& os, const LayerHierarchy& obj) { + std::string prefix = " "; + obj.dump(os, prefix, LayerHierarchy::Variant::Attached, /*isLastChild=*/false); + return os; + } + std::string getDebugStringShort() const; // Traverse the hierarchy and return true if loops are found. The outInvalidRelativeRoot // will contain the first relative root that was visited twice in a traversal. @@ -172,6 +177,8 @@ private: void updateChild(LayerHierarchy*, LayerHierarchy::Variant); void traverseInZOrder(const Visitor& visitor, LayerHierarchy::TraversalPath& parent) const; void traverse(const Visitor& visitor, LayerHierarchy::TraversalPath& parent) const; + void dump(std::ostream& out, const std::string& prefix, LayerHierarchy::Variant variant, + bool isLastChild) const; const RequestedLayerState* mLayer; LayerHierarchy* mParent = nullptr; diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp index f0826c6db3..d389a799ad 100644 --- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp +++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp @@ -39,6 +39,63 @@ void updateSurfaceDamage(const RequestedLayerState& requested, bool hasReadyFram } } +std::ostream& operator<<(std::ostream& os, const ui::Transform& transform) { + const uint32_t type = transform.getType(); + const uint32_t orientation = transform.getOrientation(); + if (type == ui::Transform::IDENTITY) { + return os; + } + + if (type & ui::Transform::UNKNOWN) { + std::string out; + transform.dump(out, "", ""); + os << out; + return os; + } + + if (type & ui::Transform::ROTATE) { + switch (orientation) { + case ui::Transform::ROT_0: + os << "ROT_0"; + break; + case ui::Transform::FLIP_H: + os << "FLIP_H"; + break; + case ui::Transform::FLIP_V: + os << "FLIP_V"; + break; + case ui::Transform::ROT_90: + os << "ROT_90"; + break; + case ui::Transform::ROT_180: + os << "ROT_180"; + break; + case ui::Transform::ROT_270: + os << "ROT_270"; + break; + case ui::Transform::ROT_INVALID: + default: + os << "ROT_INVALID"; + break; + } + } + + if (type & ui::Transform::SCALE) { + std::string out; + android::base::StringAppendF(&out, " scale x=%.4f y=%.4f ", transform.getScaleX(), + transform.getScaleY()); + os << out; + } + + if (type & ui::Transform::TRANSLATE) { + std::string out; + android::base::StringAppendF(&out, " tx=%.4f ty=%.4f ", transform.tx(), transform.ty()); + os << out; + } + + return os; +} + } // namespace LayerSnapshot::LayerSnapshot(const RequestedLayerState& state, @@ -59,6 +116,7 @@ LayerSnapshot::LayerSnapshot(const RequestedLayerState& state, } sequence = static_cast<int32_t>(state.id); name = state.name; + debugName = state.debugName; textureName = state.textureName; premultipliedAlpha = state.premultipliedAlpha; inputInfo.name = state.name; @@ -73,6 +131,8 @@ LayerSnapshot::LayerSnapshot(const RequestedLayerState& state, ? path : LayerHierarchy::TraversalPath::ROOT; reachablilty = LayerSnapshot::Reachablilty::Unreachable; + frameRateSelectionPriority = state.frameRateSelectionPriority; + layerMetadata = state.metadata; } // As documented in libhardware header, formats in the range @@ -180,13 +240,13 @@ std::string LayerSnapshot::getIsVisibleReason() const { if (handleSkipScreenshotFlag & outputFilter.toInternalDisplay) return "eLayerSkipScreenshot"; if (invalidTransform) return "invalidTransform"; if (color.a == 0.0f && !hasBlur()) return "alpha = 0 and no blur"; - if (!hasSomethingToDraw()) return "!hasSomethingToDraw"; + if (!hasSomethingToDraw()) return "nothing to draw"; // visible std::stringstream reason; if (sidebandStream != nullptr) reason << " sidebandStream"; if (externalTexture != nullptr) - reason << " buffer:" << externalTexture->getId() << " frame:" << frameNumber; + reason << " buffer=" << externalTexture->getId() << " frame=" << frameNumber; if (fillsColor() || color.a > 0.0f) reason << " color{" << color << "}"; if (drawShadows()) reason << " shadowSettings.length=" << shadowSettings.length; if (backgroundBlurRadius > 0) reason << " backgroundBlurRadius=" << backgroundBlurRadius; @@ -232,6 +292,36 @@ std::string LayerSnapshot::getDebugString() const { return debug.str(); } +std::ostream& operator<<(std::ostream& out, const LayerSnapshot& obj) { + out << "Layer [" << obj.path.id; + if (obj.path.mirrorRootId != UNASSIGNED_LAYER_ID) { + out << " mirrored from " << obj.path.mirrorRootId; + } + out << "] " << obj.name << "\n " << (obj.isVisible ? "visible" : "invisible") + << " reason=" << obj.getIsVisibleReason(); + + if (!obj.geomLayerBounds.isEmpty()) { + out << "\n bounds={" << obj.transformedBounds.left << "," << obj.transformedBounds.top + << "," << obj.transformedBounds.bottom << "," << obj.transformedBounds.right << "}"; + } + + if (obj.geomLayerTransform.getType() != ui::Transform::IDENTITY) { + out << " toDisplayTransform={" << obj.geomLayerTransform << "}"; + } + + if (obj.hasInputInfo()) { + out << "\n input{" + << "(" << obj.inputInfo.inputConfig.string() << ")"; + if (obj.touchCropId != UNASSIGNED_LAYER_ID) out << " touchCropId=" << obj.touchCropId; + if (obj.inputInfo.replaceTouchableRegionWithCrop) out << " replaceTouchableRegionWithCrop"; + auto touchableRegion = obj.inputInfo.touchableRegion.getBounds(); + out << " touchableRegion={" << touchableRegion.left << "," << touchableRegion.top << "," + << touchableRegion.bottom << "," << touchableRegion.right << "}" + << "}"; + } + return out; +} + FloatRect LayerSnapshot::sourceBounds() const { if (!externalTexture) { return geomLayerBounds; diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.h b/services/surfaceflinger/FrontEnd/LayerSnapshot.h index 2f45d52162..1416872f95 100644 --- a/services/surfaceflinger/FrontEnd/LayerSnapshot.h +++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.h @@ -67,6 +67,7 @@ struct LayerSnapshot : public compositionengine::LayerFECompositionState { // generated from the same layer, for example when mirroring. int32_t sequence; std::string name; + std::string debugName; uint32_t textureName; bool contentOpaque; bool layerOpaqueFlagSet; @@ -93,7 +94,7 @@ struct LayerSnapshot : public compositionengine::LayerFECompositionState { ui::Transform::RotationFlags fixedTransformHint; std::optional<ui::Transform::RotationFlags> transformHint; bool handleSkipScreenshotFlag = false; - int32_t frameRateSelectionPriority; + int32_t frameRateSelectionPriority = -1; LayerHierarchy::TraversalPath mirrorRootPath; uint32_t touchCropId; gui::Uid uid = gui::Uid::INVALID; @@ -145,7 +146,7 @@ struct LayerSnapshot : public compositionengine::LayerFECompositionState { bool hasInputInfo() const; FloatRect sourceBounds() const; Hwc2::IComposerClient::BlendMode getBlendMode(const RequestedLayerState& requested) const; - + friend std::ostream& operator<<(std::ostream& os, const LayerSnapshot& obj); void merge(const RequestedLayerState& requested, bool forceUpdate, bool displayChanges, bool forceFullDamage, uint32_t displayRotationFlags); }; diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp index 21f0a672b7..3a19d0b389 100644 --- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp +++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp @@ -29,6 +29,7 @@ #include "DisplayHardware/HWC2.h" #include "DisplayHardware/Hal.h" +#include "Layer.h" // eFrameRateSelectionPriority constants #include "LayerLog.h" #include "LayerSnapshotBuilder.h" #include "TimeStats/TimeStats.h" @@ -819,6 +820,13 @@ void LayerSnapshotBuilder::updateSnapshot(LayerSnapshot& snapshot, const Args& a : parentSnapshot.frameRate; } + if (forceUpdate || snapshot.clientChanges & layer_state_t::eFrameRateSelectionPriority) { + snapshot.frameRateSelectionPriority = + (requested.frameRateSelectionPriority == Layer::PRIORITY_UNSET) + ? parentSnapshot.frameRateSelectionPriority + : requested.frameRateSelectionPriority; + } + if (forceUpdate || snapshot.clientChanges & (layer_state_t::eBackgroundBlurRadiusChanged | layer_state_t::eBlurRegionsChanged | diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp index 5738262bf0..a4777d1148 100644 --- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp +++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp @@ -19,6 +19,7 @@ #undef LOG_TAG #define LOG_TAG "SurfaceFlinger" +#include <gui/TraceUtils.h> #include <log/log.h> #include <private/android_filesystem_config.h> #include <sys/types.h> @@ -126,6 +127,16 @@ RequestedLayerState::RequestedLayerState(const LayerCreationArgs& args) gameMode = gui::GameMode::Unsupported; requestedFrameRate = {}; cachingHint = gui::CachingHint::Enabled; + + if (name.length() > 77) { + std::string shortened; + shortened.append(name, 0, 36); + shortened.append("[...]"); + shortened.append(name, name.length() - 36); + debugName = std::move(shortened); + } else { + debugName = name; + } } void RequestedLayerState::merge(const ResolvedComposerState& resolvedComposerState) { @@ -370,6 +381,13 @@ std::string RequestedLayerState::getDebugString() const { return debug.str(); } +std::ostream& operator<<(std::ostream& out, const RequestedLayerState& obj) { + out << obj.debugName; + if (obj.relativeParentId != UNASSIGNED_LAYER_ID) out << " parent=" << obj.parentId; + if (!obj.handleAlive) out << " handleNotAlive"; + return out; +} + std::string RequestedLayerState::getDebugStringShort() const { return "[" + std::to_string(id) + "]" + name; } @@ -507,6 +525,51 @@ bool RequestedLayerState::willReleaseBufferOnLatch() const { return changes.test(Changes::Buffer) && !externalTexture; } +bool RequestedLayerState::backpressureEnabled() const { + return flags & layer_state_t::eEnableBackpressure; +} + +bool RequestedLayerState::isSimpleBufferUpdate(const layer_state_t& s) const { + static constexpr uint64_t requiredFlags = layer_state_t::eBufferChanged; + if ((s.what & requiredFlags) != requiredFlags) { + ATRACE_FORMAT_INSTANT("%s: false [missing required flags 0x%" PRIx64 "]", __func__, + (s.what | requiredFlags) & ~s.what); + return false; + } + + static constexpr uint64_t deniedFlags = layer_state_t::eProducerDisconnect | + layer_state_t::eLayerChanged | layer_state_t::eRelativeLayerChanged | + layer_state_t::eTransparentRegionChanged | layer_state_t::eFlagsChanged | + layer_state_t::eBlurRegionsChanged | layer_state_t::eLayerStackChanged | + layer_state_t::eAutoRefreshChanged | layer_state_t::eReparent; + if (s.what & deniedFlags) { + ATRACE_FORMAT_INSTANT("%s: false [has denied flags 0x%" PRIx64 "]", __func__, + s.what & deniedFlags); + return false; + } + + bool changedFlags = diff(s); + static constexpr auto deniedChanges = layer_state_t::ePositionChanged | + layer_state_t::eAlphaChanged | layer_state_t::eColorTransformChanged | + layer_state_t::eBackgroundColorChanged | layer_state_t::eMatrixChanged | + layer_state_t::eCornerRadiusChanged | layer_state_t::eBackgroundBlurRadiusChanged | + layer_state_t::eBufferTransformChanged | + layer_state_t::eTransformToDisplayInverseChanged | layer_state_t::eCropChanged | + layer_state_t::eDataspaceChanged | layer_state_t::eHdrMetadataChanged | + layer_state_t::eSidebandStreamChanged | layer_state_t::eColorSpaceAgnosticChanged | + layer_state_t::eShadowRadiusChanged | layer_state_t::eFixedTransformHintChanged | + layer_state_t::eTrustedOverlayChanged | layer_state_t::eStretchChanged | + layer_state_t::eBufferCropChanged | layer_state_t::eDestinationFrameChanged | + layer_state_t::eDimmingEnabledChanged | layer_state_t::eExtendedRangeBrightnessChanged; + if (changedFlags & deniedChanges) { + ATRACE_FORMAT_INSTANT("%s: false [has denied changes flags 0x%" PRIx64 "]", __func__, + s.what & deniedChanges); + return false; + } + + return true; +} + void RequestedLayerState::clearChanges() { what = 0; changes.clear(); diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.h b/services/surfaceflinger/FrontEnd/RequestedLayerState.h index 02e3bac18b..1c19d6d5fc 100644 --- a/services/surfaceflinger/FrontEnd/RequestedLayerState.h +++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.h @@ -74,6 +74,7 @@ struct RequestedLayerState : layer_state_t { Rect getBufferCrop() const; std::string getDebugString() const; std::string getDebugStringShort() const; + friend std::ostream& operator<<(std::ostream& os, const RequestedLayerState& obj); aidl::android::hardware::graphics::composer3::Composition getCompositionType() const; bool hasValidRelativeParent() const; bool hasInputInfo() const; @@ -82,6 +83,8 @@ struct RequestedLayerState : layer_state_t { bool hasReadyFrame() const; bool hasSidebandStreamFrame() const; bool willReleaseBufferOnLatch() const; + bool backpressureEnabled() const; + bool isSimpleBufferUpdate(const layer_state_t&) const; // Layer serial number. This gives layers an explicit ordering, so we // have a stable sort order when their layer stack and Z-order are @@ -116,6 +119,7 @@ struct RequestedLayerState : layer_state_t { uint32_t bgColorLayerId = UNASSIGNED_LAYER_ID; uint64_t barrierFrameNumber = 0; uint32_t barrierProducerId = 0; + std::string debugName; // book keeping states bool handleAlive = true; diff --git a/services/surfaceflinger/FrontEnd/TransactionHandler.cpp b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp index fa8eb3cf13..0d3c6ebd47 100644 --- a/services/surfaceflinger/FrontEnd/TransactionHandler.cpp +++ b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp @@ -33,7 +33,7 @@ void TransactionHandler::queueTransaction(TransactionState&& state) { ATRACE_INT("TransactionQueue", static_cast<int>(mPendingTransactionCount.load())); } -std::vector<TransactionState> TransactionHandler::flushTransactions() { +void TransactionHandler::collectTransactions() { while (!mLocklessTransactionQueue.isEmpty()) { auto maybeTransaction = mLocklessTransactionQueue.pop(); if (!maybeTransaction.has_value()) { @@ -42,7 +42,9 @@ std::vector<TransactionState> TransactionHandler::flushTransactions() { auto transaction = maybeTransaction.value(); mPendingTransactionQueues[transaction.applyToken].emplace(std::move(transaction)); } +} +std::vector<TransactionState> TransactionHandler::flushTransactions() { // Collect transaction that are ready to be applied. std::vector<TransactionState> transactions; TransactionFlushState flushState; diff --git a/services/surfaceflinger/FrontEnd/TransactionHandler.h b/services/surfaceflinger/FrontEnd/TransactionHandler.h index 865835f92d..04183bcdb8 100644 --- a/services/surfaceflinger/FrontEnd/TransactionHandler.h +++ b/services/surfaceflinger/FrontEnd/TransactionHandler.h @@ -58,6 +58,8 @@ public: using TransactionFilter = std::function<TransactionReadiness(const TransactionFlushState&)>; bool hasPendingTransactions(); + // Moves transactions from the lockless queue. + void collectTransactions(); std::vector<TransactionState> flushTransactions(); void addTransactionReadyFilter(TransactionFilter&&); void queueTransaction(TransactionState&&); diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 5a010e8af6..3a41f1599b 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -402,7 +402,7 @@ void Layer::updateTrustedPresentationState(const DisplayDevice* display, mLastComputedTrustedPresentationState = false; if (!leaveState) { - const auto outputLayer = findOutputLayerForDisplay(display); + const auto outputLayer = findOutputLayerForDisplay(display, snapshot->path); if (outputLayer != nullptr) { if (outputLayer->getState().coveredRegionExcludingDisplayOverlays) { Region coveredRegion = @@ -741,6 +741,11 @@ const char* Layer::getDebugName() const { aidl::android::hardware::graphics::composer3::Composition Layer::getCompositionType( const DisplayDevice& display) const { const auto outputLayer = findOutputLayerForDisplay(&display); + return getCompositionType(outputLayer); +} + +aidl::android::hardware::graphics::composer3::Composition Layer::getCompositionType( + const compositionengine::OutputLayer* outputLayer) const { if (outputLayer == nullptr) { return aidl::android::hardware::graphics::composer3::Composition::INVALID; } @@ -1611,7 +1616,7 @@ void Layer::miniDumpHeader(std::string& result) { result.append("\n"); } -void Layer::miniDump(std::string& result, const DisplayDevice& display) const { +void Layer::miniDumpLegacy(std::string& result, const DisplayDevice& display) const { const auto outputLayer = findOutputLayerForDisplay(&display); if (!outputLayer) { return; @@ -1662,6 +1667,41 @@ void Layer::miniDump(std::string& result, const DisplayDevice& display) const { result.append("\n"); } +void Layer::miniDump(std::string& result, const frontend::LayerSnapshot& snapshot, + const DisplayDevice& display) const { + const auto outputLayer = findOutputLayerForDisplay(&display, snapshot.path); + if (!outputLayer) { + return; + } + + StringAppendF(&result, " %s\n", snapshot.debugName.c_str()); + StringAppendF(&result, " %10zu | ", snapshot.globalZ); + StringAppendF(&result, " %10d | ", + snapshot.layerMetadata.getInt32(gui::METADATA_WINDOW_TYPE, 0)); + StringAppendF(&result, "%10s | ", toString(getCompositionType(outputLayer)).c_str()); + const auto& outputLayerState = outputLayer->getState(); + StringAppendF(&result, "%10s | ", toString(outputLayerState.bufferTransform).c_str()); + const Rect& frame = outputLayerState.displayFrame; + StringAppendF(&result, "%4d %4d %4d %4d | ", frame.left, frame.top, frame.right, frame.bottom); + const FloatRect& crop = outputLayerState.sourceCrop; + StringAppendF(&result, "%6.1f %6.1f %6.1f %6.1f | ", crop.left, crop.top, crop.right, + crop.bottom); + const auto frameRate = snapshot.frameRate; + if (frameRate.rate.isValid() || frameRate.type != FrameRateCompatibility::Default) { + StringAppendF(&result, "%s %15s %17s", to_string(frameRate.rate).c_str(), + ftl::enum_string(frameRate.type).c_str(), + ftl::enum_string(frameRate.seamlessness).c_str()); + } else { + result.append(41, ' '); + } + + const auto focused = isLayerFocusedBasedOnPriority(snapshot.frameRateSelectionPriority); + StringAppendF(&result, " [%s]\n", focused ? "*" : " "); + + result.append(kDumpTableRowLength, '-'); + result.append("\n"); +} + void Layer::dumpFrameStats(std::string& result) const { mFrameTracker.dumpStats(result); } @@ -2578,6 +2618,24 @@ compositionengine::OutputLayer* Layer::findOutputLayerForDisplay( return display->getCompositionDisplay()->getOutputLayerForLayer(layerFE); } +compositionengine::OutputLayer* Layer::findOutputLayerForDisplay( + const DisplayDevice* display, const frontend::LayerHierarchy::TraversalPath& path) const { + if (!display) return nullptr; + if (!mFlinger->mLayerLifecycleManagerEnabled) { + return display->getCompositionDisplay()->getOutputLayerForLayer( + getCompositionEngineLayerFE()); + } + sp<LayerFE> layerFE; + for (auto& [p, layer] : mLayerFEs) { + if (p == path) { + layerFE = layer; + } + } + + if (!layerFE) return nullptr; + return display->getCompositionDisplay()->getOutputLayerForLayer(layerFE); +} + Region Layer::getVisibleRegion(const DisplayDevice* display) const { const auto outputLayer = findOutputLayerForDisplay(display); return outputLayer ? outputLayer->getState().visibleRegion : Region(); diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 2fbbbdcb5c..5d77657415 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -343,6 +343,7 @@ public: virtual sp<LayerFE> getCompositionEngineLayerFE() const; virtual sp<LayerFE> copyCompositionEngineLayerFE() const; sp<LayerFE> getCompositionEngineLayerFE(const frontend::LayerHierarchy::TraversalPath&); + sp<LayerFE> getOrCreateCompositionEngineLayerFE(const frontend::LayerHierarchy::TraversalPath&); const frontend::LayerSnapshot* getLayerSnapshot() const; frontend::LayerSnapshot* editLayerSnapshot(); @@ -692,7 +693,8 @@ public: gui::LayerDebugInfo getLayerDebugInfo(const DisplayDevice*) const; - void miniDump(std::string& result, const DisplayDevice&) const; + void miniDumpLegacy(std::string& result, const DisplayDevice&) const; + void miniDump(std::string& result, const frontend::LayerSnapshot&, const DisplayDevice&) const; void dumpFrameStats(std::string& result) const; void dumpOffscreenDebugInfo(std::string& result) const; void clearFrameStats(); @@ -960,6 +962,8 @@ protected: void addZOrderRelative(const wp<Layer>& relative); void removeZOrderRelative(const wp<Layer>& relative); compositionengine::OutputLayer* findOutputLayerForDisplay(const DisplayDevice*) const; + compositionengine::OutputLayer* findOutputLayerForDisplay( + const DisplayDevice*, const frontend::LayerHierarchy::TraversalPath& path) const; bool usingRelativeZ(LayerVector::StateSet) const; virtual ui::Transform getInputTransform() const; @@ -1064,7 +1068,8 @@ private: aidl::android::hardware::graphics::composer3::Composition getCompositionType( const DisplayDevice&) const; - + aidl::android::hardware::graphics::composer3::Composition getCompositionType( + const compositionengine::OutputLayer*) const; /** * Returns an unsorted vector of all layers that are part of this tree. * That includes the current layer and all its descendants. diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp index f1fd6db0a0..607bec2e3f 100644 --- a/services/surfaceflinger/RefreshRateOverlay.cpp +++ b/services/surfaceflinger/RefreshRateOverlay.cpp @@ -148,7 +148,8 @@ auto RefreshRateOverlay::SevenSegmentDrawer::draw(int displayFps, int renderFps, LOG_ALWAYS_FATAL_IF(bufferStatus != OK, "RefreshRateOverlay: Buffer failed to allocate: %d", bufferStatus); - sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(bufferWidth, bufferHeight); + sk_sp<SkSurface> surface = SkSurfaces::Raster( + SkImageInfo::MakeN32Premul(bufferWidth, bufferHeight)); SkCanvas* canvas = surface->getCanvas(); canvas->setMatrix(canvasTransform); diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.h b/services/surfaceflinger/Scheduler/VsyncSchedule.h index 0757b5789d..47e92e107d 100644 --- a/services/surfaceflinger/Scheduler/VsyncSchedule.h +++ b/services/surfaceflinger/Scheduler/VsyncSchedule.h @@ -21,6 +21,7 @@ #include <string> #include <android-base/thread_annotations.h> +#include <ThreadContext.h> #include <ftl/enum.h> #include <ftl/optional.h> #include <ui/DisplayId.h> diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 8ffc3c3e21..c46da11a03 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -24,6 +24,7 @@ #include "SurfaceFlinger.h" +#include <aidl/android/hardware/power/Boost.h> #include <android-base/parseint.h> #include <android-base/properties.h> #include <android-base/stringprintf.h> @@ -34,7 +35,6 @@ #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h> #include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h> #include <android/hardware/configstore/1.1/types.h> -#include <android/hardware/power/Boost.h> #include <android/native_window.h> #include <android/os/IInputFlinger.h> #include <binder/IPCThreadState.h> @@ -326,6 +326,7 @@ int64_t SurfaceFlinger::dispSyncPresentTimeOffset; bool SurfaceFlinger::useHwcForRgbToYuv; bool SurfaceFlinger::hasSyncFramework; int64_t SurfaceFlinger::maxFrameBufferAcquiredBuffers; +int64_t SurfaceFlinger::minAcquiredBuffers = 1; uint32_t SurfaceFlinger::maxGraphicsWidth; uint32_t SurfaceFlinger::maxGraphicsHeight; bool SurfaceFlinger::useContextPriority; @@ -377,6 +378,7 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory, SkipInitializationTag) } SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipInitialization) { + ATRACE_CALL(); ALOGI("SurfaceFlinger is starting"); hasSyncFramework = running_without_sync_framework(true); @@ -386,6 +388,8 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipI useHwcForRgbToYuv = force_hwc_copy_for_virtual_displays(false); maxFrameBufferAcquiredBuffers = max_frame_buffer_acquired_buffers(2); + minAcquiredBuffers = + SurfaceFlingerProperties::min_acquired_buffers().value_or(minAcquiredBuffers); maxGraphicsWidth = std::max(max_graphics_width(0), 0); maxGraphicsHeight = std::max(max_graphics_height(0), 0); @@ -493,10 +497,6 @@ LatchUnsignaledConfig SurfaceFlinger::getLatchUnsignaledConfig() { return LatchUnsignaledConfig::AutoSingleLayer; } - if (base::GetBoolProperty("debug.sf.latch_unsignaled"s, false)) { - return LatchUnsignaledConfig::Always; - } - return LatchUnsignaledConfig::Disabled; } @@ -690,7 +690,7 @@ void SurfaceFlinger::bootFinished() { // wait patiently for the window manager death const String16 name("window"); - mWindowManager = defaultServiceManager()->getService(name); + mWindowManager = defaultServiceManager()->waitForService(name); if (mWindowManager != 0) { mWindowManager->linkToDeath(sp<IBinder::DeathRecipient>::fromExisting(this)); } @@ -704,7 +704,7 @@ void SurfaceFlinger::bootFinished() { LOG_EVENT_LONG(LOGTAG_SF_STOP_BOOTANIM, ns2ms(systemTime(SYSTEM_TIME_MONOTONIC))); - sp<IBinder> input(defaultServiceManager()->getService(String16("inputflinger"))); + sp<IBinder> input(defaultServiceManager()->waitForService(String16("inputflinger"))); static_cast<void>(mScheduler->schedule([=]() FTL_FAKE_GUARD(kMainThreadContext) { if (input == nullptr) { @@ -803,6 +803,7 @@ chooseRenderEngineTypeViaSysProp() { // Do not call property_set on main thread which will be blocked by init // Use StartPropertySetThread instead. void SurfaceFlinger::init() FTL_FAKE_GUARD(kMainThreadContext) { + ATRACE_CALL(); ALOGI( "SurfaceFlinger's main thread ready to run. " "Initializing graphics H/W..."); addTransactionReadyFilters(); @@ -1945,8 +1946,21 @@ status_t SurfaceFlinger::setDisplayBrightness(const sp<IBinder>& displayToken, float currentDimmingRatio = compositionDisplay->editState().sdrWhitePointNits / compositionDisplay->editState().displayBrightnessNits; - compositionDisplay->setDisplayBrightness(brightness.sdrWhitePointNits, - brightness.displayBrightnessNits); + static constexpr float kDimmingThreshold = 0.02f; + if (brightness.sdrWhitePointNits == 0.f || + abs(brightness.sdrWhitePointNits - brightness.displayBrightnessNits) / + brightness.sdrWhitePointNits >= + kDimmingThreshold) { + // to optimize, skip brightness setter if the brightness difference ratio + // is lower than threshold + compositionDisplay + ->setDisplayBrightness(brightness.sdrWhitePointNits, + brightness.displayBrightnessNits); + } else { + compositionDisplay->setDisplayBrightness(brightness.sdrWhitePointNits, + brightness.sdrWhitePointNits); + } + FTL_FAKE_GUARD(kMainThreadContext, display->stageBrightness(brightness.displayBrightness)); @@ -2020,7 +2034,7 @@ status_t SurfaceFlinger::removeHdrLayerInfoListener( } status_t SurfaceFlinger::notifyPowerBoost(int32_t boostId) { - using hardware::power::Boost; + using aidl::android::hardware::power::Boost; Boost powerBoost = static_cast<Boost>(boostId); if (powerBoost == Boost::INTERACTION) { @@ -2169,12 +2183,22 @@ void SurfaceFlinger::configure() FTL_FAKE_GUARD(kMainThreadContext) { } } -bool SurfaceFlinger::updateLayerSnapshotsLegacy(VsyncId vsyncId, frontend::Update& update, - bool transactionsFlushed, +bool SurfaceFlinger::updateLayerSnapshotsLegacy(VsyncId vsyncId, nsecs_t frameTimeNs, + bool flushTransactions, bool& outTransactionsAreEmpty) { ATRACE_CALL(); + frontend::Update update; + if (flushTransactions) { + update = flushLifecycleUpdates(); + if (mTransactionTracing) { + mTransactionTracing->addCommittedTransactions(ftl::to_underlying(vsyncId), frameTimeNs, + update, mFrontEndDisplayInfos, + mFrontEndDisplayInfosChanged); + } + } + bool needsTraversal = false; - if (transactionsFlushed) { + if (flushTransactions) { needsTraversal |= commitMirrorDisplays(vsyncId); needsTraversal |= commitCreatedLayers(vsyncId, update.layerCreatedStates); needsTraversal |= applyTransactions(update.transactions, vsyncId); @@ -2222,12 +2246,43 @@ void SurfaceFlinger::updateLayerHistory(const frontend::LayerSnapshot& snapshot) } } -bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, frontend::Update& update, - bool transactionsFlushed, bool& outTransactionsAreEmpty) { +bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, nsecs_t frameTimeNs, + bool flushTransactions, bool& outTransactionsAreEmpty) { using Changes = frontend::RequestedLayerState::Changes; ATRACE_CALL(); - { + frontend::Update update; + if (flushTransactions) { + ATRACE_NAME("TransactionHandler:flushTransactions"); + // Locking: + // 1. to prevent onHandleDestroyed from being called while the state lock is held, + // we must keep a copy of the transactions (specifically the composer + // states) around outside the scope of the lock. + // 2. Transactions and created layers do not share a lock. To prevent applying + // transactions with layers still in the createdLayer queue, collect the transactions + // before committing the created layers. + // 3. Transactions can only be flushed after adding layers, since the layer can be a newly + // created one + mTransactionHandler.collectTransactions(); + { + // TODO(b/238781169) lockless queue this and keep order. + std::scoped_lock<std::mutex> lock(mCreatedLayersLock); + update.layerCreatedStates = std::move(mCreatedLayers); + mCreatedLayers.clear(); + update.newLayers = std::move(mNewLayers); + mNewLayers.clear(); + update.layerCreationArgs = std::move(mNewLayerArgs); + mNewLayerArgs.clear(); + update.destroyedHandles = std::move(mDestroyedHandles); + mDestroyedHandles.clear(); + } + mLayerLifecycleManager.addLayers(std::move(update.newLayers)); + update.transactions = mTransactionHandler.flushTransactions(); + if (mTransactionTracing) { + mTransactionTracing->addCommittedTransactions(ftl::to_underlying(vsyncId), frameTimeNs, + update, mFrontEndDisplayInfos, + mFrontEndDisplayInfosChanged); + } mLayerLifecycleManager.applyTransactions(update.transactions); mLayerLifecycleManager.onHandlesDestroyed(update.destroyedHandles); for (auto& legacyLayer : update.layerCreatedStates) { @@ -2236,11 +2291,11 @@ bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, frontend::Update& upd mLegacyLayers[layer->sequence] = layer; } } - } - if (mLayerLifecycleManager.getGlobalChanges().test(Changes::Hierarchy)) { - ATRACE_NAME("LayerHierarchyBuilder:update"); - mLayerHierarchyBuilder.update(mLayerLifecycleManager.getLayers(), - mLayerLifecycleManager.getDestroyedLayers()); + if (mLayerLifecycleManager.getGlobalChanges().test(Changes::Hierarchy)) { + ATRACE_NAME("LayerHierarchyBuilder:update"); + mLayerHierarchyBuilder.update(mLayerLifecycleManager.getLayers(), + mLayerLifecycleManager.getDestroyedLayers()); + } } bool mustComposite = false; @@ -2414,25 +2469,16 @@ bool SurfaceFlinger::commit(const scheduler::FrameTarget& pacesetterFrameTarget) Fps::fromPeriodNsecs(vsyncPeriod.ns())); const bool flushTransactions = clearTransactionFlags(eTransactionFlushNeeded); - frontend::Update updates; - if (flushTransactions) { - updates = flushLifecycleUpdates(); - if (mTransactionTracing) { - mTransactionTracing - ->addCommittedTransactions(ftl::to_underlying(vsyncId), - pacesetterFrameTarget.frameBeginTime().ns(), - updates, mFrontEndDisplayInfos, - mFrontEndDisplayInfosChanged); - } - } bool transactionsAreEmpty; if (mLegacyFrontEndEnabled) { - mustComposite |= updateLayerSnapshotsLegacy(vsyncId, updates, flushTransactions, - transactionsAreEmpty); + mustComposite |= + updateLayerSnapshotsLegacy(vsyncId, pacesetterFrameTarget.frameBeginTime().ns(), + flushTransactions, transactionsAreEmpty); } if (mLayerLifecycleManagerEnabled) { mustComposite |= - updateLayerSnapshots(vsyncId, updates, flushTransactions, transactionsAreEmpty); + updateLayerSnapshots(vsyncId, pacesetterFrameTarget.frameBeginTime().ns(), + flushTransactions, transactionsAreEmpty); } if (transactionFlushNeeded()) { @@ -2859,31 +2905,55 @@ void SurfaceFlinger::postComposition(scheduler::FrameTargeter& pacesetterFrameTa for (auto& [compositionDisplay, listener] : hdrInfoListeners) { HdrLayerInfoReporter::HdrLayerInfo info; int32_t maxArea = 0; - mDrawingState.traverse([&, compositionDisplay = compositionDisplay](Layer* layer) { - const auto layerFe = layer->getCompositionEngineLayerFE(); - const frontend::LayerSnapshot& snapshot = *layer->getLayerSnapshot(); - if (snapshot.isVisible && - compositionDisplay->includesLayer(snapshot.outputFilter)) { - if (isHdrLayer(snapshot)) { - const auto* outputLayer = - compositionDisplay->getOutputLayerForLayer(layerFe); - if (outputLayer) { - const float desiredHdrSdrRatio = snapshot.desiredHdrSdrRatio <= 1.f - ? std::numeric_limits<float>::infinity() - : snapshot.desiredHdrSdrRatio; - info.mergeDesiredRatio(desiredHdrSdrRatio); - info.numberOfHdrLayers++; - const auto displayFrame = outputLayer->getState().displayFrame; - const int32_t area = displayFrame.width() * displayFrame.height(); - if (area > maxArea) { - maxArea = area; - info.maxW = displayFrame.width(); - info.maxH = displayFrame.height(); + auto updateInfoFn = + [&](const std::shared_ptr<compositionengine::Display>& compositionDisplay, + const frontend::LayerSnapshot& snapshot, const sp<LayerFE>& layerFe) { + if (snapshot.isVisible && + compositionDisplay->includesLayer(snapshot.outputFilter)) { + if (isHdrLayer(snapshot)) { + const auto* outputLayer = + compositionDisplay->getOutputLayerForLayer(layerFe); + if (outputLayer) { + const float desiredHdrSdrRatio = + snapshot.desiredHdrSdrRatio <= 1.f + ? std::numeric_limits<float>::infinity() + : snapshot.desiredHdrSdrRatio; + info.mergeDesiredRatio(desiredHdrSdrRatio); + info.numberOfHdrLayers++; + const auto displayFrame = outputLayer->getState().displayFrame; + const int32_t area = + displayFrame.width() * displayFrame.height(); + if (area > maxArea) { + maxArea = area; + info.maxW = displayFrame.width(); + info.maxH = displayFrame.height(); + } + } } } - } - } - }); + }; + + if (mLayerLifecycleManagerEnabled) { + mLayerSnapshotBuilder.forEachVisibleSnapshot( + [&, compositionDisplay = compositionDisplay]( + std::unique_ptr<frontend::LayerSnapshot>& snapshot) { + auto it = mLegacyLayers.find(snapshot->sequence); + LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(), + "Couldnt find layer object for %s", + snapshot->getDebugString().c_str()); + auto& legacyLayer = it->second; + sp<LayerFE> layerFe = + legacyLayer->getCompositionEngineLayerFE(snapshot->path); + + updateInfoFn(compositionDisplay, *snapshot, layerFe); + }); + } else { + mDrawingState.traverse([&, compositionDisplay = compositionDisplay](Layer* layer) { + const auto layerFe = layer->getCompositionEngineLayerFE(); + const frontend::LayerSnapshot& snapshot = *layer->getLayerSnapshot(); + updateInfoFn(compositionDisplay, snapshot, layerFe); + }); + } listener->dispatchHdrLayerInfo(info); } } @@ -4236,18 +4306,16 @@ TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyTimelin return TransactionReadiness::Ready; } -TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyBufferCheck( +TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyBufferCheckLegacy( const TransactionHandler::TransactionFlushState& flushState) { using TransactionReadiness = TransactionHandler::TransactionReadiness; auto ready = TransactionReadiness::Ready; - flushState.transaction->traverseStatesWithBuffersWhileTrue([&](const layer_state_t& s, - const std::shared_ptr< - renderengine:: - ExternalTexture>& - externalTexture) - -> bool { - sp<Layer> layer = LayerHandle::getLayer(s.surface); + flushState.transaction->traverseStatesWithBuffersWhileTrue([&](const ResolvedComposerState& + resolvedState) -> bool { + sp<Layer> layer = LayerHandle::getLayer(resolvedState.state.surface); + const auto& transaction = *flushState.transaction; + const auto& s = resolvedState.state; // check for barrier frames if (s.bufferData->hasBarrier) { // The current producerId is already a newer producer than the buffer that has a @@ -4255,7 +4323,7 @@ TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyBufferC // don't wait on the barrier since we know that's stale information. if (layer->getDrawingState().barrierProducerId > s.bufferData->producerId) { layer->callReleaseBufferCallback(s.bufferData->releaseBufferListener, - externalTexture->getBuffer(), + resolvedState.externalTexture->getBuffer(), s.bufferData->frameNumber, s.bufferData->acquireFence); // Delete the entire state at this point and not just release the buffer because @@ -4292,18 +4360,17 @@ TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyBufferC return TraverseBuffersReturnValues::STOP_TRAVERSAL; } - // ignore the acquire fence if LatchUnsignaledConfig::Always is set. - const bool checkAcquireFence = enableLatchUnsignaledConfig != LatchUnsignaledConfig::Always; const bool acquireFenceAvailable = s.bufferData && s.bufferData->flags.test(BufferData::BufferDataChange::fenceChanged) && s.bufferData->acquireFence; - const bool fenceSignaled = !checkAcquireFence || !acquireFenceAvailable || + const bool fenceSignaled = !acquireFenceAvailable || s.bufferData->acquireFence->getStatus() != Fence::Status::Unsignaled; if (!fenceSignaled) { // check fence status - const bool allowLatchUnsignaled = - shouldLatchUnsignaled(layer, s, transaction.states.size(), - flushState.firstTransaction); + const bool allowLatchUnsignaled = shouldLatchUnsignaled(s, transaction.states.size(), + flushState.firstTransaction) && + layer->isSimpleBufferUpdate(s); + if (allowLatchUnsignaled) { ATRACE_FORMAT("fence unsignaled try allowLatchUnsignaled %s", layer->getDebugName()); @@ -4328,15 +4395,118 @@ TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyBufferC return ready; } +TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyBufferCheck( + const TransactionHandler::TransactionFlushState& flushState) { + using TransactionReadiness = TransactionHandler::TransactionReadiness; + auto ready = TransactionReadiness::Ready; + flushState.transaction->traverseStatesWithBuffersWhileTrue([&](const ResolvedComposerState& + resolvedState) -> bool { + const frontend::RequestedLayerState* layer = + mLayerLifecycleManager.getLayerFromId(resolvedState.layerId); + const auto& transaction = *flushState.transaction; + const auto& s = resolvedState.state; + // check for barrier frames + if (s.bufferData->hasBarrier) { + // The current producerId is already a newer producer than the buffer that has a + // barrier. This means the incoming buffer is older and we can release it here. We + // don't wait on the barrier since we know that's stale information. + if (layer->barrierProducerId > s.bufferData->producerId) { + if (s.bufferData->releaseBufferListener) { + uint32_t currentMaxAcquiredBufferCount = + getMaxAcquiredBufferCountForCurrentRefreshRate(layer->ownerUid.val()); + ATRACE_FORMAT_INSTANT("callReleaseBufferCallback %s - %" PRIu64, + layer->name.c_str(), s.bufferData->frameNumber); + s.bufferData->releaseBufferListener + ->onReleaseBuffer({resolvedState.externalTexture->getBuffer()->getId(), + s.bufferData->frameNumber}, + s.bufferData->acquireFence + ? s.bufferData->acquireFence + : Fence::NO_FENCE, + currentMaxAcquiredBufferCount); + } + + // Delete the entire state at this point and not just release the buffer because + // everything associated with the Layer in this Transaction is now out of date. + ATRACE_FORMAT("DeleteStaleBuffer %s barrierProducerId:%d > %d", layer->name.c_str(), + layer->barrierProducerId, s.bufferData->producerId); + return TraverseBuffersReturnValues::DELETE_AND_CONTINUE_TRAVERSAL; + } + + if (layer->barrierFrameNumber < s.bufferData->barrierFrameNumber) { + const bool willApplyBarrierFrame = + flushState.bufferLayersReadyToPresent.contains(s.surface.get()) && + ((flushState.bufferLayersReadyToPresent.get(s.surface.get()) >= + s.bufferData->barrierFrameNumber)); + if (!willApplyBarrierFrame) { + ATRACE_FORMAT("NotReadyBarrier %s barrierFrameNumber:%" PRId64 " > %" PRId64, + layer->name.c_str(), layer->barrierFrameNumber, + s.bufferData->barrierFrameNumber); + ready = TransactionReadiness::NotReadyBarrier; + return TraverseBuffersReturnValues::STOP_TRAVERSAL; + } + } + } + + // If backpressure is enabled and we already have a buffer to commit, keep + // the transaction in the queue. + const bool hasPendingBuffer = + flushState.bufferLayersReadyToPresent.contains(s.surface.get()); + if (layer->backpressureEnabled() && hasPendingBuffer && transaction.isAutoTimestamp) { + ATRACE_FORMAT("hasPendingBuffer %s", layer->name.c_str()); + ready = TransactionReadiness::NotReady; + return TraverseBuffersReturnValues::STOP_TRAVERSAL; + } + + const bool acquireFenceAvailable = s.bufferData && + s.bufferData->flags.test(BufferData::BufferDataChange::fenceChanged) && + s.bufferData->acquireFence; + const bool fenceSignaled = !acquireFenceAvailable || + s.bufferData->acquireFence->getStatus() != Fence::Status::Unsignaled; + if (!fenceSignaled) { + // check fence status + const bool allowLatchUnsignaled = shouldLatchUnsignaled(s, transaction.states.size(), + flushState.firstTransaction) && + layer->isSimpleBufferUpdate(s); + if (allowLatchUnsignaled) { + ATRACE_FORMAT("fence unsignaled try allowLatchUnsignaled %s", layer->name.c_str()); + ready = TransactionReadiness::NotReadyUnsignaled; + } else { + ready = TransactionReadiness::NotReady; + auto& listener = s.bufferData->releaseBufferListener; + if (listener && + (flushState.queueProcessTime - transaction.postTime) > + std::chrono::nanoseconds(4s).count()) { + mTransactionHandler + .onTransactionQueueStalled(transaction.id, listener, + "Buffer processing hung up due to stuck " + "fence. Indicates GPU hang"); + } + ATRACE_FORMAT("fence unsignaled %s", layer->name.c_str()); + return TraverseBuffersReturnValues::STOP_TRAVERSAL; + } + } + return TraverseBuffersReturnValues::CONTINUE_TRAVERSAL; + }); + return ready; +} + void SurfaceFlinger::addTransactionReadyFilters() { mTransactionHandler.addTransactionReadyFilter( std::bind(&SurfaceFlinger::transactionReadyTimelineCheck, this, std::placeholders::_1)); - mTransactionHandler.addTransactionReadyFilter( - std::bind(&SurfaceFlinger::transactionReadyBufferCheck, this, std::placeholders::_1)); + if (mLayerLifecycleManagerEnabled) { + mTransactionHandler.addTransactionReadyFilter( + std::bind(&SurfaceFlinger::transactionReadyBufferCheck, this, + std::placeholders::_1)); + } else { + mTransactionHandler.addTransactionReadyFilter( + std::bind(&SurfaceFlinger::transactionReadyBufferCheckLegacy, this, + std::placeholders::_1)); + } } // For tests only bool SurfaceFlinger::flushTransactionQueues(VsyncId vsyncId) { + mTransactionHandler.collectTransactions(); std::vector<TransactionState> transactions = mTransactionHandler.flushTransactions(); return applyTransactions(transactions, vsyncId); } @@ -4389,18 +4559,13 @@ bool SurfaceFlinger::frameIsEarly(TimePoint expectedPresentTime, VsyncId vsyncId predictedPresentTime - expectedPresentTime >= earlyLatchVsyncThreshold; } -bool SurfaceFlinger::shouldLatchUnsignaled(const sp<Layer>& layer, const layer_state_t& state, - size_t numStates, bool firstTransaction) const { +bool SurfaceFlinger::shouldLatchUnsignaled(const layer_state_t& state, size_t numStates, + bool firstTransaction) const { if (enableLatchUnsignaledConfig == LatchUnsignaledConfig::Disabled) { ATRACE_FORMAT_INSTANT("%s: false (LatchUnsignaledConfig::Disabled)", __func__); return false; } - if (enableLatchUnsignaledConfig == LatchUnsignaledConfig::Always) { - ATRACE_FORMAT_INSTANT("%s: true (LatchUnsignaledConfig::Always)", __func__); - return true; - } - // We only want to latch unsignaled when a single layer is updated in this // transaction (i.e. not a blast sync transaction). if (numStates != 1) { @@ -4427,7 +4592,7 @@ bool SurfaceFlinger::shouldLatchUnsignaled(const sp<Layer>& layer, const layer_s } } - return layer->isSimpleBufferUpdate(state); + return true; } status_t SurfaceFlinger::setTransactionState( @@ -5557,7 +5722,7 @@ status_t SurfaceFlinger::doDump(int fd, const DumpArgs& args, bool asProto) { {"--edid"s, argsDumper(&SurfaceFlinger::dumpRawDisplayIdentificationData)}, {"--events"s, dumper(&SurfaceFlinger::dumpEvents)}, {"--frametimeline"s, argsDumper(&SurfaceFlinger::dumpFrameTimeline)}, - {"--hwclayers"s, dumper(&SurfaceFlinger::dumpHwcLayersMinidumpLocked)}, + {"--hwclayers"s, dumper(&SurfaceFlinger::dumpHwcLayersMinidumpLockedLegacy)}, {"--latency"s, argsDumper(&SurfaceFlinger::dumpStatsLocked)}, {"--latency-clear"s, argsDumper(&SurfaceFlinger::clearStatsLocked)}, {"--list"s, dumper(&SurfaceFlinger::listLayersLocked)}, @@ -5575,17 +5740,56 @@ status_t SurfaceFlinger::doDump(int fd, const DumpArgs& args, bool asProto) { // traversals, which can result in use-after-frees. std::string compositionLayers; mScheduler - ->schedule([&] { - StringAppendF(&compositionLayers, "Composition layers\n"); - mDrawingState.traverseInZOrder([&](Layer* layer) { - auto* compositionState = layer->getCompositionState(); - if (!compositionState || !compositionState->isVisible) return; - - android::base::StringAppendF(&compositionLayers, "* Layer %p (%s)\n", layer, - layer->getDebugName() ? layer->getDebugName() - : "<unknown>"); - compositionState->dump(compositionLayers); - }); + ->schedule([&]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD(kMainThreadContext) { + if (!mLayerLifecycleManagerEnabled) { + StringAppendF(&compositionLayers, "Composition layers\n"); + mDrawingState.traverseInZOrder([&](Layer* layer) { + auto* compositionState = layer->getCompositionState(); + if (!compositionState || !compositionState->isVisible) return; + android::base::StringAppendF(&compositionLayers, "* Layer %p (%s)\n", + layer, + layer->getDebugName() + ? layer->getDebugName() + : "<unknown>"); + compositionState->dump(compositionLayers); + }); + } else { + std::ostringstream out; + out << "\nComposition list\n"; + ui::LayerStack lastPrintedLayerStackHeader = ui::INVALID_LAYER_STACK; + mLayerSnapshotBuilder.forEachVisibleSnapshot( + [&](std::unique_ptr<frontend::LayerSnapshot>& snapshot) { + if (snapshot->hasSomethingToDraw()) { + if (lastPrintedLayerStackHeader != + snapshot->outputFilter.layerStack) { + lastPrintedLayerStackHeader = + snapshot->outputFilter.layerStack; + out << "LayerStack=" << lastPrintedLayerStackHeader.id + << "\n"; + } + out << " " << *snapshot << "\n"; + } + }); + + out << "\nInput list\n"; + lastPrintedLayerStackHeader = ui::INVALID_LAYER_STACK; + mLayerSnapshotBuilder.forEachInputSnapshot( + [&](const frontend::LayerSnapshot& snapshot) { + if (lastPrintedLayerStackHeader != + snapshot.outputFilter.layerStack) { + lastPrintedLayerStackHeader = + snapshot.outputFilter.layerStack; + out << "LayerStack=" << lastPrintedLayerStackHeader.id + << "\n"; + } + out << " " << snapshot << "\n"; + }); + + out << "\nLayer Hierarchy\n" + << mLayerHierarchyBuilder.getHierarchy() << "\n\n"; + compositionLayers = out.str(); + dumpHwcLayersMinidump(compositionLayers); + } }) .get(); @@ -5915,7 +6119,7 @@ void SurfaceFlinger::dumpOffscreenLayers(std::string& result) { result.append(future.get()); } -void SurfaceFlinger::dumpHwcLayersMinidumpLocked(std::string& result) const { +void SurfaceFlinger::dumpHwcLayersMinidumpLockedLegacy(std::string& result) const { for (const auto& [token, display] : mDisplays) { const auto displayId = HalDisplayId::tryCast(display->getId()); if (!displayId) { @@ -5927,7 +6131,33 @@ void SurfaceFlinger::dumpHwcLayersMinidumpLocked(std::string& result) const { Layer::miniDumpHeader(result); const DisplayDevice& ref = *display; - mDrawingState.traverseInZOrder([&](Layer* layer) { layer->miniDump(result, ref); }); + mDrawingState.traverseInZOrder([&](Layer* layer) { layer->miniDumpLegacy(result, ref); }); + result.append("\n"); + } +} + +void SurfaceFlinger::dumpHwcLayersMinidump(std::string& result) const { + for (const auto& [token, display] : mDisplays) { + const auto displayId = HalDisplayId::tryCast(display->getId()); + if (!displayId) { + continue; + } + + StringAppendF(&result, "Display %s (%s) HWC layers:\n", to_string(*displayId).c_str(), + displayId == mActiveDisplayId ? "active" : "inactive"); + Layer::miniDumpHeader(result); + + const DisplayDevice& ref = *display; + mLayerSnapshotBuilder.forEachVisibleSnapshot([&](const frontend::LayerSnapshot& snapshot) { + if (!snapshot.hasSomethingToDraw() || + ref.getLayerStack() != snapshot.outputFilter.layerStack) { + return; + } + auto it = mLegacyLayers.find(snapshot.sequence); + LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(), "Couldnt find layer object for %s", + snapshot.getDebugString().c_str()); + it->second->miniDump(result, snapshot, ref); + }); result.append("\n"); } } @@ -5976,7 +6206,10 @@ void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, const std::string& comp * Dump the visible layer list */ colorizer.bold(result); - StringAppendF(&result, "Visible layers (count = %zu)\n", mNumLayers.load()); + StringAppendF(&result, "SurfaceFlinger New Frontend Enabled:%s\n", + mLayerLifecycleManagerEnabled ? "true" : "false"); + StringAppendF(&result, "Active Layers - layers with client handles (count = %zu)\n", + mNumLayers.load()); colorizer.reset(result); result.append(compositionLayers); @@ -6049,7 +6282,9 @@ void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, const std::string& comp } result.push_back('\n'); - dumpHwcLayersMinidumpLocked(result); + if (mLegacyFrontEndEnabled) { + dumpHwcLayersMinidumpLockedLegacy(result); + } { DumpArgs plannerArgs; @@ -7798,7 +8033,7 @@ int SurfaceFlinger::calculateMaxAcquiredBufferCount(Fps refreshRate, if (presentLatency.count() % refreshRate.getPeriodNsecs()) { pipelineDepth++; } - return std::max(1ll, pipelineDepth - 1); + return std::max(minAcquiredBuffers, static_cast<int64_t>(pipelineDepth - 1)); } status_t SurfaceFlinger::getMaxAcquiredBufferCount(int* buffers) const { @@ -8272,6 +8507,7 @@ frontend::Update SurfaceFlinger::flushLifecycleUpdates() { // 2. Transactions and created layers do not share a lock. To prevent applying // transactions with layers still in the createdLayer queue, flush the transactions // before committing the created layers. + mTransactionHandler.collectTransactions(); update.transactions = mTransactionHandler.flushTransactions(); { // TODO(b/238781169) lockless queue this and keep order. diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index e27d21fc48..d97a7478dd 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -227,6 +227,10 @@ public: // FramebufferSurface static int64_t maxFrameBufferAcquiredBuffers; + // Controls the minimum acquired buffers SurfaceFlinger will suggest via + // ISurfaceComposer.getMaxAcquiredBufferCount(). + static int64_t minAcquiredBuffers; + // Controls the maximum width and height in pixels that the graphics pipeline can support for // GPU fallback composition. For example, 8k devices with 4k GPUs, or 4k devices with 2k GPUs. static uint32_t maxGraphicsWidth; @@ -714,10 +718,9 @@ private: compositionengine::CompositionRefreshArgs& refreshArgs, bool cursorOnly); void moveSnapshotsFromCompositionArgs(compositionengine::CompositionRefreshArgs& refreshArgs, const std::vector<std::pair<Layer*, LayerFE*>>& layers); - bool updateLayerSnapshotsLegacy(VsyncId vsyncId, frontend::Update& update, - bool transactionsFlushed, bool& out) - REQUIRES(kMainThreadContext); - bool updateLayerSnapshots(VsyncId vsyncId, frontend::Update& update, bool transactionsFlushed, + bool updateLayerSnapshotsLegacy(VsyncId vsyncId, nsecs_t frameTimeNs, bool transactionsFlushed, + bool& out) REQUIRES(kMainThreadContext); + bool updateLayerSnapshots(VsyncId vsyncId, nsecs_t frameTimeNs, bool transactionsFlushed, bool& out) REQUIRES(kMainThreadContext); void updateLayerHistory(const frontend::LayerSnapshot& snapshot); frontend::Update flushLifecycleUpdates() REQUIRES(kMainThreadContext); @@ -761,6 +764,9 @@ private: TransactionHandler::TransactionReadiness transactionReadyTimelineCheck( const TransactionHandler::TransactionFlushState& flushState) REQUIRES(kMainThreadContext); + TransactionHandler::TransactionReadiness transactionReadyBufferCheckLegacy( + const TransactionHandler::TransactionFlushState& flushState) + REQUIRES(kMainThreadContext); TransactionHandler::TransactionReadiness transactionReadyBufferCheck( const TransactionHandler::TransactionFlushState& flushState) REQUIRES(kMainThreadContext); @@ -785,8 +791,7 @@ private: void commitOffscreenLayers(); static LatchUnsignaledConfig getLatchUnsignaledConfig(); - bool shouldLatchUnsignaled(const sp<Layer>& layer, const layer_state_t&, size_t numStates, - bool firstTransaction) const; + bool shouldLatchUnsignaled(const layer_state_t&, size_t numStates, bool firstTransaction) const; bool applyTransactionsLocked(std::vector<TransactionState>& transactions, VsyncId) REQUIRES(mStateLock); uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock); @@ -1044,7 +1049,8 @@ private: */ void dumpAllLocked(const DumpArgs& args, const std::string& compositionLayers, std::string& result) const REQUIRES(mStateLock); - void dumpHwcLayersMinidumpLocked(std::string& result) const REQUIRES(mStateLock); + void dumpHwcLayersMinidump(std::string& result) const REQUIRES(mStateLock, kMainThreadContext); + void dumpHwcLayersMinidumpLockedLegacy(std::string& result) const REQUIRES(mStateLock); void appendSfConfigString(std::string& result) const; void listLayersLocked(std::string& result) const; diff --git a/services/surfaceflinger/TimeStats/Android.bp b/services/surfaceflinger/TimeStats/Android.bp index 4686eed54c..c3141be9db 100644 --- a/services/surfaceflinger/TimeStats/Android.bp +++ b/services/surfaceflinger/TimeStats/Android.bp @@ -7,14 +7,9 @@ package { default_applicable_licenses: ["frameworks_native_license"], } -cc_library { - name: "libtimestats", - srcs: [ - "TimeStats.cpp", - ], - header_libs: [ - "libscheduler_headers", - ], +cc_defaults { + name: "libtimestats_deps", + shared_libs: [ "android.hardware.graphics.composer@2.4", "libbase", @@ -22,17 +17,34 @@ cc_library { "liblog", "libprotobuf-cpp-lite", "libtimestats_atoms_proto", - "libtimestats_proto", "libui", "libutils", ], + + static_libs: [ + "libtimestats_proto", + ], + + export_static_lib_headers: [ + "libtimestats_proto", + ], +} + +cc_library { + name: "libtimestats", + defaults: [ + "libtimestats_deps", + ], + srcs: [ + "TimeStats.cpp", + ], + header_libs: [ + "libscheduler_headers", + ], export_include_dirs: ["."], export_header_lib_headers: [ "libscheduler_headers", ], - export_shared_lib_headers: [ - "libtimestats_proto", - ], cppflags: [ "-Wall", "-Werror", diff --git a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp index cf1ca65972..cbbcb91469 100644 --- a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp +++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp @@ -115,7 +115,7 @@ std::string TimeStatsHelper::TimeStatsLayer::toString() const { StringAppendF(&result, "badDesiredPresentFrames = %d\n", badDesiredPresentFrames); result.append("Jank payload for this layer:\n"); result.append(jankPayload.toString()); - result.append("SetFrateRate vote for this layer:\n"); + result.append("SetFrameRate vote for this layer:\n"); result.append(setFrameRateVote.toString()); const auto iter = deltas.find("present2present"); if (iter != deltas.end()) { diff --git a/services/surfaceflinger/TransactionState.h b/services/surfaceflinger/TransactionState.h index 7132a59016..31cd2d7eb6 100644 --- a/services/surfaceflinger/TransactionState.h +++ b/services/surfaceflinger/TransactionState.h @@ -89,7 +89,7 @@ struct TransactionState { void traverseStatesWithBuffersWhileTrue(Visitor&& visitor) { for (auto state = states.begin(); state != states.end();) { if (state->state.hasBufferChanges() && state->externalTexture && state->state.surface) { - int result = visitor(state->state, state->externalTexture); + int result = visitor(*state); if (result == STOP_TRAVERSAL) return; if (result == DELETE_AND_CONTINUE_TRAVERSAL) { state = states.erase(state); diff --git a/services/surfaceflinger/fuzzer/Android.bp b/services/surfaceflinger/fuzzer/Android.bp index f76a8d762a..0f9060dbdb 100644 --- a/services/surfaceflinger/fuzzer/Android.bp +++ b/services/surfaceflinger/fuzzer/Android.bp @@ -138,3 +138,18 @@ cc_fuzz { "surfaceflinger_frametracer_fuzzer.cpp", ], } + +cc_fuzz { + name: "surfaceflinger_service_fuzzer", + defaults: [ + "surfaceflinger_fuzz_defaults", + "service_fuzzer_defaults", + "fuzzer_disable_leaks", + ], + srcs: [ + "surfaceflinger_service_fuzzer.cpp", + ], + fuzz_config: { + triage_assignee: "waghpawan@google.com", + }, +} diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp index 80943b5b63..b2d4131b5d 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp +++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp @@ -24,7 +24,6 @@ namespace android::fuzz { static constexpr LatchUnsignaledConfig kLatchUnsignaledConfig[] = { - LatchUnsignaledConfig::Always, LatchUnsignaledConfig::AutoSingleLayer, LatchUnsignaledConfig::Disabled, }; diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_service_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_service_fuzzer.cpp new file mode 100644 index 0000000000..849a896ce2 --- /dev/null +++ b/services/surfaceflinger/fuzzer/surfaceflinger_service_fuzzer.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <fuzzbinder/libbinder_driver.h> + +#include "SurfaceFlinger.h" +#include "SurfaceFlingerDefaultFactory.h" + +using namespace android; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + DefaultFactory factory; + sp<SurfaceFlinger> flinger = sp<SurfaceFlinger>::make(factory); + flinger->init(); + + sp<SurfaceComposerAIDL> composerAIDL = sp<SurfaceComposerAIDL>::make(flinger); + fuzzService({flinger, composerAIDL}, FuzzedDataProvider(data, size)); + return 0; +} diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop index 689f51ad5b..5cac086c21 100644 --- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop +++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop @@ -472,6 +472,16 @@ prop { prop_name: "ro.surface_flinger.ignore_hdr_camera_layers" } +# Controls the minimum acquired buffers SurfaceFlinger will suggest via +# ISurfaceComposer.getMaxAcquiredBufferCount(). +prop { + api_name: "min_acquired_buffers" + type: Long + scope: Public + access: Readonly + prop_name: "ro.surface_flinger.min_acquired_buffers" +} + # When enabled, SurfaceFlinger will attempt to clear the per-layer HAL buffer cache slots for # buffers when they are evicted from the app cache by using additional setLayerBuffer commands. # Ideally, this behavior would always be enabled to reduce graphics memory consumption. However, @@ -484,4 +494,3 @@ prop { access: Readonly prop_name: "ro.surface_flinger.clear_slots_with_set_layer_buffer" } - diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt index 9660ff3de6..ba88acc2fb 100644 --- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt +++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt @@ -97,6 +97,11 @@ props { prop_name: "ro.surface_flinger.max_virtual_display_dimension" } prop { + api_name: "min_acquired_buffers" + type: Long + prop_name: "ro.surface_flinger.min_acquired_buffers" + } + prop { api_name: "present_time_offset_from_vsync_ns" type: Long prop_name: "ro.surface_flinger.present_time_offset_from_vsync_ns" diff --git a/services/surfaceflinger/tests/IPC_test.cpp b/services/surfaceflinger/tests/IPC_test.cpp index 40a5d5757d..18bd3b92d2 100644 --- a/services/surfaceflinger/tests/IPC_test.cpp +++ b/services/surfaceflinger/tests/IPC_test.cpp @@ -289,7 +289,7 @@ sp<IIPCTest> IPCTest::initRemoteService() { IPCThreadState::self()->joinThreadPool(); [&]() { exit(0); }(); } - sp<IBinder> binder = defaultServiceManager()->getService(serviceName); + sp<IBinder> binder = defaultServiceManager()->waitForService(serviceName); remote = interface_cast<IIPCTest>(binder); remote->setDeathToken(mDeathRecipient); } diff --git a/services/surfaceflinger/tests/unittests/ActiveDisplayRotationFlagsTest.cpp b/services/surfaceflinger/tests/unittests/ActiveDisplayRotationFlagsTest.cpp index 7077523fc3..f1bb231f26 100644 --- a/services/surfaceflinger/tests/unittests/ActiveDisplayRotationFlagsTest.cpp +++ b/services/surfaceflinger/tests/unittests/ActiveDisplayRotationFlagsTest.cpp @@ -85,7 +85,7 @@ TEST_F(ActiveDisplayRotationFlagsTest, rotate90) { ASSERT_EQ(ui::Transform::ROT_90, SurfaceFlinger::getActiveDisplayRotationFlags()); } -TEST_F(ActiveDisplayRotationFlagsTest, rotate90_inactive) { +TEST_F(ActiveDisplayRotationFlagsTest, rotate90inactive) { auto displayToken = mOuterDisplay->getDisplayToken().promote(); mFlinger.mutableDrawingState().displays.editValueFor(displayToken).orientation = ui::ROTATION_0; mFlinger.mutableCurrentState().displays.editValueFor(displayToken).orientation = @@ -95,7 +95,7 @@ TEST_F(ActiveDisplayRotationFlagsTest, rotate90_inactive) { ASSERT_EQ(ui::Transform::ROT_0, SurfaceFlinger::getActiveDisplayRotationFlags()); } -TEST_F(ActiveDisplayRotationFlagsTest, rotateBoth_innerActive) { +TEST_F(ActiveDisplayRotationFlagsTest, rotateBothInnerActive) { auto displayToken = mInnerDisplay->getDisplayToken().promote(); mFlinger.mutableDrawingState().displays.editValueFor(displayToken).orientation = ui::ROTATION_0; mFlinger.mutableCurrentState().displays.editValueFor(displayToken).orientation = @@ -110,7 +110,7 @@ TEST_F(ActiveDisplayRotationFlagsTest, rotateBoth_innerActive) { ASSERT_EQ(ui::Transform::ROT_180, SurfaceFlinger::getActiveDisplayRotationFlags()); } -TEST_F(ActiveDisplayRotationFlagsTest, rotateBoth_outerActive) { +TEST_F(ActiveDisplayRotationFlagsTest, rotateBothOuterActive) { mFlinger.mutableActiveDisplayId() = kOuterDisplayId; auto displayToken = mInnerDisplay->getDisplayToken().promote(); mFlinger.mutableDrawingState().displays.editValueFor(displayToken).orientation = ui::ROTATION_0; diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp index 86af303744..fa5fa956ed 100644 --- a/services/surfaceflinger/tests/unittests/Android.bp +++ b/services/surfaceflinger/tests/unittests/Android.bp @@ -161,7 +161,7 @@ cc_defaults { "android.hardware.power@1.1", "android.hardware.power@1.2", "android.hardware.power@1.3", - "android.hardware.power-V4-cpp", + "android.hardware.power-V4-ndk", "libaidlcommonsupport", "libcompositionengine_mocks", "libcompositionengine", diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp index d26ef3c821..8911430790 100644 --- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp +++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp @@ -1198,7 +1198,7 @@ TEST_F(FrameTimelineTest, traceDisplayFrame_predictionExpiredDoesNotTraceExpecte TEST_F(FrameTimelineTest, traceSurfaceFrame_emitsValidTracePacket) { auto tracingSession = getTracingSessionForTest(); // Layer specific increment - EXPECT_CALL(*mTimeStats, incrementJankyFrames(_)); + EXPECT_CALL(*mTimeStats, incrementJankyFrames(_)).Times(2); auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); @@ -1234,8 +1234,8 @@ TEST_F(FrameTimelineTest, traceSurfaceFrame_emitsValidTracePacket) { auto protoDroppedSurfaceFrameActualStart = createProtoActualSurfaceFrameStart(traceCookie + 2, surfaceFrameToken, displayFrameToken1, sPidOne, sLayerNameOne, - FrameTimelineEvent::PRESENT_DROPPED, false, false, - FrameTimelineEvent::JANK_NONE, + FrameTimelineEvent::PRESENT_DROPPED, true, false, + FrameTimelineEvent::JANK_DROPPED, FrameTimelineEvent::PREDICTION_VALID, true); auto protoDroppedSurfaceFrameActualEnd = createProtoFrameEnd(traceCookie + 2); @@ -1470,7 +1470,7 @@ TEST_F(FrameTimelineTest, traceSurfaceFrame_predictionExpiredDroppedFramesTraced createProtoActualSurfaceFrameStart(traceCookie + 1, surfaceFrameToken, displayFrameToken, sPidOne, sLayerNameOne, FrameTimelineEvent::PRESENT_DROPPED, false, false, - FrameTimelineEvent::JANK_NONE, + FrameTimelineEvent::JANK_DROPPED, FrameTimelineEvent::PREDICTION_EXPIRED, true); auto protoActualSurfaceFrameEnd = createProtoFrameEnd(traceCookie + 1); diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h index 43011863ff..3234483f14 100644 --- a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h +++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h @@ -308,6 +308,17 @@ protected: mLifecycleManager.applyTransactions(transactions); } + void setFrameRateSelectionPriority(uint32_t id, int32_t priority) { + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + + transactions.back().states.front().state.what = layer_state_t::eFrameRateSelectionPriority; + transactions.back().states.front().layerId = id; + transactions.back().states.front().state.frameRateSelectionPriority = priority; + mLifecycleManager.applyTransactions(transactions); + } + LayerLifecycleManager mLifecycleManager; }; diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp index 5da893ee62..d6c4b7229e 100644 --- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp @@ -20,6 +20,7 @@ #include "FrontEnd/LayerHierarchy.h" #include "FrontEnd/LayerLifecycleManager.h" #include "FrontEnd/LayerSnapshotBuilder.h" +#include "Layer.h" #include "LayerHierarchyTest.h" #define UPDATE_AND_VERIFY(BUILDER, ...) \ @@ -467,4 +468,48 @@ TEST_F(LayerSnapshotTest, cleanUpUnreachableSnapshotsAfterLayerDestruction) { EXPECT_LE(startingNumSnapshots - 2, mSnapshotBuilder.getSnapshots().size()); } +TEST_F(LayerSnapshotTest, snashotContainsMetadataFromLayerCreationArgs) { + LayerCreationArgs args(std::make_optional<uint32_t>(200)); + args.name = "testlayer"; + args.addToRoot = true; + args.metadata.setInt32(42, 24); + + std::vector<std::unique_ptr<RequestedLayerState>> layers; + layers.emplace_back(std::make_unique<RequestedLayerState>(args)); + EXPECT_TRUE(layers.back()->metadata.has(42)); + EXPECT_EQ(layers.back()->metadata.getInt32(42, 0), 24); + mLifecycleManager.addLayers(std::move(layers)); + + std::vector<uint32_t> expected = STARTING_ZORDER; + expected.push_back(200); + UPDATE_AND_VERIFY(mSnapshotBuilder, expected); + + EXPECT_TRUE(mSnapshotBuilder.getSnapshot(200)->layerMetadata.has(42)); + EXPECT_EQ(mSnapshotBuilder.getSnapshot(200)->layerMetadata.getInt32(42, 0), 24); +} + +TEST_F(LayerSnapshotTest, frameRateSelectionPriorityPassedToChildLayers) { + setFrameRateSelectionPriority(11, 1); + + setFrameRateSelectionPriority(12, 2); + + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_EQ(getSnapshot({.id = 1})->frameRateSelectionPriority, Layer::PRIORITY_UNSET); + EXPECT_EQ(getSnapshot({.id = 11})->frameRateSelectionPriority, 1); + EXPECT_EQ(getSnapshot({.id = 12})->frameRateSelectionPriority, 2); + EXPECT_EQ(getSnapshot({.id = 122})->frameRateSelectionPriority, 2); + EXPECT_EQ(getSnapshot({.id = 1221})->frameRateSelectionPriority, 2); + + // reparent and verify the child gets the new parent's framerate selection priority + reparentLayer(122, 11); + + std::vector<uint32_t> expected = {1, 11, 111, 122, 1221, 12, 121, 13, 2}; + UPDATE_AND_VERIFY(mSnapshotBuilder, expected); + EXPECT_EQ(getSnapshot({.id = 1})->frameRateSelectionPriority, Layer::PRIORITY_UNSET); + EXPECT_EQ(getSnapshot({.id = 11})->frameRateSelectionPriority, 1); + EXPECT_EQ(getSnapshot({.id = 12})->frameRateSelectionPriority, 2); + EXPECT_EQ(getSnapshot({.id = 122})->frameRateSelectionPriority, 1); + EXPECT_EQ(getSnapshot({.id = 1221})->frameRateSelectionPriority, 1); +} + } // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp index 0d66d59f26..85f66f4ed0 100644 --- a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp +++ b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp @@ -18,10 +18,10 @@ #define LOG_TAG "PowerAdvisorTest" #include <DisplayHardware/PowerAdvisor.h> -#include <compositionengine/Display.h> -#include <ftl/fake_guard.h> +#include <binder/Status.h> #include <gmock/gmock.h> #include <gtest/gtest.h> +#include <powermanager/PowerHalWrapper.h> #include <ui/DisplayId.h> #include <chrono> #include "TestableSurfaceFlinger.h" @@ -50,7 +50,7 @@ protected: TestableSurfaceFlinger mFlinger; std::unique_ptr<PowerAdvisor> mPowerAdvisor; MockPowerHalController* mMockPowerHalController; - sp<MockIPowerHintSession> mMockPowerHintSession; + std::shared_ptr<MockIPowerHintSession> mMockPowerHintSession; }; void PowerAdvisorTest::SetUp() { @@ -64,13 +64,14 @@ void PowerAdvisorTest::SetUp() { void PowerAdvisorTest::startPowerHintSession() { const std::vector<int32_t> threadIds = {1, 2, 3}; - mMockPowerHintSession = android::sp<NiceMock<MockIPowerHintSession>>::make(); + mMockPowerHintSession = ndk::SharedRefBase::make<NiceMock<MockIPowerHintSession>>(); ON_CALL(*mMockPowerHalController, createHintSession) - .WillByDefault( - Return(HalResult<sp<IPowerHintSession>>::fromStatus(binder::Status::ok(), - mMockPowerHintSession))); + .WillByDefault(Return(HalResult<std::shared_ptr<IPowerHintSession>>:: + fromStatus(binder::Status::ok(), mMockPowerHintSession))); mPowerAdvisor->enablePowerHintSession(true); mPowerAdvisor->startPowerHintSession(threadIds); + ON_CALL(*mMockPowerHintSession, updateTargetWorkDuration) + .WillByDefault(Return(testing::ByMove(ndk::ScopedAStatus::ok()))); } void PowerAdvisorTest::setExpectedTiming(Duration totalFrameTargetDuration, @@ -123,8 +124,8 @@ TEST_F(PowerAdvisorTest, hintSessionUseHwcDisplay) { EXPECT_CALL(*mMockPowerHintSession, reportActualWorkDuration(ElementsAre( Field(&WorkDuration::durationNanos, Eq(expectedDuration.ns()))))) - .Times(1); - + .Times(1) + .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::ok()))); fakeBasicFrameTiming(startTime, vsyncPeriod); setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod); mPowerAdvisor->setDisplays(displayIds); @@ -163,7 +164,8 @@ TEST_F(PowerAdvisorTest, hintSessionSubtractsHwcFenceTime) { EXPECT_CALL(*mMockPowerHintSession, reportActualWorkDuration(ElementsAre( Field(&WorkDuration::durationNanos, Eq(expectedDuration.ns()))))) - .Times(1); + .Times(1) + .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::ok()))); fakeBasicFrameTiming(startTime, vsyncPeriod); setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod); @@ -205,7 +207,8 @@ TEST_F(PowerAdvisorTest, hintSessionUsingSecondaryVirtualDisplays) { EXPECT_CALL(*mMockPowerHintSession, reportActualWorkDuration(ElementsAre( Field(&WorkDuration::durationNanos, Eq(expectedDuration.ns()))))) - .Times(1); + .Times(1) + .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::ok()))); fakeBasicFrameTiming(startTime, vsyncPeriod); setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod); diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp index 682c998542..fab3c0e887 100644 --- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp +++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp @@ -249,6 +249,11 @@ TEST_F(SchedulerTest, calculateMaxAcquiredBufferCount) { EXPECT_EQ(2, mFlinger.calculateMaxAcquiredBufferCount(60_Hz, 40ms)); EXPECT_EQ(1, mFlinger.calculateMaxAcquiredBufferCount(60_Hz, 10ms)); + + const auto savedMinAcquiredBuffers = mFlinger.mutableMinAcquiredBuffers(); + mFlinger.mutableMinAcquiredBuffers() = 2; + EXPECT_EQ(2, mFlinger.calculateMaxAcquiredBufferCount(60_Hz, 10ms)); + mFlinger.mutableMinAcquiredBuffers() = savedMinAcquiredBuffers; } MATCHER(Is120Hz, "") { diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp index 4e9f293dc3..22b72f98e5 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp @@ -23,12 +23,12 @@ #include "DisplayTransactionTestHelpers.h" #include "FakeDisplayInjector.h" -#include <android/hardware/power/Boost.h> +#include <aidl/android/hardware/power/Boost.h> namespace android { namespace { -using android::hardware::power::Boost; +using aidl::android::hardware::power::Boost; TEST_F(DisplayTransactionTest, notifyPowerBoostNotifiesTouchEvent) { using namespace std::chrono_literals; diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index 833984f995..156c40a721 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -622,6 +622,8 @@ public: return SurfaceFlinger::sActiveDisplayRotationFlags; } + auto& mutableMinAcquiredBuffers() { return SurfaceFlinger::minAcquiredBuffers; } + auto fromHandle(const sp<IBinder>& handle) { return LayerHandle::getLayer(handle); } ~TestableSurfaceFlinger() { diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp index afb8efbfff..644b8c70c6 100644 --- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp @@ -15,7 +15,7 @@ */ #undef LOG_TAG -#define LOG_TAG "CompositionTest" +#define LOG_TAG "TransactionApplicationTest" #include <compositionengine/Display.h> #include <compositionengine/mock/DisplaySurface.h> @@ -855,233 +855,13 @@ TEST_F(LatchUnsignaledDisabledTest, Flush_KeepInTheUnsignaledTheQueue) { kExpectedTransactionsPending); } -class LatchUnsignaledAlwaysTest : public LatchUnsignaledTest { -public: - void SetUp() override { - LatchUnsignaledTest::SetUp(); - SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Always; - } -}; - -TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesSignaledFromTheQueue) { - const sp<IBinder> kApplyToken = - IInterface::asBinder(TransactionCompletedListener::getIInstance()); - const auto kLayerId = 1; - const auto kExpectedTransactionsPending = 0u; - - const auto signaledTransaction = - createTransactionInfo(kApplyToken, - {createComposerState(kLayerId, fence(Fence::Status::Signaled), - layer_state_t::eBufferChanged)}); - setTransactionStates({signaledTransaction}, kExpectedTransactionsPending); -} - -TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesFromTheQueue) { - const sp<IBinder> kApplyToken = - IInterface::asBinder(TransactionCompletedListener::getIInstance()); - const auto kLayerId = 1; - const auto kExpectedTransactionsPending = 0u; - - const auto unsignaledTransaction = - createTransactionInfo(kApplyToken, - {createComposerState(kLayerId, fence(Fence::Status::Unsignaled), - layer_state_t::eBufferChanged)}); - setTransactionStates({unsignaledTransaction}, kExpectedTransactionsPending); -} - -TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesFromTheQueueSameLayerId) { - const sp<IBinder> kApplyToken = - IInterface::asBinder(TransactionCompletedListener::getIInstance()); - const auto kLayerId = 1; - const auto kExpectedTransactionsPending = 0u; - - const auto mixedTransaction = - createTransactionInfo(kApplyToken, - {createComposerState(kLayerId, fence(Fence::Status::Unsignaled), - layer_state_t::eBufferChanged), - createComposerState(kLayerId, fence(Fence::Status::Signaled), - layer_state_t::eBufferChanged)}); - setTransactionStates({mixedTransaction}, kExpectedTransactionsPending); -} - -TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesFromTheQueueDifferentLayerId) { - const sp<IBinder> kApplyToken = - IInterface::asBinder(TransactionCompletedListener::getIInstance()); - const auto kLayerId1 = 1; - const auto kLayerId2 = 2; - const auto kExpectedTransactionsPending = 0u; - - const auto mixedTransaction = - createTransactionInfo(kApplyToken, - {createComposerState(kLayerId1, fence(Fence::Status::Unsignaled), - layer_state_t::eBufferChanged), - createComposerState(kLayerId2, fence(Fence::Status::Signaled), - layer_state_t::eBufferChanged)}); - setTransactionStates({mixedTransaction}, kExpectedTransactionsPending); -} - -TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesSignaledFromTheQueue_MultipleLayers) { - const sp<IBinder> kApplyToken = - IInterface::asBinder(TransactionCompletedListener::getIInstance()); - const auto kLayerId1 = 1; - const auto kLayerId2 = 2; - const auto kExpectedTransactionsPending = 0u; - - const auto signaledTransaction = - createTransactionInfo(kApplyToken, - { - createComposerState(kLayerId1, - fence(Fence::Status::Signaled), - layer_state_t::eBufferChanged), - }); - const auto signaledTransaction2 = - createTransactionInfo(kApplyToken, - { - createComposerState(kLayerId2, - fence(Fence::Status::Signaled), - layer_state_t::eBufferChanged), - }); - setTransactionStates({signaledTransaction, signaledTransaction2}, kExpectedTransactionsPending); -} - -TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesFromTheQueueDifferentApplyToken) { - const sp<IBinder> kApplyToken1 = - IInterface::asBinder(TransactionCompletedListener::getIInstance()); - const sp<IBinder> kApplyToken2 = sp<BBinder>::make(); - const auto kLayerId1 = 1; - const auto kLayerId2 = 2; - const auto kExpectedTransactionsPending = 0u; - - const auto signaledTransaction = - createTransactionInfo(kApplyToken1, - { - createComposerState(kLayerId1, - fence(Fence::Status::Signaled), - layer_state_t::eBufferChanged), - }); - const auto unsignaledTransaction = - createTransactionInfo(kApplyToken2, - { - createComposerState(kLayerId2, - fence(Fence::Status::Unsignaled), - layer_state_t::eBufferChanged), - }); - setTransactionStates({signaledTransaction, unsignaledTransaction}, - kExpectedTransactionsPending); -} - -TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesUnsignaledFromTheQueueSameApplyToken) { - const sp<IBinder> kApplyToken = - IInterface::asBinder(TransactionCompletedListener::getIInstance()); - const auto kLayerId1 = 1; - const auto kLayerId2 = 2; - const auto kExpectedTransactionsPending = 0u; - - const auto unsignaledTransaction = - createTransactionInfo(kApplyToken, - { - createComposerState(kLayerId1, - fence(Fence::Status::Unsignaled), - layer_state_t::eBufferChanged), - }); - const auto signaledTransaction = - createTransactionInfo(kApplyToken, - { - createComposerState(kLayerId2, - fence(Fence::Status::Signaled), - layer_state_t::eBufferChanged), - }); - setTransactionStates({unsignaledTransaction, signaledTransaction}, - kExpectedTransactionsPending); -} - -TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesUnsignaledFromTheQueue) { - const sp<IBinder> kApplyToken1 = - IInterface::asBinder(TransactionCompletedListener::getIInstance()); - const sp<IBinder> kApplyToken2 = sp<BBinder>::make(); - const auto kLayerId1 = 1; - const auto kLayerId2 = 2; - const auto kExpectedTransactionsPending = 0u; - - const auto unsignaledTransaction = - createTransactionInfo(kApplyToken1, - { - createComposerState(kLayerId1, - fence(Fence::Status::Unsignaled), - layer_state_t::eBufferChanged), - }); - const auto unsignaledTransaction2 = - createTransactionInfo(kApplyToken2, - { - createComposerState(kLayerId2, - fence(Fence::Status::Unsignaled), - layer_state_t::eBufferChanged), - }); - setTransactionStates({unsignaledTransaction, unsignaledTransaction2}, - kExpectedTransactionsPending); -} - -TEST_F(LatchUnsignaledAlwaysTest, RespectsBackPressureFlag) { - const sp<IBinder> kApplyToken1 = - IInterface::asBinder(TransactionCompletedListener::getIInstance()); - const sp<IBinder> kApplyToken2 = sp<BBinder>::make(); - const auto kLayerId1 = 1; - const auto kExpectedTransactionsPending = 1u; - auto layer = - sp<Layer>::make(LayerCreationArgs(mFlinger.flinger(), nullptr, "TestLayer", 0, {})); - auto layerHandle = layer->getHandle(); - const auto setBackPressureFlagTransaction = - createTransactionInfo(kApplyToken1, - {createComposerState(kLayerId1, fence(Fence::Status::Unsignaled), - layer_state_t::eBufferChanged | - layer_state_t::eFlagsChanged, - {layerHandle})}); - setTransactionStates({setBackPressureFlagTransaction}, 0u); - - const auto unsignaledTransaction = - createTransactionInfo(kApplyToken1, - { - createComposerState(kLayerId1, - fence(Fence::Status::Unsignaled), - layer_state_t::eBufferChanged, - {layerHandle}), - }); - const auto unsignaledTransaction2 = - createTransactionInfo(kApplyToken1, - { - createComposerState(kLayerId1, - fence(Fence::Status::Unsignaled), - layer_state_t::eBufferChanged, - {layerHandle}), - }); - setTransactionStates({unsignaledTransaction, unsignaledTransaction2}, - kExpectedTransactionsPending); -} - -TEST_F(LatchUnsignaledAlwaysTest, LatchUnsignaledWhenEarlyOffset) { - const sp<IBinder> kApplyToken = - IInterface::asBinder(TransactionCompletedListener::getIInstance()); - const auto kLayerId = 1; - const auto kExpectedTransactionsPending = 0u; - - const auto unsignaledTransaction = - createTransactionInfo(kApplyToken, - { - createComposerState(kLayerId, - fence(Fence::Status::Unsignaled), - layer_state_t::eBufferChanged), - }); - - modulateVsync(); - setTransactionStates({unsignaledTransaction}, kExpectedTransactionsPending); -} - TEST(TransactionHandlerTest, QueueTransaction) { TransactionHandler handler; TransactionState transaction; transaction.applyToken = sp<BBinder>::make(); transaction.id = 42; handler.queueTransaction(std::move(transaction)); + handler.collectTransactions(); std::vector<TransactionState> transactionsReadyToBeApplied = handler.flushTransactions(); EXPECT_EQ(transactionsReadyToBeApplied.size(), 1u); diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h index 0ddc90d585..a088aabc11 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h @@ -18,14 +18,14 @@ #include "binder/Status.h" -#include <android/hardware/power/IPower.h> +#include <aidl/android/hardware/power/IPower.h> #include <gmock/gmock.h> +using aidl::android::hardware::power::Boost; +using aidl::android::hardware::power::IPower; +using aidl::android::hardware::power::IPowerHintSession; +using aidl::android::hardware::power::Mode; using android::binder::Status; -using android::hardware::power::Boost; -using android::hardware::power::IPower; -using android::hardware::power::IPowerHintSession; -using android::hardware::power::Mode; namespace android::Hwc2::mock { @@ -33,18 +33,19 @@ class MockIPower : public IPower { public: MockIPower(); - MOCK_METHOD(Status, isBoostSupported, (Boost boost, bool* ret), (override)); - MOCK_METHOD(Status, setBoost, (Boost boost, int32_t durationMs), (override)); - MOCK_METHOD(Status, isModeSupported, (Mode mode, bool* ret), (override)); - MOCK_METHOD(Status, setMode, (Mode mode, bool enabled), (override)); - MOCK_METHOD(Status, createHintSession, + MOCK_METHOD(ndk::ScopedAStatus, isBoostSupported, (Boost boost, bool* ret), (override)); + MOCK_METHOD(ndk::ScopedAStatus, setBoost, (Boost boost, int32_t durationMs), (override)); + MOCK_METHOD(ndk::ScopedAStatus, isModeSupported, (Mode mode, bool* ret), (override)); + MOCK_METHOD(ndk::ScopedAStatus, setMode, (Mode mode, bool enabled), (override)); + MOCK_METHOD(ndk::ScopedAStatus, createHintSession, (int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, - int64_t durationNanos, sp<IPowerHintSession>* session), + int64_t durationNanos, std::shared_ptr<IPowerHintSession>* session), (override)); - MOCK_METHOD(Status, getHintSessionPreferredRate, (int64_t * rate), (override)); - MOCK_METHOD(int32_t, getInterfaceVersion, (), (override)); - MOCK_METHOD(std::string, getInterfaceHash, (), (override)); - MOCK_METHOD(IBinder*, onAsBinder, (), (override)); + MOCK_METHOD(ndk::ScopedAStatus, getHintSessionPreferredRate, (int64_t * rate), (override)); + MOCK_METHOD(ndk::ScopedAStatus, getInterfaceVersion, (int32_t * version), (override)); + MOCK_METHOD(ndk::ScopedAStatus, getInterfaceHash, (std::string * hash), (override)); + MOCK_METHOD(ndk::SpAIBinder, asBinder, (), (override)); + MOCK_METHOD(bool, isRemote, (), (override)); }; } // namespace android::Hwc2::mock diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h index f4ded216cb..2b9520fca7 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h @@ -18,14 +18,14 @@ #include "binder/Status.h" -#include <android/hardware/power/IPower.h> +#include <aidl/android/hardware/power/IPower.h> #include <gmock/gmock.h> +using aidl::android::hardware::power::IPowerHintSession; +using aidl::android::hardware::power::SessionHint; using android::binder::Status; -using android::hardware::power::IPowerHintSession; -using android::hardware::power::SessionHint; -using namespace android::hardware::power; +using namespace aidl::android::hardware::power; namespace android::Hwc2::mock { @@ -33,16 +33,18 @@ class MockIPowerHintSession : public IPowerHintSession { public: MockIPowerHintSession(); - MOCK_METHOD(IBinder*, onAsBinder, (), (override)); - MOCK_METHOD(Status, pause, (), (override)); - MOCK_METHOD(Status, resume, (), (override)); - MOCK_METHOD(Status, close, (), (override)); - MOCK_METHOD(int32_t, getInterfaceVersion, (), (override)); - MOCK_METHOD(std::string, getInterfaceHash, (), (override)); - MOCK_METHOD(Status, updateTargetWorkDuration, (int64_t), (override)); - MOCK_METHOD(Status, reportActualWorkDuration, (const ::std::vector<WorkDuration>&), (override)); - MOCK_METHOD(Status, sendHint, (SessionHint), (override)); - MOCK_METHOD(Status, setThreads, (const ::std::vector<int32_t>&), (override)); + MOCK_METHOD(ndk::SpAIBinder, asBinder, (), (override)); + MOCK_METHOD(ndk::ScopedAStatus, pause, (), (override)); + MOCK_METHOD(ndk::ScopedAStatus, resume, (), (override)); + MOCK_METHOD(ndk::ScopedAStatus, close, (), (override)); + MOCK_METHOD(ndk::ScopedAStatus, getInterfaceVersion, (int32_t * version), (override)); + MOCK_METHOD(ndk::ScopedAStatus, getInterfaceHash, (std::string * hash), (override)); + MOCK_METHOD(bool, isRemote, (), (override)); + MOCK_METHOD(ndk::ScopedAStatus, updateTargetWorkDuration, (int64_t), (override)); + MOCK_METHOD(ndk::ScopedAStatus, reportActualWorkDuration, (const ::std::vector<WorkDuration>&), + (override)); + MOCK_METHOD(ndk::ScopedAStatus, sendHint, (SessionHint), (override)); + MOCK_METHOD(ndk::ScopedAStatus, setThreads, (const ::std::vector<int32_t>&), (override)); }; } // namespace android::Hwc2::mock diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h index 8e22f43b76..68fe3c52d4 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h @@ -31,8 +31,8 @@ class IPower; namespace android::Hwc2::mock { -using android::hardware::power::Boost; -using android::hardware::power::Mode; +using aidl::android::hardware::power::Boost; +using aidl::android::hardware::power::Mode; using android::power::HalResult; class MockPowerHalController : public power::PowerHalController { @@ -42,8 +42,9 @@ public: MOCK_METHOD(void, init, (), (override)); MOCK_METHOD(HalResult<void>, setBoost, (Boost, int32_t), (override)); MOCK_METHOD(HalResult<void>, setMode, (Mode, bool), (override)); - MOCK_METHOD(HalResult<sp<hardware::power::IPowerHintSession>>, createHintSession, - (int32_t, int32_t, const std::vector<int32_t>&, int64_t), (override)); + MOCK_METHOD(HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>>, + createHintSession, (int32_t, int32_t, const std::vector<int32_t>&, int64_t), + (override)); MOCK_METHOD(HalResult<int64_t>, getHintSessionPreferredRate, (), (override)); }; diff --git a/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp b/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp index aaeb8f990a..4c0910a75e 100644 --- a/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp +++ b/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp @@ -102,18 +102,6 @@ TEST_F(VibratorCallbackSchedulerTest, TestScheduleMultipleCallbacksRunsInDelayOr ASSERT_THAT(getExpiredCallbacks(), ElementsAre(3, 2, 1)); } -TEST_F(VibratorCallbackSchedulerTest, TestScheduleInParallelRunsInDelayOrder) { - std::vector<std::thread> threads; - for (int i = 0; i < 5; i++) { - threads.push_back(std::thread( - [=]() { mScheduler->schedule(createCallback(i), milliseconds(10 + 2 * i)); })); - } - std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); }); - - ASSERT_TRUE(waitForCallbacks(5, 25ms)); - ASSERT_THAT(getExpiredCallbacks(), ElementsAre(0, 1, 2, 3, 4)); -} - TEST_F(VibratorCallbackSchedulerTest, TestDestructorDropsPendingCallbacksAndKillsThread) { mScheduler->schedule(createCallback(1), 5ms); mScheduler.reset(nullptr); |