Merge "Fix test for headroom APIs" into main
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index d24edc4..4758607 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -1841,6 +1841,11 @@
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");
@@ -4651,7 +4656,7 @@
void Dumpstate::TakeScreenshot(const std::string& path) {
const std::string& real_path = path.empty() ? screenshot_path_ : path;
int status =
- RunCommand("", {"/system/bin/screencap", "-p", real_path},
+ RunCommand("", {"screencap", "-p", real_path},
CommandOptions::WithTimeout(10).Always().DropRoot().RedirectStderr().Build());
if (status == 0) {
MYLOGD("Screenshot saved on %s\n", real_path.c_str());
diff --git a/include/android/keycodes.h b/include/android/keycodes.h
index 79cdbca..495e0bd 100644
--- a/include/android/keycodes.h
+++ b/include/android/keycodes.h
@@ -843,6 +843,44 @@
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/surface_control.h b/include/android/surface_control.h
index 8d61e77..fe38e86 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -763,6 +763,69 @@
__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 0000000..f36472d
--- /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/InputConsumerNoResampling.h b/include/input/InputConsumerNoResampling.h
index 2e346bb..70d00d1 100644
--- a/include/input/InputConsumerNoResampling.h
+++ b/include/input/InputConsumerNoResampling.h
@@ -141,7 +141,7 @@
}
private:
- std::function<int(int events)> mCallback;
+ const std::function<int(int events)> mCallback;
};
sp<LooperEventCallback> mCallback;
/**
diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h
index 6a248ef..6b45dd3 100644
--- a/include/input/InputDevice.h
+++ b/include/input/InputDevice.h
@@ -131,8 +131,9 @@
PLAYER_ID = 1,
KEYBOARD_BACKLIGHT = 2,
KEYBOARD_MIC_MUTE = 3,
+ KEYBOARD_VOLUME_MUTE = 4,
- ftl_last = KEYBOARD_MIC_MUTE
+ ftl_last = KEYBOARD_VOLUME_MUTE
};
enum class InputDeviceLightCapability : uint32_t {
diff --git a/include/input/OneEuroFilter.h b/include/input/OneEuroFilter.h
new file mode 100644
index 0000000..a0168e4
--- /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 6d95ca7..1550977 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 @@
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 125cfaf..334d84b 100644
--- a/libs/battery/LongArrayMultiStateCounter.cpp
+++ b/libs/battery/LongArrayMultiStateCounter.cpp
@@ -21,59 +21,138 @@
namespace android {
namespace battery {
+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;
+ }
+}
+
+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 {
+ delete[] mData;
+ mData = nullptr;
+ }
+ }
+ 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 <>
-bool LongArrayMultiStateCounter::delta(const std::vector<uint64_t>& previousValue,
- const std::vector<uint64_t>& newValue,
- std::vector<uint64_t>* outValue) 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 (size_t i = 0; i < size; i++) {
+ // The caller ensures that denominator != 0
+ data1[i] += data2[i] * numerator / denominator;
+ }
+ } else {
+ for (size_t i = 0; i < size; i++) {
+ data1[i] += data2[i];
+ }
+ }
+}
+
+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);
+ 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;
- for (int i = size - 1; i >= 0; i--) {
- if (newValue[i] >= previousValue[i]) {
- (*outValue)[i] = newValue[i] - previousValue[i];
- } else {
- (*outValue)[i] = 0;
+ 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];
}
}
return is_delta_valid;
}
-template <>
-void LongArrayMultiStateCounter::add(std::vector<uint64_t>* value1,
- const std::vector<uint64_t>& value2, const uint64_t numerator,
- const uint64_t denominator) const {
- if (numerator != denominator) {
- for (int i = value2.size() - 1; i >= 0; i--) {
- // The caller ensures that denominator != 0
- (*value1)[i] += value2[i] * numerator / denominator;
- }
- } else {
- for (int i = value2.size() - 1; i >= 0; i--) {
- (*value1)[i] += value2[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 << ", ";
- }
- s << n;
- first = false;
- }
- s << "}";
- return s.str();
-}
-
} // namespace battery
} // namespace android
diff --git a/libs/battery/LongArrayMultiStateCounter.h b/libs/battery/LongArrayMultiStateCounter.h
index f3439f6..e00c968 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 e4e6b2a..1c74e3f 100644
--- a/libs/battery/LongArrayMultiStateCounterTest.cpp
+++ b/libs/battery/LongArrayMultiStateCounterTest.cpp
@@ -24,25 +24,25 @@
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 @@
// 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 04b7186..fadc4ff 100644
--- a/libs/battery/MultiStateCounter.h
+++ b/libs/battery/MultiStateCounter.h
@@ -35,12 +35,12 @@
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 @@
State* states;
public:
- MultiStateCounter(uint16_t stateCount, const T& emptyValue);
+ MultiStateCounter(uint16_t stateCount, const V& emptyValue);
virtual ~MultiStateCounter();
@@ -66,35 +66,35 @@
* 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 @@
* 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 @@
}
}
-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 @@
}
}
-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 @@
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 @@
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 @@
}
} 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 @@
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 @@
}
}
-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 a51a38a..589b7fe 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 @@
}
}
-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/include_rpc_unstable/binder_rpc_unstable.hpp b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
index 392ebb5..48c0ea6 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 @@
// 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 21537fc..a84a0c6 100644
--- a/libs/binder/libbinder_rpc_unstable.cpp
+++ b/libs/binder/libbinder_rpc_unstable.cpp
@@ -81,7 +81,8 @@
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 @@
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/Android.bp b/libs/binder/ndk/Android.bp
index a7423b3..5710bbf 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -82,7 +82,6 @@
llndk: {
symbol_file: "libbinder_ndk.map.txt",
- export_llndk_headers: ["libvendorsupport_llndk_headers"],
},
cflags: [
@@ -110,11 +109,9 @@
],
header_libs: [
- "libvendorsupport_llndk_headers",
"jni_headers",
],
export_header_lib_headers: [
- "libvendorsupport_llndk_headers",
"jni_headers",
],
diff --git a/libs/binder/ndk/binder_rpc.cpp b/libs/binder/ndk/binder_rpc.cpp
index 886eb4b..53ab68e 100644
--- a/libs/binder/ndk/binder_rpc.cpp
+++ b/libs/binder/ndk/binder_rpc.cpp
@@ -104,8 +104,8 @@
};
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_cpp/android/binder_interface_utils.h b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
index 0ad110e..c6518d8 100644
--- a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
@@ -30,16 +30,14 @@
#include <android/binder_auto_utils.h>
#include <android/binder_ibinder.h>
-#if defined(__ANDROID_VENDOR_API__)
-#include <android/llndk-versioning.h>
-#elif !defined(API_LEVEL_AT_LEAST)
#if defined(__BIONIC__)
-#define API_LEVEL_AT_LEAST(sdk_api_level, vendor_api_level) \
- (__builtin_available(android sdk_api_level, *))
+#define API_LEVEL_AT_LEAST(sdk_api_level) __builtin_available(android sdk_api_level, *)
+#elif defined(TRUSTY_USERSPACE)
+// TODO(b/349936395): set to true for Trusty
+#define API_LEVEL_AT_LEAST(sdk_api_level) (false)
#else
-#define API_LEVEL_AT_LEAST(sdk_api_level, vendor_api_level) (true)
+#define API_LEVEL_AT_LEAST(sdk_api_level) (true)
#endif // __BIONIC__
-#endif // __ANDROID_VENDOR_API__
#if __has_include(<android/binder_shell.h>)
#include <android/binder_shell.h>
@@ -298,9 +296,8 @@
#endif
#if defined(__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__) || __ANDROID_API__ >= 36
- if API_LEVEL_AT_LEAST (36, 202504) {
- if (codeToFunction != nullptr &&
- (&AIBinder_Class_setTransactionCodeToFunctionNameMap != nullptr)) {
+ if (API_LEVEL_AT_LEAST(36)) {
+ if (codeToFunction != nullptr) {
AIBinder_Class_setTransactionCodeToFunctionNameMap(clazz, codeToFunction,
functionCount);
}
diff --git a/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h b/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h
index 83976b3..f3f3c38 100644
--- a/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h
+++ b/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h
@@ -22,17 +22,14 @@
#include <set>
#include <sstream>
-// Include llndk-versioning.h only for non-system build as it is not available for NDK headers.
-#if defined(__ANDROID_VENDOR_API__)
-#include <android/llndk-versioning.h>
-#elif !defined(API_LEVEL_AT_LEAST)
#if defined(__BIONIC__)
-#define API_LEVEL_AT_LEAST(sdk_api_level, vendor_api_level) \
- (__builtin_available(android sdk_api_level, *))
+#define API_LEVEL_AT_LEAST(sdk_api_level) __builtin_available(android sdk_api_level, *)
+#elif defined(TRUSTY_USERSPACE)
+// TODO(b/349936395): set to true for Trusty
+#define API_LEVEL_AT_LEAST(sdk_api_level) (false)
#else
-#define API_LEVEL_AT_LEAST(sdk_api_level, vendor_api_level) (true)
+#define API_LEVEL_AT_LEAST(sdk_api_level) (true)
#endif // __BIONIC__
-#endif // __ANDROID_VENDOR_API__
namespace aidl::android::os {
@@ -44,7 +41,7 @@
class PersistableBundle {
public:
PersistableBundle() noexcept {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
mPBundle = APersistableBundle_new();
}
}
@@ -54,13 +51,13 @@
PersistableBundle(PersistableBundle&& other) noexcept : mPBundle(other.release()) {}
// duplicates, does not take ownership of the APersistableBundle*
PersistableBundle(const PersistableBundle& other) {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
mPBundle = APersistableBundle_dup(other.mPBundle);
}
}
// duplicates, does not take ownership of the APersistableBundle*
PersistableBundle& operator=(const PersistableBundle& other) {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
mPBundle = APersistableBundle_dup(other.mPBundle);
}
return *this;
@@ -70,7 +67,7 @@
binder_status_t readFromParcel(const AParcel* _Nonnull parcel) {
reset();
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return APersistableBundle_readFromParcel(parcel, &mPBundle);
} else {
return STATUS_INVALID_OPERATION;
@@ -81,7 +78,7 @@
if (!mPBundle) {
return STATUS_BAD_VALUE;
}
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return APersistableBundle_writeToParcel(mPBundle, parcel);
} else {
return STATUS_INVALID_OPERATION;
@@ -96,7 +93,7 @@
*/
void reset(APersistableBundle* _Nullable pBundle = nullptr) noexcept {
if (mPBundle) {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
APersistableBundle_delete(mPBundle);
}
mPBundle = nullptr;
@@ -109,7 +106,7 @@
* what should be used to check for equality.
*/
bool deepEquals(const PersistableBundle& rhs) const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return APersistableBundle_isEqual(get(), rhs.get());
} else {
return false;
@@ -148,7 +145,7 @@
inline std::string toString() const {
if (!mPBundle) {
return "<PersistableBundle: null>";
- } else if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ } else if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
std::ostringstream os;
os << "<PersistableBundle: ";
os << "size: " << std::to_string(APersistableBundle_size(mPBundle));
@@ -159,7 +156,7 @@
}
int32_t size() const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return APersistableBundle_size(mPBundle);
} else {
return 0;
@@ -167,7 +164,7 @@
}
int32_t erase(const std::string& key) {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return APersistableBundle_erase(mPBundle, key.c_str());
} else {
return 0;
@@ -175,37 +172,37 @@
}
void putBoolean(const std::string& key, bool val) {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
APersistableBundle_putBoolean(mPBundle, key.c_str(), val);
}
}
void putInt(const std::string& key, int32_t val) {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
APersistableBundle_putInt(mPBundle, key.c_str(), val);
}
}
void putLong(const std::string& key, int64_t val) {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
APersistableBundle_putLong(mPBundle, key.c_str(), val);
}
}
void putDouble(const std::string& key, double val) {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
APersistableBundle_putDouble(mPBundle, key.c_str(), val);
}
}
void putString(const std::string& key, const std::string& val) {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
APersistableBundle_putString(mPBundle, key.c_str(), val.c_str());
}
}
void putBooleanVector(const std::string& key, const std::vector<bool>& vec) {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
// std::vector<bool> has no ::data().
int32_t num = vec.size();
if (num > 0) {
@@ -222,7 +219,7 @@
}
void putIntVector(const std::string& key, const std::vector<int32_t>& vec) {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
int32_t num = vec.size();
if (num > 0) {
APersistableBundle_putIntVector(mPBundle, key.c_str(), vec.data(), num);
@@ -230,7 +227,7 @@
}
}
void putLongVector(const std::string& key, const std::vector<int64_t>& vec) {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
int32_t num = vec.size();
if (num > 0) {
APersistableBundle_putLongVector(mPBundle, key.c_str(), vec.data(), num);
@@ -238,7 +235,7 @@
}
}
void putDoubleVector(const std::string& key, const std::vector<double>& vec) {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
int32_t num = vec.size();
if (num > 0) {
APersistableBundle_putDoubleVector(mPBundle, key.c_str(), vec.data(), num);
@@ -246,7 +243,7 @@
}
}
void putStringVector(const std::string& key, const std::vector<std::string>& vec) {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
int32_t num = vec.size();
if (num > 0) {
char** inVec = (char**)malloc(num * sizeof(char*));
@@ -261,13 +258,13 @@
}
}
void putPersistableBundle(const std::string& key, const PersistableBundle& pBundle) {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
APersistableBundle_putPersistableBundle(mPBundle, key.c_str(), pBundle.mPBundle);
}
}
bool getBoolean(const std::string& key, bool* _Nonnull val) const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return APersistableBundle_getBoolean(mPBundle, key.c_str(), val);
} else {
return false;
@@ -275,7 +272,7 @@
}
bool getInt(const std::string& key, int32_t* _Nonnull val) const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return APersistableBundle_getInt(mPBundle, key.c_str(), val);
} else {
return false;
@@ -283,7 +280,7 @@
}
bool getLong(const std::string& key, int64_t* _Nonnull val) const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return APersistableBundle_getLong(mPBundle, key.c_str(), val);
} else {
return false;
@@ -291,7 +288,7 @@
}
bool getDouble(const std::string& key, double* _Nonnull val) const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return APersistableBundle_getDouble(mPBundle, key.c_str(), val);
} else {
return false;
@@ -303,7 +300,7 @@
}
bool getString(const std::string& key, std::string* _Nonnull val) const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
char* outString = nullptr;
bool ret = APersistableBundle_getString(mPBundle, key.c_str(), &outString,
&stringAllocator, nullptr);
@@ -321,7 +318,7 @@
const char* _Nonnull, T* _Nullable, int32_t),
const APersistableBundle* _Nonnull pBundle, const char* _Nonnull key,
std::vector<T>* _Nonnull vec) const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
int32_t bytes = 0;
// call first with nullptr to get required size in bytes
bytes = getVec(pBundle, key, nullptr, 0);
@@ -343,28 +340,28 @@
}
bool getBooleanVector(const std::string& key, std::vector<bool>* _Nonnull vec) const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return getVecInternal<bool>(&APersistableBundle_getBooleanVector, mPBundle, key.c_str(),
vec);
}
return false;
}
bool getIntVector(const std::string& key, std::vector<int32_t>* _Nonnull vec) const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return getVecInternal<int32_t>(&APersistableBundle_getIntVector, mPBundle, key.c_str(),
vec);
}
return false;
}
bool getLongVector(const std::string& key, std::vector<int64_t>* _Nonnull vec) const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return getVecInternal<int64_t>(&APersistableBundle_getLongVector, mPBundle, key.c_str(),
vec);
}
return false;
}
bool getDoubleVector(const std::string& key, std::vector<double>* _Nonnull vec) const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return getVecInternal<double>(&APersistableBundle_getDoubleVector, mPBundle,
key.c_str(), vec);
}
@@ -389,7 +386,7 @@
}
bool getStringVector(const std::string& key, std::vector<std::string>* _Nonnull vec) const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
int32_t bytes = APersistableBundle_getStringVector(mPBundle, key.c_str(), nullptr, 0,
&stringAllocator, nullptr);
if (bytes > 0) {
@@ -406,7 +403,7 @@
}
bool getPersistableBundle(const std::string& key, PersistableBundle* _Nonnull val) const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
APersistableBundle* bundle = nullptr;
bool ret = APersistableBundle_getPersistableBundle(mPBundle, key.c_str(), &bundle);
if (ret) {
@@ -438,77 +435,77 @@
}
std::set<std::string> getBooleanKeys() const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return getKeys(&APersistableBundle_getBooleanKeys, mPBundle);
} else {
return {};
}
}
std::set<std::string> getIntKeys() const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return getKeys(&APersistableBundle_getIntKeys, mPBundle);
} else {
return {};
}
}
std::set<std::string> getLongKeys() const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return getKeys(&APersistableBundle_getLongKeys, mPBundle);
} else {
return {};
}
}
std::set<std::string> getDoubleKeys() const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return getKeys(&APersistableBundle_getDoubleKeys, mPBundle);
} else {
return {};
}
}
std::set<std::string> getStringKeys() const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return getKeys(&APersistableBundle_getStringKeys, mPBundle);
} else {
return {};
}
}
std::set<std::string> getBooleanVectorKeys() const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return getKeys(&APersistableBundle_getBooleanVectorKeys, mPBundle);
} else {
return {};
}
}
std::set<std::string> getIntVectorKeys() const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return getKeys(&APersistableBundle_getIntVectorKeys, mPBundle);
} else {
return {};
}
}
std::set<std::string> getLongVectorKeys() const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return getKeys(&APersistableBundle_getLongVectorKeys, mPBundle);
} else {
return {};
}
}
std::set<std::string> getDoubleVectorKeys() const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return getKeys(&APersistableBundle_getDoubleVectorKeys, mPBundle);
} else {
return {};
}
}
std::set<std::string> getStringVectorKeys() const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return getKeys(&APersistableBundle_getStringVectorKeys, mPBundle);
} else {
return {};
}
}
std::set<std::string> getPersistableBundleKeys() const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return getKeys(&APersistableBundle_getPersistableBundleKeys, mPBundle);
} else {
return {};
diff --git a/libs/binder/ndk/include_platform/android/binder_rpc.h b/libs/binder/ndk/include_platform/android/binder_rpc.h
index 66667d3..7d54e2d 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 @@
*/
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 2ab3447..74ce315 100644
--- a/libs/binder/rust/rpcbinder/src/server/android.rs
+++ b/libs/binder/rust/rpcbinder/src/server/android.rs
@@ -18,7 +18,7 @@
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 @@
/// 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 6301c74..a62ad96 100644
--- a/libs/binder/tests/binderUtilsHostTest.cpp
+++ b/libs/binder/tests/binderUtilsHostTest.cpp
@@ -56,7 +56,7 @@
});
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 @@
// ~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/binder/trusty/ndk/include/android/llndk-versioning.h b/libs/binder/trusty/ndk/include/android/llndk-versioning.h
deleted file mode 100644
index e955a34..0000000
--- a/libs/binder/trusty/ndk/include/android/llndk-versioning.h
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * 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
-
-// TODO(b/349936395): set to true for Trusty
-#define API_LEVEL_AT_LEAST(sdk_api_level, vendor_api_level) (false)
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 1243b21..1e33abb 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -274,6 +274,7 @@
"LayerMetadata.cpp",
"LayerStatePermissions.cpp",
"LayerState.cpp",
+ "DisplayLuts.cpp",
"OccupancyTracker.cpp",
"StreamSplitter.cpp",
"ScreenCaptureResults.cpp",
@@ -341,6 +342,10 @@
"libgui_aidl_headers",
],
+ static_libs: [
+ "libsurfaceflingerflags",
+ ],
+
afdo: true,
lto: {
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 495418b..7aee903 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -286,18 +286,23 @@
if (surfaceControlChanged && mSurfaceControl != nullptr) {
BQA_LOGD("Updating SurfaceControl without recreating BBQ");
}
- bool applyTransaction = false;
// Always update the native object even though they might have the same layer handle, so we can
// get the updated transform hint from WM.
mSurfaceControl = surface;
SurfaceComposerClient::Transaction t;
+ bool applyTransaction = false;
if (surfaceControlChanged) {
#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
updateBufferReleaseProducer();
#endif
t.setFlags(mSurfaceControl, layer_state_t::eEnableBackpressure,
layer_state_t::eEnableBackpressure);
+ // Migrate the picture profile handle to the new surface control.
+ if (com_android_graphics_libgui_flags_apply_picture_profiles() &&
+ mPictureProfileHandle.has_value()) {
+ t.setPictureProfileHandle(mSurfaceControl, *mPictureProfileHandle);
+ }
applyTransaction = true;
}
mTransformHint = mSurfaceControl->getTransformHint();
@@ -679,6 +684,17 @@
if (!bufferItem.mIsAutoTimestamp) {
t->setDesiredPresentTime(bufferItem.mTimestamp);
}
+ if (com_android_graphics_libgui_flags_apply_picture_profiles() &&
+ bufferItem.mPictureProfileHandle.has_value()) {
+ t->setPictureProfileHandle(mSurfaceControl, *bufferItem.mPictureProfileHandle);
+ // The current picture profile must be maintained in case the BBQ gets its
+ // SurfaceControl switched out.
+ mPictureProfileHandle = bufferItem.mPictureProfileHandle;
+ // Clear out the picture profile if the requestor has asked for it to be cleared
+ if (mPictureProfileHandle == PictureProfileHandle::NONE) {
+ mPictureProfileHandle = std::nullopt;
+ }
+ }
// Drop stale frame timeline infos
while (!mPendingFrameTimelines.empty() &&
diff --git a/libs/gui/BufferItem.cpp b/libs/gui/BufferItem.cpp
index 5beba02..3b2d337 100644
--- a/libs/gui/BufferItem.cpp
+++ b/libs/gui/BufferItem.cpp
@@ -38,26 +38,25 @@
return static_cast<T>(static_cast<uint64_t>(hi)<<32 | lo);
}
-BufferItem::BufferItem() :
- mGraphicBuffer(nullptr),
- mFence(nullptr),
- mCrop(Rect::INVALID_RECT),
- mTransform(0),
- mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
- mTimestamp(0),
- mIsAutoTimestamp(false),
- mDataSpace(HAL_DATASPACE_UNKNOWN),
- mFrameNumber(0),
- mSlot(INVALID_BUFFER_SLOT),
- mIsDroppable(false),
- mAcquireCalled(false),
- mTransformToDisplayInverse(false),
- mSurfaceDamage(),
- mAutoRefresh(false),
- mQueuedBuffer(true),
- mIsStale(false),
- mApi(0) {
-}
+BufferItem::BufferItem()
+ : mGraphicBuffer(nullptr),
+ mFence(nullptr),
+ mCrop(Rect::INVALID_RECT),
+ mTransform(0),
+ mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
+ mTimestamp(0),
+ mIsAutoTimestamp(false),
+ mDataSpace(HAL_DATASPACE_UNKNOWN),
+ mFrameNumber(0),
+ mSlot(INVALID_BUFFER_SLOT),
+ mIsDroppable(false),
+ mAcquireCalled(false),
+ mTransformToDisplayInverse(false),
+ mSurfaceDamage(),
+ mAutoRefresh(false),
+ mQueuedBuffer(true),
+ mIsStale(false),
+ mApi(0) {}
BufferItem::~BufferItem() {}
@@ -76,6 +75,11 @@
addAligned(size, high32(mTimestamp));
addAligned(size, mIsAutoTimestamp);
addAligned(size, mDataSpace);
+#if COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES
+ addAligned(size, mPictureProfileHandle.has_value());
+ addAligned(size, low32(PictureProfileHandle::NONE.getId()));
+ addAligned(size, high32(PictureProfileHandle::NONE.getId()));
+#endif // COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES
addAligned(size, low32(mFrameNumber));
addAligned(size, high32(mFrameNumber));
addAligned(size, mSlot);
@@ -170,6 +174,16 @@
writeAligned(buffer, size, high32(mTimestamp));
writeAligned(buffer, size, mIsAutoTimestamp);
writeAligned(buffer, size, mDataSpace);
+#if COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES
+ writeAligned(buffer, size, mPictureProfileHandle.has_value());
+ if (mPictureProfileHandle.has_value()) {
+ writeAligned(buffer, size, low32(mPictureProfileHandle->getId()));
+ writeAligned(buffer, size, high32(mPictureProfileHandle->getId()));
+ } else {
+ writeAligned(buffer, size, low32(PictureProfileHandle::NONE.getId()));
+ writeAligned(buffer, size, high32(PictureProfileHandle::NONE.getId()));
+ }
+#endif // COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES
writeAligned(buffer, size, low32(mFrameNumber));
writeAligned(buffer, size, high32(mFrameNumber));
writeAligned(buffer, size, mSlot);
@@ -231,6 +245,7 @@
uint32_t timestampLo = 0, timestampHi = 0;
uint32_t frameNumberLo = 0, frameNumberHi = 0;
+ int32_t pictureProfileIdLo = 0, pictureProfileIdHi = 0;
readAligned(buffer, size, mCrop);
readAligned(buffer, size, mTransform);
@@ -240,6 +255,16 @@
mTimestamp = to64<int64_t>(timestampLo, timestampHi);
readAligned(buffer, size, mIsAutoTimestamp);
readAligned(buffer, size, mDataSpace);
+#if COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES
+ bool hasPictureProfileHandle;
+ readAligned(buffer, size, hasPictureProfileHandle);
+ readAligned(buffer, size, pictureProfileIdLo);
+ readAligned(buffer, size, pictureProfileIdHi);
+ mPictureProfileHandle = hasPictureProfileHandle
+ ? std::optional(PictureProfileHandle(
+ to64<PictureProfileId>(pictureProfileIdLo, pictureProfileIdHi)))
+ : std::nullopt;
+#endif // COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES
readAligned(buffer, size, frameNumberLo);
readAligned(buffer, size, frameNumberHi);
mFrameNumber = to64<uint64_t>(frameNumberLo, frameNumberHi);
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 473a374..39209f9 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -938,6 +938,8 @@
&getFrameTimestamps);
const Region& surfaceDamage = input.getSurfaceDamage();
const HdrMetadata& hdrMetadata = input.getHdrMetadata();
+ const std::optional<PictureProfileHandle>& pictureProfileHandle =
+ input.getPictureProfileHandle();
if (acquireFence == nullptr) {
BQ_LOGE("queueBuffer: fence is NULL");
@@ -1044,6 +1046,7 @@
item.mIsAutoTimestamp = isAutoTimestamp;
item.mDataSpace = dataSpace;
item.mHdrMetadata = hdrMetadata;
+ item.mPictureProfileHandle = pictureProfileHandle;
item.mFrameNumber = currentFrameNumber;
item.mSlot = slot;
item.mFence = acquireFence;
diff --git a/libs/gui/DisplayLuts.cpp b/libs/gui/DisplayLuts.cpp
new file mode 100644
index 0000000..8042976
--- /dev/null
+++ b/libs/gui/DisplayLuts.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "include/gui/DisplayLuts.h"
+#include <gui/DisplayLuts.h>
+#include <private/gui/ParcelUtils.h>
+
+namespace android::gui {
+
+status_t DisplayLuts::Entry::readFromParcel(const android::Parcel* parcel) {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+
+ SAFE_PARCEL(parcel->readInt32, &dimension);
+ SAFE_PARCEL(parcel->readInt32, &size);
+ SAFE_PARCEL(parcel->readInt32, &samplingKey);
+
+ return OK;
+}
+
+status_t DisplayLuts::Entry::writeToParcel(android::Parcel* parcel) const {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+
+ SAFE_PARCEL(parcel->writeInt32, dimension);
+ SAFE_PARCEL(parcel->writeInt32, size);
+ SAFE_PARCEL(parcel->writeInt32, samplingKey);
+
+ return OK;
+}
+
+status_t DisplayLuts::readFromParcel(const android::Parcel* parcel) {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+
+ SAFE_PARCEL(parcel->readUniqueFileDescriptor, &fd);
+ SAFE_PARCEL(parcel->readInt32Vector, &offsets);
+ int32_t numLutProperties;
+ SAFE_PARCEL(parcel->readInt32, &numLutProperties);
+ lutProperties.reserve(numLutProperties);
+ for (int32_t i = 0; i < numLutProperties; i++) {
+ lutProperties.push_back({});
+ SAFE_PARCEL(lutProperties.back().readFromParcel, parcel);
+ }
+ return OK;
+}
+
+status_t DisplayLuts::writeToParcel(android::Parcel* parcel) const {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+
+ SAFE_PARCEL(parcel->writeUniqueFileDescriptor, fd);
+ SAFE_PARCEL(parcel->writeInt32Vector, offsets);
+ SAFE_PARCEL(parcel->writeInt32, static_cast<int32_t>(lutProperties.size()));
+ for (auto& entry : lutProperties) {
+ SAFE_PARCEL(entry.writeToParcel, parcel);
+ }
+ return OK;
+}
+} // namespace android::gui
\ No newline at end of file
diff --git a/libs/gui/IGraphicBufferProducerFlattenables.cpp b/libs/gui/IGraphicBufferProducerFlattenables.cpp
index c8b9b67..4e92a39 100644
--- a/libs/gui/IGraphicBufferProducerFlattenables.cpp
+++ b/libs/gui/IGraphicBufferProducerFlattenables.cpp
@@ -20,21 +20,19 @@
namespace android {
constexpr size_t IGraphicBufferProducer::QueueBufferInput::minFlattenedSize() {
- return sizeof(timestamp) +
- sizeof(isAutoTimestamp) +
- sizeof(dataSpace) +
- sizeof(crop) +
- sizeof(scalingMode) +
- sizeof(transform) +
- sizeof(stickyTransform) +
- sizeof(getFrameTimestamps) +
- sizeof(slot);
+ return sizeof(timestamp) + sizeof(isAutoTimestamp) + sizeof(dataSpace) + sizeof(crop) +
+ sizeof(scalingMode) + sizeof(transform) + sizeof(stickyTransform) +
+ sizeof(getFrameTimestamps) + sizeof(slot) +
+#if COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES
+ sizeof(decltype(pictureProfileHandle.has_value())) +
+ sizeof(decltype(pictureProfileHandle.getId()));
+#else
+ 0;
+#endif // COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES
}
size_t IGraphicBufferProducer::QueueBufferInput::getFlattenedSize() const {
- return minFlattenedSize() +
- fence->getFlattenedSize() +
- surfaceDamage.getFlattenedSize() +
+ return minFlattenedSize() + fence->getFlattenedSize() + surfaceDamage.getFlattenedSize() +
hdrMetadata.getFlattenedSize();
}
@@ -57,6 +55,12 @@
FlattenableUtils::write(buffer, size, transform);
FlattenableUtils::write(buffer, size, stickyTransform);
FlattenableUtils::write(buffer, size, getFrameTimestamps);
+#if COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES
+ FlattenableUtils::write(buffer, size, pictureProfileHandle.has_value());
+ FlattenableUtils::write(buffer, size,
+ pictureProfileHandle.has_value() ? pictureProfileHandle->getId()
+ : PictureProfileHandle::NONE.getId());
+#endif // COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES
status_t result = fence->flatten(buffer, size, fds, count);
if (result != NO_ERROR) {
@@ -91,6 +95,15 @@
FlattenableUtils::read(buffer, size, transform);
FlattenableUtils::read(buffer, size, stickyTransform);
FlattenableUtils::read(buffer, size, getFrameTimestamps);
+#if COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES
+ bool hasPictureProfileHandle;
+ FlattenableUtils::read(buffer, size, hasPictureProfileHandle);
+ PictureProfileId pictureProfileId;
+ FlattenableUtils::read(buffer, size, pictureProfileId);
+ pictureProfileHandle = hasPictureProfileHandle
+ ? std::optional(PictureProfileHandle(pictureProfileId))
+ : std::nullopt;
+#endif // COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES
fence = new Fence();
status_t result = fence->unflatten(buffer, size, fds, count);
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 4b53134..c1a03fc 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -21,6 +21,7 @@
#include <android/gui/ISurfaceComposerClient.h>
#include <android/native_window.h>
#include <binder/Parcel.h>
+#include <com_android_graphics_libgui_flags.h>
#include <gui/FrameRateUtils.h>
#include <gui/IGraphicBufferProducer.h>
#include <gui/LayerState.h>
@@ -91,7 +92,9 @@
trustedOverlay(gui::TrustedOverlay::UNSET),
bufferCrop(Rect::INVALID_RECT),
destinationFrame(Rect::INVALID_RECT),
- dropInputMode(gui::DropInputMode::NONE) {
+ dropInputMode(gui::DropInputMode::NONE),
+ pictureProfileHandle(PictureProfileHandle::NONE),
+ appContentPriority(0) {
matrix.dsdx = matrix.dtdy = 1.0f;
matrix.dsdy = matrix.dtdx = 0.0f;
hdrMetadata.validTypes = 0;
@@ -202,6 +205,16 @@
if (hasBufferReleaseChannel) {
SAFE_PARCEL(output.writeParcelable, *bufferReleaseChannel);
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS_APPLY_PICTURE_PROFILES
+ SAFE_PARCEL(output.writeInt64, pictureProfileHandle.getId());
+ SAFE_PARCEL(output.writeInt32, appContentPriority);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS_APPLY_PICTURE_PROFILES
+
+ const bool hasLuts = (luts != nullptr);
+ SAFE_PARCEL(output.writeBool, hasLuts);
+ if (hasLuts) {
+ SAFE_PARCEL(output.writeParcelable, *luts);
+ }
return NO_ERROR;
}
@@ -357,6 +370,21 @@
bufferReleaseChannel = std::make_shared<gui::BufferReleaseChannel::ProducerEndpoint>();
SAFE_PARCEL(input.readParcelable, bufferReleaseChannel.get());
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS_APPLY_PICTURE_PROFILES
+ int64_t pictureProfileId;
+ SAFE_PARCEL(input.readInt64, &pictureProfileId);
+ pictureProfileHandle = PictureProfileHandle(pictureProfileId);
+ SAFE_PARCEL(input.readInt32, &appContentPriority);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS_APPLY_PICTURE_PROFILES
+
+ bool hasLuts;
+ SAFE_PARCEL(input.readBool, &hasLuts);
+ if (hasLuts) {
+ luts = std::make_shared<gui::DisplayLuts>();
+ SAFE_PARCEL(input.readParcelable, luts.get());
+ } else {
+ luts = nullptr;
+ }
return NO_ERROR;
}
@@ -745,6 +773,16 @@
what |= eBufferReleaseChannelChanged;
bufferReleaseChannel = other.bufferReleaseChannel;
}
+ if (com_android_graphics_libgui_flags_apply_picture_profiles()) {
+ if (other.what & ePictureProfileHandleChanged) {
+ what |= ePictureProfileHandleChanged;
+ pictureProfileHandle = other.pictureProfileHandle;
+ }
+ if (other.what & eAppContentPriorityChanged) {
+ what |= eAppContentPriorityChanged;
+ appContentPriority = other.appContentPriority;
+ }
+ }
if ((other.what & what) != other.what) {
ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? "
"other.what=0x%" PRIX64 " what=0x%" PRIX64 " unmerged flags=0x%" PRIX64,
@@ -826,6 +864,8 @@
CHECK_DIFF(diff, eDimmingEnabledChanged, other, dimmingEnabled);
if (other.what & eBufferReleaseChannelChanged) diff |= eBufferReleaseChannelChanged;
if (other.what & eLutsChanged) diff |= eLutsChanged;
+ CHECK_DIFF(diff, ePictureProfileHandleChanged, other, pictureProfileHandle);
+ CHECK_DIFF(diff, eAppContentPriorityChanged, other, appContentPriority);
return diff;
}
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 66e7ddd..e41f9bb 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -2735,8 +2735,8 @@
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 74097b8..61aabaa 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -20,8 +20,6 @@
#include <stdint.h>
#include <sys/types.h>
-#include <com_android_graphics_libgui_flags.h>
-
#include <android/gui/BnWindowInfosReportedListener.h>
#include <android/gui/DisplayState.h>
#include <android/gui/EdgeExtensionParameters.h>
@@ -29,6 +27,7 @@
#include <android/gui/IWindowInfosListener.h>
#include <android/gui/TrustedPresentationThresholds.h>
#include <android/os/IInputConstants.h>
+#include <com_android_graphics_libgui_flags.h>
#include <gui/DisplayLuts.h>
#include <gui/FrameRateUtils.h>
#include <gui/TraceUtils.h>
@@ -92,6 +91,7 @@
}
constexpr int64_t INVALID_VSYNC = -1;
+const constexpr char* LOG_SURFACE_CONTROL_REGISTRY = "SurfaceControlRegistry";
} // namespace
@@ -873,6 +873,7 @@
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);
@@ -1000,6 +1001,7 @@
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()));
@@ -1135,6 +1137,12 @@
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;
}
@@ -1154,6 +1162,7 @@
mFrameTimelineInfo = {};
mApplyToken = nullptr;
mMergedTransactionIds.clear();
+ mLogCallPoints = false;
}
uint64_t SurfaceComposerClient::Transaction::getId() {
@@ -1362,6 +1371,10 @@
syncCallback->wait();
}
+ if (mLogCallPoints) {
+ ALOG(LOG_DEBUG, LOG_SURFACE_CONTROL_REGISTRY, "Transaction %" PRIu64 " applied", mId);
+ }
+
mStatus = NO_ERROR;
return binderStatus;
}
@@ -1392,6 +1405,11 @@
t.registerSurfaceControlForCallback(sc);
return t.apply(/*sync=*/false, /* oneWay=*/true);
}
+
+void SurfaceComposerClient::Transaction::enableDebugLogCallPoints() {
+ mLogCallPoints = true;
+}
+
// ---------------------------------------------------------------------------
sp<IBinder> SurfaceComposerClient::createVirtualDisplay(const std::string& displayName,
@@ -1952,9 +1970,13 @@
return *this;
}
- s->luts = std::make_shared<gui::DisplayLuts>(base::unique_fd(dup(lutFd.get())), offsets,
- dimensions, sizes, samplingKeys);
s->what |= layer_state_t::eLutsChanged;
+ if (lutFd.ok()) {
+ s->luts = std::make_shared<gui::DisplayLuts>(base::unique_fd(dup(lutFd.get())), offsets,
+ dimensions, sizes, samplingKeys);
+ } else {
+ s->luts = nullptr;
+ }
registerSurfaceControlForCallback(sc);
return *this;
@@ -2110,13 +2132,13 @@
}
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;
}
@@ -2428,6 +2450,40 @@
return *this;
}
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setPictureProfileHandle(
+ const sp<SurfaceControl>& sc, const PictureProfileHandle& pictureProfileHandle) {
+ if (com_android_graphics_libgui_flags_apply_picture_profiles()) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+
+ s->what |= layer_state_t::ePictureProfileHandleChanged;
+ s->pictureProfileHandle = pictureProfileHandle;
+
+ registerSurfaceControlForCallback(sc);
+ }
+ return *this;
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setContentPriority(
+ const sp<SurfaceControl>& sc, int32_t priority) {
+ if (com_android_graphics_libgui_flags_apply_picture_profiles()) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+
+ s->what |= layer_state_t::eAppContentPriorityChanged;
+ s->appContentPriority = priority;
+
+ registerSurfaceControlForCallback(sc);
+ }
+ return *this;
+}
+
// ---------------------------------------------------------------------------
DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp<IBinder>& token) {
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 8894b66..07558aa 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -17,7 +17,9 @@
#ifndef ANDROID_GUI_BLAST_BUFFER_QUEUE_H
#define ANDROID_GUI_BLAST_BUFFER_QUEUE_H
-#include <com_android_graphics_libgui_flags.h>
+#include <optional>
+#include <queue>
+
#include <gui/BufferItem.h>
#include <gui/BufferItemConsumer.h>
#include <gui/IGraphicBufferConsumer.h>
@@ -29,7 +31,6 @@
#include <utils/RefBase.h>
#include <system/window.h>
-#include <queue>
#include <com_android_graphics_libgui_flags.h>
@@ -222,6 +223,10 @@
ui::Size mRequestedSize GUARDED_BY(mMutex);
int32_t mFormat GUARDED_BY(mMutex);
+ // Keep a copy of the current picture profile handle, so it can be moved to a new
+ // SurfaceControl when BBQ migrates via ::update.
+ std::optional<PictureProfileHandle> mPictureProfileHandle;
+
struct BufferInfo {
bool hasBuffer = false;
uint32_t width;
diff --git a/libs/gui/include/gui/BufferItem.h b/libs/gui/include/gui/BufferItem.h
index 218bb42..2f85c62 100644
--- a/libs/gui/include/gui/BufferItem.h
+++ b/libs/gui/include/gui/BufferItem.h
@@ -17,9 +17,12 @@
#ifndef ANDROID_GUI_BUFFERITEM_H
#define ANDROID_GUI_BUFFERITEM_H
+#include <optional>
+
#include <gui/HdrMetadata.h>
#include <ui/FenceTime.h>
+#include <ui/PictureProfileHandle.h>
#include <ui/Rect.h>
#include <ui/Region.h>
@@ -91,6 +94,10 @@
// mHdrMetadata is the HDR metadata associated with this buffer slot.
HdrMetadata mHdrMetadata;
+ // mPictureProfileHandle is a handle that points to a set of parameters that configure picture
+ // processing hardware to enhance the quality of buffer contents.
+ std::optional<PictureProfileHandle> mPictureProfileHandle;
+
// mFrameNumber is the number of the queued frame for this slot.
uint64_t mFrameNumber;
diff --git a/libs/gui/include/gui/DisplayLuts.h b/libs/gui/include/gui/DisplayLuts.h
index 16a360d..ab86ac4 100644
--- a/libs/gui/include/gui/DisplayLuts.h
+++ b/libs/gui/include/gui/DisplayLuts.h
@@ -16,16 +16,24 @@
#pragma once
#include <android-base/unique_fd.h>
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
#include <vector>
namespace android::gui {
-struct DisplayLuts {
+struct DisplayLuts : public Parcelable {
public:
- struct Entry {
+ struct Entry : public Parcelable {
+ Entry() {};
+ Entry(int32_t lutDimension, int32_t lutSize, int32_t lutSamplingKey)
+ : dimension(lutDimension), size(lutSize), samplingKey(lutSamplingKey) {}
int32_t dimension;
int32_t size;
int32_t samplingKey;
+
+ status_t writeToParcel(android::Parcel* parcel) const override;
+ status_t readFromParcel(const android::Parcel* parcel) override;
};
DisplayLuts() {}
@@ -42,7 +50,10 @@
}
}
- base::unique_fd& getLutFileDescriptor() { return fd; }
+ status_t writeToParcel(android::Parcel* parcel) const override;
+ status_t readFromParcel(const android::Parcel* parcel) override;
+
+ const base::unique_fd& getLutFileDescriptor() const { return fd; }
std::vector<Entry> lutProperties;
std::vector<int32_t> offsets;
diff --git a/libs/gui/include/gui/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h
index 3aac457..001e570 100644
--- a/libs/gui/include/gui/IGraphicBufferProducer.h
+++ b/libs/gui/include/gui/IGraphicBufferProducer.h
@@ -19,6 +19,7 @@
#include <stdint.h>
#include <sys/types.h>
+#include <optional>
#include <utils/Errors.h>
#include <utils/RefBase.h>
@@ -28,6 +29,7 @@
#include <ui/BufferQueueDefs.h>
#include <ui/Fence.h>
#include <ui/GraphicBuffer.h>
+#include <ui/PictureProfileHandle.h>
#include <ui/Rect.h>
#include <ui/Region.h>
@@ -365,6 +367,14 @@
const HdrMetadata& getHdrMetadata() const { return hdrMetadata; }
void setHdrMetadata(const HdrMetadata& metadata) { hdrMetadata = metadata; }
+ const std::optional<PictureProfileHandle>& getPictureProfileHandle() const {
+ return pictureProfileHandle;
+ }
+ void setPictureProfileHandle(const PictureProfileHandle& profile) {
+ pictureProfileHandle = profile;
+ }
+ void clearPictureProfileHandle() { pictureProfileHandle = std::nullopt; }
+
int64_t timestamp{0};
int isAutoTimestamp{0};
android_dataspace dataSpace{HAL_DATASPACE_UNKNOWN};
@@ -377,6 +387,7 @@
bool getFrameTimestamps{false};
int slot{-1};
HdrMetadata hdrMetadata;
+ std::optional<PictureProfileHandle> pictureProfileHandle;
};
struct QueueBufferOutput : public Flattenable<QueueBufferOutput> {
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 6bfeaec..9098dff 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -47,6 +47,7 @@
#include <ui/BlurRegion.h>
#include <ui/GraphicTypes.h>
#include <ui/LayerStack.h>
+#include <ui/PictureProfileHandle.h>
#include <ui/Rect.h>
#include <ui/Region.h>
#include <ui/Rotation.h>
@@ -224,6 +225,8 @@
eExtendedRangeBrightnessChanged = 0x10000'00000000,
eEdgeExtensionChanged = 0x20000'00000000,
eBufferReleaseChannelChanged = 0x40000'00000000,
+ ePictureProfileHandleChanged = 0x80000'00000000,
+ eAppContentPriorityChanged = 0x100000'00000000,
};
layer_state_t();
@@ -267,7 +270,8 @@
layer_state_t::eColorSpaceAgnosticChanged | layer_state_t::eColorTransformChanged |
layer_state_t::eCornerRadiusChanged | layer_state_t::eDimmingEnabledChanged |
layer_state_t::eHdrMetadataChanged | layer_state_t::eShadowRadiusChanged |
- layer_state_t::eStretchChanged;
+ layer_state_t::eStretchChanged | layer_state_t::ePictureProfileHandleChanged |
+ layer_state_t::eAppContentPriorityChanged;
// Changes which invalidates the layer's visible region in CE.
static constexpr uint64_t CONTENT_DIRTY = layer_state_t::CONTENT_CHANGES |
@@ -412,6 +416,15 @@
float currentHdrSdrRatio = 1.f;
float desiredHdrSdrRatio = 1.f;
+ // Enhance the quality of the buffer contents by configurating a picture processing pipeline
+ // with values as specified by this picture profile.
+ PictureProfileHandle pictureProfileHandle{PictureProfileHandle::NONE};
+
+ // A value indicating the significance of the layer's content to the app's desired user
+ // experience. A lower priority will result in more likelihood of getting access to limited
+ // resources, such as picture processing hardware.
+ int32_t appContentPriority = 0;
+
gui::CachingHint cachingHint = gui::CachingHint::Enabled;
TrustedPresentationThresholds trustedPresentationThresholds;
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 5ea0c16..0d7f8c2 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -38,6 +38,7 @@
#include <ui/EdgeExtensionEffect.h>
#include <ui/FrameStats.h>
#include <ui/GraphicTypes.h>
+#include <ui/PictureProfileHandle.h>
#include <ui/PixelFormat.h>
#include <ui/Rotation.h>
#include <ui/StaticDisplayInfo.h>
@@ -437,6 +438,8 @@
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 +688,8 @@
// 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(
@@ -773,6 +777,20 @@
const sp<SurfaceControl>& sc,
const std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint>& channel);
+ /**
+ * Configures a surface control to use picture processing hardware, configured as specified
+ * by the picture profile, to enhance the quality of all subsequent buffer contents.
+ */
+ Transaction& setPictureProfileHandle(const sp<SurfaceControl>& sc,
+ const PictureProfileHandle& pictureProfileHandle);
+
+ /**
+ * Configures the relative importance of the contents of the layer with respect to the app's
+ * user experience. A lower priority value will give the layer preferred access to limited
+ * resources, such as picture processing, over a layer with a higher priority value.
+ */
+ Transaction& setContentPriority(const sp<SurfaceControl>& sc, int32_t contentPriority);
+
status_t setDisplaySurface(const sp<IBinder>& token,
const sp<IGraphicBufferProducer>& bufferProducer);
@@ -809,6 +827,7 @@
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 1c7e0e4..22d32e9 100644
--- a/libs/gui/libgui_flags.aconfig
+++ b/libs/gui/libgui_flags.aconfig
@@ -2,6 +2,14 @@
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/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index 2e6ffcb..b026e64 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -27,6 +27,7 @@
#include <gui/Surface.h>
#include <ui/GraphicBuffer.h>
+#include <ui/PictureProfileHandle.h>
#include <android-base/properties.h>
@@ -1569,4 +1570,61 @@
EXPECT_EQ(ADATASPACE_UNKNOWN, dataSpace);
}
+TEST_F(BufferQueueTest, PassesThroughPictureProfileHandle) {
+ createBufferQueue();
+ sp<MockConsumer> mc(new MockConsumer);
+ mConsumer->consumerConnect(mc, false);
+
+ IGraphicBufferProducer::QueueBufferOutput qbo;
+ mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false, &qbo);
+ mProducer->setMaxDequeuedBufferCount(2);
+ mConsumer->setMaxAcquiredBufferCount(2);
+
+ // First try to pass a valid picture profile handle
+ {
+ int slot;
+ sp<Fence> fence;
+ sp<GraphicBuffer> buf;
+ IGraphicBufferProducer::QueueBufferInput qbi(0, false, HAL_DATASPACE_UNKNOWN,
+ Rect(0, 0, 1, 1),
+ NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
+ Fence::NO_FENCE);
+ qbi.setPictureProfileHandle(PictureProfileHandle(1));
+
+ EXPECT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
+ mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0, GRALLOC_USAGE_SW_READ_OFTEN,
+ nullptr, nullptr));
+ EXPECT_EQ(OK, mProducer->requestBuffer(slot, &buf));
+ EXPECT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo));
+
+ BufferItem item;
+ EXPECT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+
+ ASSERT_TRUE(item.mPictureProfileHandle.has_value());
+ ASSERT_EQ(item.mPictureProfileHandle, PictureProfileHandle(1));
+ }
+
+ // Then validate that the picture profile handle isn't sticky and is reset for the next buffer
+ {
+ int slot;
+ sp<Fence> fence;
+ sp<GraphicBuffer> buf;
+ IGraphicBufferProducer::QueueBufferInput qbi(0, false, HAL_DATASPACE_UNKNOWN,
+ Rect(0, 0, 1, 1),
+ NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
+ Fence::NO_FENCE);
+
+ EXPECT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
+ mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0, GRALLOC_USAGE_SW_READ_OFTEN,
+ nullptr, nullptr));
+ EXPECT_EQ(OK, mProducer->requestBuffer(slot, &buf));
+ EXPECT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo));
+
+ BufferItem item;
+ EXPECT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+
+ ASSERT_FALSE(item.mPictureProfileHandle.has_value());
+ }
+}
+
} // namespace android
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index a481d12..0e84d68 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -112,7 +112,7 @@
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 @@
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 @@
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 @@
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:
+ // 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 @@
injectTap(101, 101);
- EXPECT_NE(surface->consumeEvent(), nullptr);
+ surface->expectTap(1, 1);
}
/**
@@ -521,7 +523,7 @@
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 @@
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 @@
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 @@
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 @@
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, 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 @@
// 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 @@
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_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 @@
// 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 @@
// 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 @@
// 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 @@
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 @@
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_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_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 @@
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 @@
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 @@
* 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 @@
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 e4e81ad..a4ae54b 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -217,6 +217,7 @@
],
srcs: [
"AccelerationCurve.cpp",
+ "CoordinateFilter.cpp",
"Input.cpp",
"InputConsumer.cpp",
"InputConsumerNoResampling.cpp",
@@ -230,6 +231,7 @@
"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 0000000..d231474
--- /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/InputConsumerNoResampling.cpp b/libs/input/InputConsumerNoResampling.cpp
index d3653cf..2c0f77a 100644
--- a/libs/input/InputConsumerNoResampling.cpp
+++ b/libs/input/InputConsumerNoResampling.cpp
@@ -193,13 +193,6 @@
InputConsumerNoResampling::~InputConsumerNoResampling() {
ensureCalledOnLooperThread(__func__);
- while (!mOutboundQueue.empty()) {
- processOutboundEvents();
- // This is our last chance to ack the events. If we don't ack them here, we will get an ANR,
- // so keep trying to send the events as long as they are present in the queue.
- }
-
- setFdEvents(0);
// If there are any remaining unread batches, send an ack for them and don't deliver
// them to callbacks.
for (auto& [_, batches] : mBatches) {
@@ -208,6 +201,12 @@
batches.pop();
}
}
+
+ while (!mOutboundQueue.empty()) {
+ processOutboundEvents();
+ // This is our last chance to ack the events. If we don't ack them here, we will get an ANR,
+ // so keep trying to send the events as long as they are present in the queue.
+ }
// However, it is still up to the app to finish any events that have already been delivered
// to the callbacks. If we wanted to change that behaviour and auto-finish all unfinished events
// that were already sent to callbacks, we could potentially loop through "mConsumeTimes"
@@ -216,6 +215,10 @@
const size_t unfinishedEvents = mConsumeTimes.size();
LOG_IF(INFO, unfinishedEvents != 0)
<< getName() << " has " << unfinishedEvents << " unfinished event(s)";
+ // Remove the fd from epoll, so that Looper does not call 'handleReceiveCallback' anymore.
+ // This must be done at the end of the destructor; otherwise, some of the other functions may
+ // call 'setFdEvents' as a side-effect, thus adding the fd back to the epoll set of the looper.
+ setFdEvents(0);
}
int InputConsumerNoResampling::handleReceiveCallback(int events) {
diff --git a/libs/input/InputEventLabels.cpp b/libs/input/InputEventLabels.cpp
index 8db0ca5..b537feb 100644
--- a/libs/input/InputEventLabels.cpp
+++ b/libs/input/InputEventLabels.cpp
@@ -350,7 +350,26 @@
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/InputTransport.cpp b/libs/input/InputTransport.cpp
index 77dcaa9..6a55726 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -583,15 +583,6 @@
StringPrintf("publishMotionEvent(inputChannel=%s, action=%s)",
mChannel->getName().c_str(),
MotionEvent::actionToString(action).c_str()));
- if (verifyEvents()) {
- Result<void> result =
- mInputVerifier.processMovement(deviceId, source, action, pointerCount,
- pointerProperties, pointerCoords, flags);
- if (!result.ok()) {
- LOG(ERROR) << "Bad stream: " << result.error();
- return BAD_VALUE;
- }
- }
if (debugTransportPublisher()) {
std::string transformString;
transform.dump(transformString, "transform", " ");
@@ -657,8 +648,18 @@
msg.body.motion.pointers[i].properties = pointerProperties[i];
msg.body.motion.pointers[i].coords = pointerCoords[i];
}
+ const status_t status = mChannel->sendMessage(&msg);
- return mChannel->sendMessage(&msg);
+ if (status == OK && verifyEvents()) {
+ Result<void> result =
+ mInputVerifier.processMovement(deviceId, source, action, pointerCount,
+ pointerProperties, pointerCoords, flags);
+ if (!result.ok()) {
+ LOG(ERROR) << "Bad stream: " << result.error();
+ return BAD_VALUE;
+ }
+ }
+ return status;
}
status_t InputPublisher::publishFocusEvent(uint32_t seq, int32_t eventId, bool hasFocus) {
diff --git a/libs/input/KeyboardClassifier.cpp b/libs/input/KeyboardClassifier.cpp
index 0c2c7be..2a83919 100644
--- a/libs/input/KeyboardClassifier.cpp
+++ b/libs/input/KeyboardClassifier.cpp
@@ -57,14 +57,14 @@
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 0000000..400d7c9
--- /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 056db09..3ab132d 100644
--- a/libs/input/Resampler.cpp
+++ b/libs/input/Resampler.cpp
@@ -389,4 +389,34 @@
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/tests/Android.bp b/libs/input/tests/Android.bp
index 661c9f7..46e8190 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -25,6 +25,7 @@
"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/InputConsumer_test.cpp b/libs/input/tests/InputConsumer_test.cpp
index 06e19bb..226b892 100644
--- a/libs/input/tests/InputConsumer_test.cpp
+++ b/libs/input/tests/InputConsumer_test.cpp
@@ -70,11 +70,20 @@
[]() { return std::make_unique<LegacyResampler>(); });
}
- void invokeLooperCallback() const {
+ bool invokeLooperCallback() const {
sp<LooperCallback> callback;
- ASSERT_TRUE(mLooper->getFdStateDebug(mClientTestChannel->getFd(), /*ident=*/nullptr,
- /*events=*/nullptr, &callback, /*data=*/nullptr));
+ const bool found =
+ mLooper->getFdStateDebug(mClientTestChannel->getFd(), /*ident=*/nullptr,
+ /*events=*/nullptr, &callback, /*data=*/nullptr);
+ if (!found) {
+ return false;
+ }
+ if (callback == nullptr) {
+ LOG(FATAL) << "Looper has the fd of interest, but the callback is null!";
+ return false;
+ }
callback->handleEvent(mClientTestChannel->getFd(), ALOOPER_EVENT_INPUT, /*data=*/nullptr);
+ return true;
}
void assertOnBatchedInputEventPendingWasCalled() {
@@ -271,6 +280,27 @@
}
/**
+ * Check what happens when looper invokes callback after consumer has been destroyed.
+ * This reproduces a crash where the LooperEventCallback was added back to the Looper during
+ * destructor, thus allowing the looper callback to be invoked onto a null consumer object.
+ */
+TEST_F(InputConsumerTest, LooperCallbackInvokedAfterConsumerDestroyed) {
+ mClientTestChannel->enqueueMessage(
+ InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/0}.action(ACTION_DOWN).build());
+ mClientTestChannel->enqueueMessage(
+ InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/1}.action(ACTION_MOVE).build());
+ ASSERT_TRUE(invokeLooperCallback());
+ assertOnBatchedInputEventPendingWasCalled();
+ assertReceivedMotionEvent(WithMotionAction(ACTION_DOWN));
+ mClientTestChannel->assertFinishMessage(/*seq=*/0, /*handled=*/true);
+
+ // Now, destroy the consumer and invoke the looper callback again after it's been destroyed.
+ mConsumer.reset();
+ mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/false);
+ ASSERT_FALSE(invokeLooperCallback());
+}
+
+/**
* Send an event to the InputConsumer, but do not invoke "consumeBatchedInputEvents", thus leaving
* the input event unconsumed by the callbacks. Ensure that no crash occurs when the consumer is
* destroyed.
diff --git a/libs/input/tests/OneEuroFilter_test.cpp b/libs/input/tests/OneEuroFilter_test.cpp
new file mode 100644
index 0000000..270e789
--- /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/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp
index ac3a832..5ce4076 100644
--- a/libs/nativewindow/ANativeWindow.cpp
+++ b/libs/nativewindow/ANativeWindow.cpp
@@ -222,6 +222,8 @@
static_cast<int>(HAL_DATASPACE_BT2020_ITU_HLG));
static_assert(static_cast<int>(ADATASPACE_DEPTH) == static_cast<int>(HAL_DATASPACE_DEPTH));
static_assert(static_cast<int>(ADATASPACE_DYNAMIC_DEPTH) == static_cast<int>(HAL_DATASPACE_DYNAMIC_DEPTH));
+ static_assert(static_cast<int>(ADATASPACE_DISPLAY_BT2020) ==
+ static_cast<int>(HAL_DATASPACE_DISPLAY_BT2020));
if (!window || !query(window, NATIVE_WINDOW_IS_VALID)) {
return -EINVAL;
diff --git a/libs/nativewindow/include/android/data_space.h b/libs/nativewindow/include/android/data_space.h
index 8056d9a..295a307 100644
--- a/libs/nativewindow/include/android/data_space.h
+++ b/libs/nativewindow/include/android/data_space.h
@@ -578,6 +578,13 @@
*/
ADATASPACE_BT2020_ITU_HLG = 302383104, // ADATASPACE_STANDARD_BT2020 | ADATASPACE_TRANSFER_HLG |
// ADATASPACE_RANGE_LIMITED
+ /**
+ * sRGB-encoded BT. 2020
+ *
+ * Uses full range, sRGB transfer and BT2020 standard.
+ */
+ ADATASPACE_DISPLAY_BT2020 = 142999552, // ADATASPACE_STANDARD_BT2020 | ADATASPACE_TRANSFER_SRGB
+ // | ADATASPACE_RANGE_FULL
/**
* Depth
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index d248ea0..7f207f0 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -105,6 +105,7 @@
"skia/filters/KawaseBlurDualFilter.cpp",
"skia/filters/KawaseBlurFilter.cpp",
"skia/filters/LinearEffect.cpp",
+ "skia/filters/LutShader.cpp",
"skia/filters/MouriMap.cpp",
"skia/filters/StretchShaderFactory.cpp",
"skia/filters/EdgeExtensionShaderFactory.cpp",
diff --git a/libs/renderengine/skia/GaneshVkRenderEngine.cpp b/libs/renderengine/skia/GaneshVkRenderEngine.cpp
index a3a43e2..cc73f40 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 @@
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 e6123c2..ba17f71 100644
--- a/libs/renderengine/skia/GaneshVkRenderEngine.h
+++ b/libs/renderengine/skia/GaneshVkRenderEngine.h
@@ -28,6 +28,7 @@
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 390ad6e..a9332fa 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 @@
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 cf24a3b..33a47f1 100644
--- a/libs/renderengine/skia/GraphiteVkRenderEngine.h
+++ b/libs/renderengine/skia/GraphiteVkRenderEngine.h
@@ -30,6 +30,7 @@
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 4ef7d5b..ddae9fc 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -541,7 +541,7 @@
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/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
index ec9d3ef..5c46c91 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -543,6 +543,10 @@
}
}
+ if (graphicBuffer && parameters.layer.luts) {
+ shader = mLutShader.lutShader(shader, parameters.layer.luts);
+ }
+
if (parameters.requiresLinearEffect) {
const auto format = targetBuffer != nullptr
? std::optional<ui::PixelFormat>(
diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h
index b5f8898..7be4c25 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.h
+++ b/libs/renderengine/skia/SkiaRenderEngine.h
@@ -39,6 +39,7 @@
#include "filters/BlurFilter.h"
#include "filters/EdgeExtensionShaderFactory.h"
#include "filters/LinearEffect.h"
+#include "filters/LutShader.h"
#include "filters/StretchShaderFactory.h"
class SkData;
@@ -184,6 +185,7 @@
StretchShaderFactory mStretchShaderFactory;
EdgeExtensionShaderFactory mEdgeExtensionShaderFactory;
+ LutShader mLutShader;
sp<Fence> mLastDrawFence;
BlurFilter* mBlurFilter = nullptr;
diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.cpp b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
index 677a2b6..177abe6 100644
--- a/libs/renderengine/skia/SkiaVkRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
@@ -169,24 +169,26 @@
}
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 d2bb3d5..88b04df 100644
--- a/libs/renderengine/skia/SkiaVkRenderEngine.h
+++ b/libs/renderengine/skia/SkiaVkRenderEngine.h
@@ -81,7 +81,7 @@
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/renderengine/skia/filters/LutShader.cpp b/libs/renderengine/skia/filters/LutShader.cpp
new file mode 100644
index 0000000..cea46ef
--- /dev/null
+++ b/libs/renderengine/skia/filters/LutShader.cpp
@@ -0,0 +1,242 @@
+/*
+ * 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 "LutShader.h"
+
+#include <SkTileMode.h>
+#include <common/trace.h>
+#include <cutils/ashmem.h>
+#include <math/half.h>
+#include <sys/mman.h>
+
+#include "include/core/SkColorSpace.h"
+#include "src/core/SkColorFilterPriv.h"
+
+using aidl::android::hardware::graphics::composer3::LutProperties;
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+static const SkString kShader = SkString(R"(
+ uniform shader image;
+ uniform shader lut;
+ uniform int size;
+ uniform int key;
+ uniform int dimension;
+ vec4 main(vec2 xy) {
+ float4 rgba = image.eval(xy);
+ float3 linear = toLinearSrgb(rgba.rgb);
+ if (dimension == 1) {
+ // RGB
+ if (key == 0) {
+ float indexR = linear.r * float(size - 1);
+ float indexG = linear.g * float(size - 1);
+ float indexB = linear.b * float(size - 1);
+ float gainR = lut.eval(vec2(indexR, 0.0) + 0.5).r;
+ float gainG = lut.eval(vec2(indexG, 0.0) + 0.5).r;
+ float gainB = lut.eval(vec2(indexB, 0.0) + 0.5).r;
+ return float4(linear.r * gainR, linear.g * gainG, linear.b * gainB, rgba.a);
+ // MAX_RGB
+ } else if (key == 1) {
+ float4 rgba = image.eval(xy);
+ float3 linear = toLinearSrgb(rgba.rgb);
+ float maxRGB = max(linear.r, max(linear.g, linear.b));
+ float index = maxRGB * float(size - 1);
+ float gain = lut.eval(vec2(index, 0.0) + 0.5).r;
+ return float4(linear * gain, rgba.a);
+ }
+ } else if (dimension == 3) {
+ if (key == 0) {
+ float tx = linear.r * float(size - 1);
+ float ty = linear.g * float(size - 1);
+ float tz = linear.b * float(size - 1);
+
+ // calculate lower and upper bounds for each dimension
+ int x = int(tx);
+ int y = int(ty);
+ int z = int(tz);
+
+ int i000 = x + y * size + z * size * size;
+ int i100 = i000 + 1;
+ int i010 = i000 + size;
+ int i110 = i000 + size + 1;
+ int i001 = i000 + size * size;
+ int i101 = i000 + size * size + 1;
+ int i011 = i000 + size * size + size;
+ int i111 = i000 + size * size + size + 1;
+
+ // get 1d normalized indices
+ float c000 = float(i000) / float(size * size * size);
+ float c100 = float(i100) / float(size * size * size);
+ float c010 = float(i010) / float(size * size * size);
+ float c110 = float(i110) / float(size * size * size);
+ float c001 = float(i001) / float(size * size * size);
+ float c101 = float(i101) / float(size * size * size);
+ float c011 = float(i011) / float(size * size * size);
+ float c111 = float(i111) / float(size * size * size);
+
+ //TODO(b/377984618): support Tetrahedral interpolation
+ // perform trilinear interpolation
+ float3 c00 = mix(lut.eval(vec2(c000, 0.0) + 0.5).rgb,
+ lut.eval(vec2(c100, 0.0) + 0.5).rgb, linear.r);
+ float3 c01 = mix(lut.eval(vec2(c001, 0.0) + 0.5).rgb,
+ lut.eval(vec2(c101, 0.0) + 0.5).rgb, linear.r);
+ float3 c10 = mix(lut.eval(vec2(c010, 0.0) + 0.5).rgb,
+ lut.eval(vec2(c110, 0.0) + 0.5).rgb, linear.r);
+ float3 c11 = mix(lut.eval(vec2(c011, 0.0) + 0.5).rgb,
+ lut.eval(vec2(c111, 0.0) + 0.5).rgb, linear.r);
+
+ float3 c0 = mix(c00, c10, linear.g);
+ float3 c1 = mix(c01, c11, linear.g);
+
+ float3 val = mix(c0, c1, linear.b);
+
+ return float4(val, rgba.a);
+ }
+ }
+ return rgba;
+ })");
+
+sk_sp<SkShader> LutShader::generateLutShader(sk_sp<SkShader> input,
+ const std::vector<float>& buffers,
+ const int32_t offset, const int32_t length,
+ const int32_t dimension, const int32_t size,
+ const int32_t samplingKey) {
+ SFTRACE_NAME("lut shader");
+ std::vector<half> buffer(length * 4); // 4 is for RGBA
+ auto d = static_cast<LutProperties::Dimension>(dimension);
+ if (d == LutProperties::Dimension::ONE_D) {
+ auto it = buffers.begin() + offset;
+ std::generate(buffer.begin(), buffer.end(), [it, i = 0]() mutable {
+ float val = (i++ % 4 == 0) ? *it++ : 0.0f;
+ return half(val);
+ });
+ } else {
+ for (int i = 0; i < length; i++) {
+ buffer[i * 4] = half(buffers[offset + i]);
+ buffer[i * 4 + 1] = half(buffers[offset + length + i]);
+ buffer[i * 4 + 2] = half(buffers[offset + length * 2 + i]);
+ buffer[i * 4 + 3] = half(0);
+ }
+ }
+ /**
+ * 1D Lut(rgba)
+ * (R0, 0, 0, 0)
+ * (R1, 0, 0, 0)
+ * ...
+ *
+ * 3D Lut
+ * (R0, G0, B0, 0)
+ * (R1, G1, B1, 0)
+ * ...
+ */
+ SkImageInfo info = SkImageInfo::Make(length /* the number of rgba */ * 4, 1,
+ kRGBA_F16_SkColorType, kPremul_SkAlphaType);
+ SkBitmap bitmap;
+ bitmap.allocPixels(info);
+ if (!bitmap.installPixels(info, buffer.data(), info.minRowBytes())) {
+ LOG_ALWAYS_FATAL("unable to install pixels");
+ }
+
+ sk_sp<SkImage> lutImage = SkImages::RasterFromBitmap(bitmap);
+ mBuilder->child("image") = input;
+ mBuilder->child("lut") =
+ lutImage->makeRawShader(SkTileMode::kClamp, SkTileMode::kClamp,
+ d == LutProperties::Dimension::ONE_D
+ ? SkSamplingOptions(SkFilterMode::kLinear)
+ : SkSamplingOptions());
+
+ const int uSize = static_cast<int>(size);
+ const int uKey = static_cast<int>(samplingKey);
+ const int uDimension = static_cast<int>(dimension);
+ mBuilder->uniform("size") = uSize;
+ mBuilder->uniform("key") = uKey;
+ mBuilder->uniform("dimension") = uDimension;
+ return mBuilder->makeShader();
+}
+
+sk_sp<SkShader> LutShader::lutShader(sk_sp<SkShader>& input,
+ std::shared_ptr<gui::DisplayLuts> displayLuts) {
+ if (mBuilder == nullptr) {
+ const static SkRuntimeEffect::Result instance = SkRuntimeEffect::MakeForShader(kShader);
+ mBuilder = std::make_unique<SkRuntimeShaderBuilder>(instance.effect);
+ }
+
+ auto& fd = displayLuts->getLutFileDescriptor();
+ if (fd.ok()) {
+ // de-gamma the image without changing the primaries
+ SkImage* baseImage = input->isAImage((SkMatrix*)nullptr, (SkTileMode*)nullptr);
+ if (baseImage) {
+ sk_sp<SkColorSpace> baseColorSpace =
+ baseImage->colorSpace() ? baseImage->refColorSpace() : SkColorSpace::MakeSRGB();
+ sk_sp<SkColorSpace> gainmapMathColorSpace = baseColorSpace->makeLinearGamma();
+ auto colorXformSdrToGainmap =
+ SkColorFilterPriv::MakeColorSpaceXform(baseColorSpace, gainmapMathColorSpace);
+ input = input->makeWithColorFilter(colorXformSdrToGainmap);
+ }
+
+ auto& offsets = displayLuts->offsets;
+ auto& lutProperties = displayLuts->lutProperties;
+ std::vector<float> buffers;
+ int fullLength = offsets[lutProperties.size() - 1];
+ if (lutProperties[lutProperties.size() - 1].dimension == 1) {
+ fullLength += lutProperties[lutProperties.size() - 1].size;
+ } else {
+ fullLength += (lutProperties[lutProperties.size() - 1].size *
+ lutProperties[lutProperties.size() - 1].size *
+ lutProperties[lutProperties.size() - 1].size * 3);
+ }
+ size_t bufferSize = fullLength * sizeof(float);
+
+ // decode the shared memory of luts
+ float* ptr =
+ (float*)mmap(NULL, bufferSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd.get(), 0);
+ if (ptr == MAP_FAILED) {
+ LOG_ALWAYS_FATAL("mmap failed");
+ }
+ buffers = std::vector<float>(ptr, ptr + fullLength);
+ munmap(ptr, bufferSize);
+
+ for (size_t i = 0; i < offsets.size(); i++) {
+ int bufferSizePerLut = (i == offsets.size() - 1) ? buffers.size() - offsets[i]
+ : offsets[i + 1] - offsets[i];
+ // divide by 3 for 3d Lut because of 3 (RGB) channels
+ if (static_cast<LutProperties::Dimension>(lutProperties[i].dimension) ==
+ LutProperties::Dimension::THREE_D) {
+ bufferSizePerLut /= 3;
+ }
+ input = generateLutShader(input, buffers, offsets[i], bufferSizePerLut,
+ lutProperties[i].dimension, lutProperties[i].size,
+ lutProperties[i].samplingKey);
+ }
+
+ // re-gamma
+ baseImage = input->isAImage((SkMatrix*)nullptr, (SkTileMode*)nullptr);
+ if (baseImage) {
+ sk_sp<SkColorSpace> baseColorSpace =
+ baseImage->colorSpace() ? baseImage->refColorSpace() : SkColorSpace::MakeSRGB();
+ sk_sp<SkColorSpace> gainmapMathColorSpace = baseColorSpace->makeLinearGamma();
+ auto colorXformGainmapToDst =
+ SkColorFilterPriv::MakeColorSpaceXform(gainmapMathColorSpace, baseColorSpace);
+ input = input->makeWithColorFilter(colorXformGainmapToDst);
+ }
+ }
+ return input;
+}
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
\ No newline at end of file
diff --git a/libs/renderengine/skia/filters/LutShader.h b/libs/renderengine/skia/filters/LutShader.h
new file mode 100644
index 0000000..c157904
--- /dev/null
+++ b/libs/renderengine/skia/filters/LutShader.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *-
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <SkBitmap.h>
+#include <SkImage.h>
+#include <SkRuntimeEffect.h>
+
+#include <aidl/android/hardware/graphics/composer3/LutProperties.h>
+#include <gui/DisplayLuts.h>
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+class LutShader {
+public:
+ sk_sp<SkShader> lutShader(sk_sp<SkShader>& input,
+ std::shared_ptr<gui::DisplayLuts> displayLuts);
+
+private:
+ sk_sp<SkShader> generateLutShader(sk_sp<SkShader> input, const std::vector<float>& buffers,
+ const int32_t offset, const int32_t length,
+ const int32_t dimension, const int32_t size,
+ const int32_t samplingKey);
+ std::unique_ptr<SkRuntimeShaderBuilder> mBuilder;
+};
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
diff --git a/libs/sensor/Sensor.cpp b/libs/sensor/Sensor.cpp
index eddd568..797efbe 100644
--- a/libs/sensor/Sensor.cpp
+++ b/libs/sensor/Sensor.cpp
@@ -306,7 +306,18 @@
}
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/tracing_perfetto_internal.cpp b/libs/tracing_perfetto/tracing_perfetto_internal.cpp
index 9a0042a..c4f8663 100644
--- a/libs/tracing_perfetto/tracing_perfetto_internal.cpp
+++ b/libs/tracing_perfetto/tracing_perfetto_internal.cpp
@@ -253,15 +253,31 @@
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 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 12230f9..87e213e 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -136,6 +136,7 @@
"GraphicBuffer.cpp",
"GraphicBufferAllocator.cpp",
"GraphicBufferMapper.cpp",
+ "PictureProfileHandle.cpp",
"PixelFormat.cpp",
"PublicFormat.cpp",
"StaticAsserts.cpp",
diff --git a/libs/ui/Gralloc5.cpp b/libs/ui/Gralloc5.cpp
index c9ec036..2143f79 100644
--- a/libs/ui/Gralloc5.cpp
+++ b/libs/ui/Gralloc5.cpp
@@ -23,7 +23,6 @@
#include <aidlcommonsupport/NativeHandle.h>
#include <android/binder_manager.h>
#include <android/hardware/graphics/mapper/utils/IMapperMetadataTypes.h>
-#include <android/llndk-versioning.h>
#include <binder/IPCThreadState.h>
#include <dlfcn.h>
#include <ui/FatVector.h>
@@ -91,7 +90,7 @@
}
void* so = nullptr;
- if API_LEVEL_AT_LEAST (__ANDROID_API_V__, 202404) {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
so = AServiceManager_openDeclaredPassthroughHal("mapper", mapperSuffix.c_str(),
RTLD_LOCAL | RTLD_NOW);
} else {
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.cpp b/libs/ui/PictureProfileHandle.cpp
similarity index 62%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.cpp
copy to libs/ui/PictureProfileHandle.cpp
index 1ba38a8..0701e90 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.cpp
+++ b/libs/ui/PictureProfileHandle.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * 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.
@@ -14,12 +14,16 @@
* limitations under the License.
*/
-#include "MockPowerAdvisor.h"
+#include <ui/PictureProfileHandle.h>
-namespace android::Hwc2::mock {
+#include <format>
-// Explicit default instantiation is recommended.
-PowerAdvisor::PowerAdvisor() = default;
-PowerAdvisor::~PowerAdvisor() = default;
+namespace android {
-} // namespace android::Hwc2::mock
+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/PictureProfileHandle.h b/libs/ui/include/ui/PictureProfileHandle.h
new file mode 100644
index 0000000..f840650
--- /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; }
+ explicit 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 4f9d9e4..755995c 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -836,13 +836,9 @@
}
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 6ef4a3d..305feab 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -242,6 +242,9 @@
// 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 @@
touchpadTapDraggingEnabled(false),
shouldNotifyTouchpadHardwareState(false),
touchpadRightClickZoneEnabled(false),
+ touchpadThreeFingerTapShortcutEnabled(false),
stylusButtonMotionEventsEnabled(true),
stylusPointerIconEnabled(false),
mouseReverseVerticalScrollingEnabled(false),
@@ -496,6 +500,9 @@
/* 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/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index 35ba48f..013ef86 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -129,7 +129,8 @@
{"multi_intensity", InputLightClass::MULTI_INTENSITY},
{"max_brightness", InputLightClass::MAX_BRIGHTNESS},
{"kbd_backlight", InputLightClass::KEYBOARD_BACKLIGHT},
- {"mic_mute", InputLightClass::KEYBOARD_MIC_MUTE}};
+ {"mic_mute", InputLightClass::KEYBOARD_MIC_MUTE},
+ {"mute", InputLightClass::KEYBOARD_VOLUME_MUTE}};
// Mapping for input multicolor led class node names.
// https://www.kernel.org/doc/html/latest/leds/leds-class-multicolor.html
diff --git a/services/inputflinger/reader/controller/PeripheralController.cpp b/services/inputflinger/reader/controller/PeripheralController.cpp
index 49ad8b5..9eeb2b2 100644
--- a/services/inputflinger/reader/controller/PeripheralController.cpp
+++ b/services/inputflinger/reader/controller/PeripheralController.cpp
@@ -514,6 +514,8 @@
type = InputDeviceLightType::KEYBOARD_BACKLIGHT;
} else if (rawInfo.flags.test(InputLightClass::KEYBOARD_MIC_MUTE)) {
type = InputDeviceLightType::KEYBOARD_MIC_MUTE;
+ } else if (rawInfo.flags.test(InputLightClass::KEYBOARD_VOLUME_MUTE)) {
+ type = InputDeviceLightType::KEYBOARD_VOLUME_MUTE;
} else {
type = InputDeviceLightType::INPUT;
}
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index 4336945..5839b4c 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -179,6 +179,8 @@
KEYBOARD_BACKLIGHT = 0x00000100,
/* The input light has mic_mute name */
KEYBOARD_MIC_MUTE = 0x00000200,
+ /* The input light has mute name */
+ KEYBOARD_VOLUME_MUTE = 0x00000400,
};
enum class InputBatteryClass : uint32_t {
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
index 9a36bfb..ca8266b 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -375,6 +375,9 @@
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 da2c683..1959423 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
@@ -261,6 +261,14 @@
}
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 @@
// 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 c9a35c1..ad40721 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.h
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
@@ -55,6 +55,10 @@
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 @@
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/FakeInputReaderPolicy.cpp b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
index 7c5f350..67b1e8c 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.cpp
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
@@ -80,6 +80,17 @@
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 @@
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 3a2b4e9..42c9567 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.h
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.h
@@ -43,6 +43,7 @@
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 @@
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 @@
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 225ae0f..fad8f05 100644
--- a/services/inputflinger/tests/GestureConverter_test.cpp
+++ b/services/inputflinger/tests/GestureConverter_test.cpp
@@ -1272,6 +1272,27 @@
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 7dff144..ca797dc 100644
--- a/services/inputflinger/tests/InputMapperTest.cpp
+++ b/services/inputflinger/tests/InputMapperTest.cpp
@@ -53,13 +53,13 @@
}
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 fc27e4f..b6c5812 100644
--- a/services/inputflinger/tests/InputMapperTest.h
+++ b/services/inputflinger/tests/InputMapperTest.h
@@ -43,7 +43,8 @@
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/SensorInputMapper_test.cpp b/services/inputflinger/tests/SensorInputMapper_test.cpp
index 01814a6..2c12653 100644
--- a/services/inputflinger/tests/SensorInputMapper_test.cpp
+++ b/services/inputflinger/tests/SensorInputMapper_test.cpp
@@ -16,168 +16,164 @@
#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 {
-class SensorInputMapperTest : public InputMapperTest {
+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:
- 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;
+ 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));
+ }
- 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); }
+ void setupSensor(int32_t absCode, InputDeviceSensorType type, int32_t sensorDataIndex) {
+ EXPECT_CALL(mMockEventHub, mapSensor(EVENTHUB_ID, absCode))
+ .WillRepeatedly(Return(std::make_pair(type, sensorDataIndex)));
+ }
};
-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>();
+ mMapper = createInputMapper<SensorInputMapper>(*mDeviceContext,
+ mFakePolicy->getReaderConfiguration());
- ASSERT_EQ(static_cast<uint32_t>(AINPUT_SOURCE_SENSOR), mapper.getSources());
+ ASSERT_EQ(static_cast<uint32_t>(AINPUT_SOURCE_SENSOR), mMapper->getSources());
}
TEST_F(SensorInputMapperTest, ProcessAccelerometerSensor) {
- setAccelProperties();
- prepareAccelAxes();
- SensorInputMapper& mapper = constructAndAddMapper<SensorInputMapper>();
+ 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());
- 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);
+ 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);
- 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);
+ 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) {
- setGyroProperties();
- prepareGyroAxes();
- SensorInputMapper& mapper = constructAndAddMapper<SensorInputMapper>();
+ 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());
- 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);
+ 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);
- 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);
+ 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/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h
index 60c676d..a1da39a 100644
--- a/services/inputflinger/tests/fuzzers/MapperHelpers.h
+++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h
@@ -288,6 +288,7 @@
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/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index f1c79c1..8a667ae 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -177,13 +177,13 @@
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 7095b9d..82eafd4 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -42,6 +42,7 @@
"libutils",
],
static_libs: [
+ "libguiflags",
"libmath",
"librenderengine",
"libtimestats",
@@ -49,9 +50,7 @@
"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 6e60839..252adaa 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 @@
// 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 @@
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/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index 4e080b3..a5e9dde 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -148,9 +148,6 @@
virtual std::optional<LayerSettings> prepareClientComposition(
ClientCompositionTargetSettings&) const = 0;
- // Called after the layer is displayed to update the presentation fence
- virtual void onLayerDisplayed(ftl::SharedFuture<FenceResult>, ui::LayerStack layerStack) = 0;
-
// Initializes a promise for a buffer release fence and provides the future for that
// fence. This should only be called when a promise has not yet been created, or
// after the previous promise has already been fulfilled. Attempting to call this
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index 8a91a07..fb8fed0 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 @@
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 556aa24..bda7856 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 @@
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 @@
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 80c5124..2e7a7d9 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 @@
// 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 d8466ff..5519aaf 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 @@
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 @@
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 69e1efc..0ccdd22 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 @@
// 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 @@
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 @@
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 f8ffde1..c76b344 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 @@
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 0c7e4dd..712b551 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 @@
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,
@@ -101,7 +105,7 @@
aidl::android::hardware::graphics::composer3::Composition from,
aidl::android::hardware::graphics::composer3::Composition to) const;
bool isClientCompositionForced(bool isPeekingThrough) const;
- void updateLuts(std::shared_ptr<gui::DisplayLuts> luts,
+ void updateLuts(const LayerFECompositionState&,
const std::optional<std::vector<std::optional<LutProperties>>>& properties);
};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
index 28216a4..c558739 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 @@
// 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/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
index 05a5d38..c7ff704 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
@@ -50,9 +50,6 @@
std::optional<compositionengine::LayerFE::LayerSettings>(
compositionengine::LayerFE::ClientCompositionTargetSettings&));
- MOCK_METHOD(void, onLayerDisplayed, (ftl::SharedFuture<FenceResult>, ui::LayerStack),
- (override));
-
MOCK_METHOD0(createReleaseFenceFuture, ftl::Future<FenceResult>());
MOCK_METHOD1(setReleaseFence, void(const FenceResult&));
MOCK_METHOD0(getReleaseFencePromiseStatus, LayerFE::ReleaseFencePromiseStatus());
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index 33cdc54..f2c265a 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -34,7 +34,7 @@
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 @@
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 12f2094..9333ebb 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
@@ -63,7 +63,9 @@
(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 1825065..e37ce0a 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 @@
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 @@
return mId.isVirtual();
}
-std::optional<DisplayId> Display::getDisplayId() const {
+ftl::Optional<DisplayId> Display::getDisplayId() const {
return mId;
}
@@ -203,15 +205,16 @@
}
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 @@
}
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 @@
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 @@
}
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 2d10866..348111d 100644
--- a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
@@ -127,6 +127,9 @@
}
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 bb45613..98b6666 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 @@
mRenderSurface->isValid();
}
-std::optional<DisplayId> Output::getDisplayId() const {
+ftl::Optional<DisplayId> Output::getDisplayId() const {
return {};
}
@@ -433,7 +436,7 @@
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 @@
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 @@
// 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::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 @@
forceClientComposition = false;
}
}
+ commitPictureProfilesToCompositionState();
}
void Output::planComposition() {
@@ -847,7 +888,7 @@
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 @@
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 @@
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 934909d..f6d9a1a 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
@@ -286,8 +288,13 @@
}
void OutputLayer::updateLuts(
- std::shared_ptr<gui::DisplayLuts> layerFEStateLut,
+ const LayerFECompositionState& layerFEState,
const std::optional<std::vector<std::optional<LutProperties>>>& properties) {
+ auto& luts = layerFEState.luts;
+ if (!luts) {
+ return;
+ }
+
auto& state = editState();
if (!properties) {
@@ -303,7 +310,7 @@
}
}
- for (const auto& inputLut : layerFEStateLut->lutProperties) {
+ for (const auto& inputLut : luts->lutProperties) {
bool foundInHwcLuts = false;
for (const auto& hwcLut : hwcLutProperties) {
if (static_cast<int32_t>(hwcLut.dimension) ==
@@ -316,7 +323,7 @@
break;
}
}
- // if any lut properties of layerFEStateLut can not be found in hwcLutProperties,
+ // if any lut properties of luts can not be found in hwcLutProperties,
// GPU composition instead
if (!foundInHwcLuts) {
state.forceClientComposition = true;
@@ -409,10 +416,7 @@
state.whitePointNits = layerBrightnessNits;
}
- const auto& layerFEStateLut = layerFEState->luts;
- if (layerFEStateLut) {
- updateLuts(layerFEStateLut, properties);
- }
+ updateLuts(*layerFEState, properties);
// These are evaluated every frame as they can potentially change at any
// time.
@@ -422,6 +426,16 @@
}
}
+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 +657,21 @@
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 +777,16 @@
}
}
+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 +869,14 @@
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 da1f7e4..deef747 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
@@ -72,6 +72,9 @@
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 416001e..c1e59d0 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 @@
}
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 @@
}
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 f2c5672..dbffe80 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 @@
/*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 fe7dd9a..99e68eb 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 @@
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 @@
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 @@
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 @@
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 402a3d2..c743ea2 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -201,6 +201,10 @@
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 3e3f558..af2b48f 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 @@
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 @@
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 5814aa4..4c8ff58 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -44,12 +44,11 @@
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 @@
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 d724b21..933e8d1 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
@@ -250,6 +250,9 @@
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 42ddcd1..c1333c2 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 @@
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 5355a12..a274995 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::GraphicBuffer;
using android::HdrCapabilities;
using android::HdrMetadata;
+using android::PictureProfileHandle;
using android::Rect;
using android::Region;
using android::sp;
@@ -655,6 +657,16 @@
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 @@
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 799fd02..6740d8a 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 @@
[[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 @@
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 @@
[[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 @@
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 @@
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 7d77634..61d4541 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -1022,6 +1022,24 @@
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 7b04d67..e21ce1d 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 TestableSurfaceFlinger;
struct HWComposerTest;
struct CompositionInfo;
+class PictureProfileHandle;
namespace Hwc2 {
class Composer;
@@ -296,7 +298,7 @@
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 @@
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 @@
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 6a7a09b..e359a26 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -1446,6 +1446,18 @@
"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 a3d1f7f..9a89dba 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
@@ -357,6 +357,9 @@
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 47b811b..c13e444 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -378,6 +378,11 @@
}
}
+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 @@
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 cffb61e..6cda309 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -85,16 +85,20 @@
*/
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 @@
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 @@
// 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 @@
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 d709530..da536b6 100644
--- a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
@@ -166,7 +166,8 @@
}
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 11b674b..58f6b96 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
@@ -250,6 +250,7 @@
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 @@
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);
@@ -411,6 +413,13 @@
if (forceUpdate || requested.what & layer_state_t::eCropChanged) {
geomCrop = requested.crop;
}
+ if (forceUpdate || requested.what & layer_state_t::ePictureProfileHandleChanged) {
+ pictureProfileHandle = requested.pictureProfileHandle;
+ }
+ if (forceUpdate || requested.what & layer_state_t::eAppContentPriorityChanged) {
+ // TODO(b/337330263): Also consider the system-determined priority of the app
+ pictureProfilePriority = requested.appContentPriority;
+ }
if (forceUpdate || requested.what & layer_state_t::eDefaultFrameRateCompatibilityChanged) {
const auto compatibility =
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.h b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
index b7d4cc5..b8df3ed 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
@@ -77,6 +77,7 @@
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 10e212e..7569c1b 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -314,8 +314,8 @@
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 @@
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/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
index 713a5c5..ee9302b 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -163,7 +163,7 @@
uint64_t clientChanges = what | layer_state_t::diff(clientState);
layer_state_t::merge(clientState);
what = clientChanges;
- LLOGV(layerId, "requested=%" PRIu64 "flags=%" PRIu64, clientState.what, clientChanges);
+ LLOGV(layerId, "requested=%" PRIu64 " flags=%" PRIu64 " ", clientState.what, clientChanges);
if (clientState.what & layer_state_t::eFlagsChanged) {
if ((oldFlags ^ flags) &
@@ -633,7 +633,7 @@
layer_state_t::eEdgeExtensionChanged | layer_state_t::eBufferCropChanged |
layer_state_t::eDestinationFrameChanged | layer_state_t::eDimmingEnabledChanged |
layer_state_t::eExtendedRangeBrightnessChanged |
- layer_state_t::eDesiredHdrHeadroomChanged |
+ layer_state_t::eDesiredHdrHeadroomChanged | layer_state_t::eLutsChanged |
(FlagManager::getInstance().latch_unsignaled_with_auto_refresh_changed()
? layer_state_t::eFlagsChanged
: 0);
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 20ba45f..195461f 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -154,7 +154,7 @@
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 @@
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 @@
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 @@
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::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 @@
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 @@
}
}
+ // 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 @@
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 @@
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 a2716c6..c234a75 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 @@
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 @@
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/LayerFE.cpp b/services/surfaceflinger/LayerFE.cpp
index a346981..231b40b 100644
--- a/services/surfaceflinger/LayerFE.cpp
+++ b/services/surfaceflinger/LayerFE.cpp
@@ -27,7 +27,6 @@
#include "LayerFE.h"
#include "SurfaceFlinger.h"
#include "ui/FenceResult.h"
-#include "ui/LayerStack.h"
namespace android {
@@ -343,11 +342,6 @@
caster.shadow = state;
}
-void LayerFE::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult,
- ui::LayerStack layerStack) {
- mCompositionResult.releaseFences.emplace_back(std::move(futureFenceResult), layerStack);
-}
-
CompositionResult&& LayerFE::stealCompositionResult() {
return std::move(mCompositionResult);
}
diff --git a/services/surfaceflinger/LayerFE.h b/services/surfaceflinger/LayerFE.h
index 658f949..5081e10 100644
--- a/services/surfaceflinger/LayerFE.h
+++ b/services/surfaceflinger/LayerFE.h
@@ -22,14 +22,12 @@
#include "compositionengine/LayerFE.h"
#include "compositionengine/LayerFECompositionState.h"
#include "renderengine/LayerSettings.h"
-#include "ui/LayerStack.h"
#include <ftl/future.h>
namespace android {
struct CompositionResult {
- std::vector<std::pair<ftl::SharedFuture<FenceResult>, ui::LayerStack>> releaseFences;
sp<Fence> lastClientCompositionFence = nullptr;
};
@@ -41,7 +39,6 @@
// compositionengine::LayerFE overrides
const compositionengine::LayerFECompositionState* getCompositionState() const override;
bool onPreComposition(bool updatingOutputGeometryThisFrame) override;
- void onLayerDisplayed(ftl::SharedFuture<FenceResult>, ui::LayerStack) override;
const char* getDebugName() const override;
int32_t getSequence() const override;
bool hasRoundedCorners() const override;
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/PowerAdvisor/PowerAdvisor.cpp
similarity index 92%
rename from services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
rename to services/surfaceflinger/PowerAdvisor/PowerAdvisor.cpp
index c914ec3..c7d0b2c 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 android::adpf::impl {
-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;
-
-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 @@
} // 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 @@
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 @@
if (!mBootFinished.load()) {
return;
}
- sendHintSessionHint(SessionHint::CPU_LOAD_UP);
+ sendHintSessionHint(hal::SessionHint::CPU_LOAD_UP);
}
void PowerAdvisor::notifyDisplayUpdateImminentAndCpuReset() {
@@ -163,12 +150,12 @@
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 @@
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 @@
static_cast<int32_t>(getuid()),
mHintSessionThreadIds,
mTargetDuration.ns(),
- SessionTag::SURFACEFLINGER,
+ hal::SessionTag::SURFACEFLINGER,
&mSessionConfig);
if (ret.isOk()) {
mHintSession = ret.value();
@@ -326,7 +313,7 @@
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 @@
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 @@
}
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 @@
}),
};
} 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 @@
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 @@
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 @@
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
similarity index 97%
rename from services/surfaceflinger/DisplayHardware/PowerAdvisor.h
rename to services/surfaceflinger/PowerAdvisor/PowerAdvisor.h
index 1076b2b..458b46d 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 @@
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 @@
// 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 @@
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 @@
};
} // namespace impl
-} // namespace Hwc2
+} // namespace adpf
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index e385f18..7729671 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -420,6 +420,16 @@
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 @@
}
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 c3c7eb0..2daf126 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -106,6 +106,8 @@
// 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 @@
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 @@
// 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 64b85c0..e45bdfc 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -308,6 +308,12 @@
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 @@
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 @@
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 ad067be..84fa139 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -489,6 +489,20 @@
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 @@
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 @@
int explicitExact = 0;
int explicitGteLayers = 0;
int explicitCategoryVoteLayers = 0;
- int interactiveLayers = 0;
int seamedFocusedLayers = 0;
int categorySmoothSwitchOnlyLayers = 0;
@@ -563,11 +613,9 @@
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 @@
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 @@
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 @@
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 @@
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);
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index b83ff19..274e121 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -405,6 +405,14 @@
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 @@
#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 @@
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 c88b563..e77af60 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -151,9 +151,11 @@
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 8b204a2..0d03a6c 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -133,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"
@@ -153,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"
@@ -426,7 +426,11 @@
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());
@@ -1010,7 +1014,8 @@
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);
});
@@ -1353,7 +1358,8 @@
mScheduler->updatePhaseConfiguration(displayId, mode.fps);
if (emitEvent) {
- mScheduler->onDisplayModeChanged(displayId, mode);
+ mScheduler->onDisplayModeChanged(displayId, mode,
+ /*clearContentRequirements*/ false);
}
break;
case DesiredModeAction::None:
@@ -1448,7 +1454,7 @@
mScheduler->updatePhaseConfiguration(displayId, activeMode.fps);
if (pendingModeOpt->emitEvent) {
- mScheduler->onDisplayModeChanged(displayId, activeMode);
+ mScheduler->onDisplayModeChanged(displayId, activeMode, /*clearContentRequirements*/ true);
}
}
@@ -2531,17 +2537,13 @@
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);
@@ -2931,7 +2933,6 @@
mScheduler->modulateVsync({}, &VsyncModulator::onDisplayRefresh, hasGpuUseOrReuse);
mLayersWithQueuedFrames.clear();
- mLayersIdsWithQueuedFrames.clear();
doActiveLayersTracingIfNeeded(true, mVisibleRegionsDirty, pacesetterTarget.frameBeginTime(),
vsyncId);
@@ -3557,7 +3558,9 @@
}
state.isProtected = true;
state.displayName = std::move(info.name);
-
+ state.maxLayerPictureProfiles = getHwComposer().getMaxLayerPictureProfiles(displayId);
+ state.hasPictureProcessing =
+ getHwComposer().hasDisplayCapability(displayId, DisplayCapability::PICTURE_PROCESSING);
mCurrentState.displays.add(token, state);
ALOGI("Connecting %s", displayString);
return activeModeId;
@@ -3665,6 +3668,26 @@
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);
@@ -3698,6 +3721,8 @@
builder.setPixels(resolution);
builder.setIsSecure(state.isSecure);
builder.setIsProtected(state.isProtected);
+ builder.setHasPictureProcessing(state.hasPictureProcessing);
+ builder.setMaxLayerPictureProfiles(state.maxLayerPictureProfiles);
builder.setPowerAdvisor(mPowerAdvisor.get());
builder.setName(state.displayName);
auto compositionDisplay = getCompositionEngine().createDisplay(builder.build());
@@ -3756,6 +3781,10 @@
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
@@ -3790,6 +3819,10 @@
} else {
mScheduler->unregisterDisplay(display->getPhysicalId(), mActiveDisplayId);
}
+
+ if (display->isRefreshable()) {
+ decRefreshableDisplays();
+ }
}
mDisplays.erase(displayToken);
@@ -3824,6 +3857,10 @@
if (display->isVirtual()) {
releaseVirtualDisplay(display->getVirtualId());
}
+
+ if (display->isRefreshable()) {
+ decRefreshableDisplays();
+ }
}
mDisplays.erase(displayToken);
@@ -3988,7 +4025,8 @@
inputWindowCommands =
std::move(mInputWindowCommands),
inputFlinger = mInputFlinger, this,
- visibleWindowsChanged, vsyncId, frameTime]() {
+ visibleWindowsChanged, vsyncId,
+ frameTime]() mutable {
SFTRACE_NAME("BackgroundExecutor::updateInputFlinger");
if (updateWindowInfo) {
mWindowInfosListenerInvoker
@@ -5322,7 +5360,15 @@
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) {
@@ -7613,7 +7659,8 @@
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);
}
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index ad3106c..d3479b7 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"
@@ -1241,7 +1242,6 @@
// 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
@@ -1361,7 +1361,7 @@
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);
@@ -1411,6 +1411,11 @@
// 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 feeafcc..12616e3 100644
--- a/services/surfaceflinger/common/FlagManager.cpp
+++ b/services/surfaceflinger/common/FlagManager.cpp
@@ -139,6 +139,7 @@
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);
@@ -156,6 +157,8 @@
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
@@ -243,6 +246,7 @@
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, "")
@@ -260,6 +264,8 @@
FLAG_MANAGER_READ_ONLY_FLAG(force_compile_graphite_renderengine, "");
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 e4305d3..f5bea72 100644
--- a/services/surfaceflinger/common/include/common/FlagManager.h
+++ b/services/surfaceflinger/common/include/common/FlagManager.h
@@ -77,6 +77,7 @@
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;
@@ -94,6 +95,8 @@
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 2cb3989..014c736 100644
--- a/services/surfaceflinger/surfaceflinger_flags_new.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
@@ -57,6 +57,14 @@
} # 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."
@@ -68,6 +76,17 @@
} # 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"
@@ -158,6 +177,14 @@
} # 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 e6fed63..7b6e4bf 100644
--- a/services/surfaceflinger/tests/Credentials_test.cpp
+++ b/services/surfaceflinger/tests/Credentials_test.cpp
@@ -341,9 +341,9 @@
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 @@
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 @@
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 ad9a674..2dd0dd9 100644
--- a/services/surfaceflinger/tests/WindowInfosListener_test.cpp
+++ b/services/surfaceflinger/tests/WindowInfosListener_test.cpp
@@ -50,9 +50,9 @@
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 @@
.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, 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 @@
.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 @@
};
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 b472047..9794620 100644
--- a/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h
+++ b/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h
@@ -218,6 +218,17 @@
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 cb8820a..6af5143 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -29,13 +29,10 @@
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 @@
}
filegroup {
+ name: "poweradvisor_mock_sources",
+ srcs: [
+ "mock/PowerAdvisor/*.cpp",
+ ],
+}
+
+filegroup {
name: "libsurfaceflinger_mock_sources",
srcs: [
"mock/MockEventThread.cpp",
@@ -86,79 +90,7 @@
":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 d4c801f..b517ff0 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 @@
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 @@
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 4f72424..860ad2e 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 @@
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 @@
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 db3c0a1..fa976c8 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 @@
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 6e4bf2b..744c536 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 @@
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 @@
}
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 a35ae15..6aec743 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -28,7 +28,6 @@
#include "ui/GraphicTypes.h"
#include <com_android_graphics_libgui_flags.h>
-#include <com_android_graphics_surfaceflinger_flags.h>
#define UPDATE_AND_VERIFY(BUILDER, ...) \
({ \
@@ -1935,4 +1934,95 @@
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);
+}
+TEST_F(LayerSnapshotTest, shouldUpdatePictureProfileHandle) {
+ if (!com_android_graphics_libgui_flags_apply_picture_profiles()) {
+ GTEST_SKIP() << "Flag disabled, skipping test";
+ }
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+ transactions.back().states.front().layerId = 1;
+ transactions.back().states.front().state.layerId = 1;
+ transactions.back().states.front().state.what = layer_state_t::ePictureProfileHandleChanged;
+ transactions.back().states.front().state.pictureProfileHandle = PictureProfileHandle(3);
+
+ mLifecycleManager.applyTransactions(transactions);
+ EXPECT_EQ(mLifecycleManager.getGlobalChanges(), RequestedLayerState::Changes::Content);
+
+ update(mSnapshotBuilder);
+
+ EXPECT_EQ(getSnapshot(1)->clientChanges, layer_state_t::ePictureProfileHandleChanged);
+ EXPECT_EQ(getSnapshot(1)->pictureProfileHandle, PictureProfileHandle(3));
+}
+
+TEST_F(LayerSnapshotTest, shouldUpdatePictureProfilePriorityFromAppContentPriority) {
+ if (!com_android_graphics_libgui_flags_apply_picture_profiles()) {
+ GTEST_SKIP() << "Flag disabled, skipping test";
+ }
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+ transactions.back().states.front().layerId = 1;
+ transactions.back().states.front().state.layerId = 1;
+ transactions.back().states.front().state.what = layer_state_t::eAppContentPriorityChanged;
+ transactions.back().states.front().state.appContentPriority = 3;
+
+ mLifecycleManager.applyTransactions(transactions);
+ EXPECT_EQ(mLifecycleManager.getGlobalChanges(), RequestedLayerState::Changes::Content);
+
+ update(mSnapshotBuilder);
+
+ EXPECT_EQ(getSnapshot(1)->pictureProfilePriority, 3);
+}
+
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
index 71f9f88..908637a 100644
--- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
@@ -159,9 +159,9 @@
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 8375bb9..5c25f34 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 @@
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 @@
}
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 @@
}
} // 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 29e1c21..b5be8db 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
@@ -2240,6 +2240,46 @@
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 @@
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 ac09cbc..1fc874d 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -222,7 +222,7 @@
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 @@
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 6778af3..7f0b7a6 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 @@
&mFlinger->mCompositionEngine->getHwComposer());
}
- void setupPowerAdvisor(std::unique_ptr<Hwc2::PowerAdvisor> powerAdvisor) {
+ void setupPowerAdvisor(std::unique_ptr<adpf::PowerAdvisor> powerAdvisor) {
mFlinger->mPowerAdvisor = std::move(powerAdvisor);
}
@@ -1160,7 +1160,7 @@
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 3e6a768..88052db 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -186,6 +186,9 @@
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 121104d..fa74492 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
@@ -52,7 +52,7 @@
(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 @@
(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 @@
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 fa7128c..88f83d2 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h
@@ -148,6 +148,9 @@
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.cpp b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.cpp
deleted file mode 100644
index 2323ebb..0000000
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.cpp
+++ /dev/null
@@ -1,24 +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.
- */
-
-#include "mock/DisplayHardware/MockIPower.h"
-
-namespace android::Hwc2::mock {
-
-// Explicit default instantiation is recommended.
-MockIPower::MockIPower() = default;
-
-} // namespace android::Hwc2::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 4c034d7..0000000
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h
+++ /dev/null
@@ -1,70 +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::SupportInfo;
-
-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, 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));
- 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 7398cbe..82500fe 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -30,6 +30,7 @@
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
similarity index 91%
rename from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.cpp
rename to services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerAdvisor.cpp
index 1ba38a8..f4c1e52 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
similarity index 94%
rename from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
rename to services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerAdvisor.h
index 4efdfe8..5c4512a 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 @@
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
similarity index 91%
rename from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.cpp
rename to services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHalController.cpp
index 3ec5c2d..3b8de55 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
similarity index 92%
rename from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h
rename to services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHalController.h
index af1862d..fba4cd8 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 @@
} // namespace hardware
} // namespace android
-namespace android::Hwc2::mock {
+namespace android::adpf::mock {
using android::power::HalResult;
@@ -59,4 +56,4 @@
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
similarity index 85%
rename from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHintSessionWrapper.cpp
rename to services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHintSessionWrapper.cpp
index d383283..cb39783 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
similarity index 88%
rename from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHintSessionWrapper.h
rename to services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHintSessionWrapper.h
index bc6950c..4518de8 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 namespace aidl::android::hardware::power;
-namespace android::Hwc2::mock {
+namespace android::adpf::mock {
class MockPowerHintSessionWrapper : public power::PowerHintSessionWrapper {
public:
@@ -52,4 +46,4 @@
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 ba35d15..494f88f 100644
--- a/services/vibratorservice/VibratorManagerHalController.cpp
+++ b/services/vibratorservice/VibratorManagerHalController.cpp
@@ -150,6 +150,23 @@
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 93ec781..3db8ff8 100644
--- a/services/vibratorservice/VibratorManagerHalWrapper.cpp
+++ b/services/vibratorservice/VibratorManagerHalWrapper.cpp
@@ -29,6 +29,30 @@
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 @@
(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 @@
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 @@
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 70c846b..72d4752 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorManagerHalController.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorManagerHalController.h
@@ -62,6 +62,10 @@
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 9e3f221..8d4ca0e 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorManagerHalWrapper.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorManagerHalWrapper.h
@@ -38,7 +38,8 @@
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 @@
// 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 @@
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 @@
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 @@
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 c7214e0..b201670 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 ca13c0b..a2f002d 100644
--- a/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp
+++ b/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp
@@ -69,12 +69,25 @@
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 @@
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 @@
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 7877236..52c865e 100644
--- a/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp
+++ b/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp
@@ -29,6 +29,8 @@
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 @@
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());
+}