diff options
159 files changed, 3055 insertions, 1121 deletions
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index d24edc4d82..b012243c8b 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -1841,6 +1841,11 @@ Dumpstate::RunStatus Dumpstate::dumpstate() { RunCommand("DUMP VENDOR RIL LOGS", {"vril-dump"}, options.Build()); } + /* Dump USB information */ + RunCommand("typec_connector_class", {"typec_connector_class"}, + CommandOptions::WithTimeout(10).AsRootIfAvailable().Build()); + RunCommand("lsusb", {"lsusb"}, CommandOptions::WithTimeout(10).AsRootIfAvailable().Build()); + printf("========================================================\n"); printf("== Android Framework Services\n"); printf("========================================================\n"); diff --git a/data/etc/go_handheld_core_hardware.xml b/data/etc/go_handheld_core_hardware.xml index 8df7fdbb16..a092842fa9 100644 --- a/data/etc/go_handheld_core_hardware.xml +++ b/data/etc/go_handheld_core_hardware.xml @@ -51,6 +51,9 @@ <!-- Feature to specify if the device supports adding device admins. --> <feature name="android.software.device_admin" /> + <!-- Feature to specify if the device support managed users. --> + <feature name="android.software.managed_users" /> + <!-- Devices with all optimizations required to support VR Mode and pass all CDD requirements for this feature may include android.hardware.vr.high_performance --> diff --git a/include/android/keycodes.h b/include/android/keycodes.h index 79cdbcaf7b..495e0bdb1f 100644 --- a/include/android/keycodes.h +++ b/include/android/keycodes.h @@ -843,6 +843,44 @@ enum { AKEYCODE_EMOJI_PICKER = 317, /** Take Screenshot */ AKEYCODE_SCREENSHOT = 318, + /** To start dictate to an input field */ + AKEYCODE_DICTATE = 319, + /** AC New */ + AKEYCODE_NEW = 320, + /** AC Close */ + AKEYCODE_CLOSE = 321, + /** To toggle 'Do Not Disturb' mode */ + AKEYCODE_DO_NOT_DISTURB = 322, + /** To Print */ + AKEYCODE_PRINT = 323, + /** To Lock the screen */ + AKEYCODE_LOCK = 324, + /** To toggle fullscreen mode (on the current application) */ + AKEYCODE_FULLSCREEN = 325, + /** F13 key */ + AKEYCODE_F13 = 326, + /** F14 key */ + AKEYCODE_F14 = 327, + /** F15 key */ + AKEYCODE_F15 = 328, + /** F16 key */ + AKEYCODE_F16 = 329, + /** F17 key */ + AKEYCODE_F17 = 330, + /** F18 key */ + AKEYCODE_F18 = 331, + /** F19 key */ + AKEYCODE_F19 = 332, + /** F20 key */ + AKEYCODE_F20 = 333, + /** F21 key */ + AKEYCODE_F21 = 334, + /** F22 key */ + AKEYCODE_F22 = 335, + /** F23 key */ + AKEYCODE_F23 = 336, + /** F24 key */ + AKEYCODE_F24 = 337, // NOTE: If you add a new keycode here you must also add it to several other files. // Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list. diff --git a/include/android/performance_hint.h b/include/android/performance_hint.h index 3f32a5abb3..3486e9b1d7 100644 --- a/include/android/performance_hint.h +++ b/include/android/performance_hint.h @@ -52,7 +52,6 @@ * - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES */ -#include <android/api-level.h> #include <stdbool.h> #include <stdint.h> #include <unistd.h> diff --git a/include/android/surface_control.h b/include/android/surface_control.h index 8d61e772cc..fe38e86015 100644 --- a/include/android/surface_control.h +++ b/include/android/surface_control.h @@ -763,6 +763,69 @@ void ASurfaceTransaction_setFrameRateWithChangeStrategy(ASurfaceTransaction* _No __INTRODUCED_IN(31); /** + * Sets the intended frame rate for the given \a surface_control. + * + * On devices that are capable of running the display at different frame rates, + * the system may choose a display refresh rate to better match this surface's frame + * rate. Usage of this API won't introduce frame rate throttling, or affect other + * aspects of the application's frame production pipeline. However, because the system + * may change the display refresh rate, calls to this function may result in changes + * to Choreographer callback timings, and changes to the time interval at which the + * system releases buffers back to the application. + * + * You can register for changes in the refresh rate using + * \a AChoreographer_registerRefreshRateCallback. + * + * See ASurfaceTransaction_clearFrameRate(). + * + * Available since API level 36. + * + * \param desiredMinRate The desired minimum frame rate (inclusive) for the surface, specifying that + * the surface prefers the device render rate to be at least `desiredMinRate`. + * + * <p>Set `desiredMinRate` = `desiredMaxRate` to indicate the surface prefers an exact frame rate. + * + * <p>Set `desiredMinRate` = 0 to indicate the surface has no preference + * and any frame rate is acceptable. + * + * <p>The value should be greater than or equal to 0. + * + * \param desiredMaxRate The desired maximum frame rate (inclusive) for the surface, specifying that + * the surface prefers the device render rate to be at most `desiredMaxRate`. + * + * <p>Set `desiredMaxRate` = `desiredMinRate` to indicate the surface prefers an exact frame rate. + * + * <p>Set `desiredMaxRate` = positive infinity to indicate the surface has no preference + * and any frame rate is acceptable. + * + * <p>The value should be greater than or equal to `desiredMinRate`. + * + * \param fixedSourceRate The "fixed source" frame rate of the surface if the content has an + * inherently fixed frame rate, e.g. a video that has a specific frame rate. + * + * <p>When the frame rate chosen for the surface is the `fixedSourceRate` or a + * multiple, the surface can render without frame pulldown, for optimal smoothness. For + * example, a 30 fps video (`fixedSourceRate`=30) renders just as smoothly on 30 fps, + * 60 fps, 90 fps, 120 fps, and so on. + * + * <p>Setting the fixed source rate can also be used together with a desired + * frame rate min and max via setting `desiredMinRate` and `desiredMaxRate`. This still + * means the surface's content has a fixed frame rate of `fixedSourceRate`, but additionally + * specifies the preference to be in the range [`desiredMinRate`, `desiredMaxRate`]. For example, an + * app might want to specify there is 30 fps video (`fixedSourceRate`=30) as well as a smooth + * animation on the same surface which looks good when drawing within a frame rate range such as + * [`desiredMinRate`, `desiredMaxRate`] = [60,120]. + * + * \param changeFrameRateStrategy Whether display refresh rate transitions caused by this surface + * should be seamless. A seamless transition is one that doesn't have any visual interruptions, such + * as a black screen for a second or two. + */ +void ASurfaceTransaction_setFrameRateParams( + ASurfaceTransaction* _Nonnull transaction, ASurfaceControl* _Nonnull surface_control, + float desiredMinRate, float desiredMaxRate, float fixedSourceRate, + ANativeWindow_ChangeFrameRateStrategy changeFrameRateStrategy) __INTRODUCED_IN(36); + +/** * Clears the frame rate which is set for \a surface_control. * * This is equivalent to calling diff --git a/include/input/CoordinateFilter.h b/include/input/CoordinateFilter.h new file mode 100644 index 0000000000..f36472dc8c --- /dev/null +++ b/include/input/CoordinateFilter.h @@ -0,0 +1,54 @@ +/** + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <chrono> + +#include <input/Input.h> +#include <input/OneEuroFilter.h> + +namespace android { + +/** + * Pair of OneEuroFilters that independently filter X and Y coordinates. Both filters share the same + * constructor's parameters. The minimum cutoff frequency is the base cutoff frequency, that is, the + * resulting cutoff frequency in the absence of signal's speed. Likewise, beta is a scaling factor + * of the signal's speed that sets how much the signal's speed contributes to the resulting cutoff + * frequency. The adaptive cutoff frequency criterion is f_c = f_c_min + β|̇x_filtered| + */ +class CoordinateFilter { +public: + explicit CoordinateFilter(float minCutoffFreq, float beta); + + /** + * Filters in place only the AXIS_X and AXIS_Y fields from coords. Each call to filter must + * provide a timestamp strictly greater than the timestamp of the previous call. The first time + * this method is invoked no filtering takes place. Subsequent calls do overwrite `coords` with + * filtered data. + * + * @param timestamp The timestamps at which to filter. It must be greater than the one passed in + * the previous call. + * @param coords Coordinates to be overwritten by the corresponding filtered coordinates. + */ + void filter(std::chrono::duration<float> timestamp, PointerCoords& coords); + +private: + OneEuroFilter mXFilter; + OneEuroFilter mYFilter; +}; + +} // namespace android
\ No newline at end of file diff --git a/include/input/OneEuroFilter.h b/include/input/OneEuroFilter.h new file mode 100644 index 0000000000..a0168e4f91 --- /dev/null +++ b/include/input/OneEuroFilter.h @@ -0,0 +1,101 @@ +/** + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <chrono> +#include <optional> + +#include <input/Input.h> + +namespace android { + +/** + * Low pass filter with adaptive low pass frequency based on the signal's speed. The signal's cutoff + * frequency is determined by f_c = f_c_min + β|̇x_filtered|. Refer to + * https://dl.acm.org/doi/10.1145/2207676.2208639 for details on how the filter works and how to + * tune it. + */ +class OneEuroFilter { +public: + /** + * Default cutoff frequency of the filtered signal's speed. 1.0 Hz is the value in the filter's + * paper. + */ + static constexpr float kDefaultSpeedCutoffFreq = 1.0; + + OneEuroFilter() = delete; + + explicit OneEuroFilter(float minCutoffFreq, float beta, + float speedCutoffFreq = kDefaultSpeedCutoffFreq); + + OneEuroFilter(const OneEuroFilter&) = delete; + OneEuroFilter& operator=(const OneEuroFilter&) = delete; + OneEuroFilter(OneEuroFilter&&) = delete; + OneEuroFilter& operator=(OneEuroFilter&&) = delete; + + /** + * Returns the filtered value of rawPosition. Each call to filter must provide a timestamp + * strictly greater than the timestamp of the previous call. The first time the method is + * called, it returns the value of rawPosition. Any subsequent calls provide a filtered value. + * + * @param timestamp The timestamp at which to filter. It must be strictly greater than the one + * provided in the previous call. + * @param rawPosition Position to be filtered. + */ + float filter(std::chrono::duration<float> timestamp, float rawPosition); + +private: + /** + * Minimum cutoff frequency. This is the constant term in the adaptive cutoff frequency + * criterion. Units are Hertz. + */ + const float mMinCutoffFreq; + + /** + * Slope of the cutoff frequency criterion. This is the term scaling the absolute value of the + * filtered signal's speed. The data member is dimensionless, that is, it does not have units. + */ + const float mBeta; + + /** + * Cutoff frequency of the signal's speed. This is the cutoff frequency applied to the filtering + * of the signal's speed. Units are Hertz. + */ + const float mSpeedCutoffFreq; + + /** + * The timestamp from the previous call. Units are seconds. + */ + std::optional<std::chrono::duration<float>> mPrevTimestamp; + + /** + * The raw position from the previous call. + */ + std::optional<float> mPrevRawPosition; + + /** + * The filtered velocity from the previous call. Units are position per second. + */ + std::optional<float> mPrevFilteredVelocity; + + /** + * The filtered position from the previous call. + */ + std::optional<float> mPrevFilteredPosition; +}; + +} // namespace android diff --git a/include/input/Resampler.h b/include/input/Resampler.h index 6d95ca7e86..155097732c 100644 --- a/include/input/Resampler.h +++ b/include/input/Resampler.h @@ -19,11 +19,13 @@ #include <array> #include <chrono> #include <iterator> +#include <map> #include <optional> #include <vector> #include <android-base/logging.h> #include <ftl/mixins.h> +#include <input/CoordinateFilter.h> #include <input/Input.h> #include <input/InputTransport.h> #include <input/RingBuffer.h> @@ -293,4 +295,43 @@ private: inline static void addSampleToMotionEvent(const Sample& sample, MotionEvent& motionEvent); }; +/** + * Resampler that first applies the LegacyResampler resampling algorithm, then independently filters + * the X and Y coordinates with a pair of One Euro filters. + */ +class FilteredLegacyResampler final : public Resampler { +public: + /** + * Creates a resampler, using the given minCutoffFreq and beta to instantiate its One Euro + * filters. + */ + explicit FilteredLegacyResampler(float minCutoffFreq, float beta); + + void resampleMotionEvent(std::chrono::nanoseconds requestedFrameTime, MotionEvent& motionEvent, + const InputMessage* futureMessage) override; + + std::chrono::nanoseconds getResampleLatency() const override; + +private: + LegacyResampler mResampler; + + /** + * Minimum cutoff frequency of the value's low pass filter. Refer to OneEuroFilter class for a + * more detailed explanation. + */ + const float mMinCutoffFreq; + + /** + * Scaling factor of the adaptive cutoff frequency criterion. Refer to OneEuroFilter class for a + * more detailed explanation. + */ + const float mBeta; + + /* + * Note: an associative array with constant insertion and lookup times would be more efficient. + * When this was implemented, there was no container with these properties. + */ + std::map<int32_t /*pointerId*/, CoordinateFilter> mFilteredPointers; +}; + } // namespace android diff --git a/libs/battery/LongArrayMultiStateCounter.cpp b/libs/battery/LongArrayMultiStateCounter.cpp index 125cfaffa4..334d84b6b5 100644 --- a/libs/battery/LongArrayMultiStateCounter.cpp +++ b/libs/battery/LongArrayMultiStateCounter.cpp @@ -21,58 +21,137 @@ namespace android { namespace battery { -template <> -bool LongArrayMultiStateCounter::delta(const std::vector<uint64_t>& previousValue, - const std::vector<uint64_t>& newValue, - std::vector<uint64_t>* outValue) const { - size_t size = previousValue.size(); - if (newValue.size() != size) { - ALOGE("Incorrect array size: %d, should be %d", (int)newValue.size(), (int)size); - return false; +Uint64ArrayRW::Uint64ArrayRW(const Uint64Array ©) : Uint64Array(copy.size()) { + if (mSize != 0 && copy.data() != nullptr) { + mData = new uint64_t[mSize]; + memcpy(mData, copy.data(), mSize * sizeof(uint64_t)); + } else { + mData = nullptr; } +} - bool is_delta_valid = true; - for (int i = size - 1; i >= 0; i--) { - if (newValue[i] >= previousValue[i]) { - (*outValue)[i] = newValue[i] - previousValue[i]; +uint64_t *Uint64ArrayRW::dataRW() { + if (mData == nullptr) { + mData = new uint64_t[mSize]; + memset(mData, 0, mSize * sizeof(uint64_t)); + } + return mData; +} + +Uint64ArrayRW &Uint64ArrayRW::operator=(const Uint64Array &t) { + if (t.size() != mSize) { + delete[] mData; + mSize = t.size(); + mData = nullptr; + } + if (mSize != 0) { + if (t.data() != nullptr) { + if (mData == nullptr) { + mData = new uint64_t[mSize]; + } + memcpy(mData, t.data(), mSize * sizeof(uint64_t)); } else { - (*outValue)[i] = 0; - is_delta_valid = false; + delete[] mData; + mData = nullptr; } } - return is_delta_valid; + return *this; +} + +std::ostream &operator<<(std::ostream &os, const Uint64Array &v) { + os << "{"; + const uint64_t *data = v.data(); + if (data != nullptr) { + bool first = true; + for (size_t i = 0; i < v.size(); i++) { + if (!first) { + os << ", "; + } + os << data[i]; + first = false; + } + } + os << "}"; + return os; +} + +// Convenience constructor for tests +Uint64ArrayRW::Uint64ArrayRW(std::initializer_list<uint64_t> init) : Uint64Array(init.size()) { + mData = new uint64_t[mSize]; + memcpy(mData, init.begin(), mSize * sizeof(uint64_t)); +} + +// Used in tests only. +bool Uint64Array::operator==(const Uint64Array &other) const { + if (size() != other.size()) { + return false; + } + const uint64_t* thisData = data(); + const uint64_t* thatData = other.data(); + for (size_t i = 0; i < mSize; i++) { + const uint64_t v1 = thisData != nullptr ? thisData[i] : 0; + const uint64_t v2 = thatData != nullptr ? thatData[i] : 0; + if (v1 != v2) { + return false; + } + } + return true; } template <> -void LongArrayMultiStateCounter::add(std::vector<uint64_t>* value1, - const std::vector<uint64_t>& value2, const uint64_t numerator, - const uint64_t denominator) const { +void LongArrayMultiStateCounter::add(Uint64ArrayRW *value1, const Uint64Array &value2, + const uint64_t numerator, const uint64_t denominator) const { + const uint64_t* data2 = value2.data(); + if (data2 == nullptr) { + return; + } + + uint64_t* data1 = value1->dataRW(); + size_t size = value2.size(); if (numerator != denominator) { - for (int i = value2.size() - 1; i >= 0; i--) { + for (size_t i = 0; i < size; i++) { // The caller ensures that denominator != 0 - (*value1)[i] += value2[i] * numerator / denominator; + data1[i] += data2[i] * numerator / denominator; } } else { - for (int i = value2.size() - 1; i >= 0; i--) { - (*value1)[i] += value2[i]; + for (size_t i = 0; i < size; i++) { + data1[i] += data2[i]; } } } -template <> -std::string LongArrayMultiStateCounter::valueToString(const std::vector<uint64_t>& v) const { - std::stringstream s; - s << "{"; - bool first = true; - for (uint64_t n : v) { - if (!first) { - s << ", "; +template<> +bool LongArrayMultiStateCounter::delta(const Uint64ArrayRW &previousValue, + const Uint64Array &newValue, Uint64ArrayRW *outValue) const { + size_t size = previousValue.size(); + if (newValue.size() != size) { + ALOGE("Incorrect array size: %d, should be %d", (int) newValue.size(), (int) size); + return false; + } + if (outValue->size() != size) { + ALOGE("Incorrect outValue size: %d, should be %d", (int) outValue->size(), (int) size); + return false; + } + + bool is_delta_valid = true; + const uint64_t *prevData = previousValue.data(); + const uint64_t *newData = newValue.data(); + uint64_t *outData = outValue->dataRW(); + for (size_t i = 0; i < size; i++) { + if (prevData == nullptr) { + if (newData == nullptr) { + outData[i] = 0; + } else { + outData[i] = newData[i]; + } + } else if (newData == nullptr || newData[i] < prevData[i]) { + outData[i] = 0; + is_delta_valid = false; + } else { + outData[i] = newData[i] - prevData[i]; } - s << n; - first = false; } - s << "}"; - return s.str(); + return is_delta_valid; } } // namespace battery diff --git a/libs/battery/LongArrayMultiStateCounter.h b/libs/battery/LongArrayMultiStateCounter.h index f3439f6a0c..e00c96898e 100644 --- a/libs/battery/LongArrayMultiStateCounter.h +++ b/libs/battery/LongArrayMultiStateCounter.h @@ -23,7 +23,66 @@ namespace android { namespace battery { -typedef MultiStateCounter<std::vector<uint64_t>> LongArrayMultiStateCounter; +/** + * Wrapper for an array of uint64's. + */ +class Uint64Array { + protected: + size_t mSize; + + public: + Uint64Array() : Uint64Array(0) {} + + Uint64Array(size_t size) : mSize(size) {} + + virtual ~Uint64Array() {} + + size_t size() const { return mSize; } + + /** + * Returns the wrapped array. + * + * Nullable! Null should be interpreted the same as an array of zeros + */ + virtual const uint64_t *data() const { return nullptr; } + + friend std::ostream &operator<<(std::ostream &os, const Uint64Array &v); + + // Test API + bool operator==(const Uint64Array &other) const; +}; + +/** + * Mutable version of Uint64Array. + */ +class Uint64ArrayRW: public Uint64Array { + uint64_t* mData; + +public: + Uint64ArrayRW() : Uint64ArrayRW(0) {} + + Uint64ArrayRW(size_t size) : Uint64Array(size), mData(nullptr) {} + + Uint64ArrayRW(const Uint64Array ©); + + // Need an explicit copy constructor. In the initialization context C++ does not understand that + // a Uint64ArrayRW is a Uint64Array. + Uint64ArrayRW(const Uint64ArrayRW ©) : Uint64ArrayRW((const Uint64Array &) copy) {} + + // Test API + Uint64ArrayRW(std::initializer_list<uint64_t> init); + + ~Uint64ArrayRW() override { delete[] mData; } + + const uint64_t *data() const override { return mData; } + + // NonNull. Will initialize the wrapped array if it is null. + uint64_t *dataRW(); + + Uint64ArrayRW &operator=(const Uint64Array &t); +}; + +typedef MultiStateCounter<Uint64ArrayRW, Uint64Array> LongArrayMultiStateCounter; } // namespace battery } // namespace android diff --git a/libs/battery/LongArrayMultiStateCounterTest.cpp b/libs/battery/LongArrayMultiStateCounterTest.cpp index e4e6b2a49f..1c74e3fd14 100644 --- a/libs/battery/LongArrayMultiStateCounterTest.cpp +++ b/libs/battery/LongArrayMultiStateCounterTest.cpp @@ -24,25 +24,25 @@ namespace battery { class LongArrayMultiStateCounterTest : public testing::Test {}; TEST_F(LongArrayMultiStateCounterTest, stateChange) { - LongArrayMultiStateCounter testCounter(2, std::vector<uint64_t>(4)); - testCounter.updateValue(std::vector<uint64_t>({0, 0, 0, 0}), 1000); + LongArrayMultiStateCounter testCounter(2, Uint64Array(4)); + testCounter.updateValue(Uint64ArrayRW({0, 0, 0, 0}), 1000); testCounter.setState(0, 1000); testCounter.setState(1, 2000); - testCounter.updateValue(std::vector<uint64_t>({100, 200, 300, 400}), 3000); + testCounter.updateValue(Uint64ArrayRW({100, 200, 300, 400}), 3000); // Time was split in half between the two states, so the counts will be split 50:50 too - EXPECT_EQ(std::vector<uint64_t>({50, 100, 150, 200}), testCounter.getCount(0)); - EXPECT_EQ(std::vector<uint64_t>({50, 100, 150, 200}), testCounter.getCount(1)); + EXPECT_EQ(Uint64ArrayRW({50, 100, 150, 200}), testCounter.getCount(0)); + EXPECT_EQ(Uint64ArrayRW({50, 100, 150, 200}), testCounter.getCount(1)); } TEST_F(LongArrayMultiStateCounterTest, accumulation) { - LongArrayMultiStateCounter testCounter(2, std::vector<uint64_t>(4)); - testCounter.updateValue(std::vector<uint64_t>({0, 0, 0, 0}), 1000); + LongArrayMultiStateCounter testCounter(2, Uint64Array(4)); + testCounter.updateValue(Uint64ArrayRW({0, 0, 0, 0}), 1000); testCounter.setState(0, 1000); testCounter.setState(1, 2000); - testCounter.updateValue(std::vector<uint64_t>({100, 200, 300, 400}), 3000); + testCounter.updateValue(Uint64ArrayRW({100, 200, 300, 400}), 3000); testCounter.setState(0, 4000); - testCounter.updateValue(std::vector<uint64_t>({200, 300, 400, 500}), 8000); + testCounter.updateValue(Uint64ArrayRW({200, 300, 400, 500}), 8000); // The first delta is split 50:50: // 0: {50, 100, 150, 200} @@ -50,16 +50,16 @@ TEST_F(LongArrayMultiStateCounterTest, accumulation) { // The second delta is split 4:1 // 0: {80, 80, 80, 80} // 1: {20, 20, 20, 20} - EXPECT_EQ(std::vector<uint64_t>({130, 180, 230, 280}), testCounter.getCount(0)); - EXPECT_EQ(std::vector<uint64_t>({70, 120, 170, 220}), testCounter.getCount(1)); + EXPECT_EQ(Uint64ArrayRW({130, 180, 230, 280}), testCounter.getCount(0)); + EXPECT_EQ(Uint64ArrayRW({70, 120, 170, 220}), testCounter.getCount(1)); } TEST_F(LongArrayMultiStateCounterTest, toString) { - LongArrayMultiStateCounter testCounter(2, std::vector<uint64_t>(4)); - testCounter.updateValue(std::vector<uint64_t>({0, 0, 0, 0}), 1000); + LongArrayMultiStateCounter testCounter(2, Uint64Array(4)); + testCounter.updateValue(Uint64ArrayRW({0, 0, 0, 0}), 1000); testCounter.setState(0, 1000); testCounter.setState(1, 2000); - testCounter.updateValue(std::vector<uint64_t>({100, 200, 300, 400}), 3000); + testCounter.updateValue(Uint64ArrayRW({100, 200, 300, 400}), 3000); EXPECT_STREQ("[0: {50, 100, 150, 200}, 1: {50, 100, 150, 200}] updated: 3000 currentState: 1", testCounter.toString().c_str()); diff --git a/libs/battery/MultiStateCounter.h b/libs/battery/MultiStateCounter.h index 04b718698e..fadc4ffd41 100644 --- a/libs/battery/MultiStateCounter.h +++ b/libs/battery/MultiStateCounter.h @@ -35,12 +35,12 @@ namespace battery { typedef uint16_t state_t; -template <class T> +template <class T, class V> class MultiStateCounter { - uint16_t stateCount; + const uint16_t stateCount; + const V emptyValue; state_t currentState; time_t lastStateChangeTimestamp; - T emptyValue; T lastValue; time_t lastUpdateTimestamp; T deltaValue; @@ -54,7 +54,7 @@ class MultiStateCounter { State* states; public: - MultiStateCounter(uint16_t stateCount, const T& emptyValue); + MultiStateCounter(uint16_t stateCount, const V& emptyValue); virtual ~MultiStateCounter(); @@ -66,35 +66,35 @@ public: * Copies the current state and accumulated times-in-state from the source. Resets * the accumulated value. */ - void copyStatesFrom(const MultiStateCounter<T>& source); + void copyStatesFrom(const MultiStateCounter<T, V> &source); - void setValue(state_t state, const T& value); + void setValue(state_t state, const V& value); /** * Updates the value by distributing the delta from the previously set value * among states according to their respective time-in-state. * Returns the delta from the previously set value. */ - const T& updateValue(const T& value, time_t timestamp); + const V& updateValue(const V& value, time_t timestamp); /** * Updates the value by distributing the specified increment among states according * to their respective time-in-state. */ - void incrementValue(const T& increment, time_t timestamp); + void incrementValue(const V& increment, time_t timestamp); /** * Adds the specified increment to the value for the current state, without affecting * the last updated value or timestamp. Ignores partial time-in-state: the entirety of * the increment is given to the current state. */ - void addValue(const T& increment); + void addValue(const V& increment); void reset(); uint16_t getStateCount(); - const T& getCount(state_t state); + const V& getCount(state_t state); std::string toString(); @@ -104,27 +104,25 @@ private: * Returns true iff the combination of previousValue and newValue is valid * (newValue >= prevValue) */ - bool delta(const T& previousValue, const T& newValue, T* outValue) const; + bool delta(const T& previousValue, const V& newValue, T* outValue) const; /** * Adds value2 to value1 and stores the result in value1. Denominator is * guaranteed to be non-zero. */ - void add(T* value1, const T& value2, const uint64_t numerator, + void add(T* value1, const V& value2, const uint64_t numerator, const uint64_t denominator) const; - - std::string valueToString(const T& value) const; }; // ---------------------- MultiStateCounter Implementation ------------------------- // Since MultiStateCounter is a template, the implementation must be inlined. -template <class T> -MultiStateCounter<T>::MultiStateCounter(uint16_t stateCount, const T& emptyValue) +template <class T, class V> +MultiStateCounter<T, V>::MultiStateCounter(uint16_t stateCount, const V& emptyValue) : stateCount(stateCount), + emptyValue(emptyValue), currentState(0), lastStateChangeTimestamp(-1), - emptyValue(emptyValue), lastValue(emptyValue), lastUpdateTimestamp(-1), deltaValue(emptyValue), @@ -136,13 +134,13 @@ MultiStateCounter<T>::MultiStateCounter(uint16_t stateCount, const T& emptyValue } } -template <class T> -MultiStateCounter<T>::~MultiStateCounter() { +template <class T, class V> +MultiStateCounter<T, V>::~MultiStateCounter() { delete[] states; }; -template <class T> -void MultiStateCounter<T>::setEnabled(bool enabled, time_t timestamp) { +template <class T, class V> +void MultiStateCounter<T, V>::setEnabled(bool enabled, time_t timestamp) { if (enabled == isEnabled) { return; } @@ -167,8 +165,8 @@ void MultiStateCounter<T>::setEnabled(bool enabled, time_t timestamp) { } } -template <class T> -void MultiStateCounter<T>::setState(state_t state, time_t timestamp) { +template <class T, class V> +void MultiStateCounter<T, V>::setState(state_t state, time_t timestamp) { if (isEnabled && lastStateChangeTimestamp >= 0 && lastUpdateTimestamp >= 0) { // If the update arrived out-of-order, just push back the timestamp to // avoid having the situation where timeInStateSinceUpdate > timeSinceUpdate @@ -198,8 +196,8 @@ void MultiStateCounter<T>::setState(state_t state, time_t timestamp) { lastStateChangeTimestamp = timestamp; } -template <class T> -void MultiStateCounter<T>::copyStatesFrom(const MultiStateCounter<T>& source) { +template <class T, class V> +void MultiStateCounter<T, V>::copyStatesFrom(const MultiStateCounter<T, V>& source) { if (stateCount != source.stateCount) { ALOGE("State count mismatch: %u vs. %u\n", stateCount, source.stateCount); return; @@ -214,14 +212,14 @@ void MultiStateCounter<T>::copyStatesFrom(const MultiStateCounter<T>& source) { lastUpdateTimestamp = source.lastUpdateTimestamp; } -template <class T> -void MultiStateCounter<T>::setValue(state_t state, const T& value) { +template <class T, class V> +void MultiStateCounter<T, V>::setValue(state_t state, const V& value) { states[state].counter = value; } -template <class T> -const T& MultiStateCounter<T>::updateValue(const T& value, time_t timestamp) { - T* returnValue = &emptyValue; +template <class T, class V> +const V& MultiStateCounter<T, V>::updateValue(const V& value, time_t timestamp) { + const V* returnValue = &emptyValue; // If the counter is disabled, we ignore the update, except when the counter got disabled after // the previous update, in which case we still need to pick up the residual delta. @@ -250,8 +248,8 @@ const T& MultiStateCounter<T>::updateValue(const T& value, time_t timestamp) { } } else { std::stringstream str; - str << "updateValue is called with a value " << valueToString(value) - << ", which is lower than the previous value " << valueToString(lastValue) + str << "updateValue is called with a value " << value + << ", which is lower than the previous value " << lastValue << "\n"; ALOGE("%s", str.str().c_str()); @@ -276,23 +274,25 @@ const T& MultiStateCounter<T>::updateValue(const T& value, time_t timestamp) { return *returnValue; } -template <class T> -void MultiStateCounter<T>::incrementValue(const T& increment, time_t timestamp) { +template <class T, class V> +void MultiStateCounter<T, V>::incrementValue(const V& increment, time_t timestamp) { +// T newValue; +// newValue = lastValue; // Copy assignment, not initialization. T newValue = lastValue; add(&newValue, increment, 1 /* numerator */, 1 /* denominator */); updateValue(newValue, timestamp); } -template <class T> -void MultiStateCounter<T>::addValue(const T& value) { +template <class T, class V> +void MultiStateCounter<T, V>::addValue(const V& value) { if (!isEnabled) { return; } add(&states[currentState].counter, value, 1 /* numerator */, 1 /* denominator */); } -template <class T> -void MultiStateCounter<T>::reset() { +template <class T, class V> +void MultiStateCounter<T, V>::reset() { lastStateChangeTimestamp = -1; lastUpdateTimestamp = -1; for (int i = 0; i < stateCount; i++) { @@ -301,25 +301,26 @@ void MultiStateCounter<T>::reset() { } } -template <class T> -uint16_t MultiStateCounter<T>::getStateCount() { +template <class T, class V> +uint16_t MultiStateCounter<T, V>::getStateCount() { return stateCount; } -template <class T> -const T& MultiStateCounter<T>::getCount(state_t state) { +template <class T, class V> +const V& MultiStateCounter<T, V>::getCount(state_t state) { return states[state].counter; } -template <class T> -std::string MultiStateCounter<T>::toString() { +template <class T, class V> +std::string MultiStateCounter<T, V>::toString() { std::stringstream str; +// str << "LAST VALUE: " << valueToString(lastValue); str << "["; for (int i = 0; i < stateCount; i++) { if (i != 0) { str << ", "; } - str << i << ": " << valueToString(states[i].counter); + str << i << ": " << states[i].counter; if (states[i].timeInStateSinceUpdate > 0) { str << " timeInStateSinceUpdate: " << states[i].timeInStateSinceUpdate; } diff --git a/libs/battery/MultiStateCounterTest.cpp b/libs/battery/MultiStateCounterTest.cpp index a51a38a6c7..589b7fe4e3 100644 --- a/libs/battery/MultiStateCounterTest.cpp +++ b/libs/battery/MultiStateCounterTest.cpp @@ -21,7 +21,7 @@ namespace android { namespace battery { -typedef MultiStateCounter<double> DoubleMultiStateCounter; +typedef MultiStateCounter<double, double> DoubleMultiStateCounter; template <> bool DoubleMultiStateCounter::delta(const double& previousValue, const double& newValue, @@ -41,11 +41,6 @@ void DoubleMultiStateCounter::add(double* value1, const double& value2, const ui } } -template <> -std::string DoubleMultiStateCounter::valueToString(const double& v) const { - return std::to_string(v); -} - class MultiStateCounterTest : public testing::Test {}; TEST_F(MultiStateCounterTest, constructor) { diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index 6903cb5d68..2ef642a3a0 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -871,6 +871,10 @@ cc_library { symbol_file: "libbinder_rpc_unstable.map.txt", }, + header_abi_checker: { + enabled: false, + }, + // This library is intentionally limited to these targets, and it will be removed later. // Do not expand the visibility. visibility: [ diff --git a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp index 392ebb5b0a..48c0ea636b 100644 --- a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp +++ b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp @@ -37,8 +37,12 @@ enum class ARpcSession_FileDescriptorTransportMode { // Set `cid` to VMADDR_CID_LOCAL to only bind to the local vsock interface. // Returns an opaque handle to the running server instance, or null if the server // could not be started. +// Set |port| to VMADDR_PORT_ANY to pick an available ephemeral port. +// |assignedPort| will be set to the assigned port number if it is not null. +// This will be the provided |port|, or the chosen available ephemeral port when +// |port| is VMADDR_PORT_ANY. [[nodiscard]] ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int cid, - unsigned int port); + unsigned int port, unsigned int* assignedPort); // Starts a Unix domain RPC server with an open raw socket file descriptor // and a given root IBinder object. diff --git a/libs/binder/libbinder_rpc_unstable.cpp b/libs/binder/libbinder_rpc_unstable.cpp index 21537fc50d..a84a0c6e0b 100644 --- a/libs/binder/libbinder_rpc_unstable.cpp +++ b/libs/binder/libbinder_rpc_unstable.cpp @@ -81,7 +81,8 @@ RpcSession::FileDescriptorTransportMode toTransportMode( extern "C" { #ifndef __TRUSTY__ -ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int cid, unsigned int port) { +ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int cid, unsigned int port, + unsigned int* assignedPort) { auto server = RpcServer::make(); unsigned int bindCid = VMADDR_CID_ANY; // bind to the remote interface @@ -90,7 +91,7 @@ ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int cid, unsigned in cid = VMADDR_CID_ANY; // no need for a connection filter } - if (status_t status = server->setupVsockServer(bindCid, port); status != OK) { + if (status_t status = server->setupVsockServer(bindCid, port, assignedPort); status != OK) { ALOGE("Failed to set up vsock server with port %u error: %s", port, statusToString(status).c_str()); return nullptr; diff --git a/libs/binder/ndk/binder_rpc.cpp b/libs/binder/ndk/binder_rpc.cpp index 886eb4bdf8..53ab68e643 100644 --- a/libs/binder/ndk/binder_rpc.cpp +++ b/libs/binder/ndk/binder_rpc.cpp @@ -104,8 +104,8 @@ struct OnDeleteProviderHolder { }; ABinderRpc_AccessorProvider* ABinderRpc_registerAccessorProvider( - ABinderRpc_AccessorProvider_getAccessorCallback provider, const char** instances, - size_t numInstances, void* data, + ABinderRpc_AccessorProvider_getAccessorCallback provider, + const char* const* const instances, size_t numInstances, void* data, ABinderRpc_AccessorProviderUserData_deleteCallback onDelete) { if (provider == nullptr) { ALOGE("Null provider passed to ABinderRpc_registerAccessorProvider"); diff --git a/libs/binder/ndk/include_platform/android/binder_rpc.h b/libs/binder/ndk/include_platform/android/binder_rpc.h index 66667d33bd..7d54e2dad9 100644 --- a/libs/binder/ndk/include_platform/android/binder_rpc.h +++ b/libs/binder/ndk/include_platform/android/binder_rpc.h @@ -144,8 +144,9 @@ typedef void (*ABinderRpc_AccessorProviderUserData_deleteCallback)(void* _Nullab */ ABinderRpc_AccessorProvider* _Nullable ABinderRpc_registerAccessorProvider( ABinderRpc_AccessorProvider_getAccessorCallback _Nonnull provider, - const char* _Nullable* _Nonnull instances, size_t numInstances, void* _Nullable data, - ABinderRpc_AccessorProviderUserData_deleteCallback _Nullable onDelete) __INTRODUCED_IN(36); + const char* _Nullable const* const _Nonnull instances, size_t numInstances, + void* _Nullable data, ABinderRpc_AccessorProviderUserData_deleteCallback _Nullable onDelete) + __INTRODUCED_IN(36); /** * Remove an ABinderRpc_AccessorProvider from libbinder. This will remove references diff --git a/libs/binder/rust/rpcbinder/src/server/android.rs b/libs/binder/rust/rpcbinder/src/server/android.rs index 2ab34472a9..74ce315e30 100644 --- a/libs/binder/rust/rpcbinder/src/server/android.rs +++ b/libs/binder/rust/rpcbinder/src/server/android.rs @@ -18,7 +18,7 @@ use crate::session::FileDescriptorTransportMode; use binder::{unstable_api::AsNative, SpIBinder}; use binder_rpc_unstable_bindgen::ARpcServer; use foreign_types::{foreign_type, ForeignType, ForeignTypeRef}; -use std::ffi::CString; +use std::ffi::{c_uint, CString}; use std::io::{Error, ErrorKind}; use std::os::unix::io::{IntoRawFd, OwnedFd}; @@ -42,18 +42,29 @@ impl RpcServer { /// Creates a binder RPC server, serving the supplied binder service implementation on the given /// vsock port. Only connections from the given CID are accepted. /// - // Set `cid` to libc::VMADDR_CID_ANY to accept connections from any client. - // Set `cid` to libc::VMADDR_CID_LOCAL to only bind to the local vsock interface. - pub fn new_vsock(mut service: SpIBinder, cid: u32, port: u32) -> Result<RpcServer, Error> { + /// Set `cid` to [`libc::VMADDR_CID_ANY`] to accept connections from any client. + /// Set `cid` to [`libc::VMADDR_CID_LOCAL`] to only bind to the local vsock interface. + /// Set `port` to [`libc::VMADDR_PORT_ANY`] to pick an ephemeral port. + /// The assigned port is returned with RpcServer. + pub fn new_vsock( + mut service: SpIBinder, + cid: u32, + port: u32, + ) -> Result<(RpcServer, u32 /* assigned_port */), Error> { let service = service.as_native_mut(); + let mut assigned_port: c_uint = 0; // SAFETY: Service ownership is transferring to the server and won't be valid afterward. // Plus the binder objects are threadsafe. - unsafe { + let server = unsafe { Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newVsock( - service, cid, port, - )) - } + service, + cid, + port, + &mut assigned_port, + ))? + }; + Ok((server, assigned_port as _)) } /// Creates a binder RPC server, serving the supplied binder service implementation on the given diff --git a/libs/binder/tests/binderUtilsHostTest.cpp b/libs/binder/tests/binderUtilsHostTest.cpp index 6301c74842..a62ad96be1 100644 --- a/libs/binder/tests/binderUtilsHostTest.cpp +++ b/libs/binder/tests/binderUtilsHostTest.cpp @@ -56,7 +56,7 @@ TEST(UtilsHost, ExecuteLongRunning) { }); auto elapsedMs = millisSince(start); EXPECT_GE(elapsedMs, 1000); - EXPECT_LT(elapsedMs, 2000); + EXPECT_LT(elapsedMs, 3000); // b/377571547: higher to reduce flake ASSERT_TRUE(result.has_value()); EXPECT_EQ(std::nullopt, result->exitCode); @@ -65,7 +65,7 @@ TEST(UtilsHost, ExecuteLongRunning) { // ~CommandResult() called, child process is killed. // Assert that the second sleep does not finish. - EXPECT_LT(millisSince(start), 2000); + EXPECT_LT(millisSince(start), 3000); } TEST(UtilsHost, ExecuteLongRunning2) { diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp index 1243b214d3..80e148be9f 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -341,6 +341,10 @@ cc_library_shared { "libgui_aidl_headers", ], + static_libs: [ + "libsurfaceflingerflags", + ], + afdo: true, lto: { diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index fdc39ed765..495418b921 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -294,12 +294,7 @@ void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width, SurfaceComposerClient::Transaction t; if (surfaceControlChanged) { #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) - // SELinux policy may prevent this process from sending the BufferReleaseChannel's file - // descriptor to SurfaceFlinger, causing the entire transaction to be dropped. This - // transaction is applied separately to ensure we don't lose the other updates. - t.setApplyToken(mApplyToken) - .setBufferReleaseChannel(mSurfaceControl, mBufferReleaseProducer) - .apply(false /* synchronous */, true /* oneWay */); + updateBufferReleaseProducer(); #endif t.setFlags(mSurfaceControl, layer_state_t::eEnableBackpressure, layer_state_t::eEnableBackpressure); @@ -1335,6 +1330,20 @@ void BLASTBufferQueue::setApplyToken(sp<IBinder> applyToken) { #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) +void BLASTBufferQueue::updateBufferReleaseProducer() { + // SELinux policy may prevent this process from sending the BufferReleaseChannel's file + // descriptor to SurfaceFlinger, causing the entire transaction to be dropped. We send this + // transaction independently of any other updates to ensure those updates aren't lost. + SurfaceComposerClient::Transaction t; + status_t status = t.setApplyToken(mApplyToken) + .setBufferReleaseChannel(mSurfaceControl, mBufferReleaseProducer) + .apply(false /* synchronous */, true /* oneWay */); + if (status != OK) { + ALOGW("[%s] %s - failed to set buffer release channel on %s", mName.c_str(), + statusToString(status).c_str(), mSurfaceControl->getName().c_str()); + } +} + void BLASTBufferQueue::drainBufferReleaseConsumer() { ATRACE_CALL(); while (true) { diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index 66e7ddd915..e41f9bbf43 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -2735,8 +2735,8 @@ status_t Surface::unlockAndPost() bool Surface::waitForNextFrame(uint64_t lastFrame, nsecs_t timeout) { Mutex::Autolock lock(mMutex); - if (mNextFrameNumber > lastFrame) { - return true; + if (mLastFrameNumber > lastFrame) { + return true; } return mQueueBufferCondition.waitRelative(mMutex, timeout) == OK; } diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index a93fc926c2..c97dfd4cda 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -57,6 +57,7 @@ #include <ui/DisplayMode.h> #include <ui/DisplayState.h> #include <ui/DynamicDisplayInfo.h> +#include <ui/FrameRateCategoryRate.h> #include <android-base/thread_annotations.h> #include <gui/LayerStatePermissions.h> @@ -91,6 +92,7 @@ int64_t generateId() { } constexpr int64_t INVALID_VSYNC = -1; +const constexpr char* LOG_SURFACE_CONTROL_REGISTRY = "SurfaceControlRegistry"; } // namespace @@ -872,6 +874,7 @@ status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel const bool earlyWakeupEnd = parcel->readBool(); const int64_t desiredPresentTime = parcel->readInt64(); const bool isAutoTimestamp = parcel->readBool(); + const bool logCallPoints = parcel->readBool(); FrameTimelineInfo frameTimelineInfo; frameTimelineInfo.readFromParcel(parcel); @@ -999,6 +1002,7 @@ status_t SurfaceComposerClient::Transaction::writeToParcel(Parcel* parcel) const parcel->writeBool(mEarlyWakeupEnd); parcel->writeInt64(mDesiredPresentTime); parcel->writeBool(mIsAutoTimestamp); + parcel->writeBool(mLogCallPoints); mFrameTimelineInfo.writeToParcel(parcel); parcel->writeStrongBinder(mApplyToken); parcel->writeUint32(static_cast<uint32_t>(mDisplayStates.size())); @@ -1134,6 +1138,12 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::merge(Tr mergeFrameTimelineInfo(mFrameTimelineInfo, other.mFrameTimelineInfo); + mLogCallPoints |= other.mLogCallPoints; + if (mLogCallPoints) { + ALOG(LOG_DEBUG, LOG_SURFACE_CONTROL_REGISTRY, + "Transaction %" PRIu64 " merged with transaction %" PRIu64, other.getId(), mId); + } + other.clear(); return *this; } @@ -1153,6 +1163,7 @@ void SurfaceComposerClient::Transaction::clear() { mFrameTimelineInfo = {}; mApplyToken = nullptr; mMergedTransactionIds.clear(); + mLogCallPoints = false; } uint64_t SurfaceComposerClient::Transaction::getId() { @@ -1347,21 +1358,26 @@ status_t SurfaceComposerClient::Transaction::apply(bool synchronous, bool oneWay sp<IBinder> applyToken = mApplyToken ? mApplyToken : getDefaultApplyToken(); sp<ISurfaceComposer> sf(ComposerService::getComposerService()); - sf->setTransactionState(mFrameTimelineInfo, composerStates, displayStates, flags, applyToken, - mInputWindowCommands, mDesiredPresentTime, mIsAutoTimestamp, - mUncacheBuffers, hasListenerCallbacks, listenerCallbacks, mId, - mMergedTransactionIds); + status_t binderStatus = + sf->setTransactionState(mFrameTimelineInfo, composerStates, displayStates, flags, + applyToken, mInputWindowCommands, mDesiredPresentTime, + mIsAutoTimestamp, mUncacheBuffers, hasListenerCallbacks, + listenerCallbacks, mId, mMergedTransactionIds); mId = generateId(); // Clear the current states and flags clear(); - if (synchronous) { + if (synchronous && binderStatus == OK) { syncCallback->wait(); } + if (mLogCallPoints) { + ALOG(LOG_DEBUG, LOG_SURFACE_CONTROL_REGISTRY, "Transaction %" PRIu64 " applied", mId); + } + mStatus = NO_ERROR; - return NO_ERROR; + return binderStatus; } sp<IBinder> SurfaceComposerClient::Transaction::sApplyToken = new BBinder(); @@ -1375,7 +1391,7 @@ sp<IBinder> SurfaceComposerClient::Transaction::getDefaultApplyToken() { void SurfaceComposerClient::Transaction::setDefaultApplyToken(sp<IBinder> applyToken) { std::scoped_lock lock{sApplyTokenMutex}; - sApplyToken = applyToken; + sApplyToken = std::move(applyToken); } status_t SurfaceComposerClient::Transaction::sendSurfaceFlushJankDataTransaction( @@ -1390,6 +1406,11 @@ status_t SurfaceComposerClient::Transaction::sendSurfaceFlushJankDataTransaction t.registerSurfaceControlForCallback(sc); return t.apply(/*sync=*/false, /* oneWay=*/true); } + +void SurfaceComposerClient::Transaction::enableDebugLogCallPoints() { + mLogCallPoints = true; +} + // --------------------------------------------------------------------------- sp<IBinder> SurfaceComposerClient::createVirtualDisplay(const std::string& displayName, @@ -2108,13 +2129,13 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::notifyPr } SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setInputWindowInfo( - const sp<SurfaceControl>& sc, const WindowInfo& info) { + const sp<SurfaceControl>& sc, sp<WindowInfoHandle> info) { layer_state_t* s = getLayerState(sc); if (!s) { mStatus = BAD_INDEX; return *this; } - s->windowInfoHandle = new WindowInfoHandle(info); + s->windowInfoHandle = std::move(info); s->what |= layer_state_t::eInputInfoChanged; return *this; } @@ -2813,6 +2834,8 @@ void SurfaceComposerClient::getDynamicDisplayInfoInternal(gui::DynamicDisplayInf outInfo->gameContentTypeSupported = ginfo.gameContentTypeSupported; outInfo->preferredBootDisplayMode = ginfo.preferredBootDisplayMode; outInfo->hasArrSupport = ginfo.hasArrSupport; + outInfo->frameRateCategoryRate = ui::FrameRateCategoryRate(ginfo.frameRateCategoryRate.normal, + ginfo.frameRateCategoryRate.high); } status_t SurfaceComposerClient::getDynamicDisplayInfoFromId(int64_t displayId, diff --git a/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl b/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl index 70873b001b..67cc273fce 100644 --- a/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl +++ b/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl @@ -17,6 +17,7 @@ package android.gui; import android.gui.DisplayMode; +import android.gui.FrameRateCategoryRate; import android.gui.HdrCapabilities; // Information about a physical display which may change on hotplug reconnect. @@ -46,4 +47,7 @@ parcelable DynamicDisplayInfo { // Represents whether display supports ARR. boolean hasArrSupport; + + // Represents frame rate for FrameRateCategory Normal and High. + FrameRateCategoryRate frameRateCategoryRate; } diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.cpp b/libs/gui/aidl/android/gui/FrameRateCategoryRate.aidl index 2323ebb4aa..f30280139f 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.cpp +++ b/libs/gui/aidl/android/gui/FrameRateCategoryRate.aidl @@ -1,5 +1,5 @@ /* - * Copyright 2022 The Android Open Source Project + * Copyright 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,11 +14,11 @@ * limitations under the License. */ -#include "mock/DisplayHardware/MockIPower.h" +package android.gui; -namespace android::Hwc2::mock { - -// Explicit default instantiation is recommended. -MockIPower::MockIPower() = default; - -} // namespace android::Hwc2::mock +/** @hide */ +// Represents frame rate for FrameRateCategory Normal and High. +parcelable FrameRateCategoryRate { + float normal; + float high; +}
\ No newline at end of file diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h index 4fd44e52f4..8894b66c6d 100644 --- a/libs/gui/include/gui/BLASTBufferQueue.h +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -325,8 +325,14 @@ private: std::unique_ptr<gui::BufferReleaseChannel::ConsumerEndpoint> mBufferReleaseConsumer; std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint> mBufferReleaseProducer; + void updateBufferReleaseProducer() REQUIRES(mMutex); void drainBufferReleaseConsumer(); + // BufferReleaseReader is used to do blocking but interruptible reads from the buffer + // release channel. To implement this, BufferReleaseReader owns an epoll file descriptor that + // is configured to wake up when either the BufferReleaseReader::ConsumerEndpoint or an eventfd + // becomes readable. Interrupts are necessary because a free buffer may become available for + // reasons other than a buffer release from the producer. class BufferReleaseReader { public: explicit BufferReleaseReader(BLASTBufferQueue&); diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index 5ea0c1619b..7c6b3416f8 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -437,6 +437,8 @@ public: static void mergeFrameTimelineInfo(FrameTimelineInfo& t, const FrameTimelineInfo& other); // Tracks registered callbacks sp<TransactionCompletedListener> mTransactionCompletedListener = nullptr; + // Prints debug logs when enabled. + bool mLogCallPoints = false; protected: std::unordered_map<sp<IBinder>, ComposerState, IBinderHash> mComposerStates; @@ -685,7 +687,8 @@ public: // ONLY FOR BLAST ADAPTER Transaction& notifyProducerDisconnect(const sp<SurfaceControl>& sc); - Transaction& setInputWindowInfo(const sp<SurfaceControl>& sc, const gui::WindowInfo& info); + Transaction& setInputWindowInfo(const sp<SurfaceControl>& sc, + sp<gui::WindowInfoHandle> info); Transaction& setFocusedWindow(const gui::FocusRequest& request); Transaction& addWindowInfosReportedListener( @@ -809,6 +812,7 @@ public: static void setDefaultApplyToken(sp<IBinder> applyToken); static status_t sendSurfaceFlushJankDataTransaction(const sp<SurfaceControl>& sc); + void enableDebugLogCallPoints(); }; status_t clearLayerFrameStats(const sp<IBinder>& token) const; diff --git a/libs/gui/libgui_flags.aconfig b/libs/gui/libgui_flags.aconfig index 1c7e0e439c..22d32e9769 100644 --- a/libs/gui/libgui_flags.aconfig +++ b/libs/gui/libgui_flags.aconfig @@ -2,6 +2,14 @@ package: "com.android.graphics.libgui.flags" container: "system" flag { + name: "apply_picture_profiles" + namespace: "tv_os_media" + description: "This flag controls sending picture profiles from BBQ to Composer HAL" + bug: "337330263" + is_fixed_read_only: true +} # apply_picture_profiles + +flag { name: "bq_setframerate" namespace: "core_graphics" description: "This flag controls plumbing setFrameRate thru BufferQueue" diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp index a481d12ae4..0e84d68eec 100644 --- a/libs/gui/tests/EndToEndNativeInputTest.cpp +++ b/libs/gui/tests/EndToEndNativeInputTest.cpp @@ -112,7 +112,7 @@ public: mInputFlinger = getInputFlinger(); if (noInputChannel) { - mInputInfo.setInputConfig(WindowInfo::InputConfig::NO_INPUT_CHANNEL, true); + mInputInfo->editInfo()->setInputConfig(WindowInfo::InputConfig::NO_INPUT_CHANNEL, true); } else { android::os::InputChannelCore tempChannel; android::binder::Status result = @@ -121,21 +121,21 @@ public: ADD_FAILURE() << "binder call to createInputChannel failed"; } mClientChannel = InputChannel::create(std::move(tempChannel)); - mInputInfo.token = mClientChannel->getConnectionToken(); + mInputInfo->editInfo()->token = mClientChannel->getConnectionToken(); mInputConsumer = new InputConsumer(mClientChannel); } - mInputInfo.name = "Test info"; - mInputInfo.dispatchingTimeout = 5s; - mInputInfo.globalScaleFactor = 1.0; - mInputInfo.touchableRegion.orSelf(Rect(0, 0, width, height)); + mInputInfo->editInfo()->name = "Test info"; + mInputInfo->editInfo()->dispatchingTimeout = 5s; + mInputInfo->editInfo()->globalScaleFactor = 1.0; + mInputInfo->editInfo()->touchableRegion.orSelf(Rect(0, 0, width, height)); InputApplicationInfo aInfo; aInfo.token = new BBinder(); aInfo.name = "Test app info"; aInfo.dispatchingTimeoutMillis = std::chrono::duration_cast<std::chrono::milliseconds>(DISPATCHING_TIMEOUT).count(); - mInputInfo.applicationInfo = aInfo; + mInputInfo->editInfo()->applicationInfo = aInfo; } static std::unique_ptr<InputSurface> makeColorInputSurface(const sp<SurfaceComposerClient>& scc, @@ -183,20 +183,6 @@ public: return std::make_unique<InputSurface>(surfaceControl, width, height); } - InputEvent* consumeEvent(std::chrono::milliseconds timeout = 3000ms) { - mClientChannel->waitForMessage(timeout); - - InputEvent* ev; - uint32_t seqId; - status_t consumed = mInputConsumer->consume(&mInputEventFactory, true, -1, &seqId, &ev); - if (consumed != OK) { - return nullptr; - } - status_t status = mInputConsumer->sendFinishedSignal(seqId, true); - EXPECT_EQ(OK, status) << "Could not send finished signal"; - return ev; - } - void assertFocusChange(bool hasFocus) { InputEvent* ev = consumeEvent(); ASSERT_NE(ev, nullptr); @@ -314,8 +300,8 @@ public: void requestFocus(ui::LogicalDisplayId displayId = ui::LogicalDisplayId::DEFAULT) { SurfaceComposerClient::Transaction t; FocusRequest request; - request.token = mInputInfo.token; - request.windowName = mInputInfo.name; + request.token = mInputInfo->getInfo()->token; + request.windowName = mInputInfo->getInfo()->name; request.timestamp = systemTime(SYSTEM_TIME_MONOTONIC); request.displayId = displayId.val(); t.setFocusedWindow(request); @@ -323,14 +309,30 @@ public: } public: + // But should be private + sp<gui::WindowInfoHandle> mInputInfo = sp<gui::WindowInfoHandle>::make(); sp<SurfaceControl> mSurfaceControl; + +private: std::shared_ptr<InputChannel> mClientChannel; sp<IInputFlinger> mInputFlinger; - WindowInfo mInputInfo; - PreallocatedInputEventFactory mInputEventFactory; InputConsumer* mInputConsumer; + + InputEvent* consumeEvent(std::chrono::milliseconds timeout = 3000ms) { + mClientChannel->waitForMessage(timeout); + + InputEvent* ev; + uint32_t seqId; + status_t consumed = mInputConsumer->consume(&mInputEventFactory, true, -1, &seqId, &ev); + if (consumed != OK) { + return nullptr; + } + status_t status = mInputConsumer->sendFinishedSignal(seqId, true); + EXPECT_EQ(OK, status) << "Could not send finished signal"; + return ev; + } }; class BlastInputSurface : public InputSurface { @@ -458,7 +460,7 @@ TEST_F(InputSurfacesTest, can_receive_input) { injectTap(101, 101); - EXPECT_NE(surface->consumeEvent(), nullptr); + surface->expectTap(1, 1); } /** @@ -521,7 +523,7 @@ TEST_F(InputSurfacesTest, input_respects_surface_insets) { std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100); bgSurface->showAt(100, 100); - fgSurface->mInputInfo.surfaceInset = 5; + fgSurface->mInputInfo->editInfo()->surfaceInset = 5; fgSurface->showAt(100, 100); injectTap(106, 106); @@ -536,8 +538,8 @@ TEST_F(InputSurfacesTest, input_respects_surface_insets_with_replaceTouchableReg std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100); bgSurface->showAt(100, 100); - fgSurface->mInputInfo.surfaceInset = 5; - fgSurface->mInputInfo.replaceTouchableRegionWithCrop = true; + fgSurface->mInputInfo->editInfo()->surfaceInset = 5; + fgSurface->mInputInfo->editInfo()->replaceTouchableRegionWithCrop = true; fgSurface->showAt(100, 100); injectTap(106, 106); @@ -553,7 +555,7 @@ TEST_F(InputSurfacesTest, input_respects_cropped_surface_insets) { std::unique_ptr<InputSurface> childSurface = makeSurface(100, 100); parentSurface->showAt(100, 100); - childSurface->mInputInfo.surfaceInset = 10; + childSurface->mInputInfo->editInfo()->surfaceInset = 10; childSurface->showAt(100, 100); childSurface->doTransaction([&](auto& t, auto& sc) { @@ -574,7 +576,7 @@ TEST_F(InputSurfacesTest, input_respects_scaled_surface_insets) { std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100); bgSurface->showAt(100, 100); - fgSurface->mInputInfo.surfaceInset = 5; + fgSurface->mInputInfo->editInfo()->surfaceInset = 5; fgSurface->showAt(100, 100); fgSurface->doTransaction([&](auto& t, auto& sc) { t.setMatrix(sc, 2.0, 0, 0, 4.0); }); @@ -593,7 +595,7 @@ TEST_F(InputSurfacesTest, input_respects_scaled_surface_insets_overflow) { bgSurface->showAt(100, 100); // In case we pass the very big inset without any checking. - fgSurface->mInputInfo.surfaceInset = INT32_MAX; + fgSurface->mInputInfo->editInfo()->surfaceInset = INT32_MAX; fgSurface->showAt(100, 100); fgSurface->doTransaction([&](auto& t, auto& sc) { t.setMatrix(sc, 2.0, 0, 0, 2.0); }); @@ -606,13 +608,13 @@ TEST_F(InputSurfacesTest, input_respects_scaled_surface_insets_overflow) { TEST_F(InputSurfacesTest, touchable_region) { std::unique_ptr<InputSurface> surface = makeSurface(100, 100); - surface->mInputInfo.touchableRegion.set(Rect{19, 29, 21, 31}); + surface->mInputInfo->editInfo()->touchableRegion.set(Rect{19, 29, 21, 31}); surface->showAt(11, 22); // A tap within the surface but outside the touchable region should not be sent to the surface. injectTap(20, 30); - EXPECT_EQ(surface->consumeEvent(/*timeout=*/200ms), nullptr); + surface->assertNoEvent(); injectTap(31, 52); surface->expectTap(20, 30); @@ -627,7 +629,8 @@ TEST_F(InputSurfacesTest, input_respects_touchable_region_offset_overflow) { // Since the surface is offset from the origin, the touchable region will be transformed into // display space, which would trigger an overflow or an underflow. Ensure that we are protected // against such a situation. - fgSurface->mInputInfo.touchableRegion.orSelf(Rect{INT32_MIN, INT32_MIN, INT32_MAX, INT32_MAX}); + fgSurface->mInputInfo->editInfo()->touchableRegion.orSelf( + Rect{INT32_MIN, INT32_MIN, INT32_MAX, INT32_MAX}); fgSurface->showAt(100, 100); @@ -642,7 +645,8 @@ TEST_F(InputSurfacesTest, input_respects_scaled_touchable_region_overflow) { std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100); bgSurface->showAt(0, 0); - fgSurface->mInputInfo.touchableRegion.orSelf(Rect{INT32_MIN, INT32_MIN, INT32_MAX, INT32_MAX}); + fgSurface->mInputInfo->editInfo()->touchableRegion.orSelf( + Rect{INT32_MIN, INT32_MIN, INT32_MAX, INT32_MAX}); fgSurface->showAt(0, 0); fgSurface->doTransaction([&](auto& t, auto& sc) { t.setMatrix(sc, 2.0, 0, 0, 2.0); }); @@ -812,7 +816,7 @@ TEST_F(InputSurfacesTest, rotate_surface_with_scale) { TEST_F(InputSurfacesTest, rotate_surface_with_scale_and_insets) { std::unique_ptr<InputSurface> surface = makeSurface(100, 100); - surface->mInputInfo.surfaceInset = 5; + surface->mInputInfo->editInfo()->surfaceInset = 5; surface->showAt(100, 100); surface->doTransaction([](auto& t, auto& sc) { @@ -841,11 +845,12 @@ TEST_F(InputSurfacesTest, touch_flag_obscured) { // Add non touchable window to fully cover touchable window. Window behind gets touch, but // with flag AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED std::unique_ptr<InputSurface> nonTouchableSurface = makeSurface(100, 100); - nonTouchableSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true); - nonTouchableSurface->mInputInfo.ownerUid = gui::Uid{22222}; + nonTouchableSurface->mInputInfo->editInfo() + ->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true); + nonTouchableSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222}; // Overriding occlusion mode otherwise the touch would be discarded at InputDispatcher by // the default obscured/untrusted touch filter introduced in S. - nonTouchableSurface->mInputInfo.touchOcclusionMode = TouchOcclusionMode::ALLOW; + nonTouchableSurface->mInputInfo->editInfo()->touchOcclusionMode = TouchOcclusionMode::ALLOW; nonTouchableSurface->showAt(100, 100); injectTap(190, 199); @@ -861,10 +866,12 @@ TEST_F(InputSurfacesTest, touch_flag_partially_obscured_with_crop) { // AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100); std::unique_ptr<InputSurface> nonTouchableSurface = makeSurface(100, 100); - nonTouchableSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true); - parentSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true); - nonTouchableSurface->mInputInfo.ownerUid = gui::Uid{22222}; - parentSurface->mInputInfo.ownerUid = gui::Uid{22222}; + nonTouchableSurface->mInputInfo->editInfo() + ->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true); + parentSurface->mInputInfo->editInfo()->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, + true); + nonTouchableSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222}; + parentSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222}; nonTouchableSurface->showAt(0, 0); parentSurface->showAt(100, 100); @@ -885,10 +892,12 @@ TEST_F(InputSurfacesTest, touch_not_obscured_with_crop) { // the touchable window. Window behind gets touch with no obscured flags. std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100); std::unique_ptr<InputSurface> nonTouchableSurface = makeSurface(100, 100); - nonTouchableSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true); - parentSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true); - nonTouchableSurface->mInputInfo.ownerUid = gui::Uid{22222}; - parentSurface->mInputInfo.ownerUid = gui::Uid{22222}; + nonTouchableSurface->mInputInfo->editInfo() + ->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true); + parentSurface->mInputInfo->editInfo()->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, + true); + nonTouchableSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222}; + parentSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222}; nonTouchableSurface->showAt(0, 0); parentSurface->showAt(50, 50); @@ -906,8 +915,9 @@ TEST_F(InputSurfacesTest, touch_not_obscured_with_zero_sized_bql) { std::unique_ptr<InputSurface> bufferSurface = InputSurface::makeBufferInputSurface(mComposerClient, 0, 0); - bufferSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true); - bufferSurface->mInputInfo.ownerUid = gui::Uid{22222}; + bufferSurface->mInputInfo->editInfo()->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, + true); + bufferSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222}; surface->showAt(10, 10); bufferSurface->showAt(50, 50, Rect::EMPTY_RECT); @@ -921,8 +931,9 @@ TEST_F(InputSurfacesTest, touch_not_obscured_with_zero_sized_blast) { std::unique_ptr<BlastInputSurface> bufferSurface = BlastInputSurface::makeBlastInputSurface(mComposerClient, 0, 0); - bufferSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true); - bufferSurface->mInputInfo.ownerUid = gui::Uid{22222}; + bufferSurface->mInputInfo->editInfo()->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, + true); + bufferSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222}; surface->showAt(10, 10); bufferSurface->showAt(50, 50, Rect::EMPTY_RECT); @@ -965,13 +976,14 @@ TEST_F(InputSurfacesTest, strict_unobscured_input_scaled_without_crop_window) { TEST_F(InputSurfacesTest, strict_unobscured_input_obscured_window) { std::unique_ptr<InputSurface> surface = makeSurface(100, 100); - surface->mInputInfo.ownerUid = gui::Uid{11111}; + surface->mInputInfo->editInfo()->ownerUid = gui::Uid{11111}; surface->doTransaction( [&](auto& t, auto& sc) { t.setDropInputMode(sc, gui::DropInputMode::OBSCURED); }); surface->showAt(100, 100); std::unique_ptr<InputSurface> obscuringSurface = makeSurface(100, 100); - obscuringSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true); - obscuringSurface->mInputInfo.ownerUid = gui::Uid{22222}; + obscuringSurface->mInputInfo->editInfo()->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, + true); + obscuringSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222}; obscuringSurface->showAt(100, 100); injectTap(101, 101); surface->assertNoEvent(); @@ -984,13 +996,14 @@ TEST_F(InputSurfacesTest, strict_unobscured_input_obscured_window) { TEST_F(InputSurfacesTest, strict_unobscured_input_partially_obscured_window) { std::unique_ptr<InputSurface> surface = makeSurface(100, 100); - surface->mInputInfo.ownerUid = gui::Uid{11111}; + surface->mInputInfo->editInfo()->ownerUid = gui::Uid{11111}; surface->doTransaction( [&](auto& t, auto& sc) { t.setDropInputMode(sc, gui::DropInputMode::OBSCURED); }); surface->showAt(100, 100); std::unique_ptr<InputSurface> obscuringSurface = makeSurface(100, 100); - obscuringSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true); - obscuringSurface->mInputInfo.ownerUid = gui::Uid{22222}; + obscuringSurface->mInputInfo->editInfo()->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, + true); + obscuringSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222}; obscuringSurface->showAt(190, 190); injectTap(101, 101); @@ -1054,7 +1067,7 @@ TEST_F(InputSurfacesTest, ignore_touch_region_with_zero_sized_blast) { BlastInputSurface::makeBlastInputSurface(mComposerClient, 0, 0); surface->showAt(100, 100); - bufferSurface->mInputInfo.touchableRegion.orSelf(Rect(0, 0, 200, 200)); + bufferSurface->mInputInfo->editInfo()->touchableRegion.orSelf(Rect(0, 0, 200, 200)); bufferSurface->showAt(100, 100, Rect::EMPTY_RECT); injectTap(101, 101); @@ -1097,8 +1110,8 @@ TEST_F(InputSurfacesTest, cropped_container_replaces_touchable_region_with_null_ InputSurface::makeContainerInputSurface(mComposerClient, 100, 100); containerSurface->doTransaction( [&](auto& t, auto& sc) { t.reparent(sc, parentContainer->mSurfaceControl); }); - containerSurface->mInputInfo.replaceTouchableRegionWithCrop = true; - containerSurface->mInputInfo.touchableRegionCropHandle = nullptr; + containerSurface->mInputInfo->editInfo()->replaceTouchableRegionWithCrop = true; + containerSurface->mInputInfo->editInfo()->touchableRegionCropHandle = nullptr; parentContainer->showAt(10, 10, Rect(0, 0, 20, 20)); containerSurface->showAt(10, 10, Rect(0, 0, 5, 5)); @@ -1116,14 +1129,19 @@ TEST_F(InputSurfacesTest, cropped_container_replaces_touchable_region_with_null_ * in its parent's touchable region. The input events should be in the layer's coordinate space. */ TEST_F(InputSurfacesTest, uncropped_container_replaces_touchable_region_with_null_crop) { + std::unique_ptr<InputSurface> bgContainer = + InputSurface::makeContainerInputSurface(mComposerClient, 0, 0); std::unique_ptr<InputSurface> parentContainer = InputSurface::makeContainerInputSurface(mComposerClient, 0, 0); std::unique_ptr<InputSurface> containerSurface = InputSurface::makeContainerInputSurface(mComposerClient, 100, 100); containerSurface->doTransaction( [&](auto& t, auto& sc) { t.reparent(sc, parentContainer->mSurfaceControl); }); - containerSurface->mInputInfo.replaceTouchableRegionWithCrop = true; - containerSurface->mInputInfo.touchableRegionCropHandle = nullptr; + containerSurface->mInputInfo->editInfo()->replaceTouchableRegionWithCrop = true; + containerSurface->mInputInfo->editInfo()->touchableRegionCropHandle = nullptr; + parentContainer->doTransaction( + [&](auto& t, auto& sc) { t.reparent(sc, bgContainer->mSurfaceControl); }); + bgContainer->showAt(0, 0, Rect(0, 0, 100, 100)); parentContainer->showAt(10, 10, Rect(0, 0, 20, 20)); containerSurface->showAt(10, 10, Rect::INVALID_RECT); @@ -1147,8 +1165,8 @@ TEST_F(InputSurfacesTest, replace_touchable_region_with_crop) { std::unique_ptr<InputSurface> containerSurface = InputSurface::makeContainerInputSurface(mComposerClient, 100, 100); - containerSurface->mInputInfo.replaceTouchableRegionWithCrop = true; - containerSurface->mInputInfo.touchableRegionCropHandle = + containerSurface->mInputInfo->editInfo()->replaceTouchableRegionWithCrop = true; + containerSurface->mInputInfo->editInfo()->touchableRegionCropHandle = cropLayer->mSurfaceControl->getHandle(); containerSurface->showAt(10, 10, Rect::INVALID_RECT); diff --git a/libs/input/Android.bp b/libs/input/Android.bp index e4e81adf58..a4ae54b351 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -217,6 +217,7 @@ cc_library { ], srcs: [ "AccelerationCurve.cpp", + "CoordinateFilter.cpp", "Input.cpp", "InputConsumer.cpp", "InputConsumerNoResampling.cpp", @@ -230,6 +231,7 @@ cc_library { "KeyLayoutMap.cpp", "MotionPredictor.cpp", "MotionPredictorMetricsManager.cpp", + "OneEuroFilter.cpp", "PrintTools.cpp", "PropertyMap.cpp", "Resampler.cpp", diff --git a/libs/input/CoordinateFilter.cpp b/libs/input/CoordinateFilter.cpp new file mode 100644 index 0000000000..d231474577 --- /dev/null +++ b/libs/input/CoordinateFilter.cpp @@ -0,0 +1,31 @@ +/** + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "CoordinateFilter" + +#include <input/CoordinateFilter.h> + +namespace android { + +CoordinateFilter::CoordinateFilter(float minCutoffFreq, float beta) + : mXFilter{minCutoffFreq, beta}, mYFilter{minCutoffFreq, beta} {} + +void CoordinateFilter::filter(std::chrono::duration<float> timestamp, PointerCoords& coords) { + coords.setAxisValue(AMOTION_EVENT_AXIS_X, mXFilter.filter(timestamp, coords.getX())); + coords.setAxisValue(AMOTION_EVENT_AXIS_Y, mYFilter.filter(timestamp, coords.getY())); +} + +} // namespace android diff --git a/libs/input/InputEventLabels.cpp b/libs/input/InputEventLabels.cpp index 8db0ca588b..b537feb68f 100644 --- a/libs/input/InputEventLabels.cpp +++ b/libs/input/InputEventLabels.cpp @@ -350,7 +350,26 @@ namespace android { DEFINE_KEYCODE(MACRO_3), \ DEFINE_KEYCODE(MACRO_4), \ DEFINE_KEYCODE(EMOJI_PICKER), \ - DEFINE_KEYCODE(SCREENSHOT) + DEFINE_KEYCODE(SCREENSHOT), \ + DEFINE_KEYCODE(DICTATE), \ + DEFINE_KEYCODE(NEW), \ + DEFINE_KEYCODE(CLOSE), \ + DEFINE_KEYCODE(DO_NOT_DISTURB), \ + DEFINE_KEYCODE(PRINT), \ + DEFINE_KEYCODE(LOCK), \ + DEFINE_KEYCODE(FULLSCREEN), \ + DEFINE_KEYCODE(F13), \ + DEFINE_KEYCODE(F14), \ + DEFINE_KEYCODE(F15), \ + DEFINE_KEYCODE(F16), \ + DEFINE_KEYCODE(F17), \ + DEFINE_KEYCODE(F18), \ + DEFINE_KEYCODE(F19),\ + DEFINE_KEYCODE(F20), \ + DEFINE_KEYCODE(F21), \ + DEFINE_KEYCODE(F22), \ + DEFINE_KEYCODE(F23), \ + DEFINE_KEYCODE(F24) // NOTE: If you add a new axis here you must also add it to several other files. // Refer to frameworks/base/core/java/android/view/MotionEvent.java for the full list. diff --git a/libs/input/KeyboardClassifier.cpp b/libs/input/KeyboardClassifier.cpp index 0c2c7be582..2a83919283 100644 --- a/libs/input/KeyboardClassifier.cpp +++ b/libs/input/KeyboardClassifier.cpp @@ -57,14 +57,14 @@ void KeyboardClassifier::notifyKeyboardChanged(DeviceId deviceId, uint32_t deviceClasses) { if (mRustClassifier) { RustInputDeviceIdentifier rustIdentifier; - rustIdentifier.name = identifier.name; - rustIdentifier.location = identifier.location; - rustIdentifier.unique_id = identifier.uniqueId; + rustIdentifier.name = rust::String::lossy(identifier.name); + rustIdentifier.location = rust::String::lossy(identifier.location); + rustIdentifier.unique_id = rust::String::lossy(identifier.uniqueId); rustIdentifier.bus = identifier.bus; rustIdentifier.vendor = identifier.vendor; rustIdentifier.product = identifier.product; rustIdentifier.version = identifier.version; - rustIdentifier.descriptor = identifier.descriptor; + rustIdentifier.descriptor = rust::String::lossy(identifier.descriptor); android::input::keyboardClassifier::notifyKeyboardChanged(**mRustClassifier, deviceId, rustIdentifier, deviceClasses); } else { diff --git a/libs/input/OneEuroFilter.cpp b/libs/input/OneEuroFilter.cpp new file mode 100644 index 0000000000..400d7c9ab0 --- /dev/null +++ b/libs/input/OneEuroFilter.cpp @@ -0,0 +1,79 @@ +/** + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "OneEuroFilter" + +#include <chrono> +#include <cmath> + +#include <android-base/logging.h> +#include <input/CoordinateFilter.h> + +namespace android { +namespace { + +inline float cutoffFreq(float minCutoffFreq, float beta, float filteredSpeed) { + return minCutoffFreq + beta * std::abs(filteredSpeed); +} + +inline float smoothingFactor(std::chrono::duration<float> samplingPeriod, float cutoffFreq) { + return samplingPeriod.count() / (samplingPeriod.count() + (1.0 / (2.0 * M_PI * cutoffFreq))); +} + +inline float lowPassFilter(float rawPosition, float prevFilteredPosition, float smoothingFactor) { + return smoothingFactor * rawPosition + (1 - smoothingFactor) * prevFilteredPosition; +} + +} // namespace + +OneEuroFilter::OneEuroFilter(float minCutoffFreq, float beta, float speedCutoffFreq) + : mMinCutoffFreq{minCutoffFreq}, mBeta{beta}, mSpeedCutoffFreq{speedCutoffFreq} {} + +float OneEuroFilter::filter(std::chrono::duration<float> timestamp, float rawPosition) { + LOG_IF(FATAL, mPrevFilteredPosition.has_value() && (timestamp <= *mPrevTimestamp)) + << "Timestamp must be greater than mPrevTimestamp"; + + const std::chrono::duration<float> samplingPeriod = (mPrevTimestamp.has_value()) + ? (timestamp - *mPrevTimestamp) + : std::chrono::duration<float>{1.0}; + + const float rawVelocity = (mPrevFilteredPosition.has_value()) + ? ((rawPosition - *mPrevFilteredPosition) / samplingPeriod.count()) + : 0.0; + + const float speedSmoothingFactor = smoothingFactor(samplingPeriod, mSpeedCutoffFreq); + + const float filteredVelocity = (mPrevFilteredVelocity.has_value()) + ? lowPassFilter(rawVelocity, *mPrevFilteredVelocity, speedSmoothingFactor) + : rawVelocity; + + const float positionCutoffFreq = cutoffFreq(mMinCutoffFreq, mBeta, filteredVelocity); + + const float positionSmoothingFactor = smoothingFactor(samplingPeriod, positionCutoffFreq); + + const float filteredPosition = (mPrevFilteredPosition.has_value()) + ? lowPassFilter(rawPosition, *mPrevFilteredPosition, positionSmoothingFactor) + : rawPosition; + + mPrevTimestamp = timestamp; + mPrevRawPosition = rawPosition; + mPrevFilteredVelocity = filteredVelocity; + mPrevFilteredPosition = filteredPosition; + + return filteredPosition; +} + +} // namespace android diff --git a/libs/input/Resampler.cpp b/libs/input/Resampler.cpp index 056db093d1..3ab132d550 100644 --- a/libs/input/Resampler.cpp +++ b/libs/input/Resampler.cpp @@ -389,4 +389,34 @@ void LegacyResampler::resampleMotionEvent(nanoseconds frameTime, MotionEvent& mo mLastRealSample = *(mLatestSamples.end() - 1); } +// --- FilteredLegacyResampler --- + +FilteredLegacyResampler::FilteredLegacyResampler(float minCutoffFreq, float beta) + : mResampler{}, mMinCutoffFreq{minCutoffFreq}, mBeta{beta} {} + +void FilteredLegacyResampler::resampleMotionEvent(std::chrono::nanoseconds requestedFrameTime, + MotionEvent& motionEvent, + const InputMessage* futureSample) { + mResampler.resampleMotionEvent(requestedFrameTime, motionEvent, futureSample); + const size_t numSamples = motionEvent.getHistorySize() + 1; + for (size_t sampleIndex = 0; sampleIndex < numSamples; ++sampleIndex) { + for (size_t pointerIndex = 0; pointerIndex < motionEvent.getPointerCount(); + ++pointerIndex) { + const int32_t pointerId = motionEvent.getPointerProperties(pointerIndex)->id; + const nanoseconds eventTime = + nanoseconds{motionEvent.getHistoricalEventTime(sampleIndex)}; + // Refer to the static function `setMotionEventPointerCoords` for a justification of + // casting away const. + PointerCoords& pointerCoords = const_cast<PointerCoords&>( + *(motionEvent.getHistoricalRawPointerCoords(pointerIndex, sampleIndex))); + const auto& [iter, _] = mFilteredPointers.try_emplace(pointerId, mMinCutoffFreq, mBeta); + iter->second.filter(eventTime, pointerCoords); + } + } +} + +std::chrono::nanoseconds FilteredLegacyResampler::getResampleLatency() const { + return mResampler.getResampleLatency(); +} + } // namespace android diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig index 484a5df8ca..fd7704815f 100644 --- a/libs/input/input_flags.aconfig +++ b/libs/input/input_flags.aconfig @@ -224,3 +224,10 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "connected_displays_cursor" + namespace: "lse_desktop_experience" + description: "Allow cursor to transition across multiple connected displays" + bug: "362719483" +} diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index 661c9f739f..46e819061f 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -25,6 +25,7 @@ cc_test { "InputVerifier_test.cpp", "MotionPredictor_test.cpp", "MotionPredictorMetricsManager_test.cpp", + "OneEuroFilter_test.cpp", "Resampler_test.cpp", "RingBuffer_test.cpp", "TestInputChannel.cpp", diff --git a/libs/input/tests/OneEuroFilter_test.cpp b/libs/input/tests/OneEuroFilter_test.cpp new file mode 100644 index 0000000000..270e789c84 --- /dev/null +++ b/libs/input/tests/OneEuroFilter_test.cpp @@ -0,0 +1,134 @@ +/** + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <input/OneEuroFilter.h> + +#include <algorithm> +#include <chrono> +#include <cmath> +#include <numeric> +#include <vector> + +#include <gtest/gtest.h> + +#include <input/Input.h> + +namespace android { +namespace { + +using namespace std::literals::chrono_literals; +using std::chrono::duration; + +struct Sample { + duration<double> timestamp{}; + double value{}; + + friend bool operator<(const Sample& lhs, const Sample& rhs) { return lhs.value < rhs.value; } +}; + +/** + * Generates a sinusoidal signal with the passed frequency and amplitude. + */ +std::vector<Sample> generateSinusoidalSignal(duration<double> signalDuration, + double samplingFrequency, double signalFrequency, + double amplitude) { + std::vector<Sample> signal; + const duration<double> samplingPeriod{1.0 / samplingFrequency}; + for (duration<double> timestamp{0.0}; timestamp < signalDuration; timestamp += samplingPeriod) { + signal.push_back( + Sample{timestamp, + amplitude * std::sin(2.0 * M_PI * signalFrequency * timestamp.count())}); + } + return signal; +} + +double meanAbsoluteError(const std::vector<Sample>& filteredSignal, + const std::vector<Sample>& signal) { + if (filteredSignal.size() != signal.size()) { + ADD_FAILURE() << "filteredSignal and signal do not have equal number of samples"; + return std::numeric_limits<double>::max(); + } + std::vector<double> absoluteError; + for (size_t sampleIndex = 0; sampleIndex < signal.size(); ++sampleIndex) { + absoluteError.push_back( + std::abs(filteredSignal[sampleIndex].value - signal[sampleIndex].value)); + } + if (absoluteError.empty()) { + ADD_FAILURE() << "Zero division. absoluteError is empty"; + return std::numeric_limits<double>::max(); + } + return std::accumulate(absoluteError.begin(), absoluteError.end(), 0.0) / absoluteError.size(); +} + +double maxAbsoluteAmplitude(const std::vector<Sample>& signal) { + if (signal.empty()) { + ADD_FAILURE() << "Max absolute value amplitude does not exist. Signal is empty"; + return std::numeric_limits<double>::max(); + } + std::vector<Sample> absoluteSignal; + for (const Sample& sample : signal) { + absoluteSignal.push_back(Sample{sample.timestamp, std::abs(sample.value)}); + } + return std::max_element(absoluteSignal.begin(), absoluteSignal.end())->value; +} + +} // namespace + +class OneEuroFilterTest : public ::testing::Test { +protected: + // The constructor's parameters are the ones that Chromium's using. The tuning was based on a 60 + // Hz sampling frequency. Refer to their one_euro_filter.h header for additional information + // about these parameters. + OneEuroFilterTest() : mFilter{/*minCutoffFreq=*/4.7, /*beta=*/0.01} {} + + std::vector<Sample> filterSignal(const std::vector<Sample>& signal) { + std::vector<Sample> filteredSignal; + for (const Sample& sample : signal) { + filteredSignal.push_back( + Sample{sample.timestamp, mFilter.filter(sample.timestamp, sample.value)}); + } + return filteredSignal; + } + + OneEuroFilter mFilter; +}; + +TEST_F(OneEuroFilterTest, PassLowFrequencySignal) { + const std::vector<Sample> signal = + generateSinusoidalSignal(1s, /*samplingFrequency=*/60, /*signalFrequency=*/1, + /*amplitude=*/1); + + const std::vector<Sample> filteredSignal = filterSignal(signal); + + // The reason behind using the mean absolute error as a metric is that, ideally, a low frequency + // filtered signal is expected to be almost identical to the raw one. Therefore, the error + // between them should be minimal. The constant is heuristically chosen. + EXPECT_LT(meanAbsoluteError(filteredSignal, signal), 0.25); +} + +TEST_F(OneEuroFilterTest, RejectHighFrequencySignal) { + const std::vector<Sample> signal = + generateSinusoidalSignal(1s, /*samplingFrequency=*/60, /*signalFrequency=*/22.5, + /*amplitude=*/1); + + const std::vector<Sample> filteredSignal = filterSignal(signal); + + // The filtered signal should consist of values that are much closer to zero. The comparison + // constant is heuristically chosen. + EXPECT_LT(maxAbsoluteAmplitude(filteredSignal), 0.25); +} + +} // namespace android diff --git a/libs/input/tests/TestEventMatchers.h b/libs/input/tests/TestEventMatchers.h index 3589de599f..290a97d736 100644 --- a/libs/input/tests/TestEventMatchers.h +++ b/libs/input/tests/TestEventMatchers.h @@ -75,12 +75,18 @@ public: using is_gtest_matcher = void; explicit WithMotionActionMatcher(int32_t action) : mAction(action) {} - bool MatchAndExplain(const MotionEvent& event, std::ostream*) const { - bool matches = mAction == event.getAction(); - if (event.getAction() == AMOTION_EVENT_ACTION_CANCEL) { - matches &= (event.getFlags() & AMOTION_EVENT_FLAG_CANCELED) != 0; + bool MatchAndExplain(const MotionEvent& event, testing::MatchResultListener* listener) const { + if (mAction != event.getAction()) { + *listener << "expected " << MotionEvent::actionToString(mAction) << ", but got " + << MotionEvent::actionToString(event.getAction()); + return false; + } + if (event.getAction() == AMOTION_EVENT_ACTION_CANCEL && + (event.getFlags() & AMOTION_EVENT_FLAG_CANCELED) == 0) { + *listener << "event with CANCEL action is missing FLAG_CANCELED"; + return false; } - return matches; + return true; } void DescribeTo(std::ostream* os) const { diff --git a/libs/input/tests/TfLiteMotionPredictor_test.cpp b/libs/input/tests/TfLiteMotionPredictor_test.cpp index c3ac0b7cfb..0c19ebe651 100644 --- a/libs/input/tests/TfLiteMotionPredictor_test.cpp +++ b/libs/input/tests/TfLiteMotionPredictor_test.cpp @@ -89,23 +89,23 @@ TEST(TfLiteMotionPredictorTest, BuffersCopyTo) { buffers.pushSample(/*timestamp=*/1, {.position = {.x = 10, .y = 10}, .pressure = 0, - .orientation = 0, - .tilt = 0.2}); + .tilt = 0.2, + .orientation = 0}); buffers.pushSample(/*timestamp=*/2, {.position = {.x = 10, .y = 50}, .pressure = 0.4, - .orientation = M_PI / 4, - .tilt = 0.3}); + .tilt = 0.3, + .orientation = M_PI / 4}); buffers.pushSample(/*timestamp=*/3, {.position = {.x = 30, .y = 50}, .pressure = 0.5, - .orientation = -M_PI / 4, - .tilt = 0.4}); + .tilt = 0.4, + .orientation = -M_PI / 4}); buffers.pushSample(/*timestamp=*/3, {.position = {.x = 30, .y = 60}, .pressure = 0, - .orientation = 0, - .tilt = 0.5}); + .tilt = 0.5, + .orientation = 0}); buffers.copyTo(*model); const int zeroPadding = model->inputLength() - 3; diff --git a/libs/renderengine/skia/GaneshVkRenderEngine.cpp b/libs/renderengine/skia/GaneshVkRenderEngine.cpp index a3a43e20be..cc73f405a6 100644 --- a/libs/renderengine/skia/GaneshVkRenderEngine.cpp +++ b/libs/renderengine/skia/GaneshVkRenderEngine.cpp @@ -21,12 +21,15 @@ #include <include/gpu/ganesh/vk/GrVkBackendSemaphore.h> +#include <android-base/stringprintf.h> #include <common/trace.h> #include <log/log_main.h> #include <sync/sync.h> namespace android::renderengine::skia { +using base::StringAppendF; + std::unique_ptr<GaneshVkRenderEngine> GaneshVkRenderEngine::create( const RenderEngineCreationArgs& args) { std::unique_ptr<GaneshVkRenderEngine> engine(new GaneshVkRenderEngine(args)); @@ -111,4 +114,9 @@ base::unique_fd GaneshVkRenderEngine::flushAndSubmit(SkiaGpuContext* context, return res; } +void GaneshVkRenderEngine::appendBackendSpecificInfoToDump(std::string& result) { + StringAppendF(&result, "\n ------------RE Vulkan (Ganesh)----------\n"); + SkiaVkRenderEngine::appendBackendSpecificInfoToDump(result); +} + } // namespace android::renderengine::skia diff --git a/libs/renderengine/skia/GaneshVkRenderEngine.h b/libs/renderengine/skia/GaneshVkRenderEngine.h index e6123c21bf..ba17f71201 100644 --- a/libs/renderengine/skia/GaneshVkRenderEngine.h +++ b/libs/renderengine/skia/GaneshVkRenderEngine.h @@ -28,6 +28,7 @@ protected: std::unique_ptr<SkiaGpuContext> createContext(VulkanInterface& vulkanInterface) override; void waitFence(SkiaGpuContext* context, base::borrowed_fd fenceFd) override; base::unique_fd flushAndSubmit(SkiaGpuContext* context, sk_sp<SkSurface> dstSurface) override; + void appendBackendSpecificInfoToDump(std::string& result) override; private: GaneshVkRenderEngine(const RenderEngineCreationArgs& args) : SkiaVkRenderEngine(args) {} diff --git a/libs/renderengine/skia/GraphiteVkRenderEngine.cpp b/libs/renderengine/skia/GraphiteVkRenderEngine.cpp index 390ad6efd1..a9332fa4e1 100644 --- a/libs/renderengine/skia/GraphiteVkRenderEngine.cpp +++ b/libs/renderengine/skia/GraphiteVkRenderEngine.cpp @@ -25,6 +25,7 @@ #include <include/gpu/graphite/Recording.h> #include <include/gpu/graphite/vk/VulkanGraphiteTypes.h> +#include <android-base/stringprintf.h> #include <log/log_main.h> #include <sync/sync.h> @@ -33,6 +34,8 @@ namespace android::renderengine::skia { +using base::StringAppendF; + std::unique_ptr<GraphiteVkRenderEngine> GraphiteVkRenderEngine::create( const RenderEngineCreationArgs& args) { std::unique_ptr<GraphiteVkRenderEngine> engine(new GraphiteVkRenderEngine(args)); @@ -139,4 +142,9 @@ base::unique_fd GraphiteVkRenderEngine::flushAndSubmit(SkiaGpuContext* context, return drawFenceFd; } +void GraphiteVkRenderEngine::appendBackendSpecificInfoToDump(std::string& result) { + StringAppendF(&result, "\n ------------RE Vulkan (Graphite)----------\n"); + SkiaVkRenderEngine::appendBackendSpecificInfoToDump(result); +} + } // namespace android::renderengine::skia diff --git a/libs/renderengine/skia/GraphiteVkRenderEngine.h b/libs/renderengine/skia/GraphiteVkRenderEngine.h index cf24a3b756..33a47f1a7f 100644 --- a/libs/renderengine/skia/GraphiteVkRenderEngine.h +++ b/libs/renderengine/skia/GraphiteVkRenderEngine.h @@ -30,6 +30,7 @@ protected: std::unique_ptr<SkiaGpuContext> createContext(VulkanInterface& vulkanInterface) override; void waitFence(SkiaGpuContext* context, base::borrowed_fd fenceFd) override; base::unique_fd flushAndSubmit(SkiaGpuContext* context, sk_sp<SkSurface> dstSurface) override; + void appendBackendSpecificInfoToDump(std::string& result) override; private: GraphiteVkRenderEngine(const RenderEngineCreationArgs& args) : SkiaVkRenderEngine(args) {} diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp index 4ef7d5bccb..ddae9fc78f 100644 --- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp @@ -541,7 +541,7 @@ int SkiaGLRenderEngine::getContextPriority() { void SkiaGLRenderEngine::appendBackendSpecificInfoToDump(std::string& result) { const GLExtensions& extensions = GLExtensions::getInstance(); - StringAppendF(&result, "\n ------------RE GLES------------\n"); + StringAppendF(&result, "\n ------------RE GLES (Ganesh)------------\n"); StringAppendF(&result, "EGL implementation : %s\n", extensions.getEGLVersion()); StringAppendF(&result, "%s\n", extensions.getEGLExtensions()); StringAppendF(&result, "GLES: %s, %s, %s\n", extensions.getVendor(), extensions.getRenderer(), diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.cpp b/libs/renderengine/skia/SkiaVkRenderEngine.cpp index 677a2b63b2..177abe6c9f 100644 --- a/libs/renderengine/skia/SkiaVkRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaVkRenderEngine.cpp @@ -169,24 +169,26 @@ int SkiaVkRenderEngine::getContextPriority() { } void SkiaVkRenderEngine::appendBackendSpecificInfoToDump(std::string& result) { - StringAppendF(&result, "\n ------------RE Vulkan----------\n"); - StringAppendF(&result, "\n Vulkan device initialized: %d\n", sVulkanInterface.isInitialized()); - StringAppendF(&result, "\n Vulkan protected device initialized: %d\n", + // Subclasses will prepend a backend-specific name / section header + StringAppendF(&result, "Vulkan device initialized: %d\n", sVulkanInterface.isInitialized()); + StringAppendF(&result, "Vulkan protected device initialized: %d\n", sProtectedContentVulkanInterface.isInitialized()); if (!sVulkanInterface.isInitialized()) { return; } - StringAppendF(&result, "\n Instance extensions:\n"); + StringAppendF(&result, "Instance extensions: [\n"); for (const auto& name : sVulkanInterface.getInstanceExtensionNames()) { - StringAppendF(&result, "\n %s\n", name.c_str()); + StringAppendF(&result, " %s\n", name.c_str()); } + StringAppendF(&result, "]\n"); - StringAppendF(&result, "\n Device extensions:\n"); + StringAppendF(&result, "Device extensions: [\n"); for (const auto& name : sVulkanInterface.getDeviceExtensionNames()) { - StringAppendF(&result, "\n %s\n", name.c_str()); + StringAppendF(&result, " %s\n", name.c_str()); } + StringAppendF(&result, "]\n"); } } // namespace skia diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.h b/libs/renderengine/skia/SkiaVkRenderEngine.h index d2bb3d53cf..88b04df58d 100644 --- a/libs/renderengine/skia/SkiaVkRenderEngine.h +++ b/libs/renderengine/skia/SkiaVkRenderEngine.h @@ -81,7 +81,7 @@ protected: SkiaRenderEngine::Contexts createContexts() override; bool supportsProtectedContentImpl() const override; bool useProtectedContextImpl(GrProtected isProtected) override; - void appendBackendSpecificInfoToDump(std::string& result) override; + virtual void appendBackendSpecificInfoToDump(std::string& result) override; // TODO: b/300533018 - refactor this to be non-static static VulkanInterface& getVulkanInterface(bool protectedContext); diff --git a/libs/sensor/Sensor.cpp b/libs/sensor/Sensor.cpp index eddd568fb5..797efbe5df 100644 --- a/libs/sensor/Sensor.cpp +++ b/libs/sensor/Sensor.cpp @@ -306,7 +306,18 @@ Sensor::Sensor(struct sensor_t const& hwSensor, const uuid_t& uuid, int halVersi } if (halVersion > SENSORS_DEVICE_API_VERSION_1_0 && hwSensor.requiredPermission) { mRequiredPermission = hwSensor.requiredPermission; - if (!strcmp(mRequiredPermission, SENSOR_PERMISSION_BODY_SENSORS)) { + bool requiresBodySensorPermission = + !strcmp(mRequiredPermission, SENSOR_PERMISSION_BODY_SENSORS); + if (android::permission::flags::replace_body_sensor_permission_enabled()) { + if (requiresBodySensorPermission) { + ALOGE("Sensor %s using deprecated Body Sensor permission", mName.c_str()); + } + + AppOpsManager appOps; + // Lookup to see if an AppOp exists for the permission. If none + // does, the default value of -1 is used. + mRequiredAppOp = appOps.permissionToOpCode(String16(mRequiredPermission)); + } else if (requiresBodySensorPermission) { AppOpsManager appOps; mRequiredAppOp = appOps.permissionToOpCode(String16(SENSOR_PERMISSION_BODY_SENSORS)); } diff --git a/libs/tracing_perfetto/Android.bp b/libs/tracing_perfetto/Android.bp index b5c56c5c52..9a2d4f7463 100644 --- a/libs/tracing_perfetto/Android.bp +++ b/libs/tracing_perfetto/Android.bp @@ -47,4 +47,6 @@ cc_library_shared { ], host_supported: true, + // for vndbinder + vendor_available: true, } diff --git a/libs/tracing_perfetto/tracing_perfetto_internal.cpp b/libs/tracing_perfetto/tracing_perfetto_internal.cpp index 9a0042aee5..c4f866338a 100644 --- a/libs/tracing_perfetto/tracing_perfetto_internal.cpp +++ b/libs/tracing_perfetto/tracing_perfetto_internal.cpp @@ -253,15 +253,31 @@ void perfettoTraceEnd(const struct PerfettoTeCategory& category) { void perfettoTraceAsyncBeginForTrack(const struct PerfettoTeCategory& category, const char* name, const char* trackName, uint64_t cookie) { PERFETTO_TE( - category, PERFETTO_TE_SLICE_BEGIN(name), - PERFETTO_TE_NAMED_TRACK(trackName, cookie, PerfettoTeProcessTrackUuid())); + category, PERFETTO_TE_SLICE_BEGIN(name), + PERFETTO_TE_PROTO_TRACK( + PerfettoTeNamedTrackUuid(trackName, cookie, + PerfettoTeProcessTrackUuid()), + PERFETTO_TE_PROTO_FIELD_CSTR( + perfetto_protos_TrackDescriptor_atrace_name_field_number, + trackName), + PERFETTO_TE_PROTO_FIELD_VARINT( + perfetto_protos_TrackDescriptor_parent_uuid_field_number, + PerfettoTeProcessTrackUuid()))); } void perfettoTraceAsyncEndForTrack(const struct PerfettoTeCategory& category, const char* trackName, uint64_t cookie) { - PERFETTO_TE( - category, PERFETTO_TE_SLICE_END(), - PERFETTO_TE_NAMED_TRACK(trackName, cookie, PerfettoTeProcessTrackUuid())); + PERFETTO_TE( + category, PERFETTO_TE_SLICE_END(), + PERFETTO_TE_PROTO_TRACK( + PerfettoTeNamedTrackUuid(trackName, cookie, + PerfettoTeProcessTrackUuid()), + PERFETTO_TE_PROTO_FIELD_CSTR( + perfetto_protos_TrackDescriptor_atrace_name_field_number, + trackName), + PERFETTO_TE_PROTO_FIELD_VARINT( + perfetto_protos_TrackDescriptor_parent_uuid_field_number, + PerfettoTeProcessTrackUuid()))); } void perfettoTraceAsyncBegin(const struct PerfettoTeCategory& category, const char* name, @@ -281,14 +297,35 @@ void perfettoTraceInstant(const struct PerfettoTeCategory& category, const char* void perfettoTraceInstantForTrack(const struct PerfettoTeCategory& category, const char* trackName, const char* name) { PERFETTO_TE( - category, PERFETTO_TE_INSTANT(name), - PERFETTO_TE_NAMED_TRACK(trackName, 1, PerfettoTeProcessTrackUuid())); + category, PERFETTO_TE_INSTANT(name), + PERFETTO_TE_PROTO_TRACK( + PerfettoTeNamedTrackUuid(trackName, 1, + PerfettoTeProcessTrackUuid()), + PERFETTO_TE_PROTO_FIELD_CSTR( + perfetto_protos_TrackDescriptor_atrace_name_field_number, + trackName), + PERFETTO_TE_PROTO_FIELD_VARINT( + perfetto_protos_TrackDescriptor_parent_uuid_field_number, + PerfettoTeProcessTrackUuid()))); } void perfettoTraceCounter(const struct PerfettoTeCategory& category, - [[maybe_unused]] const char* name, int64_t value) { - PERFETTO_TE(category, PERFETTO_TE_COUNTER(), - PERFETTO_TE_INT_COUNTER(value)); + const char* name, int64_t value) { + PERFETTO_TE( + category, PERFETTO_TE_COUNTER(), + PERFETTO_TE_PROTO_TRACK( + PerfettoTeCounterTrackUuid(name, + PerfettoTeProcessTrackUuid()), + PERFETTO_TE_PROTO_FIELD_CSTR( + perfetto_protos_TrackDescriptor_atrace_name_field_number, + name), + PERFETTO_TE_PROTO_FIELD_VARINT( + perfetto_protos_TrackDescriptor_parent_uuid_field_number, + PerfettoTeProcessTrackUuid()), + PERFETTO_TE_PROTO_FIELD_BYTES( + perfetto_protos_TrackDescriptor_counter_field_number, + PERFETTO_NULL, 0)), + PERFETTO_TE_INT_COUNTER(value)); } } // namespace internal diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp index 12230f99d2..87e213e394 100644 --- a/libs/ui/Android.bp +++ b/libs/ui/Android.bp @@ -136,6 +136,7 @@ cc_library_shared { "GraphicBuffer.cpp", "GraphicBufferAllocator.cpp", "GraphicBufferMapper.cpp", + "PictureProfileHandle.cpp", "PixelFormat.cpp", "PublicFormat.cpp", "StaticAsserts.cpp", diff --git a/libs/ui/PictureProfileHandle.cpp b/libs/ui/PictureProfileHandle.cpp new file mode 100644 index 0000000000..0701e906f0 --- /dev/null +++ b/libs/ui/PictureProfileHandle.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2009 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 <ui/PictureProfileHandle.h> + +#include <format> + +namespace android { + +const PictureProfileHandle PictureProfileHandle::NONE(0); + +::std::string toString(const PictureProfileHandle& handle) { + return std::format("{:#010x}", handle.getId()); +} + +} // namespace android diff --git a/libs/ui/include/ui/DynamicDisplayInfo.h b/libs/ui/include/ui/DynamicDisplayInfo.h index 25a2b6ee53..af494dcf39 100644 --- a/libs/ui/include/ui/DynamicDisplayInfo.h +++ b/libs/ui/include/ui/DynamicDisplayInfo.h @@ -22,6 +22,7 @@ #include <optional> #include <vector> +#include <ui/FrameRateCategoryRate.h> #include <ui/GraphicTypes.h> #include <ui/HdrCapabilities.h> @@ -55,6 +56,9 @@ struct DynamicDisplayInfo { std::optional<ui::DisplayMode> getActiveDisplayMode() const; bool hasArrSupport; + + // Represents frame rate for FrameRateCategory Normal and High. + ui::FrameRateCategoryRate frameRateCategoryRate; }; } // namespace android::ui diff --git a/libs/ui/include/ui/FrameRateCategoryRate.h b/libs/ui/include/ui/FrameRateCategoryRate.h new file mode 100644 index 0000000000..9c392d9bc8 --- /dev/null +++ b/libs/ui/include/ui/FrameRateCategoryRate.h @@ -0,0 +1,35 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +namespace android::ui { + +// Represents frame rate for FrameRateCategory Normal and High. +class FrameRateCategoryRate { +public: + FrameRateCategoryRate(float normal = 0, float high = 0) : mNormal(normal), mHigh(high) {} + + float getNormal() const { return mNormal; } + + float getHigh() const { return mHigh; } + +private: + float mNormal; + float mHigh; +}; + +} // namespace android::ui
\ No newline at end of file diff --git a/libs/ui/include/ui/PictureProfileHandle.h b/libs/ui/include/ui/PictureProfileHandle.h new file mode 100644 index 0000000000..9b709b6155 --- /dev/null +++ b/libs/ui/include/ui/PictureProfileHandle.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <stdint.h> +#include <array> +#include <string> + +namespace android { + +/** + * An opaque value that uniquely identifies a picture profile, or a set of parameters, which + * describes the configuration of a picture processing pipeline that is applied to a graphic buffer + * to enhance its quality prior to rendering on the display. + */ +typedef int64_t PictureProfileId; + +/** + * A picture profile handle wraps the picture profile ID for type-safety, and represents an opaque + * handle that doesn't have the performance drawbacks of Binders. + */ +class PictureProfileHandle { +public: + // A profile that represents no picture processing. + static const PictureProfileHandle NONE; + + PictureProfileHandle() { *this = NONE; } + PictureProfileHandle(PictureProfileId id) : mId(id) {} + + PictureProfileId const& getId() const { return mId; } + + inline bool operator==(const PictureProfileHandle& rhs) { return mId == rhs.mId; } + inline bool operator!=(const PictureProfileHandle& rhs) { return !(*this == rhs); } + + // Is the picture profile effectively null, or not-specified? + inline bool operator!() const { return mId == NONE.mId; } + + operator bool() const { return !!*this; } + + friend ::std::string toString(const PictureProfileHandle& handle); + +private: + PictureProfileId mId; +}; + +} // namespace android diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 4f9d9e43a3..755995c15f 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -836,13 +836,9 @@ bool isStylusActiveInDisplay(ui::LogicalDisplayId displayId, } Result<void> validateWindowInfosUpdate(const gui::WindowInfosUpdate& update) { - struct HashFunction { - size_t operator()(const WindowInfo& info) const { return info.id; } - }; - - std::unordered_set<WindowInfo, HashFunction> windowSet; + std::unordered_set<int32_t> windowIds; for (const WindowInfo& info : update.windowInfos) { - const auto [_, inserted] = windowSet.insert(info); + const auto [_, inserted] = windowIds.insert(info.id); if (!inserted) { return Error() << "Duplicate entry for " << info; } diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h index 6ef4a3d6af..305feab6c7 100644 --- a/services/inputflinger/include/InputReaderBase.h +++ b/services/inputflinger/include/InputReaderBase.h @@ -242,6 +242,9 @@ struct InputReaderConfiguration { // context (a.k.a. "right") clicks. bool touchpadRightClickZoneEnabled; + // True to use three-finger tap as a customizable shortcut; false to use it as a middle-click. + bool touchpadThreeFingerTapShortcutEnabled; + // The set of currently disabled input devices. std::set<int32_t> disabledDevices; @@ -293,6 +296,7 @@ struct InputReaderConfiguration { touchpadTapDraggingEnabled(false), shouldNotifyTouchpadHardwareState(false), touchpadRightClickZoneEnabled(false), + touchpadThreeFingerTapShortcutEnabled(false), stylusButtonMotionEventsEnabled(true), stylusPointerIconEnabled(false), mouseReverseVerticalScrollingEnabled(false), @@ -496,6 +500,9 @@ public: /* Sends the Info of gestures that happen on the touchpad. */ virtual void notifyTouchpadGestureInfo(GestureType type, int32_t deviceId) = 0; + /* Notifies the policy that the user has performed a three-finger touchpad tap. */ + virtual void notifyTouchpadThreeFingerTap() = 0; + /* Gets the keyboard layout for a particular input device. */ virtual std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay( const InputDeviceIdentifier& identifier, diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp index 9a36bfbeaf..ca8266bb82 100644 --- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp @@ -375,6 +375,9 @@ std::list<NotifyArgs> TouchpadInputMapper::reconfigure(nsecs_t when, mPropertyProvider.getProperty("Button Right Click Zone Enable") .setBoolValues({config.touchpadRightClickZoneEnabled}); mTouchpadHardwareStateNotificationsEnabled = config.shouldNotifyTouchpadHardwareState; + + mGestureConverter.setThreeFingerTapShortcutEnabled( + config.touchpadThreeFingerTapShortcutEnabled); } std::list<NotifyArgs> out; if ((!changes.any() && config.pointerCaptureRequest.isEnable()) || diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp index da2c683d95..19594233fa 100644 --- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp +++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp @@ -261,6 +261,14 @@ std::list<NotifyArgs> GestureConverter::handleButtonsChange(nsecs_t when, nsecs_ } const uint32_t buttonsPressed = gesture.details.buttons.down; + const uint32_t buttonsReleased = gesture.details.buttons.up; + + if (mThreeFingerTapShortcutEnabled && gesture.details.buttons.is_tap && + buttonsPressed == GESTURES_BUTTON_MIDDLE && buttonsReleased == GESTURES_BUTTON_MIDDLE) { + mReaderContext.getPolicy()->notifyTouchpadThreeFingerTap(); + return out; + } + bool pointerDown = isPointerDown(mButtonState) || buttonsPressed & (GESTURES_BUTTON_LEFT | GESTURES_BUTTON_MIDDLE | GESTURES_BUTTON_RIGHT); @@ -291,7 +299,6 @@ std::list<NotifyArgs> GestureConverter::handleButtonsChange(nsecs_t when, nsecs_ // changes: a set of buttons going down, followed by a set of buttons going up. mButtonState = newButtonState; - const uint32_t buttonsReleased = gesture.details.buttons.up; for (uint32_t button = 1; button <= GESTURES_BUTTON_FORWARD; button <<= 1) { if (buttonsReleased & button) { uint32_t actionButton = gesturesButtonToMotionEventButton(button); diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.h b/services/inputflinger/reader/mapper/gestures/GestureConverter.h index c9a35c151f..ad40721363 100644 --- a/services/inputflinger/reader/mapper/gestures/GestureConverter.h +++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.h @@ -55,6 +55,10 @@ public: void setBoundsInLogicalDisplay(FloatRect bounds) { mBoundsInLogicalDisplay = bounds; } + void setThreeFingerTapShortcutEnabled(bool enabled) { + mThreeFingerTapShortcutEnabled = enabled; + } + void populateMotionRanges(InputDeviceInfo& info) const; [[nodiscard]] std::list<NotifyArgs> handleGesture(nsecs_t when, nsecs_t readTime, @@ -101,6 +105,8 @@ private: const bool mEnableFlingStop; const bool mEnableNoFocusChange; + bool mThreeFingerTapShortcutEnabled; + std::optional<ui::LogicalDisplayId> mDisplayId; FloatRect mBoundsInLogicalDisplay{}; ui::Rotation mOrientation = ui::ROTATION_0; diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp index 744cf4a514..600ae526f1 100644 --- a/services/inputflinger/tests/Android.bp +++ b/services/inputflinger/tests/Android.bp @@ -78,6 +78,7 @@ cc_test { "PreferStylusOverTouch_test.cpp", "PropertyProvider_test.cpp", "RotaryEncoderInputMapper_test.cpp", + "SensorInputMapper_test.cpp", "SlopController_test.cpp", "SwitchInputMapper_test.cpp", "SyncQueue_test.cpp", diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.cpp b/services/inputflinger/tests/FakeInputReaderPolicy.cpp index 7c5f350c0e..67b1e8c249 100644 --- a/services/inputflinger/tests/FakeInputReaderPolicy.cpp +++ b/services/inputflinger/tests/FakeInputReaderPolicy.cpp @@ -80,6 +80,17 @@ void FakeInputReaderPolicy::assertTouchpadHardwareStateNotified() { ASSERT_TRUE(success) << "Timed out waiting for hardware state to be notified"; } +void FakeInputReaderPolicy::assertTouchpadThreeFingerTapNotified() { + std::unique_lock lock(mLock); + base::ScopedLockAssertion assumeLocked(mLock); + + const bool success = + mTouchpadThreeFingerTapNotified.wait_for(lock, WAIT_TIMEOUT, [this]() REQUIRES(mLock) { + return mTouchpadThreeFingerTapHasBeenReported; + }); + ASSERT_TRUE(success) << "Timed out waiting for three-finger tap to be notified"; +} + void FakeInputReaderPolicy::clearViewports() { mViewports.clear(); mConfig.setDisplayViewports(mViewports); @@ -259,6 +270,12 @@ void FakeInputReaderPolicy::notifyTouchpadGestureInfo(GestureType type, int32_t std::scoped_lock lock(mLock); } +void FakeInputReaderPolicy::notifyTouchpadThreeFingerTap() { + std::scoped_lock lock(mLock); + mTouchpadThreeFingerTapHasBeenReported = true; + mTouchpadThreeFingerTapNotified.notify_all(); +} + std::shared_ptr<KeyCharacterMap> FakeInputReaderPolicy::getKeyboardLayoutOverlay( const InputDeviceIdentifier&, const std::optional<KeyboardLayoutInfo>) { return nullptr; diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.h b/services/inputflinger/tests/FakeInputReaderPolicy.h index 3a2b4e9ed9..42c956789b 100644 --- a/services/inputflinger/tests/FakeInputReaderPolicy.h +++ b/services/inputflinger/tests/FakeInputReaderPolicy.h @@ -43,6 +43,7 @@ public: void assertStylusGestureNotified(int32_t deviceId); void assertStylusGestureNotNotified(); void assertTouchpadHardwareStateNotified(); + void assertTouchpadThreeFingerTapNotified(); virtual void clearViewports(); std::optional<DisplayViewport> getDisplayViewportByUniqueId(const std::string& uniqueId) const; @@ -86,6 +87,7 @@ private: void notifyTouchpadHardwareState(const SelfContainedHardwareState& schs, int32_t deviceId) override; void notifyTouchpadGestureInfo(GestureType type, int32_t deviceId) override; + void notifyTouchpadThreeFingerTap() override; std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay( const InputDeviceIdentifier&, const std::optional<KeyboardLayoutInfo>) override; std::string getDeviceAlias(const InputDeviceIdentifier&) override; @@ -109,6 +111,9 @@ private: std::condition_variable mTouchpadHardwareStateNotified; std::optional<SelfContainedHardwareState> mTouchpadHardwareState GUARDED_BY(mLock){}; + std::condition_variable mTouchpadThreeFingerTapNotified; + bool mTouchpadThreeFingerTapHasBeenReported{false}; + uint32_t mNextPointerCaptureSequenceNumber{0}; }; diff --git a/services/inputflinger/tests/GestureConverter_test.cpp b/services/inputflinger/tests/GestureConverter_test.cpp index 225ae0f67c..fad8f058ed 100644 --- a/services/inputflinger/tests/GestureConverter_test.cpp +++ b/services/inputflinger/tests/GestureConverter_test.cpp @@ -1272,6 +1272,27 @@ TEST_F(GestureConverterTest, Tap) { WithDisplayId(ui::LogicalDisplayId::DEFAULT))))); } +TEST_F(GestureConverterTest, ThreeFingerTap_TriggersShortcut) { + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + converter.setDisplayId(ui::LogicalDisplayId::DEFAULT); + converter.setThreeFingerTapShortcutEnabled(true); + + Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*vx=*/0, + /*vy=*/0, GESTURES_FLING_TAP_DOWN); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture); + // We don't need to check args here, since it's covered by the FlingTapDown test. + + Gesture tapGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, + /*down=*/GESTURES_BUTTON_MIDDLE, /*up=*/GESTURES_BUTTON_MIDDLE, + /*is_tap=*/true); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, tapGesture); + + ASSERT_TRUE(args.empty()); + mFakePolicy->assertTouchpadThreeFingerTapNotified(); +} + TEST_F(GestureConverterTest, Click) { // Click should produce button press/release events InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); diff --git a/services/inputflinger/tests/InputMapperTest.cpp b/services/inputflinger/tests/InputMapperTest.cpp index 7dff144f87..ca797dce95 100644 --- a/services/inputflinger/tests/InputMapperTest.cpp +++ b/services/inputflinger/tests/InputMapperTest.cpp @@ -53,13 +53,13 @@ void InputMapperUnitTest::SetUpWithBus(int bus) { } void InputMapperUnitTest::setupAxis(int axis, bool valid, int32_t min, int32_t max, - int32_t resolution) { + int32_t resolution, int32_t flat, int32_t fuzz) { EXPECT_CALL(mMockEventHub, getAbsoluteAxisInfo(EVENTHUB_ID, axis)) .WillRepeatedly(Return(valid ? std::optional<RawAbsoluteAxisInfo>{{ .minValue = min, .maxValue = max, - .flat = 0, - .fuzz = 0, + .flat = flat, + .fuzz = fuzz, .resolution = resolution, }} : std::nullopt)); diff --git a/services/inputflinger/tests/InputMapperTest.h b/services/inputflinger/tests/InputMapperTest.h index fc27e4fefd..b6c5812ec5 100644 --- a/services/inputflinger/tests/InputMapperTest.h +++ b/services/inputflinger/tests/InputMapperTest.h @@ -43,7 +43,8 @@ protected: virtual void SetUp() override { SetUpWithBus(0); } virtual void SetUpWithBus(int bus); - void setupAxis(int axis, bool valid, int32_t min, int32_t max, int32_t resolution); + void setupAxis(int axis, bool valid, int32_t min, int32_t max, int32_t resolution, + int32_t flat = 0, int32_t fuzz = 0); void expectScanCodes(bool present, std::set<int> scanCodes); diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 18469e0b02..ee3b2a24d7 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -28,7 +28,6 @@ #include <MultiTouchInputMapper.h> #include <NotifyArgsBuilders.h> #include <PeripheralController.h> -#include <SensorInputMapper.h> #include <SingleTouchInputMapper.h> #include <TestEventMatchers.h> #include <TestInputListener.h> @@ -3032,159 +3031,6 @@ TEST_F(InputDeviceTest, KernelBufferOverflowResetsMappers) { mapper.assertProcessWasCalled(); } -// --- SensorInputMapperTest --- - -class SensorInputMapperTest : public InputMapperTest { -protected: - static const int32_t ACCEL_RAW_MIN; - static const int32_t ACCEL_RAW_MAX; - static const int32_t ACCEL_RAW_FUZZ; - static const int32_t ACCEL_RAW_FLAT; - static const int32_t ACCEL_RAW_RESOLUTION; - - static const int32_t GYRO_RAW_MIN; - static const int32_t GYRO_RAW_MAX; - static const int32_t GYRO_RAW_FUZZ; - static const int32_t GYRO_RAW_FLAT; - static const int32_t GYRO_RAW_RESOLUTION; - - static const float GRAVITY_MS2_UNIT; - static const float DEGREE_RADIAN_UNIT; - - void prepareAccelAxes(); - void prepareGyroAxes(); - void setAccelProperties(); - void setGyroProperties(); - void SetUp() override { InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::SENSOR); } -}; - -const int32_t SensorInputMapperTest::ACCEL_RAW_MIN = -32768; -const int32_t SensorInputMapperTest::ACCEL_RAW_MAX = 32768; -const int32_t SensorInputMapperTest::ACCEL_RAW_FUZZ = 16; -const int32_t SensorInputMapperTest::ACCEL_RAW_FLAT = 0; -const int32_t SensorInputMapperTest::ACCEL_RAW_RESOLUTION = 8192; - -const int32_t SensorInputMapperTest::GYRO_RAW_MIN = -2097152; -const int32_t SensorInputMapperTest::GYRO_RAW_MAX = 2097152; -const int32_t SensorInputMapperTest::GYRO_RAW_FUZZ = 16; -const int32_t SensorInputMapperTest::GYRO_RAW_FLAT = 0; -const int32_t SensorInputMapperTest::GYRO_RAW_RESOLUTION = 1024; - -const float SensorInputMapperTest::GRAVITY_MS2_UNIT = 9.80665f; -const float SensorInputMapperTest::DEGREE_RADIAN_UNIT = 0.0174533f; - -void SensorInputMapperTest::prepareAccelAxes() { - mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_X, ACCEL_RAW_MIN, ACCEL_RAW_MAX, ACCEL_RAW_FUZZ, - ACCEL_RAW_FLAT, ACCEL_RAW_RESOLUTION); - mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_Y, ACCEL_RAW_MIN, ACCEL_RAW_MAX, ACCEL_RAW_FUZZ, - ACCEL_RAW_FLAT, ACCEL_RAW_RESOLUTION); - mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_Z, ACCEL_RAW_MIN, ACCEL_RAW_MAX, ACCEL_RAW_FUZZ, - ACCEL_RAW_FLAT, ACCEL_RAW_RESOLUTION); -} - -void SensorInputMapperTest::prepareGyroAxes() { - mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_RX, GYRO_RAW_MIN, GYRO_RAW_MAX, GYRO_RAW_FUZZ, - GYRO_RAW_FLAT, GYRO_RAW_RESOLUTION); - mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_RY, GYRO_RAW_MIN, GYRO_RAW_MAX, GYRO_RAW_FUZZ, - GYRO_RAW_FLAT, GYRO_RAW_RESOLUTION); - mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_RZ, GYRO_RAW_MIN, GYRO_RAW_MAX, GYRO_RAW_FUZZ, - GYRO_RAW_FLAT, GYRO_RAW_RESOLUTION); -} - -void SensorInputMapperTest::setAccelProperties() { - mFakeEventHub->addSensorAxis(EVENTHUB_ID, /* absCode */ 0, InputDeviceSensorType::ACCELEROMETER, - /* sensorDataIndex */ 0); - mFakeEventHub->addSensorAxis(EVENTHUB_ID, /* absCode */ 1, InputDeviceSensorType::ACCELEROMETER, - /* sensorDataIndex */ 1); - mFakeEventHub->addSensorAxis(EVENTHUB_ID, /* absCode */ 2, InputDeviceSensorType::ACCELEROMETER, - /* sensorDataIndex */ 2); - mFakeEventHub->setMscEvent(EVENTHUB_ID, MSC_TIMESTAMP); - addConfigurationProperty("sensor.accelerometer.reportingMode", "0"); - addConfigurationProperty("sensor.accelerometer.maxDelay", "100000"); - addConfigurationProperty("sensor.accelerometer.minDelay", "5000"); - addConfigurationProperty("sensor.accelerometer.power", "1.5"); -} - -void SensorInputMapperTest::setGyroProperties() { - mFakeEventHub->addSensorAxis(EVENTHUB_ID, /* absCode */ 3, InputDeviceSensorType::GYROSCOPE, - /* sensorDataIndex */ 0); - mFakeEventHub->addSensorAxis(EVENTHUB_ID, /* absCode */ 4, InputDeviceSensorType::GYROSCOPE, - /* sensorDataIndex */ 1); - mFakeEventHub->addSensorAxis(EVENTHUB_ID, /* absCode */ 5, InputDeviceSensorType::GYROSCOPE, - /* sensorDataIndex */ 2); - mFakeEventHub->setMscEvent(EVENTHUB_ID, MSC_TIMESTAMP); - addConfigurationProperty("sensor.gyroscope.reportingMode", "0"); - addConfigurationProperty("sensor.gyroscope.maxDelay", "100000"); - addConfigurationProperty("sensor.gyroscope.minDelay", "5000"); - addConfigurationProperty("sensor.gyroscope.power", "0.8"); -} - -TEST_F(SensorInputMapperTest, GetSources) { - SensorInputMapper& mapper = constructAndAddMapper<SensorInputMapper>(); - - ASSERT_EQ(static_cast<uint32_t>(AINPUT_SOURCE_SENSOR), mapper.getSources()); -} - -TEST_F(SensorInputMapperTest, ProcessAccelerometerSensor) { - setAccelProperties(); - prepareAccelAxes(); - SensorInputMapper& mapper = constructAndAddMapper<SensorInputMapper>(); - - ASSERT_TRUE(mapper.enableSensor(InputDeviceSensorType::ACCELEROMETER, - std::chrono::microseconds(10000), - std::chrono::microseconds(0))); - ASSERT_TRUE(mFakeEventHub->isDeviceEnabled(EVENTHUB_ID)); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_X, 20000); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_Y, -20000); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_Z, 40000); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_MSC, MSC_TIMESTAMP, 1000); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - - NotifySensorArgs args; - std::vector<float> values = {20000.0f / ACCEL_RAW_RESOLUTION * GRAVITY_MS2_UNIT, - -20000.0f / ACCEL_RAW_RESOLUTION * GRAVITY_MS2_UNIT, - 40000.0f / ACCEL_RAW_RESOLUTION * GRAVITY_MS2_UNIT}; - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifySensorWasCalled(&args)); - ASSERT_EQ(args.source, AINPUT_SOURCE_SENSOR); - ASSERT_EQ(args.deviceId, DEVICE_ID); - ASSERT_EQ(args.sensorType, InputDeviceSensorType::ACCELEROMETER); - ASSERT_EQ(args.accuracy, InputDeviceSensorAccuracy::ACCURACY_HIGH); - ASSERT_EQ(args.hwTimestamp, ARBITRARY_TIME); - ASSERT_EQ(args.values, values); - mapper.flushSensor(InputDeviceSensorType::ACCELEROMETER); -} - -TEST_F(SensorInputMapperTest, ProcessGyroscopeSensor) { - setGyroProperties(); - prepareGyroAxes(); - SensorInputMapper& mapper = constructAndAddMapper<SensorInputMapper>(); - - ASSERT_TRUE(mapper.enableSensor(InputDeviceSensorType::GYROSCOPE, - std::chrono::microseconds(10000), - std::chrono::microseconds(0))); - ASSERT_TRUE(mFakeEventHub->isDeviceEnabled(EVENTHUB_ID)); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_RX, 20000); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_RY, -20000); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_RZ, 40000); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_MSC, MSC_TIMESTAMP, 1000); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - - NotifySensorArgs args; - std::vector<float> values = {20000.0f / GYRO_RAW_RESOLUTION * DEGREE_RADIAN_UNIT, - -20000.0f / GYRO_RAW_RESOLUTION * DEGREE_RADIAN_UNIT, - 40000.0f / GYRO_RAW_RESOLUTION * DEGREE_RADIAN_UNIT}; - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifySensorWasCalled(&args)); - ASSERT_EQ(args.source, AINPUT_SOURCE_SENSOR); - ASSERT_EQ(args.deviceId, DEVICE_ID); - ASSERT_EQ(args.sensorType, InputDeviceSensorType::GYROSCOPE); - ASSERT_EQ(args.accuracy, InputDeviceSensorAccuracy::ACCURACY_HIGH); - ASSERT_EQ(args.hwTimestamp, ARBITRARY_TIME); - ASSERT_EQ(args.values, values); - mapper.flushSensor(InputDeviceSensorType::GYROSCOPE); -} - // --- KeyboardInputMapperTest --- class KeyboardInputMapperTest : public InputMapperTest { diff --git a/services/inputflinger/tests/SensorInputMapper_test.cpp b/services/inputflinger/tests/SensorInputMapper_test.cpp new file mode 100644 index 0000000000..2c1265378a --- /dev/null +++ b/services/inputflinger/tests/SensorInputMapper_test.cpp @@ -0,0 +1,179 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SensorInputMapper.h" + +#include <cstdint> +#include <list> +#include <optional> +#include <utility> +#include <vector> + +#include <EventHub.h> +#include <NotifyArgs.h> +#include <ftl/enum.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <input/Input.h> +#include <input/InputDevice.h> +#include <input/PrintTools.h> +#include <linux/input-event-codes.h> + +#include "InputMapperTest.h" +#include "TestEventMatchers.h" + +namespace android { + +using testing::AllOf; +using testing::ElementsAre; +using testing::Return; +using testing::VariantWith; + +namespace { + +constexpr int32_t ACCEL_RAW_MIN = -32768; +constexpr int32_t ACCEL_RAW_MAX = 32768; +constexpr int32_t ACCEL_RAW_FUZZ = 16; +constexpr int32_t ACCEL_RAW_FLAT = 0; +constexpr int32_t ACCEL_RAW_RESOLUTION = 8192; + +constexpr int32_t GYRO_RAW_MIN = -2097152; +constexpr int32_t GYRO_RAW_MAX = 2097152; +constexpr int32_t GYRO_RAW_FUZZ = 16; +constexpr int32_t GYRO_RAW_FLAT = 0; +constexpr int32_t GYRO_RAW_RESOLUTION = 1024; + +constexpr float GRAVITY_MS2_UNIT = 9.80665f; +constexpr float DEGREE_RADIAN_UNIT = 0.0174533f; + +} // namespace + +class SensorInputMapperTest : public InputMapperUnitTest { +protected: + void SetUp() override { + InputMapperUnitTest::SetUp(); + EXPECT_CALL(mMockEventHub, getDeviceClasses(EVENTHUB_ID)) + .WillRepeatedly(Return(InputDeviceClass::SENSOR)); + // The mapper requests info on all ABS axes, including ones which aren't actually used, so + // just return nullopt for all axes we don't explicitly set up. + EXPECT_CALL(mMockEventHub, getAbsoluteAxisInfo(EVENTHUB_ID, testing::_)) + .WillRepeatedly(Return(std::nullopt)); + } + + void setupSensor(int32_t absCode, InputDeviceSensorType type, int32_t sensorDataIndex) { + EXPECT_CALL(mMockEventHub, mapSensor(EVENTHUB_ID, absCode)) + .WillRepeatedly(Return(std::make_pair(type, sensorDataIndex))); + } +}; + +TEST_F(SensorInputMapperTest, GetSources) { + mMapper = createInputMapper<SensorInputMapper>(*mDeviceContext, + mFakePolicy->getReaderConfiguration()); + + ASSERT_EQ(static_cast<uint32_t>(AINPUT_SOURCE_SENSOR), mMapper->getSources()); +} + +TEST_F(SensorInputMapperTest, ProcessAccelerometerSensor) { + EXPECT_CALL(mMockEventHub, hasMscEvent(EVENTHUB_ID, MSC_TIMESTAMP)) + .WillRepeatedly(Return(true)); + setupSensor(ABS_X, InputDeviceSensorType::ACCELEROMETER, /*sensorDataIndex=*/0); + setupSensor(ABS_Y, InputDeviceSensorType::ACCELEROMETER, /*sensorDataIndex=*/1); + setupSensor(ABS_Z, InputDeviceSensorType::ACCELEROMETER, /*sensorDataIndex=*/2); + setupAxis(ABS_X, /*valid=*/true, ACCEL_RAW_MIN, ACCEL_RAW_MAX, ACCEL_RAW_RESOLUTION, + ACCEL_RAW_FLAT, ACCEL_RAW_FUZZ); + setupAxis(ABS_Y, /*valid=*/true, ACCEL_RAW_MIN, ACCEL_RAW_MAX, ACCEL_RAW_RESOLUTION, + ACCEL_RAW_FLAT, ACCEL_RAW_FUZZ); + setupAxis(ABS_Z, /*valid=*/true, ACCEL_RAW_MIN, ACCEL_RAW_MAX, ACCEL_RAW_RESOLUTION, + ACCEL_RAW_FLAT, ACCEL_RAW_FUZZ); + mPropertyMap.addProperty("sensor.accelerometer.reportingMode", "0"); + mPropertyMap.addProperty("sensor.accelerometer.maxDelay", "100000"); + mPropertyMap.addProperty("sensor.accelerometer.minDelay", "5000"); + mPropertyMap.addProperty("sensor.accelerometer.power", "1.5"); + mMapper = createInputMapper<SensorInputMapper>(*mDeviceContext, + mFakePolicy->getReaderConfiguration()); + + EXPECT_CALL(mMockEventHub, enableDevice(EVENTHUB_ID)); + ASSERT_TRUE(mMapper->enableSensor(InputDeviceSensorType::ACCELEROMETER, + std::chrono::microseconds(10000), + std::chrono::microseconds(0))); + std::list<NotifyArgs> args; + args += process(ARBITRARY_TIME, EV_ABS, ABS_X, 20000); + args += process(ARBITRARY_TIME, EV_ABS, ABS_Y, -20000); + args += process(ARBITRARY_TIME, EV_ABS, ABS_Z, 40000); + args += process(ARBITRARY_TIME, EV_MSC, MSC_TIMESTAMP, 1000); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + + std::vector<float> values = {20000.0f / ACCEL_RAW_RESOLUTION * GRAVITY_MS2_UNIT, + -20000.0f / ACCEL_RAW_RESOLUTION * GRAVITY_MS2_UNIT, + 40000.0f / ACCEL_RAW_RESOLUTION * GRAVITY_MS2_UNIT}; + + ASSERT_EQ(args.size(), 1u); + const NotifySensorArgs& arg = std::get<NotifySensorArgs>(args.front()); + ASSERT_EQ(arg.source, AINPUT_SOURCE_SENSOR); + ASSERT_EQ(arg.deviceId, DEVICE_ID); + ASSERT_EQ(arg.sensorType, InputDeviceSensorType::ACCELEROMETER); + ASSERT_EQ(arg.accuracy, InputDeviceSensorAccuracy::ACCURACY_HIGH); + ASSERT_EQ(arg.hwTimestamp, ARBITRARY_TIME); + ASSERT_EQ(arg.values, values); + mMapper->flushSensor(InputDeviceSensorType::ACCELEROMETER); +} + +TEST_F(SensorInputMapperTest, ProcessGyroscopeSensor) { + EXPECT_CALL(mMockEventHub, hasMscEvent(EVENTHUB_ID, MSC_TIMESTAMP)) + .WillRepeatedly(Return(true)); + setupSensor(ABS_RX, InputDeviceSensorType::GYROSCOPE, /*sensorDataIndex=*/0); + setupSensor(ABS_RY, InputDeviceSensorType::GYROSCOPE, /*sensorDataIndex=*/1); + setupSensor(ABS_RZ, InputDeviceSensorType::GYROSCOPE, /*sensorDataIndex=*/2); + setupAxis(ABS_RX, /*valid=*/true, GYRO_RAW_MIN, GYRO_RAW_MAX, GYRO_RAW_RESOLUTION, + GYRO_RAW_FLAT, GYRO_RAW_FUZZ); + setupAxis(ABS_RY, /*valid=*/true, GYRO_RAW_MIN, GYRO_RAW_MAX, GYRO_RAW_RESOLUTION, + GYRO_RAW_FLAT, GYRO_RAW_FUZZ); + setupAxis(ABS_RZ, /*valid=*/true, GYRO_RAW_MIN, GYRO_RAW_MAX, GYRO_RAW_RESOLUTION, + GYRO_RAW_FLAT, GYRO_RAW_FUZZ); + mPropertyMap.addProperty("sensor.gyroscope.reportingMode", "0"); + mPropertyMap.addProperty("sensor.gyroscope.maxDelay", "100000"); + mPropertyMap.addProperty("sensor.gyroscope.minDelay", "5000"); + mPropertyMap.addProperty("sensor.gyroscope.power", "0.8"); + mMapper = createInputMapper<SensorInputMapper>(*mDeviceContext, + mFakePolicy->getReaderConfiguration()); + + EXPECT_CALL(mMockEventHub, enableDevice(EVENTHUB_ID)); + ASSERT_TRUE(mMapper->enableSensor(InputDeviceSensorType::GYROSCOPE, + std::chrono::microseconds(10000), + std::chrono::microseconds(0))); + std::list<NotifyArgs> args; + args += process(ARBITRARY_TIME, EV_ABS, ABS_RX, 20000); + args += process(ARBITRARY_TIME, EV_ABS, ABS_RY, -20000); + args += process(ARBITRARY_TIME, EV_ABS, ABS_RZ, 40000); + args += process(ARBITRARY_TIME, EV_MSC, MSC_TIMESTAMP, 1000); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + + std::vector<float> values = {20000.0f / GYRO_RAW_RESOLUTION * DEGREE_RADIAN_UNIT, + -20000.0f / GYRO_RAW_RESOLUTION * DEGREE_RADIAN_UNIT, + 40000.0f / GYRO_RAW_RESOLUTION * DEGREE_RADIAN_UNIT}; + + ASSERT_EQ(args.size(), 1u); + const NotifySensorArgs& arg = std::get<NotifySensorArgs>(args.front()); + ASSERT_EQ(arg.source, AINPUT_SOURCE_SENSOR); + ASSERT_EQ(arg.deviceId, DEVICE_ID); + ASSERT_EQ(arg.sensorType, InputDeviceSensorType::GYROSCOPE); + ASSERT_EQ(arg.accuracy, InputDeviceSensorAccuracy::ACCURACY_HIGH); + ASSERT_EQ(arg.hwTimestamp, ARBITRARY_TIME); + ASSERT_EQ(arg.values, values); + mMapper->flushSensor(InputDeviceSensorType::GYROSCOPE); +} + +} // namespace android
\ No newline at end of file diff --git a/services/inputflinger/tests/TestEventMatchers.h b/services/inputflinger/tests/TestEventMatchers.h index f58d8fd47a..7078e49343 100644 --- a/services/inputflinger/tests/TestEventMatchers.h +++ b/services/inputflinger/tests/TestEventMatchers.h @@ -108,20 +108,33 @@ public: using is_gtest_matcher = void; explicit WithMotionActionMatcher(int32_t action) : mAction(action) {} - bool MatchAndExplain(const NotifyMotionArgs& args, std::ostream*) const { - bool matches = mAction == args.action; - if (args.action == AMOTION_EVENT_ACTION_CANCEL) { - matches &= (args.flags & AMOTION_EVENT_FLAG_CANCELED) != 0; + bool MatchAndExplain(const NotifyMotionArgs& args, + testing::MatchResultListener* listener) const { + if (mAction != args.action) { + *listener << "expected " << MotionEvent::actionToString(mAction) << ", but got " + << MotionEvent::actionToString(args.action); + return false; } - return matches; + if (args.action == AMOTION_EVENT_ACTION_CANCEL && + (args.flags & AMOTION_EVENT_FLAG_CANCELED) == 0) { + *listener << "event with CANCEL action is missing FLAG_CANCELED"; + return false; + } + return true; } - bool MatchAndExplain(const MotionEvent& event, std::ostream*) const { - bool matches = mAction == event.getAction(); - if (event.getAction() == AMOTION_EVENT_ACTION_CANCEL) { - matches &= (event.getFlags() & AMOTION_EVENT_FLAG_CANCELED) != 0; + bool MatchAndExplain(const MotionEvent& event, testing::MatchResultListener* listener) const { + if (mAction != event.getAction()) { + *listener << "expected " << MotionEvent::actionToString(mAction) << ", but got " + << MotionEvent::actionToString(event.getAction()); + return false; } - return matches; + if (event.getAction() == AMOTION_EVENT_ACTION_CANCEL && + (event.getFlags() & AMOTION_EVENT_FLAG_CANCELED) == 0) { + *listener << "event with CANCEL action is missing FLAG_CANCELED"; + return false; + } + return true; } void DescribeTo(std::ostream* os) const { diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h index 60c676da58..a1da39ae14 100644 --- a/services/inputflinger/tests/fuzzers/MapperHelpers.h +++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h @@ -288,6 +288,7 @@ public: void notifyTouchpadHardwareState(const SelfContainedHardwareState& schs, int32_t deviceId) override {} void notifyTouchpadGestureInfo(GestureType type, int32_t deviceId) override {} + void notifyTouchpadThreeFingerTap() override {} std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay( const InputDeviceIdentifier& identifier, const std::optional<KeyboardLayoutInfo> layoutInfo) override { diff --git a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp index 1589c9937d..f5c4fc547c 100644 --- a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp +++ b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp @@ -35,6 +35,7 @@ using aidl::android::hardware::power::IPowerHintSession; using aidl::android::hardware::power::Mode; using aidl::android::hardware::power::SessionConfig; using aidl::android::hardware::power::SessionTag; +using aidl::android::hardware::power::SupportInfo; using android::binder::Status; using namespace android; @@ -65,6 +66,7 @@ public: (int32_t tgid, int32_t uid, ChannelConfig* _aidl_return), (override)); MOCK_METHOD(ndk::ScopedAStatus, closeSessionChannel, (int32_t tgid, int32_t uid), (override)); MOCK_METHOD(ndk::ScopedAStatus, getHintSessionPreferredRate, (int64_t * rate), (override)); + MOCK_METHOD(ndk::ScopedAStatus, getSupportInfo, (SupportInfo * _aidl_return), (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)); diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp index f1c79c11e1..8a667aef72 100644 --- a/services/surfaceflinger/Android.bp +++ b/services/surfaceflinger/Android.bp @@ -177,13 +177,13 @@ cc_library_headers { filegroup { name: "libsurfaceflinger_backend_sources", srcs: [ + "PowerAdvisor/*.cpp", "DisplayHardware/AidlComposerHal.cpp", "DisplayHardware/ComposerHal.cpp", "DisplayHardware/FramebufferSurface.cpp", "DisplayHardware/HWC2.cpp", "DisplayHardware/HWComposer.cpp", "DisplayHardware/HidlComposerHal.cpp", - "DisplayHardware/PowerAdvisor.cpp", "DisplayHardware/VirtualDisplaySurface.cpp", ], } diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp index 7095b9d565..82eafd4fa8 100644 --- a/services/surfaceflinger/CompositionEngine/Android.bp +++ b/services/surfaceflinger/CompositionEngine/Android.bp @@ -42,6 +42,7 @@ cc_defaults { "libutils", ], static_libs: [ + "libguiflags", "libmath", "librenderengine", "libtimestats", @@ -49,9 +50,7 @@ cc_defaults { "libaidlcommonsupport", "libprocessgroup", "libprocessgroup_util", - "libcgrouprc", "libjsoncpp", - "libcgrouprc_format", ], header_libs: [ "android.hardware.graphics.composer@2.1-command-buffer", diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h index 6e60839dd9..252adaa8e3 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h @@ -24,7 +24,7 @@ #include <ui/Size.h> #include <ui/StaticDisplayInfo.h> -#include "DisplayHardware/PowerAdvisor.h" +#include "PowerAdvisor/PowerAdvisor.h" namespace android::compositionengine { @@ -46,9 +46,15 @@ struct DisplayCreationArgs { // content. bool isProtected = false; + // True if this display has picture processing hardware and pipelines. + bool hasPictureProcessing = false; + + // The number of layer-specific picture-processing pipelines. + int32_t maxLayerPictureProfiles = 0; + // Optional pointer to the power advisor interface, if one is needed for // this display. - Hwc2::PowerAdvisor* powerAdvisor = nullptr; + adpf::PowerAdvisor* powerAdvisor = nullptr; // Debugging. Human readable name for the display. std::string name; @@ -82,7 +88,17 @@ public: return *this; } - DisplayCreationArgsBuilder& setPowerAdvisor(Hwc2::PowerAdvisor* powerAdvisor) { + DisplayCreationArgsBuilder& setHasPictureProcessing(bool hasPictureProcessing) { + mArgs.hasPictureProcessing = hasPictureProcessing; + return *this; + } + + DisplayCreationArgsBuilder& setMaxLayerPictureProfiles(int32_t maxLayerPictureProfiles) { + mArgs.maxLayerPictureProfiles = maxLayerPictureProfiles; + return *this; + } + + DisplayCreationArgsBuilder& setPowerAdvisor(adpf::PowerAdvisor* powerAdvisor) { mArgs.powerAdvisor = powerAdvisor; return *this; } diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h index 8a91a07115..fb8fed0743 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h @@ -25,6 +25,7 @@ #include <ui/BlurRegion.h> #include <ui/FloatRect.h> #include <ui/LayerStack.h> +#include <ui/PictureProfileHandle.h> #include <ui/Rect.h> #include <ui/Region.h> #include <ui/ShadowSettings.h> @@ -219,6 +220,14 @@ struct LayerFECompositionState { float currentHdrSdrRatio = 1.f; float desiredHdrSdrRatio = 1.f; + // A picture profile handle refers to a PictureProfile configured on the display, which is a + // set of parameters that configures the picture processing hardware that is used to enhance + // the quality of buffer contents. + PictureProfileHandle pictureProfileHandle{PictureProfileHandle::NONE}; + + // A layer's priority in terms of limited picture processing pipeline utilization. + int64_t pictureProfilePriority; + gui::CachingHint cachingHint = gui::CachingHint::Enabled; std::shared_ptr<gui::DisplayLuts> luts; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h index 556aa249a3..bda7856596 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h @@ -16,6 +16,8 @@ #pragma once +#include <ftl/future.h> +#include <ftl/optional.h> #include <cstdint> #include <iterator> #include <optional> @@ -26,18 +28,18 @@ #include <vector> #include <compositionengine/LayerFE.h> -#include <ftl/future.h> #include <renderengine/LayerSettings.h> +#include <ui/DisplayIdentification.h> #include <ui/Fence.h> #include <ui/FenceTime.h> #include <ui/GraphicTypes.h> #include <ui/LayerStack.h> +#include <ui/PictureProfileHandle.h> #include <ui/Region.h> #include <ui/Transform.h> #include <utils/StrongPointer.h> #include <utils/Vector.h> -#include <ui/DisplayIdentification.h> #include "DisplayHardware/HWComposer.h" namespace android { @@ -167,7 +169,7 @@ public: virtual bool isValid() const = 0; // Returns the DisplayId the output represents, if it has one - virtual std::optional<DisplayId> getDisplayId() const = 0; + virtual ftl::Optional<DisplayId> getDisplayId() const = 0; // Enables (or disables) composition on this output virtual void setCompositionEnabled(bool) = 0; @@ -331,6 +333,9 @@ protected: virtual bool canPredictCompositionStrategy(const CompositionRefreshArgs&) = 0; virtual const aidl::android::hardware::graphics::composer3::OverlayProperties* getOverlaySupport() = 0; + virtual bool hasPictureProcessing() const = 0; + virtual int32_t getMaxLayerPictureProfiles() const = 0; + virtual void applyPictureProfile() = 0; }; } // namespace compositionengine diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h index 80c5124c76..2e7a7d9c5a 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h @@ -21,6 +21,7 @@ #include <string> #include <vector> +#include <ui/PictureProfileHandle.h> #include <ui/Transform.h> #include <utils/StrongPointer.h> @@ -86,6 +87,16 @@ public: // longer cares about. virtual void uncacheBuffers(const std::vector<uint64_t>& bufferIdsToUncache) = 0; + // Get the relative priority of the layer's picture profile with respect to the importance of + // the visual content to the user experience. Lower is higher priority. + virtual int64_t getPictureProfilePriority() const = 0; + + // The picture profile handle for the layer. + virtual const PictureProfileHandle& getPictureProfileHandle() const = 0; + + // Commit the picture profile to the composition state. + virtual void commitPictureProfileToCompositionState() = 0; + // Recalculates the state of the output layer from the output-independent // layer. If includeGeometry is false, the geometry state can be skipped. // internalDisplayRotationFlags must be set to the rotation flags for the diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h index d8466ffdff..5519aafe11 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h @@ -30,7 +30,7 @@ #include <ui/DisplayIdentification.h> #include "DisplayHardware/HWComposer.h" -#include "DisplayHardware/PowerAdvisor.h" +#include "PowerAdvisor/PowerAdvisor.h" namespace android::compositionengine { @@ -45,7 +45,7 @@ public: virtual ~Display(); // compositionengine::Output overrides - std::optional<DisplayId> getDisplayId() const override; + ftl::Optional<DisplayId> getDisplayId() const override; bool isValid() const override; void dump(std::string&) const override; using compositionengine::impl::Output::setReleasedLayers; @@ -100,11 +100,16 @@ private: void setHintSessionGpuStart(TimePoint startTime) override; void setHintSessionGpuFence(std::unique_ptr<FenceTime>&& gpuFence) override; void setHintSessionRequiresRenderEngine(bool requiresRenderEngine) override; - DisplayId mId; - bool mIsDisconnected = false; - Hwc2::PowerAdvisor* mPowerAdvisor = nullptr; const aidl::android::hardware::graphics::composer3::OverlayProperties* getOverlaySupport() override; + bool hasPictureProcessing() const override; + int32_t getMaxLayerPictureProfiles() const override; + + DisplayId mId; + bool mIsDisconnected = false; + adpf::PowerAdvisor* mPowerAdvisor = nullptr; + bool mHasPictureProcessing = false; + int32_t mMaxLayerPictureProfiles = 0; }; // This template factory function standardizes the implementation details of the diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h index 69e1efc4a7..0ccdd22919 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h @@ -16,6 +16,11 @@ #pragma once +#include <ftl/optional.h> +#include <memory> +#include <utility> +#include <vector> + #include <compositionengine/CompositionEngine.h> #include <compositionengine/LayerFECompositionState.h> #include <compositionengine/Output.h> @@ -28,10 +33,6 @@ #include <renderengine/DisplaySettings.h> #include <renderengine/LayerSettings.h> -#include <memory> -#include <utility> -#include <vector> - namespace android::compositionengine::impl { // The implementation class contains the common implementation, but does not @@ -43,7 +44,7 @@ public: // compositionengine::Output overrides bool isValid() const override; - std::optional<DisplayId> getDisplayId() const override; + ftl::Optional<DisplayId> getDisplayId() const override; void setCompositionEnabled(bool) override; void setLayerCachingEnabled(bool) override; void setLayerCachingTexturePoolEnabled(bool) override; @@ -84,13 +85,14 @@ public: bool supportsOffloadPresent() const override { return false; } void offloadPresentNextFrame() override; - void uncacheBuffers(const std::vector<uint64_t>& bufferIdsToUncache) override; void rebuildLayerStacks(const CompositionRefreshArgs&, LayerFESet&) override; void collectVisibleLayers(const CompositionRefreshArgs&, compositionengine::Output::CoverageState&) override; void ensureOutputLayerIfVisible(sp<compositionengine::LayerFE>&, compositionengine::Output::CoverageState&) override; void setReleasedLayers(const compositionengine::CompositionRefreshArgs&) override; + void uncacheBuffers(const std::vector<uint64_t>& bufferIdsToUncache) override; + void commitPictureProfilesToCompositionState(); void updateCompositionState(const compositionengine::CompositionRefreshArgs&) override; void planComposition() override; @@ -151,6 +153,9 @@ protected: void setHintSessionRequiresRenderEngine(bool requiresRenderEngine) override; bool isPowerHintSessionEnabled() override; bool isPowerHintSessionGpuReportingEnabled() override; + bool hasPictureProcessing() const override; + int32_t getMaxLayerPictureProfiles() const override; + void applyPictureProfile() override; void dumpBase(std::string&) const; // Implemented by the final implementation for the final state it uses. diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h index f8ffde1e51..c76b34481b 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h @@ -35,6 +35,7 @@ #include <compositionengine/CompositionRefreshArgs.h> #include <compositionengine/ProjectionSpace.h> #include <ui/LayerStack.h> +#include <ui/PictureProfileHandle.h> #include <ui/Rect.h> #include <ui/Region.h> #include <ui/Transform.h> @@ -170,6 +171,8 @@ struct OutputCompositionState { ICEPowerCallback* powerCallback = nullptr; + PictureProfileHandle pictureProfileHandle; + // Debugging void dump(std::string& result) const; }; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h index 0c7e4dd071..cc491c2853 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h @@ -25,6 +25,7 @@ #include <compositionengine/LayerFE.h> #include <compositionengine/OutputLayer.h> #include <ui/FloatRect.h> +#include <ui/PictureProfileHandle.h> #include <ui/Rect.h> #include <ui/DisplayIdentification.h> @@ -48,6 +49,9 @@ public: void setHwcLayer(std::shared_ptr<HWC2::Layer>) override; void uncacheBuffers(const std::vector<uint64_t>& bufferIdsToUncache) override; + int64_t getPictureProfilePriority() const override; + const PictureProfileHandle& getPictureProfileHandle() const override; + void commitPictureProfileToCompositionState() override; void updateCompositionState(bool includeGeometry, bool forceClientComposition, ui::Transform::RotationFlags, diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h index 28216a475c..c558739464 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h @@ -22,6 +22,7 @@ #include <renderengine/ExternalTexture.h> #include <ui/FloatRect.h> #include <ui/GraphicTypes.h> +#include <ui/PictureProfileHandle.h> #include <ui/Rect.h> #include <ui/Region.h> @@ -101,6 +102,9 @@ struct OutputLayerCompositionState { // order to save power. Region outputSpaceBlockingRegionHint; + // The picture profile for this layer. + PictureProfileHandle pictureProfileHandle; + // Overrides the buffer, acquire fence, and display frame stored in LayerFECompositionState struct { std::shared_ptr<renderengine::ExternalTexture> buffer = nullptr; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h index 33cdc54b90..f2c265ad2e 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h @@ -34,7 +34,7 @@ public: virtual ~Output(); MOCK_CONST_METHOD0(isValid, bool()); - MOCK_CONST_METHOD0(getDisplayId, std::optional<DisplayId>()); + MOCK_CONST_METHOD0(getDisplayId, ftl::Optional<DisplayId>()); MOCK_METHOD1(setCompositionEnabled, void(bool)); MOCK_METHOD1(setLayerCachingEnabled, void(bool)); @@ -142,6 +142,9 @@ public: MOCK_METHOD(bool, isPowerHintSessionGpuReportingEnabled, ()); MOCK_METHOD((const aidl::android::hardware::graphics::composer3::OverlayProperties*), getOverlaySupport, ()); + MOCK_METHOD(bool, hasPictureProcessing, (), (const)); + MOCK_METHOD(int32_t, getMaxLayerPictureProfiles, (), (const)); + MOCK_METHOD(void, applyPictureProfile, ()); }; } // namespace android::compositionengine::mock diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h index 12f20942cb..9333ebb8cd 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h @@ -63,7 +63,9 @@ public: (ndk::ScopedFileDescriptor, (std::vector<std::pair< int, aidl::android::hardware::graphics::composer3::LutProperties>>))); - + MOCK_METHOD(int64_t, getPictureProfilePriority, (), (const)); + MOCK_METHOD(const PictureProfileHandle&, getPictureProfileHandle, (), (const)); + MOCK_METHOD(void, commitPictureProfileToCompositionState, ()); MOCK_CONST_METHOD1(dump, void(std::string&)); }; diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp index 1825065c8f..e37ce0a0eb 100644 --- a/services/surfaceflinger/CompositionEngine/src/Display.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp @@ -36,7 +36,7 @@ // TODO(b/129481165): remove the #pragma below and fix conversion issues #pragma clang diagnostic pop // ignored "-Wconversion" -#include "DisplayHardware/PowerAdvisor.h" +#include "PowerAdvisor/PowerAdvisor.h" using aidl::android::hardware::graphics::composer3::Capability; using aidl::android::hardware::graphics::composer3::DisplayCapability; @@ -54,6 +54,8 @@ Display::~Display() = default; void Display::setConfiguration(const compositionengine::DisplayCreationArgs& args) { mId = args.id; mPowerAdvisor = args.powerAdvisor; + mHasPictureProcessing = args.hasPictureProcessing; + mMaxLayerPictureProfiles = args.maxLayerPictureProfiles; editState().isSecure = args.isSecure; editState().isProtected = args.isProtected; editState().displaySpace.setBounds(args.pixels); @@ -80,7 +82,7 @@ bool Display::isVirtual() const { return mId.isVirtual(); } -std::optional<DisplayId> Display::getDisplayId() const { +ftl::Optional<DisplayId> Display::getDisplayId() const { return mId; } @@ -203,15 +205,16 @@ void Display::setReleasedLayers(const compositionengine::CompositionRefreshArgs& } void Display::applyDisplayBrightness(bool applyImmediately) { - if (const auto displayId = ftl::Optional(getDisplayId()).and_then(PhysicalDisplayId::tryCast); - displayId && getState().displayBrightness) { + if (!getState().displayBrightness) { + return; + } + if (auto displayId = PhysicalDisplayId::tryCast(mId)) { auto& hwc = getCompositionEngine().getHwComposer(); - const status_t result = - hwc.setDisplayBrightness(*displayId, *getState().displayBrightness, - getState().displayBrightnessNits, - Hwc2::Composer::DisplayBrightnessOptions{ - .applyImmediately = applyImmediately}) - .get(); + status_t result = hwc.setDisplayBrightness(*displayId, *getState().displayBrightness, + getState().displayBrightnessNits, + Hwc2::Composer::DisplayBrightnessOptions{ + .applyImmediately = applyImmediately}) + .get(); ALOGE_IF(result != NO_ERROR, "setDisplayBrightness failed for %s: %d, (%s)", getName().c_str(), result, strerror(-result)); } @@ -288,8 +291,8 @@ void Display::applyCompositionStrategy(const std::optional<DeviceRequestedChange } bool Display::getSkipColorTransform() const { - const auto& hwc = getCompositionEngine().getHwComposer(); - if (const auto halDisplayId = HalDisplayId::tryCast(mId)) { + auto& hwc = getCompositionEngine().getHwComposer(); + if (auto halDisplayId = HalDisplayId::tryCast(mId)) { return hwc.hasDisplayCapability(*halDisplayId, DisplayCapability::SKIP_CLIENT_COLOR_TRANSFORM); } @@ -462,6 +465,14 @@ Display::getOverlaySupport() { return &getCompositionEngine().getHwComposer().getOverlaySupport(); } +bool Display::hasPictureProcessing() const { + return mHasPictureProcessing; +} + +int32_t Display::getMaxLayerPictureProfiles() const { + return mMaxLayerPictureProfiles; +} + void Display::finishFrame(GpuCompositionResult&& result) { // We only need to actually compose the display if: // 1) It is being handled by hardware composer, which may need this to @@ -476,8 +487,8 @@ void Display::finishFrame(GpuCompositionResult&& result) { } bool Display::supportsOffloadPresent() const { - if (const auto halDisplayId = HalDisplayId::tryCast(mId)) { - const auto& hwc = getCompositionEngine().getHwComposer(); + if (auto halDisplayId = HalDisplayId::tryCast(mId)) { + auto& hwc = getCompositionEngine().getHwComposer(); return hwc.hasDisplayCapability(*halDisplayId, DisplayCapability::MULTI_THREADED_PRESENT); } diff --git a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp index 2d10866db3..348111d06e 100644 --- a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp +++ b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp @@ -127,6 +127,9 @@ void LayerFECompositionState::dump(std::string& out) const { } dumpVal(out, "colorTransform", colorTransform); dumpVal(out, "caching hint", toString(cachingHint)); + if (pictureProfileHandle) { + dumpVal(out, "pictureProfile", toString(pictureProfileHandle)); + } out.append("\n"); } diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp index bb456138d6..98b6666a1f 100644 --- a/services/surfaceflinger/CompositionEngine/src/Output.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp @@ -32,9 +32,12 @@ #include <compositionengine/impl/planner/Planner.h> #include <ftl/algorithm.h> #include <ftl/future.h> +#include <ftl/optional.h> #include <scheduler/FrameTargeter.h> #include <scheduler/Time.h> +#include <com_android_graphics_libgui_flags.h> + #include <optional> #include <thread> @@ -111,7 +114,7 @@ bool Output::isValid() const { mRenderSurface->isValid(); } -std::optional<DisplayId> Output::getDisplayId() const { +ftl::Optional<DisplayId> Output::getDisplayId() const { return {}; } @@ -433,7 +436,7 @@ void Output::prepare(const compositionengine::CompositionRefreshArgs& refreshArg ftl::Future<std::monostate> Output::present( const compositionengine::CompositionRefreshArgs& refreshArgs) { const auto stringifyExpectedPresentTime = [this, &refreshArgs]() -> std::string { - return ftl::Optional(getDisplayId()) + return getDisplayId() .and_then(PhysicalDisplayId::tryCast) .and_then([&refreshArgs](PhysicalDisplayId id) { return refreshArgs.frameTargets.get(id); @@ -500,15 +503,6 @@ void Output::offloadPresentNextFrame() { updateHwcAsyncWorker(); } -void Output::uncacheBuffers(std::vector<uint64_t> const& bufferIdsToUncache) { - if (bufferIdsToUncache.empty()) { - return; - } - for (auto outputLayer : getOutputLayersOrderedByZ()) { - outputLayer->uncacheBuffers(bufferIdsToUncache); - } -} - void Output::rebuildLayerStacks(const compositionengine::CompositionRefreshArgs& refreshArgs, LayerFESet& layerFESet) { auto& outputState = editState(); @@ -776,11 +770,11 @@ void Output::ensureOutputLayerIfVisible(sp<compositionengine::LayerFE>& layerFE, // The layer is visible. Either reuse the existing outputLayer if we have // one, or create a new one if we do not. - auto result = ensureOutputLayer(prevOutputLayerIndex, layerFE); + auto outputLayer = ensureOutputLayer(prevOutputLayerIndex, layerFE); // Store the layer coverage information into the layer state as some of it // is useful later. - auto& outputLayerState = result->editState(); + auto& outputLayerState = outputLayer->editState(); outputLayerState.visibleRegion = visibleRegion; outputLayerState.visibleNonTransparentRegion = visibleNonTransparentRegion; outputLayerState.coveredRegion = coveredRegion; @@ -798,6 +792,52 @@ void Output::ensureOutputLayerIfVisible(sp<compositionengine::LayerFE>& layerFE, } } +void Output::uncacheBuffers(std::vector<uint64_t> const& bufferIdsToUncache) { + if (bufferIdsToUncache.empty()) { + return; + } + for (auto outputLayer : getOutputLayersOrderedByZ()) { + outputLayer->uncacheBuffers(bufferIdsToUncache); + } +} + +void Output::commitPictureProfilesToCompositionState() { + if (!com_android_graphics_libgui_flags_apply_picture_profiles()) { + return; + } + if (!hasPictureProcessing()) { + return; + } + auto compare = [](const ::android::compositionengine::OutputLayer* lhs, + const ::android::compositionengine::OutputLayer* rhs) { + return lhs->getPictureProfilePriority() > rhs->getPictureProfilePriority(); + }; + std::priority_queue<::android::compositionengine::OutputLayer*, + std::vector<::android::compositionengine::OutputLayer*>, decltype(compare)> + layersWithProfiles; + for (auto outputLayer : getOutputLayersOrderedByZ()) { + if (outputLayer->getPictureProfileHandle()) { + layersWithProfiles.push(outputLayer); + } + } + + // TODO(b/337330263): Use the default display picture profile from SurfaceFlinger + editState().pictureProfileHandle = PictureProfileHandle::NONE; + + // When layer-specific picture processing is supported, apply as many high priority profiles as + // possible to the layers, and ignore the low priority layers. + if (getMaxLayerPictureProfiles() > 0) { + for (int i = 0; i < getMaxLayerPictureProfiles() && !layersWithProfiles.empty(); + layersWithProfiles.pop(), ++i) { + layersWithProfiles.top()->commitPictureProfileToCompositionState(); + } + // No layer-specific picture processing, so apply the highest priority picture profile to + // the entire display. + } else if (!layersWithProfiles.empty()) { + editState().pictureProfileHandle = layersWithProfiles.top()->getPictureProfileHandle(); + } +} + void Output::setReleasedLayers(const compositionengine::CompositionRefreshArgs&) { // The base class does nothing with this call. } @@ -826,6 +866,7 @@ void Output::updateCompositionState(const compositionengine::CompositionRefreshA forceClientComposition = false; } } + commitPictureProfilesToCompositionState(); } void Output::planComposition() { @@ -847,7 +888,7 @@ void Output::writeCompositionState(const compositionengine::CompositionRefreshAr return; } - if (auto frameTargetPtrOpt = ftl::Optional(getDisplayId()) + if (auto frameTargetPtrOpt = getDisplayId() .and_then(PhysicalDisplayId::tryCast) .and_then([&refreshArgs](PhysicalDisplayId id) { return refreshArgs.frameTargets.get(id); @@ -858,6 +899,8 @@ void Output::writeCompositionState(const compositionengine::CompositionRefreshAr editState().frameInterval = refreshArgs.frameInterval; editState().powerCallback = refreshArgs.powerCallback; + applyPictureProfile(); + compositionengine::OutputLayer* peekThroughLayer = nullptr; sp<GraphicBuffer> previousOverride = nullptr; bool includeGeometry = refreshArgs.updatingGeometryThisFrame; @@ -1763,5 +1806,34 @@ float Output::getHdrSdrRatio(const std::shared_ptr<renderengine::ExternalTexture return getState().displayBrightnessNits / getState().sdrWhitePointNits; } +bool Output::hasPictureProcessing() const { + return false; +} + +int32_t Output::getMaxLayerPictureProfiles() const { + return 0; +} + +void Output::applyPictureProfile() { + if (!com_android_graphics_libgui_flags_apply_picture_profiles()) { + return; + } + + // TODO(b/337330263): Move this into the Display class and add a Display unit test. + if (!getState().pictureProfileHandle) { + return; + } + if (!getDisplayId()) { + return; + } + if (auto displayId = PhysicalDisplayId::tryCast(*getDisplayId())) { + auto& hwc = getCompositionEngine().getHwComposer(); + const status_t error = + hwc.setDisplayPictureProfileHandle(*displayId, getState().pictureProfileHandle); + ALOGE_IF(error, "setDisplayPictureProfileHandle failed for %s: %d, (%s)", getName().c_str(), + error, strerror(-error)); + } +} + } // namespace impl } // namespace android::compositionengine diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp index 934909d066..e31d6848eb 100644 --- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp +++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + #include <DisplayHardware/Hal.h> #include <android-base/stringprintf.h> #include <compositionengine/DisplayColorProfile.h> @@ -22,11 +23,12 @@ #include <compositionengine/impl/OutputCompositionState.h> #include <compositionengine/impl/OutputLayer.h> #include <compositionengine/impl/OutputLayerCompositionState.h> +#include <ui/FloatRect.h> +#include <ui/HdrRenderTypeUtils.h> #include <cstdint> #include "system/graphics-base-v1.0.h" -#include "ui/FloatRect.h" -#include <ui/HdrRenderTypeUtils.h> +#include <com_android_graphics_libgui_flags.h> // TODO(b/129481165): remove the #pragma below and fix conversion issues #pragma clang diagnostic push @@ -422,6 +424,16 @@ void OutputLayer::updateCompositionState( } } +void OutputLayer::commitPictureProfileToCompositionState() { + if (!com_android_graphics_libgui_flags_apply_picture_profiles()) { + return; + } + const auto* layerState = getLayerFE().getCompositionState(); + if (layerState) { + editState().pictureProfileHandle = getLayerFE().getCompositionState()->pictureProfileHandle; + } +} + void OutputLayer::writeStateToHWC(bool includeGeometry, bool skipLayer, uint32_t z, bool zIsOverridden, bool isPeekingThrough) { const auto& state = getState(); @@ -643,6 +655,21 @@ void OutputLayer::writeOutputDependentPerFrameStateToHWC(HWC2::Layer* hwcLayer) ALOGE("[%s] Failed to set brightness %f: %s (%d)", getLayerFE().getDebugName(), dimmingRatio, to_string(error).c_str(), static_cast<int32_t>(error)); } + + if (com_android_graphics_libgui_flags_apply_picture_profiles() && + outputDependentState.pictureProfileHandle) { + if (auto error = + hwcLayer->setPictureProfileHandle(outputDependentState.pictureProfileHandle); + error != hal::Error::NONE) { + ALOGE("[%s] Failed to set picture profile handle: %s (%d)", getLayerFE().getDebugName(), + toString(outputDependentState.pictureProfileHandle).c_str(), + static_cast<int32_t>(error)); + } + // Reset the picture profile state, as it needs to be re-committed on each present cycle + // when Output decides that the limited picture-processing hardware should be used by this + // layer. + editState().pictureProfileHandle = PictureProfileHandle::NONE; + } } void OutputLayer::writeOutputIndependentPerFrameStateToHWC( @@ -748,6 +775,16 @@ void OutputLayer::uncacheBuffers(const std::vector<uint64_t>& bufferIdsToUncache } } +int64_t OutputLayer::getPictureProfilePriority() const { + const auto* layerState = getLayerFE().getCompositionState(); + return layerState ? layerState->pictureProfilePriority : 0; +} + +const PictureProfileHandle& OutputLayer::getPictureProfileHandle() const { + const auto* layerState = getLayerFE().getCompositionState(); + return layerState ? layerState->pictureProfileHandle : PictureProfileHandle::NONE; +} + void OutputLayer::writeBufferStateToHWC(HWC2::Layer* hwcLayer, const LayerFECompositionState& outputIndependentState, bool skipLayer) { @@ -830,14 +867,14 @@ void OutputLayer::writeCursorPositionToHWC() const { return; } - const auto* layerFEState = getLayerFE().getCompositionState(); - if (!layerFEState) { + const auto* layerState = getLayerFE().getCompositionState(); + if (!layerState) { return; } const auto& outputState = getOutput().getState(); - Rect frame = layerFEState->cursorFrame; + Rect frame = layerState->cursorFrame; frame.intersect(outputState.layerStackSpace.getContent(), &frame); Rect position = outputState.transform.transform(frame); diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp index da1f7e49f8..deef74728d 100644 --- a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp +++ b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp @@ -72,6 +72,9 @@ void OutputLayerCompositionState::dump(std::string& out) const { dumpVal(out, "dataspace", toString(dataspace), dataspace); dumpVal(out, "whitePointNits", whitePointNits); dumpVal(out, "dimmingRatio", dimmingRatio); + if (pictureProfileHandle) { + dumpVal(out, "pictureProfile", toString(pictureProfileHandle)); + } dumpVal(out, "override buffer", overrideInfo.buffer.get()); dumpVal(out, "override acquire fence", overrideInfo.acquireFence.get()); dumpVal(out, "override display frame", overrideInfo.displayFrame); diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp index 416001e374..c1e59d01de 100644 --- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp @@ -39,7 +39,7 @@ #include "ftl/future.h" #include "mock/DisplayHardware/MockHWC2.h" #include "mock/DisplayHardware/MockHWComposer.h" -#include "mock/DisplayHardware/MockPowerAdvisor.h" +#include "mock/PowerAdvisor/MockPowerAdvisor.h" #include <aidl/android/hardware/graphics/composer3/Composition.h> @@ -192,7 +192,7 @@ struct DisplayTestCommon : public testing::Test { } StrictMock<android::mock::HWComposer> mHwComposer; - StrictMock<Hwc2::mock::PowerAdvisor> mPowerAdvisor; + StrictMock<adpf::mock::PowerAdvisor> mPowerAdvisor; StrictMock<renderengine::mock::RenderEngine> mRenderEngine; StrictMock<mock::CompositionEngine> mCompositionEngine; sp<mock::NativeWindow> mNativeWindow = sp<StrictMock<mock::NativeWindow>>::make(); @@ -1035,7 +1035,7 @@ struct DisplayFunctionalTest : public testing::Test { } NiceMock<android::mock::HWComposer> mHwComposer; - NiceMock<Hwc2::mock::PowerAdvisor> mPowerAdvisor; + NiceMock<adpf::mock::PowerAdvisor> mPowerAdvisor; NiceMock<mock::CompositionEngine> mCompositionEngine; sp<mock::NativeWindow> mNativeWindow = sp<NiceMock<mock::NativeWindow>>::make(); sp<mock::DisplaySurface> mDisplaySurface = sp<NiceMock<mock::DisplaySurface>>::make(); diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp index f2c5672ae4..dbffe80a3a 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include <com_android_graphics_libgui_flags.h> #include <compositionengine/impl/HwcBufferCache.h> #include <compositionengine/impl/OutputLayer.h> #include <compositionengine/impl/OutputLayerCompositionState.h> @@ -1332,6 +1333,71 @@ TEST_F(OutputLayerWriteStateToHWCTest, setCompositionTypeRefreshRateIndicator) { /*zIsOverridden*/ false, /*isPeekingThrough*/ false); } +TEST_F(OutputLayerWriteStateToHWCTest, setsPictureProfileWhenCommitted) { + if (!com_android_graphics_libgui_flags_apply_picture_profiles()) { + GTEST_SKIP() << "Feature flag disabled, skipping"; + } + mLayerFEState.compositionType = Composition::DEVICE; + mLayerFEState.pictureProfileHandle = PictureProfileHandle(1); + + expectGeometryCommonCalls(); + expectPerFrameCommonCalls(); + expectSetHdrMetadataAndBufferCalls(); + expectSetCompositionTypeCall(Composition::DEVICE); + + EXPECT_CALL(*mHwcLayer, setPictureProfileHandle(PictureProfileHandle(1))); + + mOutputLayer.commitPictureProfileToCompositionState(); + mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0, + /*zIsOverridden*/ false, /*isPeekingThrough*/ false); +} + +TEST_F(OutputLayerWriteStateToHWCTest, doesNotSetPictureProfileWhenNotCommitted) { + if (!com_android_graphics_libgui_flags_apply_picture_profiles()) { + GTEST_SKIP() << "Feature flag disabled, skipping"; + } + mLayerFEState.compositionType = Composition::DEVICE; + mLayerFEState.pictureProfileHandle = PictureProfileHandle(1); + + expectGeometryCommonCalls(); + expectPerFrameCommonCalls(); + expectSetHdrMetadataAndBufferCalls(); + expectSetCompositionTypeCall(Composition::DEVICE); + + EXPECT_CALL(*mHwcLayer, setPictureProfileHandle(_)).Times(0); + + mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0, + /*zIsOverridden*/ false, /*isPeekingThrough*/ false); +} + +TEST_F(OutputLayerWriteStateToHWCTest, doesNotSetPictureProfileWhenNotCommittedLater) { + if (!com_android_graphics_libgui_flags_apply_picture_profiles()) { + GTEST_SKIP() << "Feature flag disabled, skipping"; + } + mLayerFEState.compositionType = Composition::DEVICE; + mLayerFEState.pictureProfileHandle = PictureProfileHandle(1); + + expectGeometryCommonCalls(); + expectPerFrameCommonCalls(); + expectSetHdrMetadataAndBufferCalls(); + expectSetCompositionTypeCall(Composition::DEVICE); + + EXPECT_CALL(*mHwcLayer, setPictureProfileHandle(PictureProfileHandle(1))); + + mOutputLayer.commitPictureProfileToCompositionState(); + mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0, + /*zIsOverridden*/ false, /*isPeekingThrough*/ false); + + expectGeometryCommonCalls(); + expectPerFrameCommonCalls(); + expectSetHdrMetadataAndBufferCalls(kExpectedHwcSlot, nullptr, kFence); + + EXPECT_CALL(*mHwcLayer, setPictureProfileHandle(PictureProfileHandle(1))).Times(0); + // No committing of picture profile before writing the state + mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0, + /*zIsOverridden*/ false, /*isPeekingThrough*/ false); +} + /* * OutputLayer::uncacheBuffers */ diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp index fe7dd9a3bc..99e68eb71a 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp @@ -15,6 +15,7 @@ */ #include <android-base/stringprintf.h> +#include <com_android_graphics_libgui_flags.h> #include <com_android_graphics_surfaceflinger_flags.h> #include <compositionengine/LayerFECompositionState.h> #include <compositionengine/impl/Output.h> @@ -37,11 +38,14 @@ #include <cstdint> #include <variant> +#include <com_android_graphics_surfaceflinger_flags.h> + #include <common/FlagManager.h> #include <common/test/FlagUtils.h> #include "CallOrderStateMachineHelper.h" #include "RegionMatcher.h" #include "mock/DisplayHardware/MockHWC2.h" +#include "mock/DisplayHardware/MockHWComposer.h" namespace android::compositionengine { namespace { @@ -142,6 +146,24 @@ struct OutputTest : public testing::Test { public: using impl::Output::injectOutputLayerForTest; virtual void injectOutputLayerForTest(std::unique_ptr<compositionengine::OutputLayer>) = 0; + + virtual ftl::Optional<DisplayId> getDisplayId() const override { return mId; } + + virtual bool hasPictureProcessing() const override { return mHasPictureProcessing; } + virtual int32_t getMaxLayerPictureProfiles() const override { + return mMaxLayerPictureProfiles; + } + + void setDisplayIdForTest(DisplayId value) { mId = value; } + + void setHasPictureProcessingForTest(bool value) { mHasPictureProcessing = value; } + + void setMaxLayerPictureProfilesForTest(int32_t value) { mMaxLayerPictureProfiles = value; } + + private: + ftl::Optional<DisplayId> mId; + bool mHasPictureProcessing; + int32_t mMaxLayerPictureProfiles; }; static std::shared_ptr<Output> createOutput( @@ -157,6 +179,7 @@ struct OutputTest : public testing::Test { mOutput->editState().displaySpace.setBounds( ui::Size(kDefaultDisplaySize.getWidth(), kDefaultDisplaySize.getHeight())); EXPECT_CALL(mCompositionEngine, getRenderEngine()).WillRepeatedly(ReturnRef(mRenderEngine)); + EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer)); } void injectOutputLayer(InjectedLayer& layer) { @@ -169,6 +192,7 @@ struct OutputTest : public testing::Test { static const Rect kDefaultDisplaySize; + StrictMock<::android::mock::HWComposer> mHwComposer; StrictMock<mock::CompositionEngine> mCompositionEngine; StrictMock<renderengine::mock::RenderEngine> mRenderEngine; mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>(); @@ -5043,6 +5067,123 @@ TEST_F(OutputUpdateAndWriteCompositionStateTest, handlesBlurRegionRequests) { mOutput->writeCompositionState(args); } +TEST_F(OutputUpdateAndWriteCompositionStateTest, assignsDisplayProfileBasedOnLayerPriority) { + if (!com_android_graphics_libgui_flags_apply_picture_profiles()) { + GTEST_SKIP() << "Feature flag disabled, skipping"; + } + + mOutput->setDisplayIdForTest(PhysicalDisplayId::fromPort(1)); + // Has only one display-global picture processing pipeline + mOutput->setHasPictureProcessingForTest(true); + mOutput->setMaxLayerPictureProfilesForTest(0); + + InjectedLayer layer1; + injectOutputLayer(layer1); + PictureProfileHandle profileForLayer1(1); + EXPECT_CALL(*layer1.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(3)); + EXPECT_CALL(*layer1.outputLayer, getPictureProfileHandle()) + .WillRepeatedly(ReturnRef(profileForLayer1)); + + InjectedLayer layer2; + injectOutputLayer(layer2); + PictureProfileHandle profileForLayer2(2); + EXPECT_CALL(*layer2.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(1)); + EXPECT_CALL(*layer2.outputLayer, getPictureProfileHandle()) + .WillRepeatedly(ReturnRef(profileForLayer2)); + + InjectedLayer layer3; + injectOutputLayer(layer3); + PictureProfileHandle profileForLayer3(3); + EXPECT_CALL(*layer3.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(2)); + EXPECT_CALL(*layer3.outputLayer, getPictureProfileHandle()) + .WillRepeatedly(ReturnRef(profileForLayer3)); + + // Because StrictMock + EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false)); + EXPECT_CALL(*layer1.outputLayer, updateCompositionState(_, _, _, _)); + EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(_, _, _, _, _)); + EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false)); + EXPECT_CALL(*layer2.outputLayer, updateCompositionState(_, _, _, _)); + EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(_, _, _, _, _)); + EXPECT_CALL(*layer3.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false)); + EXPECT_CALL(*layer3.outputLayer, updateCompositionState(_, _, _, _)); + EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(_, _, _, _, _)); + + // No layer picture profiles should be committed + EXPECT_CALL(*layer1.outputLayer, commitPictureProfileToCompositionState).Times(0); + EXPECT_CALL(*layer2.outputLayer, commitPictureProfileToCompositionState).Times(0); + EXPECT_CALL(*layer3.outputLayer, commitPictureProfileToCompositionState).Times(0); + + // Sets display picture profile to the highest priority layer's profile + EXPECT_CALL(mHwComposer, setDisplayPictureProfileHandle(_, Eq(profileForLayer2))); + + mOutput->editState().isEnabled = true; + CompositionRefreshArgs args; + args.updatingGeometryThisFrame = false; + args.devOptForceClientComposition = false; + mOutput->updateCompositionState(args); + mOutput->planComposition(); + mOutput->writeCompositionState(args); +} + +TEST_F(OutputUpdateAndWriteCompositionStateTest, assignsLayerProfileBasedOnLayerPriority) { + if (!com_android_graphics_libgui_flags_apply_picture_profiles()) { + GTEST_SKIP() << "Feature flag disabled, skipping"; + } + mOutput->setDisplayIdForTest(PhysicalDisplayId::fromPort(1)); + // Has 2 layer-specific picture processing pipelines + mOutput->setHasPictureProcessingForTest(true); + mOutput->setMaxLayerPictureProfilesForTest(2); + + InjectedLayer layer1; + injectOutputLayer(layer1); + PictureProfileHandle profileForLayer1(1); + EXPECT_CALL(*layer1.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(3)); + EXPECT_CALL(*layer1.outputLayer, getPictureProfileHandle()) + .WillRepeatedly(ReturnRef(profileForLayer1)); + + InjectedLayer layer2; + injectOutputLayer(layer2); + PictureProfileHandle profileForLayer2(2); + EXPECT_CALL(*layer2.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(1)); + EXPECT_CALL(*layer2.outputLayer, getPictureProfileHandle()) + .WillRepeatedly(ReturnRef(profileForLayer2)); + + InjectedLayer layer3; + injectOutputLayer(layer3); + PictureProfileHandle profileForLayer3(3); + EXPECT_CALL(*layer3.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(2)); + EXPECT_CALL(*layer3.outputLayer, getPictureProfileHandle()) + .WillRepeatedly(ReturnRef(profileForLayer3)); + + // Because StrictMock + EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false)); + EXPECT_CALL(*layer1.outputLayer, updateCompositionState(_, _, _, _)); + EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(_, _, _, _, _)); + EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false)); + EXPECT_CALL(*layer2.outputLayer, updateCompositionState(_, _, _, _)); + EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(_, _, _, _, _)); + EXPECT_CALL(*layer3.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false)); + EXPECT_CALL(*layer3.outputLayer, updateCompositionState(_, _, _, _)); + EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(_, _, _, _, _)); + + // The two highest priority layers should have their picture profiles committed + EXPECT_CALL(*layer1.outputLayer, commitPictureProfileToCompositionState).Times(0); + EXPECT_CALL(*layer2.outputLayer, commitPictureProfileToCompositionState); + EXPECT_CALL(*layer3.outputLayer, commitPictureProfileToCompositionState); + + // No display picture profile is sent + EXPECT_CALL(mHwComposer, setDisplayPictureProfileHandle).Times(0); + + mOutput->editState().isEnabled = true; + CompositionRefreshArgs args; + args.updatingGeometryThisFrame = false; + args.devOptForceClientComposition = false; + mOutput->updateCompositionState(args); + mOutput->planComposition(); + mOutput->writeCompositionState(args); +} + TEST_F(GenerateClientCompositionRequestsTest, handlesLandscapeModeSplitScreenRequests) { // In split-screen landscape mode, the screen is rotated 90 degrees, with // one layer on the left covering the left side of the output, and one layer diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp index 402a3d2e2f..c743ea2ff4 100644 --- a/services/surfaceflinger/DisplayDevice.cpp +++ b/services/surfaceflinger/DisplayDevice.cpp @@ -201,6 +201,10 @@ bool DisplayDevice::isPoweredOn() const { return mPowerMode != hal::PowerMode::OFF; } +bool DisplayDevice::isRefreshable() const { + return mPowerMode == hal::PowerMode::DOZE || mPowerMode == hal::PowerMode::ON; +} + ui::Dataspace DisplayDevice::getCompositionDataSpace() const { return mCompositionDisplay->getState().dataspace; } diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h index 3e3f558cee..af2b48fc07 100644 --- a/services/surfaceflinger/DisplayDevice.h +++ b/services/surfaceflinger/DisplayDevice.h @@ -42,7 +42,6 @@ #include "DisplayHardware/DisplayMode.h" #include "DisplayHardware/Hal.h" -#include "DisplayHardware/PowerAdvisor.h" #include "FrontEnd/DisplayInfo.h" #include "Scheduler/RefreshRateSelector.h" #include "ThreadContext.h" @@ -173,6 +172,7 @@ public: hardware::graphics::composer::hal::PowerMode getPowerMode() const; void setPowerMode(hardware::graphics::composer::hal::PowerMode); bool isPoweredOn() const; + bool isRefreshable() const; void tracePowerMode(); // Enables layer caching on this DisplayDevice @@ -285,6 +285,8 @@ struct DisplayDeviceState { bool isProtected = false; // Refer to DisplayDevice::mRequestedRefreshRate, for virtual display only Fps requestedRefreshRate; + int32_t maxLayerPictureProfiles = 0; + bool hasPictureProcessing = false; private: static std::atomic<int32_t> sNextSequenceId; diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp index 5814aa4354..4c8ff58530 100644 --- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp @@ -44,12 +44,11 @@ using hardware::Return; using aidl::android::hardware::graphics::composer3::BnComposerCallback; using aidl::android::hardware::graphics::composer3::Capability; using aidl::android::hardware::graphics::composer3::ClientTargetPropertyWithBrightness; +using aidl::android::hardware::graphics::composer3::CommandResultPayload; using aidl::android::hardware::graphics::composer3::Luts; using aidl::android::hardware::graphics::composer3::PowerMode; using aidl::android::hardware::graphics::composer3::VirtualDisplay; -using aidl::android::hardware::graphics::composer3::CommandResultPayload; - using AidlColorMode = aidl::android::hardware::graphics::composer3::ColorMode; using AidlContentType = aidl::android::hardware::graphics::composer3::ContentType; using AidlDisplayIdentification = @@ -1639,6 +1638,41 @@ Error AidlComposer::getPhysicalDisplayOrientation(Display displayId, return Error::NONE; } +Error AidlComposer::getMaxLayerPictureProfiles(Display display, int32_t* outMaxProfiles) { + const auto status = mAidlComposerClient->getMaxLayerPictureProfiles(translate<int64_t>(display), + outMaxProfiles); + if (!status.isOk()) { + ALOGE("getMaxLayerPictureProfiles failed %s", status.getDescription().c_str()); + return static_cast<Error>(status.getServiceSpecificError()); + } + return Error::NONE; +} + +Error AidlComposer::setDisplayPictureProfileId(Display display, PictureProfileId id) { + Error error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().setDisplayPictureProfileId(translate<int64_t>(display), id); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; +} + +Error AidlComposer::setLayerPictureProfileId(Display display, Layer layer, PictureProfileId id) { + Error error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().setLayerPictureProfileId(translate<int64_t>(display), + translate<int64_t>(layer), id); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; +} + ftl::Optional<std::reference_wrapper<ComposerClientWriter>> AidlComposer::getWriter(Display display) REQUIRES_SHARED(mMutex) { return mWriters.get(display); diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h index d724b218c0..933e8d186f 100644 --- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h @@ -250,6 +250,9 @@ public: std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>* outLuts) override; Error setLayerLuts(Display display, Layer layer, Luts& luts) override; + Error getMaxLayerPictureProfiles(Display, int32_t* outMaxProfiles) override; + Error setDisplayPictureProfileId(Display, PictureProfileId id) override; + Error setLayerPictureProfileId(Display, Layer, PictureProfileId id) override; private: // Many public functions above simply write a command into the command diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h index 42ddcd18c8..c1333c29b5 100644 --- a/services/surfaceflinger/DisplayHardware/ComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h @@ -29,11 +29,13 @@ #include <math/mat4.h> #include <ui/DisplayedFrameStats.h> #include <ui/GraphicBuffer.h> +#include <ui/PictureProfileHandle.h> #include <utils/StrongPointer.h> #include <aidl/android/hardware/graphics/common/DisplayDecorationSupport.h> #include <aidl/android/hardware/graphics/common/HdrConversionCapability.h> #include <aidl/android/hardware/graphics/common/HdrConversionStrategy.h> +#include <aidl/android/hardware/graphics/common/Transform.h> #include <aidl/android/hardware/graphics/composer3/Capability.h> #include <aidl/android/hardware/graphics/composer3/ClientTargetPropertyWithBrightness.h> #include <aidl/android/hardware/graphics/composer3/Color.h> @@ -44,7 +46,6 @@ #include <aidl/android/hardware/graphics/composer3/IComposerCallback.h> #include <aidl/android/hardware/graphics/composer3/OverlayProperties.h> -#include <aidl/android/hardware/graphics/common/Transform.h> #include <optional> // TODO(b/129481165): remove the #pragma below and fix conversion issues @@ -307,6 +308,9 @@ public: virtual Error getRequestedLuts(Display display, std::vector<Layer>* outLayers, std::vector<V3_0::DisplayLuts::LayerLut>* outLuts) = 0; virtual Error setLayerLuts(Display display, Layer layer, V3_0::Luts& luts) = 0; + virtual Error getMaxLayerPictureProfiles(Display display, int32_t* outMaxProfiles) = 0; + virtual Error setDisplayPictureProfileId(Display display, PictureProfileId id) = 0; + virtual Error setLayerPictureProfileId(Display display, Layer layer, PictureProfileId id) = 0; }; } // namespace Hwc2 diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp index 5355a12cda..a274995eec 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.cpp +++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp @@ -31,6 +31,7 @@ #include <ui/Fence.h> #include <ui/FloatRect.h> #include <ui/GraphicBuffer.h> +#include <ui/PictureProfileHandle.h> #include <algorithm> #include <cinttypes> @@ -53,6 +54,7 @@ using android::FloatRect; using android::GraphicBuffer; using android::HdrCapabilities; using android::HdrMetadata; +using android::PictureProfileHandle; using android::Rect; using android::Region; using android::sp; @@ -655,6 +657,16 @@ Error Display::setIdleTimerEnabled(std::chrono::milliseconds timeout) { return static_cast<Error>(error); } +Error Display::getMaxLayerPictureProfiles(int32_t* outMaxProfiles) { + const auto error = mComposer.getMaxLayerPictureProfiles(mId, outMaxProfiles); + return static_cast<Error>(error); +} + +Error Display::setPictureProfileHandle(const PictureProfileHandle& handle) { + const auto error = mComposer.setDisplayPictureProfileId(mId, handle.getId()); + return static_cast<Error>(error); +} + // For use by Device void Display::setConnected(bool connected) { @@ -1086,6 +1098,15 @@ Error Layer::setLuts(aidl::android::hardware::graphics::composer3::Luts& luts) { return static_cast<Error>(intError); } +Error Layer::setPictureProfileHandle(const PictureProfileHandle& handle) { + if (CC_UNLIKELY(!mDisplay)) { + return Error::BAD_DISPLAY; + } + const auto intError = + mComposer.setLayerPictureProfileId(mDisplay->getId(), mId, handle.getId()); + return static_cast<Error>(intError); +} + } // namespace impl } // namespace HWC2 } // namespace android diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h index 799fd02586..6740d8a832 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.h +++ b/services/surfaceflinger/DisplayHardware/HWC2.h @@ -24,6 +24,7 @@ #include <gui/HdrMetadata.h> #include <math/mat4.h> #include <ui/HdrCapabilities.h> +#include <ui/PictureProfileHandle.h> #include <ui/Region.h> #include <ui/StaticDisplayInfo.h> #include <utils/Log.h> @@ -199,6 +200,9 @@ public: [[nodiscard]] virtual hal::Error setIdleTimerEnabled(std::chrono::milliseconds timeout) = 0; [[nodiscard]] virtual hal::Error getPhysicalDisplayOrientation( Hwc2::AidlTransform* outTransform) const = 0; + [[nodiscard]] virtual hal::Error getMaxLayerPictureProfiles(int32_t* maxProfiles) = 0; + [[nodiscard]] virtual hal::Error setPictureProfileHandle( + const PictureProfileHandle& handle) = 0; }; namespace impl { @@ -282,6 +286,8 @@ public: std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>* support) override; hal::Error setIdleTimerEnabled(std::chrono::milliseconds timeout) override; + hal::Error getMaxLayerPictureProfiles(int32_t* maxProfiles) override; + hal::Error setPictureProfileHandle(const android::PictureProfileHandle& handle) override; // Other Display methods hal::HWDisplayId getId() const override { return mId; } @@ -377,6 +383,8 @@ public: [[nodiscard]] virtual hal::Error setBlockingRegion(const android::Region& region) = 0; [[nodiscard]] virtual hal::Error setLuts( aidl::android::hardware::graphics::composer3::Luts& luts) = 0; + [[nodiscard]] virtual hal::Error setPictureProfileHandle( + const PictureProfileHandle& handle) = 0; }; namespace impl { @@ -428,6 +436,7 @@ public: hal::Error setBrightness(float brightness) override; hal::Error setBlockingRegion(const android::Region& region) override; hal::Error setLuts(aidl::android::hardware::graphics::composer3::Luts&) override; + hal::Error setPictureProfileHandle(const PictureProfileHandle& handle) override; private: // These are references to data owned by HWComposer, which will outlive @@ -449,6 +458,7 @@ private: android::HdrMetadata mHdrMetadata; android::mat4 mColorMatrix; uint32_t mBufferSlot; + android::PictureProfileHandle profile; }; } // namespace impl diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index 7d77634722..61d4541e77 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -1022,6 +1022,24 @@ status_t HWComposer::setContentType(PhysicalDisplayId displayId, hal::ContentTyp return NO_ERROR; } +int32_t HWComposer::getMaxLayerPictureProfiles(PhysicalDisplayId displayId) { + int32_t maxProfiles = 0; + RETURN_IF_INVALID_DISPLAY(displayId, 0); + const auto error = mDisplayData[displayId].hwcDisplay->getMaxLayerPictureProfiles(&maxProfiles); + RETURN_IF_HWC_ERROR(error, displayId, 0); + return maxProfiles; +} + +status_t HWComposer::setDisplayPictureProfileHandle(PhysicalDisplayId displayId, + const PictureProfileHandle& handle) { + RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); + const auto error = mDisplayData[displayId].hwcDisplay->setPictureProfileHandle(handle); + if (error != hal::Error::UNSUPPORTED) { + RETURN_IF_HWC_ERROR(error, displayId, INVALID_OPERATION); + } + return NO_ERROR; +} + const std::unordered_map<std::string, bool>& HWComposer::getSupportedLayerGenericMetadata() const { return mSupportedLayerGenericMetadata; } diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h index 7b04d6755a..e21ce1d095 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.h +++ b/services/surfaceflinger/DisplayHardware/HWComposer.h @@ -29,6 +29,7 @@ #include <ftl/future.h> #include <ui/DisplayIdentification.h> #include <ui/FenceTime.h> +#include <ui/PictureProfileHandle.h> // TODO(b/129481165): remove the #pragma below and fix conversion issues #pragma clang diagnostic push @@ -65,6 +66,7 @@ class GraphicBuffer; class TestableSurfaceFlinger; struct HWComposerTest; struct CompositionInfo; +class PictureProfileHandle; namespace Hwc2 { class Composer; @@ -296,7 +298,7 @@ public: virtual std::optional<PhysicalDisplayId> toPhysicalDisplayId(hal::HWDisplayId) const = 0; virtual std::optional<hal::HWDisplayId> fromPhysicalDisplayId(PhysicalDisplayId) const = 0; - // Composer 3.0 + // AIDL Composer virtual status_t setBootDisplayMode(PhysicalDisplayId, hal::HWConfigId) = 0; virtual status_t clearBootDisplayMode(PhysicalDisplayId) = 0; virtual std::optional<hal::HWConfigId> getPreferredBootDisplayMode(PhysicalDisplayId) = 0; @@ -315,8 +317,10 @@ public: virtual status_t setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId, bool enabled) = 0; virtual status_t notifyExpectedPresent(PhysicalDisplayId, TimePoint expectedPresentTime, Fps frameInterval) = 0; - // mapper virtual HWC2::Display::LutFileDescriptorMapper& getLutFileDescriptorMapper() = 0; + virtual int32_t getMaxLayerPictureProfiles(PhysicalDisplayId) = 0; + virtual status_t setDisplayPictureProfileHandle(PhysicalDisplayId, + const PictureProfileHandle& handle) = 0; }; static inline bool operator==(const android::HWComposer::DeviceRequestedChanges& lhs, @@ -480,9 +484,10 @@ public: status_t setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId, bool enabled) override; status_t notifyExpectedPresent(PhysicalDisplayId, TimePoint expectedPresentTime, Fps frameInterval) override; - - // get a mapper HWC2::Display::LutFileDescriptorMapper& getLutFileDescriptorMapper() override; + int32_t getMaxLayerPictureProfiles(PhysicalDisplayId) override; + status_t setDisplayPictureProfileHandle(PhysicalDisplayId, + const android::PictureProfileHandle& profile) override; // for debugging ---------------------------------------------------------- void dump(std::string& out) const override; diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp index 6a7a09b5ae..e359a26f16 100644 --- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp @@ -1446,6 +1446,18 @@ Error HidlComposer::getPhysicalDisplayOrientation(Display, AidlTransform*) { "OptionalFeature::PhysicalDisplayOrientation is not supported on HIDL"); } +Error HidlComposer::getMaxLayerPictureProfiles(Display, int32_t*) { + return Error::UNSUPPORTED; +} + +Error HidlComposer::setDisplayPictureProfileId(Display, PictureProfileId) { + return Error::UNSUPPORTED; +} + +Error HidlComposer::setLayerPictureProfileId(Display, Layer, PictureProfileId) { + return Error::UNSUPPORTED; +} + void HidlComposer::registerCallback(ComposerCallback& callback) { const bool vsyncSwitchingSupported = isSupported(Hwc2::Composer::OptionalFeature::RefreshRateSwitching); diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h index a3d1f7f291..9a89dbaa9e 100644 --- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h @@ -357,6 +357,9 @@ public: override; Error setLayerLuts(Display, Layer, aidl::android::hardware::graphics::composer3::Luts&) override; + Error getMaxLayerPictureProfiles(Display, int32_t* outMaxProfiles) override; + Error setDisplayPictureProfileId(Display, PictureProfileId) override; + Error setLayerPictureProfileId(Display, Layer, PictureProfileId) override; private: class CommandWriter : public CommandWriterBase { diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp index 47b811b721..c13e444a99 100644 --- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp +++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp @@ -378,6 +378,11 @@ void SurfaceFrame::setAcquireFenceTime(nsecs_t acquireFenceTime) { } } +void SurfaceFrame::setDesiredPresentTime(nsecs_t desiredPresentTime) { + std::scoped_lock lock(mMutex); + mActuals.desiredPresentTime = desiredPresentTime; +} + void SurfaceFrame::setDropTime(nsecs_t dropTime) { std::scoped_lock lock(mMutex); mDropTime = dropTime; @@ -1456,6 +1461,30 @@ float FrameTimeline::computeFps(const std::unordered_set<int32_t>& layerIds) { static_cast<float>(totalPresentToPresentWalls); } +void FrameTimeline::generateFrameStats(int32_t layer, size_t count, FrameStats* outStats) const { + std::scoped_lock lock(mMutex); + + // TODO: Include FPS calculation here + for (auto displayFrame : mDisplayFrames) { + if (!count--) { + break; + } + + if (displayFrame->getActuals().presentTime <= 0) { + continue; + } + + for (const auto& surfaceFrame : displayFrame->getSurfaceFrames()) { + if (surfaceFrame->getLayerId() == layer) { + outStats->actualPresentTimesNano.push_back(surfaceFrame->getActuals().presentTime); + outStats->desiredPresentTimesNano.push_back( + surfaceFrame->getActuals().desiredPresentTime); + outStats->frameReadyTimesNano.push_back(surfaceFrame->getActuals().endTime); + } + } + } +} + std::optional<size_t> FrameTimeline::getFirstSignalFenceIndex() const { for (size_t i = 0; i < mPendingPresentFences.size(); i++) { const auto& [fence, _] = mPendingPresentFences[i]; diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h index cffb61ee10..6cda309440 100644 --- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h +++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h @@ -85,16 +85,20 @@ enum class FrameStartMetadata : int8_t { */ struct TimelineItem { TimelineItem(const nsecs_t startTime = 0, const nsecs_t endTime = 0, - const nsecs_t presentTime = 0) - : startTime(startTime), endTime(endTime), presentTime(presentTime) {} + const nsecs_t presentTime = 0, const nsecs_t desiredPresentTime = 0) + : startTime(startTime), + endTime(endTime), + presentTime(presentTime), + desiredPresentTime(desiredPresentTime) {} nsecs_t startTime; nsecs_t endTime; nsecs_t presentTime; + nsecs_t desiredPresentTime; bool operator==(const TimelineItem& other) const { return startTime == other.startTime && endTime == other.endTime && - presentTime == other.presentTime; + presentTime == other.presentTime && desiredPresentTime != other.desiredPresentTime; } bool operator!=(const TimelineItem& other) const { return !(*this == other); } @@ -183,6 +187,7 @@ public: void setActualStartTime(nsecs_t actualStartTime); void setActualQueueTime(nsecs_t actualQueueTime); void setAcquireFenceTime(nsecs_t acquireFenceTime); + void setDesiredPresentTime(nsecs_t desiredPresentTime); void setDropTime(nsecs_t dropTime); void setPresentState(PresentState presentState, nsecs_t lastLatchTime = 0); void setRenderRate(Fps renderRate); @@ -341,6 +346,9 @@ public: // containing at least one layer ID. virtual float computeFps(const std::unordered_set<int32_t>& layerIds) = 0; + // Supports the legacy FrameStats interface + virtual void generateFrameStats(int32_t layer, size_t count, FrameStats* outStats) const = 0; + // Restores the max number of display frames to default. Called by SF backdoor. virtual void reset() = 0; }; @@ -501,6 +509,7 @@ public: void parseArgs(const Vector<String16>& args, std::string& result) override; void setMaxDisplayFrames(uint32_t size) override; float computeFps(const std::unordered_set<int32_t>& layerIds) override; + void generateFrameStats(int32_t layer, size_t count, FrameStats* outStats) const override; void reset() override; // Sets up the perfetto tracing backend and data source. diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp index d709530990..da536b6660 100644 --- a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp +++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp @@ -166,7 +166,8 @@ void LayerHierarchy::dump(std::ostream& out, const std::string& prefix, } out << "(Mirroring) "; } - out << *mLayer; + + out << *mLayer << " pid=" << mLayer->ownerPid.val() << " uid=" << mLayer->ownerUid.val(); } for (size_t i = 0; i < mChildren.size(); i++) { diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp index 11b674b846..a8be50a074 100644 --- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp +++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp @@ -250,6 +250,7 @@ std::string LayerSnapshot::getIsVisibleReason() const { if (drawShadows()) reason << " shadowSettings.length=" << shadowSettings.length; if (backgroundBlurRadius > 0) reason << " backgroundBlurRadius=" << backgroundBlurRadius; if (blurRegions.size() > 0) reason << " blurRegions.size()=" << blurRegions.size(); + if (contentDirty) reason << " contentDirty"; return reason.str(); } @@ -359,8 +360,9 @@ void LayerSnapshot::merge(const RequestedLayerState& requested, bool forceUpdate uint32_t displayRotationFlags) { clientChanges = requested.what; changes = requested.changes; - contentDirty = requested.what & layer_state_t::CONTENT_DIRTY; - hasReadyFrame = requested.autoRefresh; + autoRefresh = requested.autoRefresh; + contentDirty = requested.what & layer_state_t::CONTENT_DIRTY || autoRefresh; + hasReadyFrame = autoRefresh; sidebandStreamHasFrame = requested.hasSidebandStreamFrame(); updateSurfaceDamage(requested, requested.hasReadyFrame(), forceFullDamage, surfaceDamage); diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.h b/services/surfaceflinger/FrontEnd/LayerSnapshot.h index b7d4cc5d06..b8df3ed748 100644 --- a/services/surfaceflinger/FrontEnd/LayerSnapshot.h +++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.h @@ -77,6 +77,7 @@ struct LayerSnapshot : public compositionengine::LayerFECompositionState { gui::LayerMetadata layerMetadata; gui::LayerMetadata relativeLayerMetadata; bool hasReadyFrame; // used in post composition to check if there is another frame ready + bool autoRefresh; ui::Transform localTransformInverse; gui::WindowInfo inputInfo; ui::Transform localTransform; diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp index 10e212e7b5..7569c1b8de 100644 --- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp +++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp @@ -314,8 +314,8 @@ void updateMetadataAndGameMode(LayerSnapshot& snapshot, const RequestedLayerStat void clearChanges(LayerSnapshot& snapshot) { snapshot.changes.clear(); snapshot.clientChanges = 0; - snapshot.contentDirty = false; - snapshot.hasReadyFrame = false; + snapshot.contentDirty = snapshot.autoRefresh; + snapshot.hasReadyFrame = snapshot.autoRefresh; snapshot.sidebandStreamHasFrame = false; snapshot.surfaceDamage.clear(); } @@ -724,10 +724,12 @@ void LayerSnapshotBuilder::updateSnapshot(LayerSnapshot& snapshot, const Args& a if (args.displayChanges) snapshot.changes |= RequestedLayerState::Changes::Geometry; snapshot.reachablilty = LayerSnapshot::Reachablilty::Reachable; snapshot.clientChanges |= (parentSnapshot.clientChanges & layer_state_t::AFFECTS_CHILDREN); + // mark the content as dirty if the parent state changes can dirty the child's content (for + // example alpha) + snapshot.contentDirty |= (snapshot.clientChanges & layer_state_t::CONTENT_DIRTY) != 0; snapshot.isHiddenByPolicyFromParent = parentSnapshot.isHiddenByPolicyFromParent || parentSnapshot.invalidTransform || requested.isHiddenByPolicy() || (args.excludeLayerIds.find(path.id) != args.excludeLayerIds.end()); - const bool forceUpdate = args.forceUpdate == ForceUpdateFlags::ALL || snapshot.clientChanges & layer_state_t::eReparent || snapshot.changes.any(RequestedLayerState::Changes::Visibility | diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 20ba45f96e..195461f47e 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -154,7 +154,7 @@ Layer::Layer(const surfaceflinger::LayerCreationArgs& args) mDrawingState.metadata = args.metadata; mDrawingState.frameTimelineInfo = {}; mDrawingState.postTime = -1; - mFrameTracker.setDisplayRefreshPeriod( + mDeprecatedFrameTracker.setDisplayRefreshPeriod( args.flinger->mScheduler->getPacesetterVsyncPeriod().ns()); mOwnerUid = args.ownerUid; @@ -472,6 +472,9 @@ std::shared_ptr<frametimeline::SurfaceFrame> Layer::createSurfaceFrameForTransac getSequence(), mName, mTransactionName, /*isBuffer*/ false, gameMode); + // Buffer hasn't yet been latched, so use mDrawingState + surfaceFrame->setDesiredPresentTime(mDrawingState.desiredPresentTime); + surfaceFrame->setActualStartTime(info.startTimeNanos); // For Transactions, the post time is considered to be both queue and acquire fence time. surfaceFrame->setActualQueueTime(postTime); @@ -490,6 +493,8 @@ std::shared_ptr<frametimeline::SurfaceFrame> Layer::createSurfaceFrameForBuffer( mFlinger->mFrameTimeline->createSurfaceFrameForToken(info, mOwnerPid, mOwnerUid, getSequence(), mName, debugName, /*isBuffer*/ true, gameMode); + // Buffer hasn't yet been latched, so use mDrawingState + surfaceFrame->setDesiredPresentTime(mDrawingState.desiredPresentTime); surfaceFrame->setActualStartTime(info.startTimeNanos); // For buffers, acquire fence time will set during latch. surfaceFrame->setActualQueueTime(queueTime); @@ -514,6 +519,8 @@ void Layer::setFrameTimelineVsyncForSkippedFrames(const FrameTimelineInfo& info, mOwnerPid, mOwnerUid, getSequence(), mName, debugName, /*isBuffer*/ false, gameMode); + // Buffer hasn't yet been latched, so use mDrawingState + surfaceFrame->setDesiredPresentTime(mDrawingState.desiredPresentTime); surfaceFrame->setActualStartTime(skippedFrameTimelineInfo.skippedFrameStartTimeNanos); // For Transactions, the post time is considered to be both queue and acquire fence time. surfaceFrame->setActualQueueTime(postTime); @@ -605,15 +612,42 @@ void Layer::miniDump(std::string& result, const frontend::LayerSnapshot& snapsho } void Layer::dumpFrameStats(std::string& result) const { - mFrameTracker.dumpStats(result); + if (FlagManager::getInstance().deprecate_frame_tracker()) { + FrameStats fs = FrameStats(); + getFrameStats(&fs); + for (auto desired = fs.desiredPresentTimesNano.begin(), + actual = fs.actualPresentTimesNano.begin(), + ready = fs.frameReadyTimesNano.begin(); + desired != fs.desiredPresentTimesNano.end() && + actual != fs.actualPresentTimesNano.end() && ready != fs.frameReadyTimesNano.end(); + ++desired, ++actual, ++ready) { + result.append(std::format("{}\t{}\t{}\n", *desired, *actual, *ready)); + } + + result.push_back('\n'); + } else { + mDeprecatedFrameTracker.dumpStats(result); + } } void Layer::clearFrameStats() { - mFrameTracker.clearStats(); + if (FlagManager::getInstance().deprecate_frame_tracker()) { + mFrameStatsHistorySize = 0; + } else { + mDeprecatedFrameTracker.clearStats(); + } } void Layer::getFrameStats(FrameStats* outStats) const { - mFrameTracker.getStats(outStats); + if (FlagManager::getInstance().deprecate_frame_tracker()) { + if (auto ftl = getTimeline()) { + float fps = ftl->get().computeFps({getSequence()}); + ftl->get().generateFrameStats(getSequence(), mFrameStatsHistorySize, outStats); + outStats->refreshPeriodNano = Fps::fromValue(fps).getPeriodNsecs(); + } + } else { + mDeprecatedFrameTracker.getStats(outStats); + } } void Layer::onDisconnect() { @@ -1348,9 +1382,9 @@ void Layer::onCompositionPresented(const DisplayDevice* display, handle->compositorTiming = compositorTiming; } - // Update mFrameTracker. + // Update mDeprecatedFrameTracker. nsecs_t desiredPresentTime = mBufferInfo.mDesiredPresentTime; - mFrameTracker.setDesiredPresentTime(desiredPresentTime); + mDeprecatedFrameTracker.setDesiredPresentTime(desiredPresentTime); const int32_t layerId = getSequence(); mFlinger->mTimeStats->setDesiredTime(layerId, mCurrentFrameNumber, desiredPresentTime); @@ -1370,15 +1404,15 @@ void Layer::onCompositionPresented(const DisplayDevice* display, } } + // The SurfaceFrame's AcquireFence is the same as this. std::shared_ptr<FenceTime> frameReadyFence = mBufferInfo.mFenceTime; if (frameReadyFence->isValid()) { - mFrameTracker.setFrameReadyFence(std::move(frameReadyFence)); + mDeprecatedFrameTracker.setFrameReadyFence(std::move(frameReadyFence)); } else { // There was no fence for this frame, so assume that it was ready // to be presented at the desired present time. - mFrameTracker.setFrameReadyTime(desiredPresentTime); + mDeprecatedFrameTracker.setFrameReadyTime(desiredPresentTime); } - if (display) { const auto activeMode = display->refreshRateSelector().getActiveMode(); const Fps refreshRate = activeMode.fps; @@ -1393,7 +1427,7 @@ void Layer::onCompositionPresented(const DisplayDevice* display, mFlinger->mFrameTracer->traceFence(layerId, getCurrentBufferId(), mCurrentFrameNumber, presentFence, FrameTracer::FrameEvent::PRESENT_FENCE); - mFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence)); + mDeprecatedFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence)); } else if (const auto displayId = PhysicalDisplayId::tryCast(display->getId()); displayId && mFlinger->getHwComposer().isConnected(*displayId)) { // The HWC doesn't support present fences, so use the present timestamp instead. @@ -1414,11 +1448,12 @@ void Layer::onCompositionPresented(const DisplayDevice* display, mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(), mCurrentFrameNumber, actualPresentTime, FrameTracer::FrameEvent::PRESENT_FENCE); - mFrameTracker.setActualPresentTime(actualPresentTime); + mDeprecatedFrameTracker.setActualPresentTime(actualPresentTime); } } - mFrameTracker.advanceFrame(); + mFrameStatsHistorySize++; + mDeprecatedFrameTracker.advanceFrame(); mBufferInfo.mFrameLatencyNeeded = false; } diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index a2716c6b1f..c234a75693 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -18,6 +18,7 @@ #include <android/gui/DropInputMode.h> #include <android/gui/ISurfaceComposerClient.h> +#include <com_android_graphics_surfaceflinger_flags.h> #include <ftl/small_map.h> #include <gui/BufferQueue.h> #include <gui/LayerState.h> @@ -44,6 +45,7 @@ #include <scheduler/Seamlessness.h> #include <cstdint> +#include <functional> #include <optional> #include <vector> @@ -433,8 +435,12 @@ protected: uint32_t mTransactionFlags{0}; + // Leverages FrameTimeline to generate FrameStats. Since FrameTimeline already has the data, + // statistical history needs to only be tracked by count of frames. + // TODO: Deprecate the '--latency-clear' and get rid of this. + std::atomic<uint16_t> mFrameStatsHistorySize; // Timestamp history for UIAutomation. Thread safe. - FrameTracker mFrameTracker; + FrameTracker mDeprecatedFrameTracker; // main thread sp<NativeHandle> mSidebandStream; @@ -556,6 +562,9 @@ private: std::vector<std::pair<frontend::LayerHierarchy::TraversalPath, sp<LayerFE>>> mLayerFEs; bool mHandleAlive = false; + std::optional<std::reference_wrapper<frametimeline::FrameTimeline>> getTimeline() const { + return *mFlinger->mFrameTimeline; + } }; std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate); diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/PowerAdvisor/PowerAdvisor.cpp index c914ec3066..c7d0b2c9ef 100644 --- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp +++ b/services/surfaceflinger/PowerAdvisor/PowerAdvisor.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -//#define LOG_NDEBUG 0 +// #define LOG_NDEBUG 0 #define ATRACE_TAG ATRACE_TAG_GRAPHICS @@ -24,6 +24,7 @@ #include <unistd.h> #include <cinttypes> #include <cstdint> +#include <functional> #include <optional> #include <android-base/properties.h> @@ -33,45 +34,29 @@ #include <binder/IServiceManager.h> -#include "../SurfaceFlingerProperties.h" +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wconversion" +#include <powermanager/PowerHalController.h> +#include <powermanager/PowerHintSessionWrapper.h> +#pragma clang diagnostic pop +#include <common/FlagManager.h> #include "PowerAdvisor.h" -#include "SurfaceFlinger.h" -namespace android { -namespace Hwc2 { +namespace hal = aidl::android::hardware::power; -PowerAdvisor::~PowerAdvisor() = default; - -namespace impl { - -using aidl::android::hardware::power::Boost; -using aidl::android::hardware::power::ChannelConfig; -using aidl::android::hardware::power::Mode; -using aidl::android::hardware::power::SessionHint; -using aidl::android::hardware::power::SessionTag; -using aidl::android::hardware::power::WorkDuration; -using aidl::android::hardware::power::WorkDurationFixedV1; +namespace android::adpf::impl { -using aidl::android::hardware::common::fmq::MQDescriptor; using aidl::android::hardware::common::fmq::SynchronizedReadWrite; -using aidl::android::hardware::power::ChannelMessage; using android::hardware::EventFlag; -using ChannelMessageContents = ChannelMessage::ChannelMessageContents; -using MsgQueue = android::AidlMessageQueue<ChannelMessage, SynchronizedReadWrite>; +using ChannelMessageContents = hal::ChannelMessage::ChannelMessageContents; +using MsgQueue = android::AidlMessageQueue<hal::ChannelMessage, SynchronizedReadWrite>; using FlagQueue = android::AidlMessageQueue<int8_t, SynchronizedReadWrite>; PowerAdvisor::~PowerAdvisor() = default; namespace { -std::chrono::milliseconds getUpdateTimeout() { - // Default to a timeout of 80ms if nothing else is specified - static std::chrono::milliseconds timeout = - std::chrono::milliseconds(sysprop::display_update_imminent_timeout_ms(80)); - return timeout; -} - void traceExpensiveRendering(bool enabled) { if (enabled) { SFTRACE_ASYNC_BEGIN("ExpensiveRendering", 0); @@ -82,28 +67,30 @@ void traceExpensiveRendering(bool enabled) { } // namespace -PowerAdvisor::PowerAdvisor(SurfaceFlinger& flinger) - : mPowerHal(std::make_unique<power::PowerHalController>()), mFlinger(flinger) { - if (getUpdateTimeout() > 0ms) { - mScreenUpdateTimer.emplace("UpdateImminentTimer", getUpdateTimeout(), +PowerAdvisor::PowerAdvisor(std::function<void()>&& sfDisableExpensiveFn, + std::chrono::milliseconds timeout) + : mPowerHal(std::make_unique<power::PowerHalController>()) { + if (timeout > 0ms) { + mScreenUpdateTimer.emplace("UpdateImminentTimer", timeout, /* resetCallback */ nullptr, /* timeoutCallback */ - [this] { + [this, disableExpensiveFn = std::move(sfDisableExpensiveFn), + timeout] { while (true) { auto timeSinceLastUpdate = std::chrono::nanoseconds( systemTime() - mLastScreenUpdatedTime.load()); - if (timeSinceLastUpdate >= getUpdateTimeout()) { + if (timeSinceLastUpdate >= timeout) { break; } // We may try to disable expensive rendering and allow // for sending DISPLAY_UPDATE_IMMINENT hints too early if // we idled very shortly after updating the screen, so // make sure we wait enough time. - std::this_thread::sleep_for(getUpdateTimeout() - + std::this_thread::sleep_for(timeout - timeSinceLastUpdate); } mSendUpdateImminent.store(true); - mFlinger.disableExpensiveRendering(); + disableExpensiveFn(); }); } } @@ -132,7 +119,7 @@ void PowerAdvisor::setExpensiveRenderingExpected(DisplayId displayId, bool expec const bool expectsExpensiveRendering = !mExpensiveDisplays.empty(); if (mNotifiedExpensiveRendering != expectsExpensiveRendering) { - auto ret = getPowerHal().setMode(Mode::EXPENSIVE_RENDERING, expectsExpensiveRendering); + auto ret = getPowerHal().setMode(hal::Mode::EXPENSIVE_RENDERING, expectsExpensiveRendering); if (!ret.isOk()) { if (ret.isUnsupported()) { mHasExpensiveRendering = false; @@ -151,7 +138,7 @@ void PowerAdvisor::notifyCpuLoadUp() { if (!mBootFinished.load()) { return; } - sendHintSessionHint(SessionHint::CPU_LOAD_UP); + sendHintSessionHint(hal::SessionHint::CPU_LOAD_UP); } void PowerAdvisor::notifyDisplayUpdateImminentAndCpuReset() { @@ -163,12 +150,12 @@ void PowerAdvisor::notifyDisplayUpdateImminentAndCpuReset() { if (mSendUpdateImminent.exchange(false)) { ALOGV("AIDL notifyDisplayUpdateImminentAndCpuReset"); - sendHintSessionHint(SessionHint::CPU_LOAD_RESET); + sendHintSessionHint(hal::SessionHint::CPU_LOAD_RESET); if (!mHasDisplayUpdateImminent) { ALOGV("Skipped sending DISPLAY_UPDATE_IMMINENT because HAL doesn't support it"); } else { - auto ret = getPowerHal().setBoost(Boost::DISPLAY_UPDATE_IMMINENT, 0); + auto ret = getPowerHal().setBoost(hal::Boost::DISPLAY_UPDATE_IMMINENT, 0); if (ret.isUnsupported()) { mHasDisplayUpdateImminent = false; } @@ -205,7 +192,7 @@ bool PowerAdvisor::shouldCreateSessionWithConfig() { FlagManager::getInstance().adpf_use_fmq_channel(); } -void PowerAdvisor::sendHintSessionHint(SessionHint hint) { +void PowerAdvisor::sendHintSessionHint(hal::SessionHint hint) { if (!mBootFinished || !usePowerHintSession()) { ALOGV("Power hint session is not enabled, skip sending session hint"); return; @@ -236,7 +223,7 @@ bool PowerAdvisor::ensurePowerHintSessionRunning() { static_cast<int32_t>(getuid()), mHintSessionThreadIds, mTargetDuration.ns(), - SessionTag::SURFACEFLINGER, + hal::SessionTag::SURFACEFLINGER, &mSessionConfig); if (ret.isOk()) { mHintSession = ret.value(); @@ -326,7 +313,7 @@ void PowerAdvisor::reportActualWorkDuration() { return; } SFTRACE_CALL(); - std::optional<WorkDuration> actualDuration = estimateWorkDuration(); + std::optional<hal::WorkDuration> actualDuration = estimateWorkDuration(); if (!actualDuration.has_value() || actualDuration->durationNanos < 0) { ALOGV("Failed to send actual work duration, skipping"); return; @@ -377,7 +364,7 @@ void PowerAdvisor::reportActualWorkDuration() { mHintSessionQueue.clear(); } -template <ChannelMessage::ChannelMessageContents::Tag T, class In> +template <hal::ChannelMessage::ChannelMessageContents::Tag T, class In> bool PowerAdvisor::writeHintSessionMessage(In* contents, size_t count) { if (!mMsgQueue) { ALOGV("Skip using FMQ with message tag %hhd as it's not supported", T); @@ -395,13 +382,13 @@ bool PowerAdvisor::writeHintSessionMessage(In* contents, size_t count) { } for (size_t i = 0; i < count; ++i) { if constexpr (T == ChannelMessageContents::Tag::workDuration) { - const WorkDuration& duration = contents[i]; - new (tx.getSlot(i)) ChannelMessage{ + const hal::WorkDuration& duration = contents[i]; + new (tx.getSlot(i)) hal::ChannelMessage{ .sessionID = static_cast<int32_t>(mSessionConfig.id), .timeStampNanos = (i == count - 1) ? ::android::uptimeNanos() : duration.timeStampNanos, .data = ChannelMessageContents::make<ChannelMessageContents::Tag::workDuration, - WorkDurationFixedV1>({ + hal::WorkDurationFixedV1>({ .durationNanos = duration.durationNanos, .workPeriodStartTimestampNanos = duration.workPeriodStartTimestampNanos, .cpuDurationNanos = duration.cpuDurationNanos, @@ -409,7 +396,7 @@ bool PowerAdvisor::writeHintSessionMessage(In* contents, size_t count) { }), }; } else { - new (tx.getSlot(i)) ChannelMessage{ + new (tx.getSlot(i)) hal::ChannelMessage{ .sessionID = static_cast<int32_t>(mSessionConfig.id), .timeStampNanos = ::android::uptimeNanos(), .data = ChannelMessageContents::make<T, In>(std::move(contents[i])), @@ -572,7 +559,7 @@ std::vector<DisplayId> PowerAdvisor::getOrderedDisplayIds( return sortedDisplays; } -std::optional<WorkDuration> PowerAdvisor::estimateWorkDuration() { +std::optional<hal::WorkDuration> PowerAdvisor::estimateWorkDuration() { if (!mExpectedPresentTimes.isFull() || !mCommitStartTimes.isFull()) { return std::nullopt; } @@ -657,7 +644,7 @@ std::optional<WorkDuration> PowerAdvisor::estimateWorkDuration() { Duration combinedDuration = combineTimingEstimates(totalDuration, flingerDuration); Duration cpuDuration = combineTimingEstimates(totalDurationWithoutGpu, flingerDuration); - WorkDuration duration{ + hal::WorkDuration duration{ .timeStampNanos = TimePoint::now().ns(), .durationNanos = combinedDuration.ns(), .workPeriodStartTimestampNanos = mCommitStartTimes[0].ns(), @@ -760,6 +747,4 @@ power::PowerHalController& PowerAdvisor::getPowerHal() { return *mPowerHal; } -} // namespace impl -} // namespace Hwc2 -} // namespace android +} // namespace android::adpf::impl diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/PowerAdvisor/PowerAdvisor.h index 1076b2b79b..458b46d500 100644 --- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h +++ b/services/surfaceflinger/PowerAdvisor/PowerAdvisor.h @@ -17,7 +17,7 @@ #pragma once #include <atomic> -#include <chrono> +#include <future> #include <unordered_map> #include <unordered_set> @@ -30,10 +30,8 @@ #pragma clang diagnostic ignored "-Wconversion" #include <aidl/android/hardware/power/IPower.h> #include <fmq/AidlMessageQueue.h> -#include <powermanager/PowerHalController.h> #pragma clang diagnostic pop -#include <compositionengine/impl/OutputCompositionState.h> #include <scheduler/Time.h> #include <ui/DisplayIdentification.h> #include "../Scheduler/OneShotTimer.h" @@ -42,13 +40,16 @@ using namespace std::chrono_literals; namespace android { -class SurfaceFlinger; +namespace power { +class PowerHalController; +class PowerHintSessionWrapper; +} // namespace power -namespace Hwc2 { +namespace adpf { class PowerAdvisor { public: - virtual ~PowerAdvisor(); + virtual ~PowerAdvisor() = default; // Initializes resources that cannot be initialized on construction virtual void init() = 0; @@ -113,9 +114,9 @@ namespace impl { // PowerAdvisor is a wrapper around IPower HAL which takes into account the // full state of the system when sending out power hints to things like the GPU. -class PowerAdvisor final : public Hwc2::PowerAdvisor { +class PowerAdvisor final : public adpf::PowerAdvisor { public: - PowerAdvisor(SurfaceFlinger& flinger); + PowerAdvisor(std::function<void()>&& function, std::chrono::milliseconds timeout); ~PowerAdvisor() override; void init() override; @@ -159,7 +160,6 @@ private: std::unordered_set<DisplayId> mExpensiveDisplays; bool mNotifiedExpensiveRendering = false; - SurfaceFlinger& mFlinger; std::atomic_bool mSendUpdateImminent = true; std::atomic<nsecs_t> mLastScreenUpdatedTime = 0; std::optional<scheduler::OneShotTimer> mScreenUpdateTimer; @@ -326,5 +326,5 @@ private: }; } // namespace impl -} // namespace Hwc2 +} // namespace adpf } // namespace android diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp index 011fd9e20a..21d3396ebe 100644 --- a/services/surfaceflinger/RegionSamplingThread.cpp +++ b/services/surfaceflinger/RegionSamplingThread.cpp @@ -353,22 +353,13 @@ void RegionSamplingThread::captureSample() { sampledBounds.getSize(), ui::Dataspace::V0_SRGB, displayWeak, RenderArea::Options::CAPTURE_SECURE_LAYERS); - FenceResult fenceResult; - if (FlagManager::getInstance().single_hop_screenshot() && - mFlinger.mRenderEngine->isThreaded()) { - std::vector<sp<LayerFE>> layerFEs; - auto displayState = mFlinger.getSnapshotsFromMainThread(renderAreaBuilder, - getLayerSnapshotsFn, layerFEs); - fenceResult = mFlinger.captureScreenshot(renderAreaBuilder, buffer, kRegionSampling, - kGrayscale, kIsProtected, kAttachGainmap, nullptr, - displayState, layerFEs) - .get(); - } else { - fenceResult = mFlinger.captureScreenshotLegacy(renderAreaBuilder, getLayerSnapshotsFn, - buffer, kRegionSampling, kGrayscale, - kIsProtected, kAttachGainmap, nullptr) - .get(); - } + std::vector<std::pair<Layer*, sp<LayerFE>>> layers; + auto displayState = + mFlinger.getSnapshotsFromMainThread(renderAreaBuilder, getLayerSnapshotsFn, layers); + FenceResult fenceResult = + mFlinger.captureScreenshot(renderAreaBuilder, buffer, kRegionSampling, kGrayscale, + kIsProtected, kAttachGainmap, nullptr, displayState, layers) + .get(); if (fenceResult.ok()) { fenceResult.value()->waitForever(LOG_TAG); } diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp index e385f18243..7729671401 100644 --- a/services/surfaceflinger/Scheduler/EventThread.cpp +++ b/services/surfaceflinger/Scheduler/EventThread.cpp @@ -420,6 +420,16 @@ void EventThread::enableSyntheticVsync(bool enable) { mCondition.notify_all(); } +void EventThread::omitVsyncDispatching(bool omitted) { + std::lock_guard<std::mutex> lock(mMutex); + if (!mVSyncState || mVSyncState->omitted == omitted) { + return; + } + + mVSyncState->omitted = omitted; + mCondition.notify_all(); +} + void EventThread::onVsync(nsecs_t vsyncTime, nsecs_t wakeupTime, nsecs_t readyTime) { std::lock_guard<std::mutex> lock(mMutex); mLastVsyncCallbackTime = TimePoint::fromNs(vsyncTime); @@ -521,7 +531,17 @@ void EventThread::threadMain(std::unique_lock<std::mutex>& lock) { } if (mVSyncState && vsyncRequested) { - mState = mVSyncState->synthetic ? State::SyntheticVSync : State::VSync; + const bool vsyncOmitted = + FlagManager::getInstance().no_vsyncs_on_screen_off() && mVSyncState->omitted; + if (vsyncOmitted) { + mState = State::Idle; + SFTRACE_INT("VsyncPendingScreenOn", 1); + } else { + mState = mVSyncState->synthetic ? State::SyntheticVSync : State::VSync; + if (FlagManager::getInstance().no_vsyncs_on_screen_off()) { + SFTRACE_INT("VsyncPendingScreenOn", 0); + } + } } else { ALOGW_IF(!mVSyncState, "Ignoring VSYNC request while display is disconnected"); mState = State::Idle; diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h index c3c7eb0ee1..2daf126d77 100644 --- a/services/surfaceflinger/Scheduler/EventThread.h +++ b/services/surfaceflinger/Scheduler/EventThread.h @@ -106,6 +106,8 @@ public: // Feed clients with fake VSYNC, e.g. while the display is off. virtual void enableSyntheticVsync(bool) = 0; + virtual void omitVsyncDispatching(bool) = 0; + virtual void onHotplugReceived(PhysicalDisplayId displayId, bool connected) = 0; virtual void onHotplugConnectionError(int32_t connectionError) = 0; @@ -165,6 +167,8 @@ public: void enableSyntheticVsync(bool) override; + void omitVsyncDispatching(bool) override; + void onHotplugReceived(PhysicalDisplayId displayId, bool connected) override; void onHotplugConnectionError(int32_t connectionError) override; @@ -240,6 +244,9 @@ private: // True if VSYNC should be faked, e.g. when display is off. bool synthetic = false; + + // True if VSYNC should not be delivered to apps. Used when the display is off. + bool omitted = false; }; // TODO(b/74619554): Create per-display threads waiting on respective VSYNC signals, diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp index 64b85c080e..e45bdfce13 100644 --- a/services/surfaceflinger/Scheduler/LayerHistory.cpp +++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp @@ -308,6 +308,12 @@ void LayerHistory::partitionLayers(nsecs_t now, bool isVrrDevice) { const auto setFrameRateVoteType = info->isVisible() ? voteType : LayerVoteType::NoVote; + const bool hasSetFrameRateOpinion = frameRate.isValid() && !frameRate.isNoVote(); + const bool hasCategoryOpinion = + frameRate.category != FrameRateCategory::NoPreference && + frameRate.category != FrameRateCategory::Default; + const bool hasFrameRateOpinion = hasSetFrameRateOpinion || hasCategoryOpinion; + if (gameModeFrameRateOverride.isValid()) { info->setLayerVote({gameFrameRateOverrideVoteType, gameModeFrameRateOverride}); SFTRACE_FORMAT_INSTANT("GameModeFrameRateOverride"); @@ -315,7 +321,7 @@ void LayerHistory::partitionLayers(nsecs_t now, bool isVrrDevice) { trace(*info, gameFrameRateOverrideVoteType, gameModeFrameRateOverride.getIntValue()); } - } else if (frameRate.isValid() && frameRate.isVoteValidForMrr(isVrrDevice)) { + } else if (hasFrameRateOpinion && frameRate.isVoteValidForMrr(isVrrDevice)) { info->setLayerVote({setFrameRateVoteType, isValuelessVote ? 0_Hz : frameRate.vote.rate, frameRate.vote.seamlessness, frameRate.category}); @@ -332,7 +338,7 @@ void LayerHistory::partitionLayers(nsecs_t now, bool isVrrDevice) { gameDefaultFrameRateOverride.getIntValue()); } } else { - if (frameRate.isValid() && !frameRate.isVoteValidForMrr(isVrrDevice)) { + if (hasFrameRateOpinion && !frameRate.isVoteValidForMrr(isVrrDevice)) { SFTRACE_FORMAT_INSTANT("Reset layer to ignore explicit vote on MRR %s: %s " "%s %s", info->getName().c_str(), diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp index eca8df27d6..84fa1390a4 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp +++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp @@ -489,6 +489,20 @@ auto RefreshRateSelector::getRankedFrameRates(const std::vector<LayerRequirement return mGetRankedFrameRatesCache->result; } +using LayerRequirementPtrs = std::vector<const RefreshRateSelector::LayerRequirement*>; +using PerUidLayerRequirements = std::unordered_map<uid_t, LayerRequirementPtrs>; + +PerUidLayerRequirements groupLayersByUid( + const std::vector<RefreshRateSelector::LayerRequirement>& layers) { + PerUidLayerRequirements layersByUid; + for (const auto& layer : layers) { + const auto it = layersByUid.emplace(layer.ownerUid, LayerRequirementPtrs()).first; + auto& layersWithSameUid = it->second; + layersWithSameUid.push_back(&layer); + } + return layersByUid; +} + auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequirement>& layers, GlobalSignals signals, Fps pacesetterFps) const -> RankedFrameRates { @@ -525,6 +539,43 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi return {ranking, GlobalSignals{.powerOnImminent = true}}; } + // A method for UI Toolkit to send the touch signal via "HighHint" category vote, + // which will touch boost when there are no ExplicitDefault layer votes on the app. + // At most one app can have the "HighHint" touch boost vote at a time. + // This accounts for cases such as games that use `setFrameRate` + // with Default compatibility to limit the frame rate and disabling touch boost. + bool isAppTouchBoost = false; + const auto layersByUid = groupLayersByUid(layers); + for (const auto& [uid, layersWithSameUid] : layersByUid) { + bool hasHighHint = false; + bool hasExplicitDefault = false; + for (const auto& layer : layersWithSameUid) { + switch (layer->vote) { + case LayerVoteType::ExplicitDefault: + hasExplicitDefault = true; + break; + case LayerVoteType::ExplicitCategory: + if (layer->frameRateCategory == FrameRateCategory::HighHint) { + hasHighHint = true; + } + break; + default: + // No action + break; + } + if (hasHighHint && hasExplicitDefault) { + break; + } + } + + if (hasHighHint && !hasExplicitDefault) { + // Focused app has touch signal (HighHint) and no frame rate ExplicitDefault votes + // (which prevents touch boost due to games use case). + isAppTouchBoost = true; + break; + } + } + int noVoteLayers = 0; // Layers that prefer the same mode ("no-op"). int noPreferenceLayers = 0; @@ -535,7 +586,6 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi int explicitExact = 0; int explicitGteLayers = 0; int explicitCategoryVoteLayers = 0; - int interactiveLayers = 0; int seamedFocusedLayers = 0; int categorySmoothSwitchOnlyLayers = 0; @@ -563,11 +613,9 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi explicitGteLayers++; break; case LayerVoteType::ExplicitCategory: - if (layer.frameRateCategory == FrameRateCategory::HighHint) { - // HighHint does not count as an explicit signal from an app. It may be - // be a touch signal. - interactiveLayers++; - } else { + // HighHint does not count as an explicit signal from an app. It is a touch signal + // sent from UI Toolkit. + if (layer.frameRateCategory != FrameRateCategory::HighHint) { explicitCategoryVoteLayers++; } if (layer.frameRateCategory == FrameRateCategory::NoPreference) { @@ -882,14 +930,11 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi return explicitCategoryVoteLayers + noVoteLayers + explicitGteLayers != layers.size(); }; - // A method for UI Toolkit to send the touch signal via "HighHint" category vote, - // which will touch boost when there are no ExplicitDefault layer votes. This is an - // incomplete solution but accounts for cases such as games that use `setFrameRate` with default + // This accounts for cases such as games that use `setFrameRate` with Default // compatibility to limit the frame rate, which should not have touch boost. - const bool hasInteraction = signals.touch || interactiveLayers > 0; - - if (hasInteraction && explicitDefaultVoteLayers == 0 && isTouchBoostForExplicitExact() && - isTouchBoostForCategory()) { + const bool isLateGlobalTouchBoost = signals.touch && explicitDefaultVoteLayers == 0; + const bool isLateTouchBoost = isLateGlobalTouchBoost || isAppTouchBoost; + if (isLateTouchBoost && isTouchBoostForExplicitExact() && isTouchBoostForCategory()) { const auto touchRefreshRates = rankFrameRates(anchorGroup, RefreshRateOrder::Descending); using fps_approx_ops::operator<; @@ -917,42 +962,6 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi return {ranking, kNoSignals}; } -using LayerRequirementPtrs = std::vector<const RefreshRateSelector::LayerRequirement*>; -using PerUidLayerRequirements = std::unordered_map<uid_t, LayerRequirementPtrs>; - -PerUidLayerRequirements groupLayersByUid( - const std::vector<RefreshRateSelector::LayerRequirement>& layers) { - PerUidLayerRequirements layersByUid; - for (const auto& layer : layers) { - const auto it = layersByUid.emplace(layer.ownerUid, LayerRequirementPtrs()).first; - auto& layersWithSameUid = it->second; - layersWithSameUid.push_back(&layer); - } - - // Remove uids that can't have a frame rate override - for (auto it = layersByUid.begin(); it != layersByUid.end();) { - const auto& layersWithSameUid = it->second; - bool skipUid = false; - for (const auto& layer : layersWithSameUid) { - using LayerVoteType = RefreshRateSelector::LayerVoteType; - - if (layer->vote == LayerVoteType::Max || layer->vote == LayerVoteType::Heuristic) { - ALOGV("%s: %s skips uid=%d due to the vote", __func__, - formatLayerInfo(*layer, layer->weight).c_str(), layer->ownerUid); - skipUid = true; - break; - } - } - if (skipUid) { - it = layersByUid.erase(it); - } else { - ++it; - } - } - - return layersByUid; -} - auto RefreshRateSelector::getFrameRateOverrides(const std::vector<LayerRequirement>& layers, Fps displayRefreshRate, GlobalSignals globalSignals) const @@ -997,6 +1006,7 @@ auto RefreshRateSelector::getFrameRateOverrides(const std::vector<LayerRequireme bool hasExplicitExactOrMultiple = false; bool hasExplicitDefault = false; bool hasHighHint = false; + bool hasSkipOverrideLayer = false; for (const auto& layer : layersWithSameUid) { switch (layer->vote) { case LayerVoteType::ExplicitExactOrMultiple: @@ -1010,15 +1020,25 @@ auto RefreshRateSelector::getFrameRateOverrides(const std::vector<LayerRequireme hasHighHint = true; } break; + case LayerVoteType::Max: + case LayerVoteType::Heuristic: + hasSkipOverrideLayer = true; + break; default: // No action break; } - if (hasExplicitExactOrMultiple && hasExplicitDefault && hasHighHint) { + if (hasExplicitExactOrMultiple && hasExplicitDefault && hasHighHint && + hasSkipOverrideLayer) { break; } } + if (hasSkipOverrideLayer) { + ALOGV("%s: Skipping due to vote(s): uid=%d", __func__, uid); + continue; + } + // Layers with ExplicitExactOrMultiple expect touch boost if (globalSignals.touch && hasExplicitExactOrMultiple) { ALOGV("%s: Skipping for touch (input signal): uid=%d", __func__, uid); @@ -1652,9 +1672,9 @@ std::chrono::milliseconds RefreshRateSelector::getIdleTimerTimeout() { FpsRange RefreshRateSelector::getFrameRateCategoryRange(FrameRateCategory category) { switch (category) { case FrameRateCategory::High: - return FpsRange{90_Hz, 120_Hz}; + return FpsRange{kFrameRateCategoryRateHigh, 120_Hz}; case FrameRateCategory::Normal: - return FpsRange{60_Hz, 120_Hz}; + return FpsRange{kFrameRateCategoryRateNormal, 120_Hz}; case FrameRateCategory::Low: return FpsRange{48_Hz, 120_Hz}; case FrameRateCategory::HighHint: diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h index a398c01a8f..ee3a4f7bdc 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateSelector.h +++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h @@ -52,6 +52,12 @@ public: // The lowest Render Frame Rate that will ever be selected static constexpr Fps kMinSupportedFrameRate = 20_Hz; + // Start range for FrameRateCategory Normal and High. + static constexpr Fps kFrameRateCategoryRateHigh = 90_Hz; + static constexpr Fps kFrameRateCategoryRateNormal = 60_Hz; + static constexpr std::pair<Fps, Fps> kFrameRateCategoryRates = {kFrameRateCategoryRateNormal, + kFrameRateCategoryRateHigh}; + class Policy { static constexpr int kAllowGroupSwitchingDefault = false; @@ -433,6 +439,8 @@ public: bool isVrrDevice() const; + std::pair<Fps, Fps> getFrameRateCategoryRates() const { return kFrameRateCategoryRates; } + private: friend struct TestableRefreshRateSelector; diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index b83ff19fe7..274e121a79 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -405,6 +405,14 @@ void Scheduler::enableSyntheticVsync(bool enable) { eventThreadFor(Cycle::Render).enableSyntheticVsync(enable); } +void Scheduler::omitVsyncDispatching(bool omitted) { + eventThreadFor(Cycle::Render).omitVsyncDispatching(omitted); + // Note: If we don't couple Cycle::LastComposite event thread, there is a black screen + // after boot. This is most likely sysui or system_server dependency on sf instance + // Choreographer + eventThreadFor(Cycle::LastComposite).omitVsyncDispatching(omitted); +} + void Scheduler::onFrameRateOverridesChanged() { const auto [pacesetterId, supportsFrameRateOverrideByContent] = [this] { std::scoped_lock lock(mDisplayLock); @@ -428,7 +436,8 @@ void Scheduler::onHdcpLevelsChanged(Cycle cycle, PhysicalDisplayId displayId, #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-value" // b/369277774 -bool Scheduler::onDisplayModeChanged(PhysicalDisplayId displayId, const FrameRateMode& mode) { +bool Scheduler::onDisplayModeChanged(PhysicalDisplayId displayId, const FrameRateMode& mode, + bool clearContentRequirements) { const bool isPacesetter = FTL_FAKE_GUARD(kMainThreadContext, (std::scoped_lock(mDisplayLock), displayId == mPacesetterDisplayId)); @@ -437,9 +446,11 @@ bool Scheduler::onDisplayModeChanged(PhysicalDisplayId displayId, const FrameRat std::lock_guard<std::mutex> lock(mPolicyLock); mPolicy.emittedModeOpt = mode; - // Invalidate content based refresh rate selection so it could be calculated - // again for the new refresh rate. - mPolicy.contentRequirements.clear(); + if (clearContentRequirements) { + // Invalidate content based refresh rate selection so it could be calculated + // again for the new refresh rate. + mPolicy.contentRequirements.clear(); + } } if (hasEventThreads()) { diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h index c88b563805..e77af609e2 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -151,9 +151,11 @@ public: void dispatchHotplugError(int32_t errorCode); // Returns true if the PhysicalDisplayId is the pacesetter. - bool onDisplayModeChanged(PhysicalDisplayId, const FrameRateMode&) EXCLUDES(mPolicyLock); + bool onDisplayModeChanged(PhysicalDisplayId, const FrameRateMode&, + bool clearContentRequirements) EXCLUDES(mPolicyLock); void enableSyntheticVsync(bool = true) REQUIRES(kMainThreadContext); + void omitVsyncDispatching(bool) REQUIRES(kMainThreadContext); void onHdcpLevelsChanged(Cycle, PhysicalDisplayId, int32_t, int32_t); diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index d35a76ad4b..a7ab117679 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -92,6 +92,7 @@ #include <ui/DisplayStatInfo.h> #include <ui/DisplayState.h> #include <ui/DynamicDisplayInfo.h> +#include <ui/FrameRateCategoryRate.h> #include <ui/GraphicBufferAllocator.h> #include <ui/HdrRenderTypeUtils.h> #include <ui/LayerStack.h> @@ -132,7 +133,6 @@ #include "DisplayHardware/FramebufferSurface.h" #include "DisplayHardware/HWComposer.h" #include "DisplayHardware/Hal.h" -#include "DisplayHardware/PowerAdvisor.h" #include "DisplayHardware/VirtualDisplaySurface.h" #include "DisplayRenderArea.h" #include "Effects/Daltonizer.h" @@ -152,6 +152,7 @@ #include "LayerVector.h" #include "MutexUtils.h" #include "NativeWindowSurface.h" +#include "PowerAdvisor/PowerAdvisor.h" #include "RegionSamplingThread.h" #include "RenderAreaBuilder.h" #include "Scheduler/EventThread.h" @@ -425,7 +426,11 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory, SkipInitializationTag) mEmulatedDisplayDensity(getDensityFromProperty("qemu.sf.lcd_density", false)), mInternalDisplayDensity( getDensityFromProperty("ro.sf.lcd_density", !mEmulatedDisplayDensity)), - mPowerAdvisor(std::make_unique<Hwc2::impl::PowerAdvisor>(*this)), + mPowerAdvisor(std::make_unique< + adpf::impl::PowerAdvisor>([this] { disableExpensiveRendering(); }, + std::chrono::milliseconds( + sysprop::display_update_imminent_timeout_ms( + 80)))), mWindowInfosListenerInvoker(sp<WindowInfosListenerInvoker>::make()), mSkipPowerOnForQuiescent(base::GetBoolProperty("ro.boot.quiescent"s, false)) { ALOGI("Using HWComposer service: %s", mHwcServiceName.c_str()); @@ -1009,7 +1014,8 @@ void SurfaceFlinger::init() FTL_FAKE_GUARD(kMainThreadContext) { config.cacheUltraHDR = base::GetBoolProperty("ro.surface_flinger.prime_shader_cache.ultrahdr"s, false); config.cacheEdgeExtension = - base::GetBoolProperty("debug.sf.edge_extension_shader"s, true); + base::GetBoolProperty("debug.sf.prime_shader_cache.edge_extension_shader"s, + true); return getRenderEngine().primeCache(config); }); @@ -1217,6 +1223,10 @@ void SurfaceFlinger::getDynamicDisplayInfoInternal(ui::DynamicDisplayInfo*& info info->activeDisplayModeId = ftl::to_underlying(mode.modePtr->getId()); info->renderFrameRate = mode.fps.getValue(); info->hasArrSupport = mode.modePtr->getVrrConfig() && FlagManager::getInstance().vrr_config(); + + const auto [normal, high] = display->refreshRateSelector().getFrameRateCategoryRates(); + ui::FrameRateCategoryRate frameRateCategoryRate(normal.getValue(), high.getValue()); + info->frameRateCategoryRate = frameRateCategoryRate; info->activeColorMode = display->getCompositionDisplay()->getState().colorMode; info->hdrCapabilities = filterOut4k30(display->getHdrCapabilities()); @@ -1348,7 +1358,8 @@ void SurfaceFlinger::setDesiredMode(display::DisplayModeRequest&& desiredMode) { mScheduler->updatePhaseConfiguration(displayId, mode.fps); if (emitEvent) { - mScheduler->onDisplayModeChanged(displayId, mode); + mScheduler->onDisplayModeChanged(displayId, mode, + /*clearContentRequirements*/ false); } break; case DesiredModeAction::None: @@ -1443,7 +1454,7 @@ void SurfaceFlinger::finalizeDisplayModeChange(PhysicalDisplayId displayId) { mScheduler->updatePhaseConfiguration(displayId, activeMode.fps); if (pendingModeOpt->emitEvent) { - mScheduler->onDisplayModeChanged(displayId, activeMode); + mScheduler->onDisplayModeChanged(displayId, activeMode, /*clearContentRequirements*/ true); } } @@ -2526,17 +2537,13 @@ bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, nsecs_t frameTimeNs, frontend::LayerSnapshot* snapshot = mLayerSnapshotBuilder.getSnapshot(it->second->sequence); gui::GameMode gameMode = (snapshot) ? snapshot->gameMode : gui::GameMode::Unsupported; mLayersWithQueuedFrames.emplace(it->second, gameMode); - mLayersIdsWithQueuedFrames.emplace(it->second->sequence); } updateLayerHistory(latchTime); mLayerSnapshotBuilder.forEachSnapshot([&](const frontend::LayerSnapshot& snapshot) { - // update output dirty region if we have a queued buffer that is visible or a snapshot - // recently became invisible - // TODO(b/360050020) investigate if we need to update dirty region when layer color changes - if ((snapshot.isVisible && - (mLayersIdsWithQueuedFrames.find(snapshot.path.id) != - mLayersIdsWithQueuedFrames.end())) || + // update output's dirty region if a snapshot is visible and its + // content is dirty or if a snapshot recently became invisible + if ((snapshot.isVisible && snapshot.contentDirty) || (!snapshot.isVisible && snapshot.changes.test(Changes::Visibility))) { Region visibleReg; visibleReg.set(snapshot.transformedBoundsWithoutTransparentRegion); @@ -2926,7 +2933,6 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( mScheduler->modulateVsync({}, &VsyncModulator::onDisplayRefresh, hasGpuUseOrReuse); mLayersWithQueuedFrames.clear(); - mLayersIdsWithQueuedFrames.clear(); doActiveLayersTracingIfNeeded(true, mVisibleRegionsDirty, pacesetterTarget.frameBeginTime(), vsyncId); @@ -3660,6 +3666,26 @@ sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal( return display; } +void SurfaceFlinger::incRefreshableDisplays() { + if (FlagManager::getInstance().no_vsyncs_on_screen_off()) { + mRefreshableDisplays++; + if (mRefreshableDisplays == 1) { + ftl::FakeGuard guard(kMainThreadContext); + mScheduler->omitVsyncDispatching(false); + } + } +} + +void SurfaceFlinger::decRefreshableDisplays() { + if (FlagManager::getInstance().no_vsyncs_on_screen_off()) { + mRefreshableDisplays--; + if (mRefreshableDisplays == 0) { + ftl::FakeGuard guard(kMainThreadContext); + mScheduler->omitVsyncDispatching(true); + } + } +} + void SurfaceFlinger::processDisplayAdded(const wp<IBinder>& displayToken, const DisplayDeviceState& state) { ui::Size resolution(0, 0); @@ -3751,6 +3777,10 @@ void SurfaceFlinger::processDisplayAdded(const wp<IBinder>& displayToken, display->adjustRefreshRate(mScheduler->getPacesetterRefreshRate()); } + if (display->isRefreshable()) { + incRefreshableDisplays(); + } + mDisplays.try_emplace(displayToken, std::move(display)); // For an external display, loadDisplayModes already attempted to select the same mode @@ -3785,6 +3815,10 @@ void SurfaceFlinger::processDisplayRemoved(const wp<IBinder>& displayToken) { } else { mScheduler->unregisterDisplay(display->getPhysicalId(), mActiveDisplayId); } + + if (display->isRefreshable()) { + decRefreshableDisplays(); + } } mDisplays.erase(displayToken); @@ -3819,6 +3853,10 @@ void SurfaceFlinger::processDisplayChanged(const wp<IBinder>& displayToken, if (display->isVirtual()) { releaseVirtualDisplay(display->getVirtualId()); } + + if (display->isRefreshable()) { + decRefreshableDisplays(); + } } mDisplays.erase(displayToken); @@ -3983,7 +4021,8 @@ void SurfaceFlinger::updateInputFlinger(VsyncId vsyncId, TimePoint frameTime) { inputWindowCommands = std::move(mInputWindowCommands), inputFlinger = mInputFlinger, this, - visibleWindowsChanged, vsyncId, frameTime]() { + visibleWindowsChanged, vsyncId, + frameTime]() mutable { SFTRACE_NAME("BackgroundExecutor::updateInputFlinger"); if (updateWindowInfo) { mWindowInfosListenerInvoker @@ -5317,7 +5356,15 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: activeDisplay->isPoweredOn(), "Trying to change power mode on inactive display without powering off active display"); + const bool couldRefresh = display->isRefreshable(); display->setPowerMode(mode); + const bool canRefresh = display->isRefreshable(); + + if (couldRefresh && !canRefresh) { + decRefreshableDisplays(); + } else if (!couldRefresh && canRefresh) { + incRefreshableDisplays(); + } const auto activeMode = display->refreshRateSelector().getActiveMode().modePtr; if (currentMode == hal::PowerMode::OFF) { @@ -7158,9 +7205,10 @@ void SurfaceFlinger::attachReleaseFenceFutureToLayer(Layer* layer, LayerFE* laye // typically a layer with DRM contents, or have the GRALLOC_USAGE_PROTECTED set on the buffer. // A protected layer has no implication on whether it's secure, which is explicitly set by // application to avoid being screenshot or drawn via unsecure display. -bool SurfaceFlinger::layersHasProtectedLayer(const std::vector<sp<LayerFE>>& layers) const { +bool SurfaceFlinger::layersHasProtectedLayer( + const std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) const { bool protectedLayerFound = false; - for (auto& layerFE : layers) { + for (auto& [_, layerFE] : layers) { protectedLayerFound |= (layerFE->mSnapshot->isVisible && layerFE->mSnapshot->hasProtectedContent); if (protectedLayerFound) { @@ -7176,15 +7224,21 @@ bool SurfaceFlinger::layersHasProtectedLayer(const std::vector<sp<LayerFE>>& lay // risk of deadlocks. std::optional<SurfaceFlinger::OutputCompositionState> SurfaceFlinger::getSnapshotsFromMainThread( RenderAreaBuilderVariant& renderAreaBuilder, GetLayerSnapshotsFunction getLayerSnapshotsFn, - std::vector<sp<LayerFE>>& layerFEs) { + std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) { return mScheduler - ->schedule([=, this, &renderAreaBuilder, &layerFEs]() REQUIRES(kMainThreadContext) { + ->schedule([=, this, &renderAreaBuilder, &layers]() REQUIRES(kMainThreadContext) { SFTRACE_NAME("getSnapshotsFromMainThread"); - auto layers = getLayerSnapshotsFn(); - for (auto& [layer, layerFE] : layers) { - attachReleaseFenceFutureToLayer(layer, layerFE.get(), ui::INVALID_LAYER_STACK); + layers = getLayerSnapshotsFn(); + // Non-threaded RenderEngine eventually returns to the main thread a 2nd time + // to complete the screenshot. Release fences should only be added during the 2nd + // hop to main thread in order to avoid potential deadlocks from waiting for the + // the future fence to fire. + if (mRenderEngine->isThreaded()) { + for (auto& [layer, layerFE] : layers) { + attachReleaseFenceFutureToLayer(layer, layerFE.get(), + ui::INVALID_LAYER_STACK); + } } - layerFEs = extractLayerFEs(layers); return getDisplayStateFromRenderAreaBuilder(renderAreaBuilder); }) .get(); @@ -7205,79 +7259,41 @@ void SurfaceFlinger::captureScreenCommon(RenderAreaBuilderVariant renderAreaBuil return; } - if (FlagManager::getInstance().single_hop_screenshot() && mRenderEngine->isThreaded()) { - std::vector<sp<LayerFE>> layerFEs; - auto displayState = - getSnapshotsFromMainThread(renderAreaBuilder, getLayerSnapshotsFn, layerFEs); - - const bool supportsProtected = getRenderEngine().supportsProtectedContent(); - bool hasProtectedLayer = false; - if (allowProtected && supportsProtected) { - hasProtectedLayer = layersHasProtectedLayer(layerFEs); - } - const bool isProtected = hasProtectedLayer && allowProtected && supportsProtected; - const uint32_t usage = GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_RENDER | - GRALLOC_USAGE_HW_TEXTURE | - (isProtected ? GRALLOC_USAGE_PROTECTED - : GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN); - sp<GraphicBuffer> buffer = - getFactory().createGraphicBuffer(bufferSize.getWidth(), bufferSize.getHeight(), - static_cast<android_pixel_format>(reqPixelFormat), - 1 /* layerCount */, usage, "screenshot"); - - const status_t bufferStatus = buffer->initCheck(); - if (bufferStatus != OK) { - // Animations may end up being really janky, but don't crash here. - // Otherwise an irreponsible process may cause an SF crash by allocating - // too much. - ALOGE("%s: Buffer failed to allocate: %d", __func__, bufferStatus); - invokeScreenCaptureError(bufferStatus, captureListener); - return; - } - const std::shared_ptr<renderengine::ExternalTexture> texture = std::make_shared< - renderengine::impl::ExternalTexture>(buffer, getRenderEngine(), - renderengine::impl::ExternalTexture::Usage:: - WRITEABLE); - auto futureFence = captureScreenshot(renderAreaBuilder, texture, false /* regionSampling */, - grayscale, isProtected, attachGainmap, captureListener, - displayState, layerFEs); - futureFence.get(); - - } else { - const bool supportsProtected = getRenderEngine().supportsProtectedContent(); - bool hasProtectedLayer = false; - if (allowProtected && supportsProtected) { - auto layers = mScheduler->schedule([=]() { return getLayerSnapshotsFn(); }).get(); - hasProtectedLayer = layersHasProtectedLayer(extractLayerFEs(layers)); - } - const bool isProtected = hasProtectedLayer && allowProtected && supportsProtected; - const uint32_t usage = GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_RENDER | - GRALLOC_USAGE_HW_TEXTURE | - (isProtected ? GRALLOC_USAGE_PROTECTED - : GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN); - sp<GraphicBuffer> buffer = - getFactory().createGraphicBuffer(bufferSize.getWidth(), bufferSize.getHeight(), - static_cast<android_pixel_format>(reqPixelFormat), - 1 /* layerCount */, usage, "screenshot"); - - const status_t bufferStatus = buffer->initCheck(); - if (bufferStatus != OK) { - // Animations may end up being really janky, but don't crash here. - // Otherwise an irreponsible process may cause an SF crash by allocating - // too much. - ALOGE("%s: Buffer failed to allocate: %d", __func__, bufferStatus); - invokeScreenCaptureError(bufferStatus, captureListener); - return; - } - const std::shared_ptr<renderengine::ExternalTexture> texture = std::make_shared< - renderengine::impl::ExternalTexture>(buffer, getRenderEngine(), - renderengine::impl::ExternalTexture::Usage:: - WRITEABLE); - auto futureFence = captureScreenshotLegacy(renderAreaBuilder, getLayerSnapshotsFn, texture, - false /* regionSampling */, grayscale, - isProtected, attachGainmap, captureListener); - futureFence.get(); + std::vector<std::pair<Layer*, sp<LayerFE>>> layers; + auto displayState = getSnapshotsFromMainThread(renderAreaBuilder, getLayerSnapshotsFn, layers); + + const bool supportsProtected = getRenderEngine().supportsProtectedContent(); + bool hasProtectedLayer = false; + if (allowProtected && supportsProtected) { + hasProtectedLayer = layersHasProtectedLayer(layers); + } + const bool isProtected = hasProtectedLayer && allowProtected && supportsProtected; + const uint32_t usage = GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_RENDER | + GRALLOC_USAGE_HW_TEXTURE | + (isProtected ? GRALLOC_USAGE_PROTECTED + : GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN); + sp<GraphicBuffer> buffer = + getFactory().createGraphicBuffer(bufferSize.getWidth(), bufferSize.getHeight(), + static_cast<android_pixel_format>(reqPixelFormat), + 1 /* layerCount */, usage, "screenshot"); + + const status_t bufferStatus = buffer->initCheck(); + if (bufferStatus != OK) { + // Animations may end up being really janky, but don't crash here. + // Otherwise an irreponsible process may cause an SF crash by allocating + // too much. + ALOGE("%s: Buffer failed to allocate: %d", __func__, bufferStatus); + invokeScreenCaptureError(bufferStatus, captureListener); + return; } + const std::shared_ptr<renderengine::ExternalTexture> texture = std::make_shared< + renderengine::impl::ExternalTexture>(buffer, getRenderEngine(), + renderengine::impl::ExternalTexture::Usage:: + WRITEABLE); + auto futureFence = + captureScreenshot(renderAreaBuilder, texture, false /* regionSampling */, grayscale, + isProtected, attachGainmap, captureListener, displayState, layers); + futureFence.get(); } std::optional<SurfaceFlinger::OutputCompositionState> @@ -7316,22 +7332,13 @@ SurfaceFlinger::getDisplayStateFromRenderAreaBuilder(RenderAreaBuilderVariant& r return std::nullopt; } -std::vector<sp<LayerFE>> SurfaceFlinger::extractLayerFEs( - const std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) const { - std::vector<sp<LayerFE>> layerFEs; - layerFEs.reserve(layers.size()); - for (const auto& [_, layerFE] : layers) { - layerFEs.push_back(layerFE); - } - return layerFEs; -} - ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshot( const RenderAreaBuilderVariant& renderAreaBuilder, const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling, bool grayscale, bool isProtected, bool attachGainmap, const sp<IScreenCaptureListener>& captureListener, - std::optional<OutputCompositionState>& displayState, std::vector<sp<LayerFE>>& layerFEs) { + std::optional<OutputCompositionState>& displayState, + std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) { SFTRACE_CALL(); ScreenCaptureResults captureResults; @@ -7350,11 +7357,9 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshot( float displayBrightnessNits = displayState.value().displayBrightnessNits; float sdrWhitePointNits = displayState.value().sdrWhitePointNits; - // Empty vector needed to pass into renderScreenImpl for legacy path - std::vector<std::pair<Layer*, sp<android::LayerFE>>> layers; ftl::SharedFuture<FenceResult> renderFuture = renderScreenImpl(renderArea.get(), buffer, regionSampling, grayscale, isProtected, - attachGainmap, captureResults, displayState, layers, layerFEs); + captureResults, displayState, layers); if (captureResults.capturedHdrLayers && attachGainmap && FlagManager::getInstance().true_hdr_screenshots()) { @@ -7389,8 +7394,7 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshot( ScreenCaptureResults unusedResults; ftl::SharedFuture<FenceResult> hdrRenderFuture = renderScreenImpl(renderArea.get(), hdrTexture, regionSampling, grayscale, - isProtected, attachGainmap, unusedResults, displayState, - layers, layerFEs); + isProtected, unusedResults, displayState, layers); renderFuture = ftl::Future(std::move(renderFuture)) @@ -7436,75 +7440,14 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshot( return renderFuture; } -ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshotLegacy( - RenderAreaBuilderVariant renderAreaBuilder, GetLayerSnapshotsFunction getLayerSnapshotsFn, - const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling, - bool grayscale, bool isProtected, bool attachGainmap, - const sp<IScreenCaptureListener>& captureListener) { - SFTRACE_CALL(); - - auto takeScreenshotFn = [=, this, renderAreaBuilder = std::move(renderAreaBuilder)]() REQUIRES( - kMainThreadContext) mutable -> ftl::SharedFuture<FenceResult> { - auto layers = getLayerSnapshotsFn(); - for (auto& [layer, layerFE] : layers) { - attachReleaseFenceFutureToLayer(layer, layerFE.get(), ui::INVALID_LAYER_STACK); - } - auto displayState = getDisplayStateFromRenderAreaBuilder(renderAreaBuilder); - - ScreenCaptureResults captureResults; - std::unique_ptr<const RenderArea> renderArea = - std::visit([](auto&& arg) -> std::unique_ptr<RenderArea> { return arg.build(); }, - renderAreaBuilder); - - if (!renderArea) { - ALOGW("Skipping screen capture because of invalid render area."); - if (captureListener) { - captureResults.fenceResult = base::unexpected(NO_MEMORY); - captureListener->onScreenCaptureCompleted(captureResults); - } - return ftl::yield<FenceResult>(base::unexpected(NO_ERROR)).share(); - } - - auto layerFEs = extractLayerFEs(layers); - ftl::SharedFuture<FenceResult> renderFuture = - renderScreenImpl(renderArea.get(), buffer, regionSampling, grayscale, isProtected, - attachGainmap, captureResults, displayState, layers, layerFEs); - - if (captureListener) { - // Defer blocking on renderFuture back to the Binder thread. - return ftl::Future(std::move(renderFuture)) - .then([captureListener, captureResults = std::move(captureResults)]( - FenceResult fenceResult) mutable -> FenceResult { - captureResults.fenceResult = std::move(fenceResult); - captureListener->onScreenCaptureCompleted(captureResults); - return base::unexpected(NO_ERROR); - }) - .share(); - } - return renderFuture; - }; - - // TODO(b/294936197): Run takeScreenshotsFn() in a binder thread to reduce the number - // of calls on the main thread. - auto future = - mScheduler->schedule(FTL_FAKE_GUARD(kMainThreadContext, std::move(takeScreenshotFn))); - - // Flatten nested futures. - auto chain = ftl::Future(std::move(future)).then([](ftl::SharedFuture<FenceResult> future) { - return future; - }); - - return chain.share(); -} - ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl( const RenderArea* renderArea, const std::shared_ptr<renderengine::ExternalTexture>& buffer, - bool regionSampling, bool grayscale, bool isProtected, bool attachGainmap, - ScreenCaptureResults& captureResults, std::optional<OutputCompositionState>& displayState, - std::vector<std::pair<Layer*, sp<LayerFE>>>& layers, std::vector<sp<LayerFE>>& layerFEs) { + bool regionSampling, bool grayscale, bool isProtected, ScreenCaptureResults& captureResults, + std::optional<OutputCompositionState>& displayState, + std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) { SFTRACE_CALL(); - for (auto& layerFE : layerFEs) { + for (auto& [_, layerFE] : layers) { frontend::LayerSnapshot* snapshot = layerFE->mSnapshot.get(); captureResults.capturedSecureLayers |= (snapshot->isVisible && snapshot->isSecure); captureResults.capturedHdrLayers |= isHdrLayer(*snapshot); @@ -7563,29 +7506,32 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl( captureResults.buffer = capturedBuffer->getBuffer(); ui::LayerStack layerStack{ui::DEFAULT_LAYER_STACK}; - if (!layerFEs.empty()) { - const sp<LayerFE>& layerFE = layerFEs.back(); + if (!layers.empty()) { + const sp<LayerFE>& layerFE = layers.back().second; layerStack = layerFE->getCompositionState()->outputFilter.layerStack; } - auto copyLayerFEs = [&layerFEs]() { - std::vector<sp<compositionengine::LayerFE>> ceLayerFEs; - ceLayerFEs.reserve(layerFEs.size()); - for (const auto& layerFE : layerFEs) { - ceLayerFEs.push_back(layerFE); - } - return ceLayerFEs; - }; - auto present = [this, buffer = capturedBuffer, dataspace = captureResults.capturedDataspace, sdrWhitePointNits, displayBrightnessNits, grayscale, isProtected, - layerFEs = copyLayerFEs(), layerStack, regionSampling, + layers = std::move(layers), layerStack, regionSampling, renderArea = std::move(renderArea), renderIntent, enableLocalTonemapping]() -> FenceResult { std::unique_ptr<compositionengine::CompositionEngine> compositionEngine = mFactory.createCompositionEngine(); compositionEngine->setRenderEngine(mRenderEngine.get()); + std::vector<sp<compositionengine::LayerFE>> layerFEs; + layerFEs.reserve(layers.size()); + for (auto& [layer, layerFE] : layers) { + // Release fences were not yet added for non-threaded render engine. To avoid + // deadlocks between main thread and binder threads waiting for the future fence + // result, fences should be added to layers in the same hop onto the main thread. + if (!mRenderEngine->isThreaded()) { + attachReleaseFenceFutureToLayer(layer, layerFE.get(), ui::INVALID_LAYER_STACK); + } + layerFEs.push_back(layerFE); + } + compositionengine::Output::ColorProfile colorProfile{.dataspace = dataspace, .renderIntent = renderIntent}; @@ -7643,13 +7589,9 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl( // // TODO(b/196334700) Once we use RenderEngineThreaded everywhere we can always defer the call // to CompositionEngine::present. - ftl::SharedFuture<FenceResult> presentFuture; - if (FlagManager::getInstance().single_hop_screenshot() && mRenderEngine->isThreaded()) { - presentFuture = ftl::yield(present()).share(); - } else { - presentFuture = mRenderEngine->isThreaded() ? ftl::defer(std::move(present)).share() - : ftl::yield(present()).share(); - } + ftl::SharedFuture<FenceResult> presentFuture = mRenderEngine->isThreaded() + ? ftl::yield(present()).share() + : mScheduler->schedule(std::move(present)).share(); return presentFuture; } @@ -7713,7 +7655,8 @@ status_t SurfaceFlinger::applyRefreshRateSelectorPolicy( ALOGV("Setting desired display mode specs: %s", currentPolicy.toString().c_str()); if (const bool isPacesetter = - mScheduler->onDisplayModeChanged(displayId, selector.getActiveMode())) { + mScheduler->onDisplayModeChanged(displayId, selector.getActiveMode(), + /*clearContentRequirements*/ true)) { mDisplayModeController.updateKernelIdleTimer(displayId); } @@ -8601,6 +8544,9 @@ void SurfaceComposerAIDL::getDynamicDisplayInfoInternal(ui::DynamicDisplayInfo& outInfo->activeDisplayModeId = info.activeDisplayModeId; outInfo->renderFrameRate = info.renderFrameRate; outInfo->hasArrSupport = info.hasArrSupport; + gui::FrameRateCategoryRate& frameRateCategoryRate = outInfo->frameRateCategoryRate; + frameRateCategoryRate.normal = info.frameRateCategoryRate.getNormal(); + frameRateCategoryRate.high = info.frameRateCategoryRate.getHigh(); outInfo->supportedColorModes.clear(); outInfo->supportedColorModes.reserve(info.supportedColorModes.size()); diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 31218edbe7..d3479b7e52 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -57,6 +57,7 @@ #include <utils/threads.h> #include <compositionengine/OutputColorSetting.h> +#include <compositionengine/impl/OutputCompositionState.h> #include <scheduler/Fps.h> #include <scheduler/PresentLatencyTracker.h> #include <scheduler/Time.h> @@ -70,7 +71,6 @@ #include "Display/PhysicalDisplay.h" #include "DisplayDevice.h" #include "DisplayHardware/HWC2.h" -#include "DisplayHardware/PowerAdvisor.h" #include "DisplayIdGenerator.h" #include "Effects/Daltonizer.h" #include "FrontEnd/DisplayInfo.h" @@ -81,6 +81,7 @@ #include "FrontEnd/TransactionHandler.h" #include "LayerVector.h" #include "MutexUtils.h" +#include "PowerAdvisor/PowerAdvisor.h" #include "Scheduler/ISchedulerCallback.h" #include "Scheduler/RefreshRateSelector.h" #include "Scheduler/Scheduler.h" @@ -851,13 +852,14 @@ private: void attachReleaseFenceFutureToLayer(Layer* layer, LayerFE* layerFE, ui::LayerStack layerStack); // Checks if a protected layer exists in a list of layers. - bool layersHasProtectedLayer(const std::vector<sp<LayerFE>>& layers) const; + bool layersHasProtectedLayer(const std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) const; using OutputCompositionState = compositionengine::impl::OutputCompositionState; std::optional<OutputCompositionState> getSnapshotsFromMainThread( RenderAreaBuilderVariant& renderAreaBuilder, - GetLayerSnapshotsFunction getLayerSnapshotsFn, std::vector<sp<LayerFE>>& layerFEs); + GetLayerSnapshotsFunction getLayerSnapshotsFn, + std::vector<std::pair<Layer*, sp<LayerFE>>>& layers); void captureScreenCommon(RenderAreaBuilderVariant, GetLayerSnapshotsFunction, ui::Size bufferSize, ui::PixelFormat, bool allowProtected, @@ -866,32 +868,19 @@ private: std::optional<OutputCompositionState> getDisplayStateFromRenderAreaBuilder( RenderAreaBuilderVariant& renderAreaBuilder) REQUIRES(kMainThreadContext); - // Legacy layer raw pointer is not safe to access outside the main thread. - // Creates a new vector consisting only of LayerFEs, which can be safely - // accessed outside the main thread. - std::vector<sp<LayerFE>> extractLayerFEs( - const std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) const; - ftl::SharedFuture<FenceResult> captureScreenshot( const RenderAreaBuilderVariant& renderAreaBuilder, const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling, bool grayscale, bool isProtected, bool attachGainmap, const sp<IScreenCaptureListener>& captureListener, std::optional<OutputCompositionState>& displayState, - std::vector<sp<LayerFE>>& layerFEs); - - ftl::SharedFuture<FenceResult> captureScreenshotLegacy( - RenderAreaBuilderVariant, GetLayerSnapshotsFunction, - const std::shared_ptr<renderengine::ExternalTexture>&, bool regionSampling, - bool grayscale, bool isProtected, bool attachGainmap, - const sp<IScreenCaptureListener>&); + std::vector<std::pair<Layer*, sp<LayerFE>>>& layers); ftl::SharedFuture<FenceResult> renderScreenImpl( const RenderArea*, const std::shared_ptr<renderengine::ExternalTexture>&, - bool regionSampling, bool grayscale, bool isProtected, bool attachGainmap, - ScreenCaptureResults&, std::optional<OutputCompositionState>& displayState, - std::vector<std::pair<Layer*, sp<LayerFE>>>& layers, - std::vector<sp<LayerFE>>& layerFEs); + bool regionSampling, bool grayscale, bool isProtected, ScreenCaptureResults&, + std::optional<OutputCompositionState>& displayState, + std::vector<std::pair<Layer*, sp<LayerFE>>>& layers); void readPersistentProperties(); @@ -1253,7 +1242,6 @@ private: // latched. std::unordered_set<std::pair<sp<Layer>, gui::GameMode>, LayerIntHash> mLayersWithQueuedFrames; std::unordered_set<sp<Layer>, SpHash<Layer>> mLayersWithBuffersRemoved; - std::unordered_set<uint32_t> mLayersIdsWithQueuedFrames; // Sorted list of layers that were composed during previous frame. This is used to // avoid an expensive traversal of the layer hierarchy when there are no @@ -1373,7 +1361,7 @@ private: sp<os::IInputFlinger> mInputFlinger; InputWindowCommands mInputWindowCommands; - std::unique_ptr<Hwc2::PowerAdvisor> mPowerAdvisor; + std::unique_ptr<adpf::PowerAdvisor> mPowerAdvisor; void enableRefreshRateOverlay(bool enable) REQUIRES(mStateLock, kMainThreadContext); @@ -1423,6 +1411,11 @@ private: // Whether a display should be turned on when initialized bool mSkipPowerOnForQuiescent; + // used for omitting vsync callbacks to apps when the display is not updatable + int mRefreshableDisplays GUARDED_BY(mStateLock) = 0; + void incRefreshableDisplays() REQUIRES(mStateLock); + void decRefreshableDisplays() REQUIRES(mStateLock); + frontend::LayerLifecycleManager mLayerLifecycleManager GUARDED_BY(kMainThreadContext); frontend::LayerHierarchyBuilder mLayerHierarchyBuilder GUARDED_BY(kMainThreadContext); frontend::LayerSnapshotBuilder mLayerSnapshotBuilder GUARDED_BY(kMainThreadContext); diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp index 658bca6f31..12616e3edb 100644 --- a/services/surfaceflinger/common/FlagManager.cpp +++ b/services/surfaceflinger/common/FlagManager.cpp @@ -139,6 +139,7 @@ void FlagManager::dump(std::string& result) const { DUMP_READ_ONLY_FLAG(vrr_bugfix_dropped_frame); DUMP_READ_ONLY_FLAG(restore_blur_step); DUMP_READ_ONLY_FLAG(dont_skip_on_early_ro); + DUMP_READ_ONLY_FLAG(no_vsyncs_on_screen_off); DUMP_READ_ONLY_FLAG(protected_if_client); DUMP_READ_ONLY_FLAG(idle_screen_refresh_rate_timeout); DUMP_READ_ONLY_FLAG(graphite_renderengine); @@ -153,10 +154,11 @@ void FlagManager::dump(std::string& result) const { DUMP_READ_ONLY_FLAG(override_trusted_overlay); DUMP_READ_ONLY_FLAG(flush_buffer_slots_to_uncache); DUMP_READ_ONLY_FLAG(force_compile_graphite_renderengine); - DUMP_READ_ONLY_FLAG(single_hop_screenshot); DUMP_READ_ONLY_FLAG(trace_frame_rate_override); DUMP_READ_ONLY_FLAG(true_hdr_screenshots); DUMP_READ_ONLY_FLAG(display_config_error_hal); + DUMP_READ_ONLY_FLAG(connected_display_hdr); + DUMP_READ_ONLY_FLAG(deprecate_frame_tracker); #undef DUMP_READ_ONLY_FLAG #undef DUMP_SERVER_FLAG @@ -244,6 +246,7 @@ FLAG_MANAGER_READ_ONLY_FLAG(vulkan_renderengine, "debug.renderengine.vulkan") FLAG_MANAGER_READ_ONLY_FLAG(renderable_buffer_usage, "") FLAG_MANAGER_READ_ONLY_FLAG(restore_blur_step, "debug.renderengine.restore_blur_step") FLAG_MANAGER_READ_ONLY_FLAG(dont_skip_on_early_ro, "") +FLAG_MANAGER_READ_ONLY_FLAG(no_vsyncs_on_screen_off, "debug.sf.no_vsyncs_on_screen_off") FLAG_MANAGER_READ_ONLY_FLAG(protected_if_client, "") FLAG_MANAGER_READ_ONLY_FLAG(vrr_bugfix_24q4, ""); FLAG_MANAGER_READ_ONLY_FLAG(vrr_bugfix_dropped_frame, "") @@ -259,9 +262,10 @@ FLAG_MANAGER_READ_ONLY_FLAG(local_tonemap_screenshots, "debug.sf.local_tonemap_s FLAG_MANAGER_READ_ONLY_FLAG(override_trusted_overlay, ""); FLAG_MANAGER_READ_ONLY_FLAG(flush_buffer_slots_to_uncache, ""); FLAG_MANAGER_READ_ONLY_FLAG(force_compile_graphite_renderengine, ""); -FLAG_MANAGER_READ_ONLY_FLAG(single_hop_screenshot, ""); FLAG_MANAGER_READ_ONLY_FLAG(true_hdr_screenshots, "debug.sf.true_hdr_screenshots"); FLAG_MANAGER_READ_ONLY_FLAG(display_config_error_hal, ""); +FLAG_MANAGER_READ_ONLY_FLAG(connected_display_hdr, ""); +FLAG_MANAGER_READ_ONLY_FLAG(deprecate_frame_tracker, ""); /// Trunk stable server flags /// FLAG_MANAGER_SERVER_FLAG(refresh_rate_overlay_on_external_display, "") diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h index 4f34718f4e..f5bea7237f 100644 --- a/services/surfaceflinger/common/include/common/FlagManager.h +++ b/services/surfaceflinger/common/include/common/FlagManager.h @@ -77,6 +77,7 @@ public: bool renderable_buffer_usage() const; bool restore_blur_step() const; bool dont_skip_on_early_ro() const; + bool no_vsyncs_on_screen_off() const; bool protected_if_client() const; bool idle_screen_refresh_rate_timeout() const; bool graphite_renderengine() const; @@ -91,10 +92,11 @@ public: bool override_trusted_overlay() const; bool flush_buffer_slots_to_uncache() const; bool force_compile_graphite_renderengine() const; - bool single_hop_screenshot() const; bool trace_frame_rate_override() const; bool true_hdr_screenshots() const; bool display_config_error_hal() const; + bool connected_display_hdr() const; + bool deprecate_frame_tracker() const; protected: // overridden for unit tests diff --git a/services/surfaceflinger/surfaceflinger_flags_new.aconfig b/services/surfaceflinger/surfaceflinger_flags_new.aconfig index f758879687..014c736a6b 100644 --- a/services/surfaceflinger/surfaceflinger_flags_new.aconfig +++ b/services/surfaceflinger/surfaceflinger_flags_new.aconfig @@ -21,12 +21,20 @@ flag { flag { name: "arr_setframerate_api" namespace: "core_graphics" - description: "New setFrameRate API for Android 16" + description: "New SDK Surface#setFrameRate API and Surface.FrameRateParams for Android 16" bug: "356987016" is_fixed_read_only: true } # arr_setframerate_api flag { + name: "arr_surfacecontrol_setframerate_api" + namespace: "core_graphics" + description: "New SDK SurfaceControl.Transaction#setFrameRate API for Android 16" + bug: "356987016" + is_fixed_read_only: true +} # arr_surfacecontrol_setframerate_api + +flag { name: "ce_fence_promise" namespace: "window_surfaces" description: "Moves logic for buffer release fences into LayerFE" @@ -49,6 +57,14 @@ flag { } # commit_not_composited flag { + name: "connected_display_hdr" + namespace: "core_graphics" + description: "enable connected display hdr capability" + bug: "374182788" + is_fixed_read_only: true +} # connected_display_hdr + +flag { name: "correct_dpi_with_display_size" namespace: "core_graphics" description: "indicate whether missing or likely incorrect dpi should be corrected using the display size." @@ -60,6 +76,17 @@ flag { } # correct_dpi_with_display_size flag { + name: "deprecate_frame_tracker" + namespace: "core_graphics" + description: "Deprecate using FrameTracker to accumulate and provide FrameStats" + bug: "241394120" + is_fixed_read_only: true + metadata { + purpose: PURPOSE_BUGFIX + } +} # deprecate_frame_tracker + +flag { name: "deprecate_vsync_sf" namespace: "core_graphics" description: "Depracate eVsyncSourceSurfaceFlinger and use vsync_app everywhere" @@ -150,6 +177,14 @@ flag { } # local_tonemap_screenshots flag { + name: "no_vsyncs_on_screen_off" + namespace: "core_graphics" + description: "Stop vsync / Choreographer callbacks to apps when the screen is off" + bug: "331636736" + is_fixed_read_only: true +} # no_vsyncs_on_screen_off + +flag { name: "single_hop_screenshot" namespace: "window_surfaces" description: "Only access SF main thread once during a screenshot" diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp index e6fed63d96..7b6e4bff6a 100644 --- a/services/surfaceflinger/tests/Credentials_test.cpp +++ b/services/surfaceflinger/tests/Credentials_test.cpp @@ -341,9 +341,9 @@ TEST_F(CredentialsTest, TransactionPermissionTest) { WindowInfosListenerUtils windowInfosListenerUtils; std::string name = "Test Layer"; sp<IBinder> token = sp<BBinder>::make(); - WindowInfo windowInfo; - windowInfo.name = name; - windowInfo.token = token; + auto windowInfo = sp<gui::WindowInfoHandle>::make(); + windowInfo->editInfo()->name = name; + windowInfo->editInfo()->token = token; sp<SurfaceControl> surfaceControl = mComposerClient->createSurface(String8(name.c_str()), 100, 100, PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceBufferState); @@ -370,7 +370,8 @@ TEST_F(CredentialsTest, TransactionPermissionTest) { UIDFaker f(AID_SYSTEM); auto windowIsPresentAndNotTrusted = [&](const std::vector<WindowInfo>& windowInfos) { auto foundWindowInfo = - WindowInfosListenerUtils::findMatchingWindowInfo(windowInfo, windowInfos); + WindowInfosListenerUtils::findMatchingWindowInfo(*windowInfo->getInfo(), + windowInfos); if (!foundWindowInfo) { return false; } @@ -386,7 +387,8 @@ TEST_F(CredentialsTest, TransactionPermissionTest) { Transaction().setTrustedOverlay(surfaceControl, true).apply(/*synchronous=*/true); auto windowIsPresentAndTrusted = [&](const std::vector<WindowInfo>& windowInfos) { auto foundWindowInfo = - WindowInfosListenerUtils::findMatchingWindowInfo(windowInfo, windowInfos); + WindowInfosListenerUtils::findMatchingWindowInfo(*windowInfo->getInfo(), + windowInfos); if (!foundWindowInfo) { return false; } diff --git a/services/surfaceflinger/tests/WindowInfosListener_test.cpp b/services/surfaceflinger/tests/WindowInfosListener_test.cpp index ad9a674456..2dd0dd9bd3 100644 --- a/services/surfaceflinger/tests/WindowInfosListener_test.cpp +++ b/services/surfaceflinger/tests/WindowInfosListener_test.cpp @@ -50,9 +50,9 @@ protected: TEST_F(WindowInfosListenerTest, WindowInfoAddedAndRemoved) { std::string name = "Test Layer"; sp<IBinder> token = sp<BBinder>::make(); - WindowInfo windowInfo; - windowInfo.name = name; - windowInfo.token = token; + auto windowInfo = sp<gui::WindowInfoHandle>::make(); + windowInfo->editInfo()->name = name; + windowInfo->editInfo()->token = token; sp<SurfaceControl> surfaceControl = mClient->createSurface(String8(name.c_str()), 100, 100, PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceBufferState); @@ -65,14 +65,14 @@ TEST_F(WindowInfosListenerTest, WindowInfoAddedAndRemoved) { .apply(); auto windowPresent = [&](const std::vector<WindowInfo>& windowInfos) { - return findMatchingWindowInfo(windowInfo, windowInfos); + return findMatchingWindowInfo(*windowInfo->getInfo(), windowInfos); }; ASSERT_TRUE(waitForWindowInfosPredicate(windowPresent)); Transaction().reparent(surfaceControl, nullptr).apply(); auto windowNotPresent = [&](const std::vector<WindowInfo>& windowInfos) { - return !findMatchingWindowInfo(windowInfo, windowInfos); + return !findMatchingWindowInfo(*windowInfo->getInfo(), windowInfos); }; ASSERT_TRUE(waitForWindowInfosPredicate(windowNotPresent)); } @@ -80,9 +80,9 @@ TEST_F(WindowInfosListenerTest, WindowInfoAddedAndRemoved) { TEST_F(WindowInfosListenerTest, WindowInfoChanged) { std::string name = "Test Layer"; sp<IBinder> token = sp<BBinder>::make(); - WindowInfo windowInfo; - windowInfo.name = name; - windowInfo.token = token; + auto windowInfo = sp<gui::WindowInfoHandle>::make(); + windowInfo->editInfo()->name = name; + windowInfo->editInfo()->token = token; sp<SurfaceControl> surfaceControl = mClient->createSurface(String8(name.c_str()), 100, 100, PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceBufferState); @@ -96,7 +96,7 @@ TEST_F(WindowInfosListenerTest, WindowInfoChanged) { .apply(); auto windowIsPresentAndTouchableRegionEmpty = [&](const std::vector<WindowInfo>& windowInfos) { - auto foundWindowInfo = findMatchingWindowInfo(windowInfo, windowInfos); + auto foundWindowInfo = findMatchingWindowInfo(*windowInfo->getInfo(), windowInfos); if (!foundWindowInfo) { return false; } @@ -104,19 +104,19 @@ TEST_F(WindowInfosListenerTest, WindowInfoChanged) { }; ASSERT_TRUE(waitForWindowInfosPredicate(windowIsPresentAndTouchableRegionEmpty)); - windowInfo.addTouchableRegion({0, 0, 50, 50}); + windowInfo->editInfo()->addTouchableRegion({0, 0, 50, 50}); Transaction().setInputWindowInfo(surfaceControl, windowInfo).apply(); auto windowIsPresentAndTouchableRegionMatches = [&](const std::vector<WindowInfo>& windowInfos) { - auto foundWindowInfo = findMatchingWindowInfo(windowInfo, windowInfos); + auto foundWindowInfo = findMatchingWindowInfo(*windowInfo->getInfo(), windowInfos); if (!foundWindowInfo) { return false; } auto touchableRegion = foundWindowInfo->transform.transform(foundWindowInfo->touchableRegion); - return touchableRegion.hasSameRects(windowInfo.touchableRegion); + return touchableRegion.hasSameRects(windowInfo->getInfo()->touchableRegion); }; ASSERT_TRUE(waitForWindowInfosPredicate(windowIsPresentAndTouchableRegionMatches)); } diff --git a/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h b/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h index b472047bd6..97946205ba 100644 --- a/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h +++ b/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h @@ -218,6 +218,17 @@ public: mLifecycleManager.applyTransactions(transactions); } + void setAutoRefresh(uint32_t id, bool autoRefresh) { + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + + transactions.back().states.front().state.what = layer_state_t::eAutoRefreshChanged; + transactions.back().states.front().layerId = id; + transactions.back().states.front().state.autoRefresh = autoRefresh; + mLifecycleManager.applyTransactions(transactions); + } + void hideLayer(uint32_t id) { setFlags(id, layer_state_t::eLayerHidden, layer_state_t::eLayerHidden); } diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp index cb8820a209..6af51435c3 100644 --- a/services/surfaceflinger/tests/unittests/Android.bp +++ b/services/surfaceflinger/tests/unittests/Android.bp @@ -29,13 +29,10 @@ package { filegroup { name: "libsurfaceflinger_backend_mock_sources", srcs: [ + ":poweradvisor_mock_sources", "mock/DisplayHardware/MockComposer.cpp", "mock/DisplayHardware/MockHWC2.cpp", "mock/DisplayHardware/MockHWComposer.cpp", - "mock/DisplayHardware/MockIPower.cpp", - "mock/DisplayHardware/MockPowerHintSessionWrapper.cpp", - "mock/DisplayHardware/MockPowerAdvisor.cpp", - "mock/DisplayHardware/MockPowerHalController.cpp", "mock/system/window/MockNativeWindow.cpp", ], } @@ -54,6 +51,13 @@ cc_library_headers { } filegroup { + name: "poweradvisor_mock_sources", + srcs: [ + "mock/PowerAdvisor/*.cpp", + ], +} + +filegroup { name: "libsurfaceflinger_mock_sources", srcs: [ "mock/MockEventThread.cpp", @@ -86,79 +90,7 @@ cc_test { ":libsurfaceflinger_backend_mock_sources", ":libsurfaceflinger_mock_sources", ":libsurfaceflinger_sources", - "libsurfaceflinger_unittest_main.cpp", - "ActiveDisplayRotationFlagsTest.cpp", - "BackgroundExecutorTest.cpp", - "CommitTest.cpp", - "CompositionTest.cpp", - "DaltonizerTest.cpp", - "DisplayIdGeneratorTest.cpp", - "DisplayTransactionTest.cpp", - "DisplayDevice_GetBestColorModeTest.cpp", - "DisplayDevice_SetDisplayBrightnessTest.cpp", - "DisplayDevice_SetProjectionTest.cpp", - "DisplayModeControllerTest.cpp", - "EventThreadTest.cpp", - "FlagManagerTest.cpp", - "FpsReporterTest.cpp", - "FpsTest.cpp", - "FramebufferSurfaceTest.cpp", - "FrameRateOverrideMappingsTest.cpp", - "FrameTimelineTest.cpp", - "HWComposerTest.cpp", - "JankTrackerTest.cpp", - "OneShotTimerTest.cpp", - "LayerHistoryIntegrationTest.cpp", - "LayerInfoTest.cpp", - "LayerMetadataTest.cpp", - "LayerHierarchyTest.cpp", - "LayerLifecycleManagerTest.cpp", - "LayerSnapshotTest.cpp", - "LayerTestUtils.cpp", - "MessageQueueTest.cpp", - "PowerAdvisorTest.cpp", - "SmallAreaDetectionAllowMappingsTest.cpp", - "SurfaceFlinger_ColorMatrixTest.cpp", - "SurfaceFlinger_CreateDisplayTest.cpp", - "SurfaceFlinger_DestroyDisplayTest.cpp", - "SurfaceFlinger_DisplayModeSwitching.cpp", - "SurfaceFlinger_DisplayTransactionCommitTest.cpp", - "SurfaceFlinger_ExcludeDolbyVisionTest.cpp", - "SurfaceFlinger_FoldableTest.cpp", - "SurfaceFlinger_GetDisplayNativePrimariesTest.cpp", - "SurfaceFlinger_GetDisplayStatsTest.cpp", - "SurfaceFlinger_HdrOutputControlTest.cpp", - "SurfaceFlinger_HotplugTest.cpp", - "SurfaceFlinger_InitializeDisplaysTest.cpp", - "SurfaceFlinger_NotifyExpectedPresentTest.cpp", - "SurfaceFlinger_NotifyPowerBoostTest.cpp", - "SurfaceFlinger_PowerHintTest.cpp", - "SurfaceFlinger_SetDisplayStateTest.cpp", - "SurfaceFlinger_SetPowerModeInternalTest.cpp", - "SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp", - "SchedulerTest.cpp", - "RefreshRateSelectorTest.cpp", - "RefreshRateStatsTest.cpp", - "RegionSamplingTest.cpp", - "TestableScheduler.cpp", - "TimeStatsTest.cpp", - "FrameTracerTest.cpp", - "TransactionApplicationTest.cpp", - "TransactionFrameTracerTest.cpp", - "TransactionProtoParserTest.cpp", - "TransactionSurfaceFrameTest.cpp", - "TransactionTraceWriterTest.cpp", - "TransactionTracingTest.cpp", - "TunnelModeEnabledReporterTest.cpp", - "VSyncCallbackRegistrationTest.cpp", - "VSyncDispatchTimerQueueTest.cpp", - "VSyncDispatchRealtimeTest.cpp", - "VsyncModulatorTest.cpp", - "VSyncPredictorTest.cpp", - "VSyncReactorTest.cpp", - "VsyncConfigurationTest.cpp", - "VsyncScheduleTest.cpp", - "WindowInfosListenerInvokerTest.cpp", + "*.cpp", ], } diff --git a/services/surfaceflinger/tests/unittests/CommitAndCompositeTest.h b/services/surfaceflinger/tests/unittests/CommitAndCompositeTest.h index d4c801f050..b517ff02ad 100644 --- a/services/surfaceflinger/tests/unittests/CommitAndCompositeTest.h +++ b/services/surfaceflinger/tests/unittests/CommitAndCompositeTest.h @@ -22,8 +22,8 @@ #include "TestableSurfaceFlinger.h" #include "mock/DisplayHardware/MockComposer.h" -#include "mock/DisplayHardware/MockPowerAdvisor.h" #include "mock/MockTimeStats.h" +#include "mock/PowerAdvisor/MockPowerAdvisor.h" #include "mock/system/window/MockNativeWindow.h" namespace android { @@ -33,11 +33,11 @@ struct CommitAndCompositeTest : testing::Test { void SetUp() override { mFlinger.setupMockScheduler({.displayId = DEFAULT_DISPLAY_ID}); mComposer = new Hwc2::mock::Composer(); - mPowerAdvisor = new Hwc2::mock::PowerAdvisor(); + mPowerAdvisor = new adpf::mock::PowerAdvisor(); mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine)); mFlinger.setupTimeStats(std::shared_ptr<TimeStats>(mTimeStats)); mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer)); - mFlinger.setupPowerAdvisor(std::unique_ptr<Hwc2::PowerAdvisor>(mPowerAdvisor)); + mFlinger.setupPowerAdvisor(std::unique_ptr<adpf::PowerAdvisor>(mPowerAdvisor)); constexpr bool kIsPrimary = true; FakeHwcDisplayInjector(DEFAULT_DISPLAY_ID, hal::DisplayType::PHYSICAL, kIsPrimary) @@ -79,7 +79,7 @@ struct CommitAndCompositeTest : testing::Test { sp<compositionengine::mock::DisplaySurface>::make(); sp<mock::NativeWindow> mNativeWindow = sp<mock::NativeWindow>::make(); mock::TimeStats* mTimeStats = new mock::TimeStats(); - Hwc2::mock::PowerAdvisor* mPowerAdvisor = nullptr; + adpf::mock::PowerAdvisor* mPowerAdvisor = nullptr; Hwc2::mock::Composer* mComposer = nullptr; }; diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp index 4f72424bd7..860ad2e013 100644 --- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp +++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp @@ -40,10 +40,10 @@ #include "Layer.h" #include "TestableSurfaceFlinger.h" #include "mock/DisplayHardware/MockComposer.h" -#include "mock/DisplayHardware/MockPowerAdvisor.h" #include "mock/MockEventThread.h" #include "mock/MockTimeStats.h" #include "mock/MockVsyncController.h" +#include "mock/PowerAdvisor/MockPowerAdvisor.h" #include "mock/system/window/MockNativeWindow.h" namespace android { @@ -110,9 +110,9 @@ public: mFlinger.setupTimeStats(std::shared_ptr<TimeStats>(mTimeStats)); mComposer = new Hwc2::mock::Composer(); - mPowerAdvisor = new Hwc2::mock::PowerAdvisor(); + mPowerAdvisor = new adpf::mock::PowerAdvisor(); mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer)); - mFlinger.setupPowerAdvisor(std::unique_ptr<Hwc2::PowerAdvisor>(mPowerAdvisor)); + mFlinger.setupPowerAdvisor(std::unique_ptr<adpf::PowerAdvisor>(mPowerAdvisor)); mFlinger.mutableMaxRenderTargetSize() = 16384; } @@ -158,7 +158,7 @@ public: Hwc2::mock::Composer* mComposer = nullptr; renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine(); mock::TimeStats* mTimeStats = new mock::TimeStats(); - Hwc2::mock::PowerAdvisor* mPowerAdvisor = nullptr; + adpf::mock::PowerAdvisor* mPowerAdvisor = nullptr; sp<Fence> mClientTargetAcquireFence = Fence::NO_FENCE; diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h index db3c0a1d69..fa976c8091 100644 --- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h +++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h @@ -47,10 +47,10 @@ #include "TestableSurfaceFlinger.h" #include "mock/DisplayHardware/MockComposer.h" #include "mock/DisplayHardware/MockDisplayMode.h" -#include "mock/DisplayHardware/MockPowerAdvisor.h" #include "mock/MockEventThread.h" #include "mock/MockNativeWindowSurface.h" #include "mock/MockVsyncController.h" +#include "mock/PowerAdvisor/MockPowerAdvisor.h" #include "mock/system/window/MockNativeWindow.h" namespace android { @@ -118,7 +118,7 @@ public: sp<GraphicBuffer> mBuffer = sp<GraphicBuffer>::make(1u, 1u, PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_SW_READ_OFTEN); - Hwc2::mock::PowerAdvisor mPowerAdvisor; + adpf::mock::PowerAdvisor mPowerAdvisor; FakeDisplayInjector mFakeDisplayInjector{mFlinger, mPowerAdvisor, mNativeWindow}; diff --git a/services/surfaceflinger/tests/unittests/FakeDisplayInjector.h b/services/surfaceflinger/tests/unittests/FakeDisplayInjector.h index 6e4bf2b06e..744c53637a 100644 --- a/services/surfaceflinger/tests/unittests/FakeDisplayInjector.h +++ b/services/surfaceflinger/tests/unittests/FakeDisplayInjector.h @@ -19,14 +19,14 @@ #include <gmock/gmock.h> #include "TestableSurfaceFlinger.h" -#include "mock/DisplayHardware/MockPowerAdvisor.h" +#include "mock/PowerAdvisor/MockPowerAdvisor.h" #include "mock/system/window/MockNativeWindow.h" namespace android { using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector; +using android::adpf::mock::PowerAdvisor; using android::hardware::graphics::composer::hal::HWDisplayId; -using android::Hwc2::mock::PowerAdvisor; struct FakeDisplayInjectorArgs { PhysicalDisplayId displayId = PhysicalDisplayId::fromPort(255u); @@ -36,7 +36,7 @@ struct FakeDisplayInjectorArgs { class FakeDisplayInjector { public: - FakeDisplayInjector(TestableSurfaceFlinger& flinger, Hwc2::mock::PowerAdvisor& powerAdvisor, + FakeDisplayInjector(TestableSurfaceFlinger& flinger, PowerAdvisor& powerAdvisor, sp<mock::NativeWindow> nativeWindow) : mFlinger(flinger), mPowerAdvisor(powerAdvisor), mNativeWindow(nativeWindow) {} @@ -89,7 +89,7 @@ public: } TestableSurfaceFlinger& mFlinger; - Hwc2::mock::PowerAdvisor& mPowerAdvisor; + PowerAdvisor& mPowerAdvisor; sp<mock::NativeWindow> mNativeWindow; }; diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp index a35ae15c03..e6b8a26487 100644 --- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp @@ -1935,4 +1935,54 @@ TEST_F(LayerSnapshotTest, shouldUpdateInputWhenNoInputInfo) { EXPECT_FALSE(getSnapshot(2)->hasInputInfo()); } +// content dirty test +TEST_F(LayerSnapshotTest, contentDirtyWhenParentAlphaChanges) { + setAlpha(1, 0.5); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_TRUE(getSnapshot(1)->contentDirty); + EXPECT_TRUE(getSnapshot(11)->contentDirty); + EXPECT_TRUE(getSnapshot(111)->contentDirty); + + // subsequent updates clear the dirty bit + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_FALSE(getSnapshot(1)->contentDirty); + EXPECT_FALSE(getSnapshot(11)->contentDirty); + EXPECT_FALSE(getSnapshot(111)->contentDirty); +} + +TEST_F(LayerSnapshotTest, contentDirtyWhenAutoRefresh) { + setAutoRefresh(1, true); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_TRUE(getSnapshot(1)->contentDirty); + + // subsequent updates don't clear the dirty bit + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_TRUE(getSnapshot(1)->contentDirty); + + // second update after removing auto refresh will clear content dirty + setAutoRefresh(1, false); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_FALSE(getSnapshot(1)->contentDirty); +} + +TEST_F(LayerSnapshotTest, contentDirtyWhenColorChanges) { + setColor(1, {1, 2, 3}); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_TRUE(getSnapshot(1)->contentDirty); + + // subsequent updates clear the dirty bit + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_FALSE(getSnapshot(1)->contentDirty); +} + +TEST_F(LayerSnapshotTest, contentDirtyWhenParentGeometryChanges) { + setPosition(1, 2, 3); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_TRUE(getSnapshot(1)->contentDirty); + + // subsequent updates clear the dirty bit + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_FALSE(getSnapshot(1)->contentDirty); +} } // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp index 71f9f88ba7..908637ae76 100644 --- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp +++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp @@ -159,9 +159,9 @@ TEST_F(MessageQueueTest, commitTwiceWithCallback) { constexpr VsyncId vsyncId{42}; EXPECT_CALL(mTokenManager, - generateTokenForPredictions(frametimeline::TimelineItem(kStartTime.ns(), - kEndTime.ns(), - kPresentTime.ns()))) + generateTokenForPredictions( + frametimeline::TimelineItem(kStartTime.ns(), kEndTime.ns(), + kPresentTime.ns(), kPresentTime.ns()))) .WillOnce(Return(ftl::to_underlying(vsyncId))); EXPECT_CALL(*mEventQueue.mHandler, dispatchFrame(vsyncId, kPresentTime)).Times(1); EXPECT_NO_FATAL_FAILURE( diff --git a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp index 8375bb9a56..5c25f34ae7 100644 --- a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp +++ b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp @@ -17,7 +17,8 @@ #undef LOG_TAG #define LOG_TAG "PowerAdvisorTest" -#include <DisplayHardware/PowerAdvisor.h> +#include "PowerAdvisor/PowerAdvisor.h" + #include <android_os.h> #include <binder/Status.h> #include <com_android_graphics_surfaceflinger_flags.h> @@ -29,18 +30,17 @@ #include <ui/DisplayId.h> #include <chrono> #include <future> -#include "TestableSurfaceFlinger.h" -#include "mock/DisplayHardware/MockPowerHalController.h" -#include "mock/DisplayHardware/MockPowerHintSessionWrapper.h" +#include "mock/PowerAdvisor/MockPowerHalController.h" +#include "mock/PowerAdvisor/MockPowerHintSessionWrapper.h" using namespace android; -using namespace android::Hwc2::mock; +using namespace android::adpf::mock; using namespace android::hardware::power; using namespace std::chrono_literals; using namespace testing; using namespace android::power; -namespace android::Hwc2::impl { +namespace android::adpf::impl { class PowerAdvisorTest : public testing::Test { public: @@ -73,7 +73,6 @@ public: void testGpuScenario(GpuTestConfig& config, WorkDuration& ret); protected: - TestableSurfaceFlinger mFlinger; std::unique_ptr<PowerAdvisor> mPowerAdvisor; MockPowerHalController* mMockPowerHalController; std::shared_ptr<MockPowerHintSessionWrapper> mMockPowerHintSession; @@ -98,7 +97,7 @@ int64_t PowerAdvisorTest::toNanos(Duration d) { } void PowerAdvisorTest::SetUp() { - mPowerAdvisor = std::make_unique<impl::PowerAdvisor>(*mFlinger.flinger()); + mPowerAdvisor = std::make_unique<impl::PowerAdvisor>([]() {}, 80ms); mPowerAdvisor->mPowerHal = std::make_unique<NiceMock<MockPowerHalController>>(); mMockPowerHalController = reinterpret_cast<MockPowerHalController*>(mPowerAdvisor->mPowerHal.get()); @@ -844,4 +843,4 @@ TEST_F(PowerAdvisorTest, fmq_sendHint_queueFull) { } } // namespace -} // namespace android::Hwc2::impl +} // namespace android::adpf::impl diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp index 29e1c21878..b5be8dba96 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp @@ -2240,6 +2240,46 @@ TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_Touch EXPECT_FALSE(actualRankedFrameRates.consideredSignals.touch); } +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_touchBoost_twoUids_arr) { + if (GetParam() != Config::FrameRateOverride::Enabled) { + return; + } + + SET_FLAG_FOR_TEST(flags::vrr_config, true); + // Device with VRR config mode + auto selector = createSelector(kVrrMode_120, kModeId120); + + std::vector<LayerRequirement> layers = {{.ownerUid = 1234, .weight = 1.f}, + {.ownerUid = 5678, .weight = 1.f}}; + auto& lr1 = layers[0]; + auto& lr2 = layers[1]; + + lr1.vote = LayerVoteType::ExplicitCategory; + lr1.frameRateCategory = FrameRateCategory::Normal; + lr1.name = "ExplicitCategory Normal"; + lr2.vote = LayerVoteType::ExplicitDefault; + lr2.desiredRefreshRate = 30_Hz; + lr2.name = "30Hz ExplicitDefault"; + auto actualRankedFrameRates = selector.getRankedFrameRates(layers, {.touch = true}); + // No global touch boost, for example a game that uses setFrameRate(30, default compatibility). + // However see 60 due to Normal vote. + EXPECT_FRAME_RATE_MODE(kVrrMode120TE240, 60_Hz, + actualRankedFrameRates.ranking.front().frameRateMode); + EXPECT_FALSE(actualRankedFrameRates.consideredSignals.touch); + + lr1.vote = LayerVoteType::ExplicitCategory; + lr1.frameRateCategory = FrameRateCategory::HighHint; + lr1.name = "ExplicitCategory HighHint"; + lr2.vote = LayerVoteType::ExplicitDefault; + lr2.desiredRefreshRate = 30_Hz; + lr2.name = "30Hz ExplicitDefault"; + // Gets touch boost because the touched (HighHint) app is different from the 30 Default app. + actualRankedFrameRates = selector.getRankedFrameRates(layers, {.touch = true}); + EXPECT_FRAME_RATE_MODE(kVrrMode120TE240, 120_Hz, + actualRankedFrameRates.ranking.front().frameRateMode); + EXPECT_TRUE(actualRankedFrameRates.consideredSignals.touch); +} + TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_idleTimer_60_120_nonVrr) { SET_FLAG_FOR_TEST(flags::vrr_config, false); @@ -3825,6 +3865,51 @@ TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_twoUids) { EXPECT_TRUE(frameRateOverrides.empty()); } +TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_twoUids_arr) { + if (GetParam() != Config::FrameRateOverride::Enabled) { + return; + } + + SET_FLAG_FOR_TEST(flags::vrr_config, true); + // Device with VRR config mode + auto selector = createSelector(kVrrMode_120, kModeId120); + + std::vector<LayerRequirement> layers = {{.ownerUid = 1234, .weight = 1.f}, + {.ownerUid = 5678, .weight = 1.f}}; + auto& lr1 = layers[0]; + auto& lr2 = layers[1]; + + lr1.vote = LayerVoteType::ExplicitCategory; + lr1.frameRateCategory = FrameRateCategory::Normal; + lr1.name = "ExplicitCategory Normal"; + lr2.vote = LayerVoteType::ExplicitDefault; + lr2.desiredRefreshRate = 30_Hz; + lr2.name = "30Hz ExplicitDefault"; + // No global touch boost, for example a game that uses setFrameRate(30, default compatibility). + // The `displayFrameRate` is 60. + // However 30 Default app still gets frame rate override. + auto frameRateOverrides = selector.getFrameRateOverrides(layers, 60_Hz, {}); + EXPECT_EQ(2u, frameRateOverrides.size()); + ASSERT_EQ(1u, frameRateOverrides.count(1234)); + EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); + ASSERT_EQ(1u, frameRateOverrides.count(5678)); + EXPECT_EQ(30_Hz, frameRateOverrides.at(5678)); + + lr1.vote = LayerVoteType::ExplicitCategory; + lr1.frameRateCategory = FrameRateCategory::HighHint; + lr1.name = "ExplicitCategory HighHint"; + lr2.vote = LayerVoteType::ExplicitDefault; + lr2.desiredRefreshRate = 30_Hz; + lr2.name = "30Hz ExplicitDefault"; + // Gets touch boost because the touched (HighHint) app is different from the 30 Default app. + // The `displayFrameRate` is 120 (late touch boost). + // However 30 Default app still gets frame rate override. + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); + EXPECT_EQ(1u, frameRateOverrides.size()); + ASSERT_EQ(1u, frameRateOverrides.count(5678)); + EXPECT_EQ(30_Hz, frameRateOverrides.at(5678)); +} + TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_withFrameRateCategory) { if (GetParam() == Config::FrameRateOverride::Disabled) { return; diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp index ac09cbcea6..1fc874dad8 100644 --- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp +++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp @@ -222,7 +222,7 @@ TEST_F(SchedulerTest, emitModeChangeEvent) { const auto selectorPtr = std::make_shared<RefreshRateSelector>(kDisplay1Modes, kDisplay1Mode120->getId()); mScheduler->registerDisplay(kDisplayId1, selectorPtr); - mScheduler->onDisplayModeChanged(kDisplayId1, kDisplay1Mode120_120); + mScheduler->onDisplayModeChanged(kDisplayId1, kDisplay1Mode120_120, true); mScheduler->setContentRequirements({kLayer}); @@ -250,7 +250,7 @@ TEST_F(SchedulerTest, emitModeChangeEvent) { EXPECT_CALL(*mEventThread, onModeChanged(kDisplay1Mode120_120)).Times(1); mScheduler->touchTimerCallback(TimerState::Reset); - mScheduler->onDisplayModeChanged(kDisplayId1, kDisplay1Mode120_120); + mScheduler->onDisplayModeChanged(kDisplayId1, kDisplay1Mode120_120, true); } TEST_F(SchedulerTest, calculateMaxAcquiredBufferCount) { diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index 4dec5f6b6a..7f0b7a6585 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -44,22 +44,22 @@ #include "NativeWindowSurface.h" #include "RenderArea.h" #include "Scheduler/RefreshRateSelector.h" +#include "Scheduler/VSyncTracker.h" +#include "Scheduler/VsyncController.h" #include "SurfaceFlinger.h" #include "TestableScheduler.h" #include "android/gui/ISurfaceComposerClient.h" + #include "mock/DisplayHardware/MockComposer.h" #include "mock/DisplayHardware/MockDisplayMode.h" -#include "mock/DisplayHardware/MockPowerAdvisor.h" #include "mock/MockEventThread.h" #include "mock/MockFrameTimeline.h" #include "mock/MockFrameTracer.h" #include "mock/MockSchedulerCallback.h" -#include "mock/system/window/MockNativeWindow.h" - -#include "Scheduler/VSyncTracker.h" -#include "Scheduler/VsyncController.h" #include "mock/MockVSyncTracker.h" #include "mock/MockVsyncController.h" +#include "mock/PowerAdvisor/MockPowerAdvisor.h" +#include "mock/system/window/MockNativeWindow.h" namespace android { @@ -190,7 +190,7 @@ public: &mFlinger->mCompositionEngine->getHwComposer()); } - void setupPowerAdvisor(std::unique_ptr<Hwc2::PowerAdvisor> powerAdvisor) { + void setupPowerAdvisor(std::unique_ptr<adpf::PowerAdvisor> powerAdvisor) { mFlinger->mPowerAdvisor = std::move(powerAdvisor); } @@ -472,12 +472,10 @@ public: ScreenCaptureResults captureResults; auto displayState = std::optional{display->getCompositionDisplay()->getState()}; auto layers = getLayerSnapshotsFn(); - auto layerFEs = mFlinger->extractLayerFEs(layers); return mFlinger->renderScreenImpl(renderArea.get(), buffer, regionSampling, false /* grayscale */, false /* isProtected */, - false /* attachGainmap */, captureResults, displayState, - layers, layerFEs); + captureResults, displayState, layers); } auto getLayerSnapshotsForScreenshotsFn(ui::LayerStack layerStack, uint32_t uid) { @@ -1162,7 +1160,7 @@ private: scheduler::mock::NoOpSchedulerCallback mNoOpSchedulerCallback; std::unique_ptr<frametimeline::impl::TokenManager> mTokenManager; scheduler::TestableScheduler* mScheduler = nullptr; - Hwc2::mock::PowerAdvisor mPowerAdvisor; + adpf::mock::PowerAdvisor mPowerAdvisor; }; } // namespace android diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h index 3e6a768db8..88052db7e2 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h @@ -186,6 +186,9 @@ public: std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*)); MOCK_METHOD(Error, setLayerLuts, (Display, Layer, aidl::android::hardware::graphics::composer3::Luts&)); + MOCK_METHOD(Error, getMaxLayerPictureProfiles, (Display, int32_t*)); + MOCK_METHOD(Error, setDisplayPictureProfileId, (Display, PictureProfileId id)); + MOCK_METHOD(Error, setLayerPictureProfileId, (Display, Layer, PictureProfileId id)); }; } // namespace Hwc2::mock diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h index 121104d61c..fa74492ac5 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h @@ -52,7 +52,7 @@ public: (override)); MOCK_METHOD(hal::Error, getName, (std::string *), (const, override)); MOCK_METHOD(hal::Error, getRequests, - (hal::DisplayRequest *, (std::unordered_map<Layer *, hal::LayerRequest> *)), + (hal::DisplayRequest*, (std::unordered_map<Layer*, hal::LayerRequest>*)), (override)); MOCK_METHOD((ftl::Expected<ui::DisplayConnectionType, hal::Error>), getConnectionType, (), (const, override)); @@ -111,7 +111,9 @@ public: (aidl::android::hardware::graphics::composer3::OverlayProperties *), (const override)); MOCK_METHOD(hal::Error, getRequestedLuts, - ((HWC2::Display::LayerLuts*), (HWC2::Display::LutFileDescriptorMapper&)), + (HWC2::Display::LayerLuts*, HWC2::Display::LutFileDescriptorMapper&), (override)); + MOCK_METHOD(hal::Error, getMaxLayerPictureProfiles, (int32_t*), (override)); + MOCK_METHOD(hal::Error, setPictureProfileHandle, (const android::PictureProfileHandle&), (override)); }; @@ -151,6 +153,8 @@ public: MOCK_METHOD(hal::Error, setBlockingRegion, (const android::Region &), (override)); MOCK_METHOD(hal::Error, setLuts, (aidl::android::hardware::graphics::composer3::Luts&), (override)); + MOCK_METHOD(hal::Error, setPictureProfileHandle, (const android::PictureProfileHandle&), + (override)); }; } // namespace android::HWC2::mock diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h index fa7128c557..88f83d2e07 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h @@ -148,6 +148,9 @@ public: MOCK_METHOD(status_t, notifyExpectedPresent, (PhysicalDisplayId, TimePoint, Fps)); MOCK_METHOD(HWC2::Display::LutFileDescriptorMapper&, getLutFileDescriptorMapper, (), (override)); + MOCK_METHOD(int32_t, getMaxLayerPictureProfiles, (PhysicalDisplayId)); + MOCK_METHOD(status_t, setDisplayPictureProfileHandle, + (PhysicalDisplayId, const PictureProfileHandle&)); }; } // namespace android::mock diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h deleted file mode 100644 index ed1405b058..0000000000 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "binder/Status.h" - -// FMQ library in IPower does questionable conversions -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wconversion" -#include <aidl/android/hardware/power/IPower.h> -#pragma clang diagnostic pop - -#include <gmock/gmock.h> - -using aidl::android::hardware::power::Boost; -using aidl::android::hardware::power::ChannelConfig; -using aidl::android::hardware::power::IPower; -using aidl::android::hardware::power::IPowerHintSession; -using aidl::android::hardware::power::SessionConfig; -using aidl::android::hardware::power::SessionTag; - -using aidl::android::hardware::power::Mode; -using android::binder::Status; - -namespace android::Hwc2::mock { - -class MockIPower : public IPower { -public: - MockIPower(); - - 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, std::shared_ptr<IPowerHintSession>* session), - (override)); - MOCK_METHOD(ndk::ScopedAStatus, getHintSessionPreferredRate, (int64_t * rate), (override)); - MOCK_METHOD(ndk::ScopedAStatus, createHintSessionWithConfig, - (int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, - int64_t durationNanos, SessionTag tag, SessionConfig* config, - std::shared_ptr<IPowerHintSession>* _aidl_return), - (override)); - MOCK_METHOD(ndk::ScopedAStatus, getSessionChannel, - (int32_t tgid, int32_t uid, ChannelConfig* _aidl_return), (override)); - MOCK_METHOD(ndk::ScopedAStatus, closeSessionChannel, (int32_t tgid, int32_t uid), (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/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h index 7398cbebe3..82500fef10 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h +++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h @@ -30,6 +30,7 @@ public: MOCK_METHOD(sp<EventThreadConnection>, createEventConnection, (EventRegistrationFlags), (const, override)); MOCK_METHOD(void, enableSyntheticVsync, (bool), (override)); + MOCK_METHOD(void, omitVsyncDispatching, (bool), (override)); MOCK_METHOD(void, onHotplugReceived, (PhysicalDisplayId, bool), (override)); MOCK_METHOD(void, onHotplugConnectionError, (int32_t), (override)); MOCK_METHOD(void, onModeChanged, (const scheduler::FrameRateMode&), (override)); diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.cpp b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerAdvisor.cpp index 1ba38a822a..f4c1e52537 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.cpp +++ b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerAdvisor.cpp @@ -16,10 +16,10 @@ #include "MockPowerAdvisor.h" -namespace android::Hwc2::mock { +namespace android::adpf::mock { // Explicit default instantiation is recommended. PowerAdvisor::PowerAdvisor() = default; PowerAdvisor::~PowerAdvisor() = default; -} // namespace android::Hwc2::mock +} // namespace android::adpf::mock diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerAdvisor.h index 4efdfe877b..5c4512a2df 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h +++ b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerAdvisor.h @@ -18,11 +18,11 @@ #include <gmock/gmock.h> -#include "DisplayHardware/PowerAdvisor.h" +#include "PowerAdvisor/PowerAdvisor.h" -namespace android::Hwc2::mock { +namespace android::adpf::mock { -class PowerAdvisor : public android::Hwc2::PowerAdvisor { +class PowerAdvisor : public android::adpf::PowerAdvisor { public: PowerAdvisor(); ~PowerAdvisor() override; @@ -65,4 +65,4 @@ public: MOCK_METHOD(void, setTotalFrameTargetWorkDuration, (Duration targetDuration), (override)); }; -} // namespace android::Hwc2::mock +} // namespace android::adpf::mock diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.cpp b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHalController.cpp index 3ec5c2d09b..3b8de55607 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.cpp +++ b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHalController.cpp @@ -16,9 +16,9 @@ #include "MockPowerHalController.h" -namespace android::Hwc2::mock { +namespace android::adpf::mock { MockPowerHalController::MockPowerHalController() = default; MockPowerHalController::~MockPowerHalController() = default; -} // namespace android::Hwc2::mock +} // namespace android::adpf::mock diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHalController.h index af1862d1cf..fba4cd82fa 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h +++ b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHalController.h @@ -19,10 +19,7 @@ #include <gmock/gmock.h> #include <scheduler/Time.h> -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wconversion" #include <powermanager/PowerHalController.h> -#pragma clang diagnostic pop namespace android { namespace hardware { @@ -32,7 +29,7 @@ class IPower; } // namespace hardware } // namespace android -namespace android::Hwc2::mock { +namespace android::adpf::mock { using android::power::HalResult; @@ -59,4 +56,4 @@ public: MOCK_METHOD(HalResult<void>, closeSessionChannel, (int tgid, int uid), (override)); }; -} // namespace android::Hwc2::mock
\ No newline at end of file +} // namespace android::adpf::mock
\ No newline at end of file diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHintSessionWrapper.cpp b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHintSessionWrapper.cpp index d383283d8e..cb39783b77 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHintSessionWrapper.cpp +++ b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHintSessionWrapper.cpp @@ -14,12 +14,12 @@ * limitations under the License. */ -#include "mock/DisplayHardware/MockPowerHintSessionWrapper.h" +#include "MockPowerHintSessionWrapper.h" -namespace android::Hwc2::mock { +namespace android::adpf::mock { // Explicit default instantiation is recommended. MockPowerHintSessionWrapper::MockPowerHintSessionWrapper() : power::PowerHintSessionWrapper(nullptr) {} -} // namespace android::Hwc2::mock +} // namespace android::adpf::mock diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHintSessionWrapper.h b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHintSessionWrapper.h index bc6950cccb..4518de8c16 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHintSessionWrapper.h +++ b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHintSessionWrapper.h @@ -16,13 +16,7 @@ #pragma once -#include "binder/Status.h" - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wconversion" -#include <aidl/android/hardware/power/IPower.h> #include <powermanager/PowerHintSessionWrapper.h> -#pragma clang diagnostic pop #include <gmock/gmock.h> @@ -34,7 +28,7 @@ using android::binder::Status; using namespace aidl::android::hardware::power; -namespace android::Hwc2::mock { +namespace android::adpf::mock { class MockPowerHintSessionWrapper : public power::PowerHintSessionWrapper { public: @@ -52,4 +46,4 @@ public: MOCK_METHOD(power::HalResult<SessionConfig>, getSessionConfig, (), (override)); }; -} // namespace android::Hwc2::mock +} // namespace android::adpf::mock diff --git a/services/vibratorservice/VibratorManagerHalController.cpp b/services/vibratorservice/VibratorManagerHalController.cpp index ba35d15bf2..494f88f10e 100644 --- a/services/vibratorservice/VibratorManagerHalController.cpp +++ b/services/vibratorservice/VibratorManagerHalController.cpp @@ -150,6 +150,23 @@ HalResult<void> ManagerHalController::cancelSynced() { return apply(cancelSyncedFn, "cancelSynced"); } +HalResult<std::shared_ptr<Aidl::IVibrationSession>> ManagerHalController::startSession( + const std::vector<int32_t>& ids, const Aidl::VibrationSessionConfig& config, + const std::function<void()>& completionCallback) { + hal_fn<std::shared_ptr<Aidl::IVibrationSession>> startSessionFn = + [&](std::shared_ptr<ManagerHalWrapper> hal) { + return hal->startSession(ids, config, completionCallback); + }; + return apply(startSessionFn, "startSession"); +} + +HalResult<void> ManagerHalController::clearSessions() { + hal_fn<void> clearSessionsFn = [](std::shared_ptr<ManagerHalWrapper> hal) { + return hal->clearSessions(); + }; + return apply(clearSessionsFn, "clearSessions"); +} + }; // namespace vibrator }; // namespace android diff --git a/services/vibratorservice/VibratorManagerHalWrapper.cpp b/services/vibratorservice/VibratorManagerHalWrapper.cpp index 93ec781b21..3db8ff8699 100644 --- a/services/vibratorservice/VibratorManagerHalWrapper.cpp +++ b/services/vibratorservice/VibratorManagerHalWrapper.cpp @@ -29,6 +29,30 @@ namespace vibrator { constexpr int32_t SINGLE_VIBRATOR_ID = 0; const constexpr char* MISSING_VIBRATOR_MESSAGE_PREFIX = "No vibrator with id="; +HalResult<void> ManagerHalWrapper::prepareSynced(const std::vector<int32_t>&) { + return HalResult<void>::unsupported(); +} + +HalResult<void> ManagerHalWrapper::triggerSynced(const std::function<void()>&) { + return HalResult<void>::unsupported(); +} + +HalResult<void> ManagerHalWrapper::cancelSynced() { + return HalResult<void>::unsupported(); +} + +HalResult<std::shared_ptr<Aidl::IVibrationSession>> ManagerHalWrapper::startSession( + const std::vector<int32_t>&, const Aidl::VibrationSessionConfig&, + const std::function<void()>&) { + return HalResult<std::shared_ptr<Aidl::IVibrationSession>>::unsupported(); +} + +HalResult<void> ManagerHalWrapper::clearSessions() { + return HalResult<void>::unsupported(); +} + +// ------------------------------------------------------------------------------------------------- + HalResult<void> LegacyManagerHalWrapper::ping() { auto pingFn = [](HalWrapper* hal) { return hal->ping(); }; return mController->doWithRetry<void>(pingFn, "ping"); @@ -59,18 +83,6 @@ HalResult<std::shared_ptr<HalController>> LegacyManagerHalWrapper::getVibrator(i (MISSING_VIBRATOR_MESSAGE_PREFIX + std::to_string(id)).c_str()); } -HalResult<void> LegacyManagerHalWrapper::prepareSynced(const std::vector<int32_t>&) { - return HalResult<void>::unsupported(); -} - -HalResult<void> LegacyManagerHalWrapper::triggerSynced(const std::function<void()>&) { - return HalResult<void>::unsupported(); -} - -HalResult<void> LegacyManagerHalWrapper::cancelSynced() { - return HalResult<void>::unsupported(); -} - // ------------------------------------------------------------------------------------------------- std::shared_ptr<HalWrapper> AidlManagerHalWrapper::connectToVibrator( @@ -186,6 +198,17 @@ HalResult<void> AidlManagerHalWrapper::triggerSynced( return HalResultFactory::fromStatus(getHal()->triggerSynced(cb)); } +HalResult<std::shared_ptr<Aidl::IVibrationSession>> AidlManagerHalWrapper::startSession( + const std::vector<int32_t>& ids, const Aidl::VibrationSessionConfig& config, + const std::function<void()>& completionCallback) { + auto cb = ndk::SharedRefBase::make<HalCallbackWrapper>(completionCallback); + std::shared_ptr<Aidl::IVibrationSession> session; + auto status = getHal()->startSession(ids, config, cb, &session); + return HalResultFactory::fromStatus<std::shared_ptr<Aidl::IVibrationSession>>(std::move(status), + std::move( + session)); +} + HalResult<void> AidlManagerHalWrapper::cancelSynced() { auto ret = HalResultFactory::fromStatus(getHal()->cancelSynced()); if (ret.isOk()) { @@ -200,6 +223,10 @@ HalResult<void> AidlManagerHalWrapper::cancelSynced() { return ret; } +HalResult<void> AidlManagerHalWrapper::clearSessions() { + return HalResultFactory::fromStatus(getHal()->clearSessions()); +} + std::shared_ptr<Aidl::IVibratorManager> AidlManagerHalWrapper::getHal() { std::lock_guard<std::mutex> lock(mHandleMutex); return mHandle; diff --git a/services/vibratorservice/include/vibratorservice/VibratorManagerHalController.h b/services/vibratorservice/include/vibratorservice/VibratorManagerHalController.h index 70c846b4ae..72d4752ba4 100644 --- a/services/vibratorservice/include/vibratorservice/VibratorManagerHalController.h +++ b/services/vibratorservice/include/vibratorservice/VibratorManagerHalController.h @@ -62,6 +62,10 @@ public: HalResult<void> prepareSynced(const std::vector<int32_t>& ids) override final; HalResult<void> triggerSynced(const std::function<void()>& completionCallback) override final; HalResult<void> cancelSynced() override final; + HalResult<std::shared_ptr<IVibrationSession>> startSession( + const std::vector<int32_t>& ids, const VibrationSessionConfig& config, + const std::function<void()>& completionCallback) override final; + HalResult<void> clearSessions() override final; private: Connector mConnector; diff --git a/services/vibratorservice/include/vibratorservice/VibratorManagerHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorManagerHalWrapper.h index 9e3f221fa7..8d4ca0eda3 100644 --- a/services/vibratorservice/include/vibratorservice/VibratorManagerHalWrapper.h +++ b/services/vibratorservice/include/vibratorservice/VibratorManagerHalWrapper.h @@ -38,7 +38,8 @@ enum class ManagerCapabilities : int32_t { aidl::android::hardware::vibrator::IVibratorManager::CAP_MIXED_TRIGGER_PERFORM, MIXED_TRIGGER_COMPOSE = aidl::android::hardware::vibrator::IVibratorManager::CAP_MIXED_TRIGGER_COMPOSE, - TRIGGER_CALLBACK = aidl::android::hardware::vibrator::IVibratorManager::CAP_TRIGGER_CALLBACK + TRIGGER_CALLBACK = aidl::android::hardware::vibrator::IVibratorManager::CAP_TRIGGER_CALLBACK, + START_SESSIONS = aidl::android::hardware::vibrator::IVibratorManager::CAP_START_SESSIONS }; inline ManagerCapabilities operator|(ManagerCapabilities lhs, ManagerCapabilities rhs) { @@ -64,6 +65,9 @@ inline ManagerCapabilities& operator&=(ManagerCapabilities& lhs, ManagerCapabili // Wrapper for VibratorManager HAL handlers. class ManagerHalWrapper { public: + using IVibrationSession = aidl::android::hardware::vibrator::IVibrationSession; + using VibrationSessionConfig = aidl::android::hardware::vibrator::VibrationSessionConfig; + ManagerHalWrapper() = default; virtual ~ManagerHalWrapper() = default; @@ -78,9 +82,13 @@ public: virtual HalResult<std::vector<int32_t>> getVibratorIds() = 0; virtual HalResult<std::shared_ptr<HalController>> getVibrator(int32_t id) = 0; - virtual HalResult<void> prepareSynced(const std::vector<int32_t>& ids) = 0; - virtual HalResult<void> triggerSynced(const std::function<void()>& completionCallback) = 0; - virtual HalResult<void> cancelSynced() = 0; + virtual HalResult<void> prepareSynced(const std::vector<int32_t>& ids); + virtual HalResult<void> triggerSynced(const std::function<void()>& completionCallback); + virtual HalResult<void> cancelSynced(); + virtual HalResult<std::shared_ptr<IVibrationSession>> startSession( + const std::vector<int32_t>& ids, const VibrationSessionConfig& config, + const std::function<void()>& completionCallback); + virtual HalResult<void> clearSessions(); }; // Wrapper for the VibratorManager over single Vibrator HAL. @@ -98,10 +106,6 @@ public: HalResult<std::vector<int32_t>> getVibratorIds() override final; HalResult<std::shared_ptr<HalController>> getVibrator(int32_t id) override final; - HalResult<void> prepareSynced(const std::vector<int32_t>& ids) override final; - HalResult<void> triggerSynced(const std::function<void()>& completionCallback) override final; - HalResult<void> cancelSynced() override final; - private: const std::shared_ptr<HalController> mController; }; @@ -126,6 +130,10 @@ public: HalResult<void> prepareSynced(const std::vector<int32_t>& ids) override final; HalResult<void> triggerSynced(const std::function<void()>& completionCallback) override final; HalResult<void> cancelSynced() override final; + HalResult<std::shared_ptr<IVibrationSession>> startSession( + const std::vector<int32_t>& ids, const VibrationSessionConfig& config, + const std::function<void()>& completionCallback) override final; + HalResult<void> clearSessions() override final; private: std::mutex mHandleMutex; diff --git a/services/vibratorservice/test/VibratorManagerHalControllerTest.cpp b/services/vibratorservice/test/VibratorManagerHalControllerTest.cpp index c7214e054e..b201670901 100644 --- a/services/vibratorservice/test/VibratorManagerHalControllerTest.cpp +++ b/services/vibratorservice/test/VibratorManagerHalControllerTest.cpp @@ -27,6 +27,8 @@ #include "test_mocks.h" #include "test_utils.h" +using aidl::android::hardware::vibrator::IVibrationSession; +using aidl::android::hardware::vibrator::VibrationSessionConfig; using android::vibrator::HalController; using namespace android; @@ -34,6 +36,7 @@ using namespace testing; static constexpr int MAX_ATTEMPTS = 2; static const std::vector<int32_t> VIBRATOR_IDS = {1, 2}; +static const VibrationSessionConfig SESSION_CONFIG; static constexpr int VIBRATOR_ID = 1; // ------------------------------------------------------------------------------------------------- @@ -52,6 +55,11 @@ public: MOCK_METHOD(vibrator::HalResult<void>, triggerSynced, (const std::function<void()>& completionCallback), (override)); MOCK_METHOD(vibrator::HalResult<void>, cancelSynced, (), (override)); + MOCK_METHOD(vibrator::HalResult<std::shared_ptr<IVibrationSession>>, startSession, + (const std::vector<int32_t>& ids, const VibrationSessionConfig& s, + const std::function<void()>& completionCallback), + (override)); + MOCK_METHOD(vibrator::HalResult<void>, clearSessions, (), (override)); }; // ------------------------------------------------------------------------------------------------- @@ -79,7 +87,8 @@ protected: void setHalExpectations(int32_t cardinality, vibrator::HalResult<void> voidResult, vibrator::HalResult<vibrator::ManagerCapabilities> capabilitiesResult, vibrator::HalResult<std::vector<int32_t>> idsResult, - vibrator::HalResult<std::shared_ptr<HalController>> vibratorResult) { + vibrator::HalResult<std::shared_ptr<HalController>> vibratorResult, + vibrator::HalResult<std::shared_ptr<IVibrationSession>> sessionResult) { EXPECT_CALL(*mMockHal.get(), ping()) .Times(Exactly(cardinality)) .WillRepeatedly(Return(voidResult)); @@ -101,10 +110,16 @@ protected: EXPECT_CALL(*mMockHal.get(), cancelSynced()) .Times(Exactly(cardinality)) .WillRepeatedly(Return(voidResult)); + EXPECT_CALL(*mMockHal.get(), startSession(_, _, _)) + .Times(Exactly(cardinality)) + .WillRepeatedly(Return(sessionResult)); + EXPECT_CALL(*mMockHal.get(), clearSessions()) + .Times(Exactly(cardinality)) + .WillRepeatedly(Return(voidResult)); if (cardinality > 1) { // One reconnection for each retry. - EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(7 * (cardinality - 1))); + EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(9 * (cardinality - 1))); } else { EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(0)); } @@ -127,7 +142,8 @@ TEST_F(VibratorManagerHalControllerTest, TestApiCallsAreForwardedToHal) { vibrator::HalResult<vibrator::ManagerCapabilities>::ok( vibrator::ManagerCapabilities::SYNC), vibrator::HalResult<std::vector<int32_t>>::ok(VIBRATOR_IDS), - vibrator::HalResult<std::shared_ptr<HalController>>::ok(nullptr)); + vibrator::HalResult<std::shared_ptr<HalController>>::ok(nullptr), + vibrator::HalResult<std::shared_ptr<IVibrationSession>>::ok(nullptr)); ASSERT_TRUE(mController->ping().isOk()); @@ -146,6 +162,8 @@ TEST_F(VibratorManagerHalControllerTest, TestApiCallsAreForwardedToHal) { ASSERT_TRUE(mController->prepareSynced(VIBRATOR_IDS).isOk()); ASSERT_TRUE(mController->triggerSynced([]() {}).isOk()); ASSERT_TRUE(mController->cancelSynced().isOk()); + ASSERT_TRUE(mController->startSession(VIBRATOR_IDS, SESSION_CONFIG, []() {}).isOk()); + ASSERT_TRUE(mController->clearSessions().isOk()); ASSERT_EQ(1, mConnectCounter); } @@ -154,7 +172,8 @@ TEST_F(VibratorManagerHalControllerTest, TestUnsupportedApiResultDoesNotResetHal setHalExpectations(/* cardinality= */ 1, vibrator::HalResult<void>::unsupported(), vibrator::HalResult<vibrator::ManagerCapabilities>::unsupported(), vibrator::HalResult<std::vector<int32_t>>::unsupported(), - vibrator::HalResult<std::shared_ptr<HalController>>::unsupported()); + vibrator::HalResult<std::shared_ptr<HalController>>::unsupported(), + vibrator::HalResult<std::shared_ptr<IVibrationSession>>::unsupported()); ASSERT_TRUE(mController->ping().isUnsupported()); ASSERT_TRUE(mController->getCapabilities().isUnsupported()); @@ -163,6 +182,8 @@ TEST_F(VibratorManagerHalControllerTest, TestUnsupportedApiResultDoesNotResetHal ASSERT_TRUE(mController->prepareSynced(VIBRATOR_IDS).isUnsupported()); ASSERT_TRUE(mController->triggerSynced([]() {}).isUnsupported()); ASSERT_TRUE(mController->cancelSynced().isUnsupported()); + ASSERT_TRUE(mController->startSession(VIBRATOR_IDS, SESSION_CONFIG, []() {}).isUnsupported()); + ASSERT_TRUE(mController->clearSessions().isUnsupported()); ASSERT_EQ(1, mConnectCounter); } @@ -171,7 +192,8 @@ TEST_F(VibratorManagerHalControllerTest, TestOperationFailedApiResultDoesNotRese setHalExpectations(/* cardinality= */ 1, vibrator::HalResult<void>::failed("msg"), vibrator::HalResult<vibrator::ManagerCapabilities>::failed("msg"), vibrator::HalResult<std::vector<int32_t>>::failed("msg"), - vibrator::HalResult<std::shared_ptr<HalController>>::failed("msg")); + vibrator::HalResult<std::shared_ptr<HalController>>::failed("msg"), + vibrator::HalResult<std::shared_ptr<IVibrationSession>>::failed("msg")); ASSERT_TRUE(mController->ping().isFailed()); ASSERT_TRUE(mController->getCapabilities().isFailed()); @@ -180,6 +202,8 @@ TEST_F(VibratorManagerHalControllerTest, TestOperationFailedApiResultDoesNotRese ASSERT_TRUE(mController->prepareSynced(VIBRATOR_IDS).isFailed()); ASSERT_TRUE(mController->triggerSynced([]() {}).isFailed()); ASSERT_TRUE(mController->cancelSynced().isFailed()); + ASSERT_TRUE(mController->startSession(VIBRATOR_IDS, SESSION_CONFIG, []() {}).isFailed()); + ASSERT_TRUE(mController->clearSessions().isFailed()); ASSERT_EQ(1, mConnectCounter); } @@ -188,7 +212,9 @@ TEST_F(VibratorManagerHalControllerTest, TestTransactionFailedApiResultResetsHal setHalExpectations(MAX_ATTEMPTS, vibrator::HalResult<void>::transactionFailed("m"), vibrator::HalResult<vibrator::ManagerCapabilities>::transactionFailed("m"), vibrator::HalResult<std::vector<int32_t>>::transactionFailed("m"), - vibrator::HalResult<std::shared_ptr<HalController>>::transactionFailed("m")); + vibrator::HalResult<std::shared_ptr<HalController>>::transactionFailed("m"), + vibrator::HalResult<std::shared_ptr<IVibrationSession>>::transactionFailed( + "m")); ASSERT_TRUE(mController->ping().isFailed()); ASSERT_TRUE(mController->getCapabilities().isFailed()); @@ -197,6 +223,8 @@ TEST_F(VibratorManagerHalControllerTest, TestTransactionFailedApiResultResetsHal ASSERT_TRUE(mController->prepareSynced(VIBRATOR_IDS).isFailed()); ASSERT_TRUE(mController->triggerSynced([]() {}).isFailed()); ASSERT_TRUE(mController->cancelSynced().isFailed()); + ASSERT_TRUE(mController->startSession(VIBRATOR_IDS, SESSION_CONFIG, []() {}).isFailed()); + ASSERT_TRUE(mController->clearSessions().isFailed()); ASSERT_EQ(1, mConnectCounter); } diff --git a/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp b/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp index ca13c0b70e..a2f002dc7b 100644 --- a/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp +++ b/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp @@ -69,12 +69,25 @@ public: MOCK_METHOD(bool, isRemote, (), (override)); }; +class MockIVibrationSession : public IVibrationSession { +public: + MockIVibrationSession() = default; + + MOCK_METHOD(ndk::ScopedAStatus, close, (), (override)); + MOCK_METHOD(ndk::ScopedAStatus, abort, (), (override)); + MOCK_METHOD(ndk::ScopedAStatus, getInterfaceVersion, (int32_t*), (override)); + MOCK_METHOD(ndk::ScopedAStatus, getInterfaceHash, (std::string*), (override)); + MOCK_METHOD(ndk::SpAIBinder, asBinder, (), (override)); + MOCK_METHOD(bool, isRemote, (), (override)); +}; + // ------------------------------------------------------------------------------------------------- class VibratorManagerHalWrapperAidlTest : public Test { public: void SetUp() override { mMockVibrator = ndk::SharedRefBase::make<StrictMock<vibrator::MockIVibrator>>(); + mMockSession = ndk::SharedRefBase::make<StrictMock<MockIVibrationSession>>(); mMockHal = ndk::SharedRefBase::make<StrictMock<MockIVibratorManager>>(); mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>(); mWrapper = std::make_unique<vibrator::AidlManagerHalWrapper>(mMockScheduler, mMockHal); @@ -86,11 +99,13 @@ protected: std::unique_ptr<vibrator::ManagerHalWrapper> mWrapper = nullptr; std::shared_ptr<StrictMock<MockIVibratorManager>> mMockHal = nullptr; std::shared_ptr<StrictMock<vibrator::MockIVibrator>> mMockVibrator = nullptr; + std::shared_ptr<StrictMock<MockIVibrationSession>> mMockSession = nullptr; }; // ------------------------------------------------------------------------------------------------- static const std::vector<int32_t> kVibratorIds = {1, 2}; +static const VibrationSessionConfig kSessionConfig; static constexpr int kVibratorId = 1; TEST_F(VibratorManagerHalWrapperAidlTest, TestGetCapabilitiesDoesNotCacheFailedResult) { @@ -319,3 +334,35 @@ TEST_F(VibratorManagerHalWrapperAidlTest, TestCancelSyncedReloadsAllControllers) ASSERT_TRUE(mWrapper->getVibratorIds().isOk()); ASSERT_TRUE(mWrapper->cancelSynced().isOk()); } + +TEST_F(VibratorManagerHalWrapperAidlTest, TestStartSession) { + EXPECT_CALL(*mMockHal.get(), startSession(_, _, _, _)) + .Times(Exactly(3)) + .WillOnce(Return(ndk::ScopedAStatus::fromStatus(STATUS_UNKNOWN_TRANSACTION))) + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY))) + .WillOnce( + DoAll(DoAll(SetArgPointee<3>(mMockSession), Return(ndk::ScopedAStatus::ok())))); + + std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); + auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); + + ASSERT_TRUE(mWrapper->startSession(kVibratorIds, kSessionConfig, callback).isUnsupported()); + ASSERT_TRUE(mWrapper->startSession(kVibratorIds, kSessionConfig, callback).isFailed()); + + auto result = mWrapper->startSession(kVibratorIds, kSessionConfig, callback); + ASSERT_TRUE(result.isOk()); + ASSERT_NE(nullptr, result.value().get()); + ASSERT_EQ(0, *callbackCounter.get()); +} + +TEST_F(VibratorManagerHalWrapperAidlTest, TestClearSessions) { + EXPECT_CALL(*mMockHal.get(), clearSessions()) + .Times(Exactly(3)) + .WillOnce(Return(ndk::ScopedAStatus::fromStatus(STATUS_UNKNOWN_TRANSACTION))) + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY))) + .WillOnce(Return(ndk::ScopedAStatus::ok())); + + ASSERT_TRUE(mWrapper->clearSessions().isUnsupported()); + ASSERT_TRUE(mWrapper->clearSessions().isFailed()); + ASSERT_TRUE(mWrapper->clearSessions().isOk()); +} diff --git a/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp b/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp index 78772369bf..52c865e638 100644 --- a/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp +++ b/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp @@ -29,6 +29,8 @@ using aidl::android::hardware::vibrator::CompositeEffect; using aidl::android::hardware::vibrator::CompositePrimitive; using aidl::android::hardware::vibrator::Effect; using aidl::android::hardware::vibrator::EffectStrength; +using aidl::android::hardware::vibrator::IVibrationSession; +using aidl::android::hardware::vibrator::VibrationSessionConfig; using std::chrono::milliseconds; @@ -112,3 +114,12 @@ TEST_F(VibratorManagerHalWrapperLegacyTest, TestSyncedOperationsUnsupported) { ASSERT_TRUE(mWrapper->triggerSynced([]() {}).isUnsupported()); ASSERT_TRUE(mWrapper->cancelSynced().isUnsupported()); } + +TEST_F(VibratorManagerHalWrapperLegacyTest, TestSessionOperationsUnsupported) { + std::vector<int32_t> vibratorIds; + vibratorIds.push_back(0); + VibrationSessionConfig config; + + ASSERT_TRUE(mWrapper->startSession(vibratorIds, config, []() {}).isUnsupported()); + ASSERT_TRUE(mWrapper->clearSessions().isUnsupported()); +} |