summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
Diffstat (limited to 'libs')
-rw-r--r--libs/battery/LongArrayMultiStateCounter.cpp147
-rw-r--r--libs/battery/LongArrayMultiStateCounter.h61
-rw-r--r--libs/battery/LongArrayMultiStateCounterTest.cpp28
-rw-r--r--libs/battery/MultiStateCounter.h91
-rw-r--r--libs/battery/MultiStateCounterTest.cpp7
-rw-r--r--libs/binder/Android.bp4
-rw-r--r--libs/binder/BackendUnifiedServiceManager.cpp7
-rw-r--r--libs/binder/BackendUnifiedServiceManager.h1
-rw-r--r--libs/binder/IServiceManager.cpp11
-rw-r--r--libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp6
-rw-r--r--libs/binder/libbinder_rpc_unstable.cpp5
-rw-r--r--libs/binder/ndk/Android.bp3
-rw-r--r--libs/binder/ndk/binder_rpc.cpp4
-rw-r--r--libs/binder/ndk/include_cpp/android/binder_interface_utils.h17
-rw-r--r--libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h101
-rw-r--r--libs/binder/ndk/include_platform/android/binder_rpc.h5
-rw-r--r--libs/binder/rust/Android.bp6
-rw-r--r--libs/binder/rust/Cargo.toml15
-rw-r--r--libs/binder/rust/build.rs4
-rw-r--r--libs/binder/rust/rpcbinder/src/server/android.rs27
-rw-r--r--libs/binder/rust/src/binder.rs13
-rw-r--r--libs/binder/rust/src/lib.rs24
-rw-r--r--libs/binder/rust/src/native.rs33
-rw-r--r--libs/binder/rust/src/parcel.rs2
-rw-r--r--libs/binder/rust/src/proxy.rs2
-rw-r--r--libs/binder/rust/src/service.rs1
-rw-r--r--libs/binder/rust/sys/BinderBindings.hpp10
-rw-r--r--libs/binder/rust/sys/Cargo.toml14
-rw-r--r--libs/binder/rust/sys/build.rs59
-rw-r--r--libs/binder/rust/sys/lib.rs1
-rw-r--r--libs/binder/tests/binderUtilsHostTest.cpp4
-rw-r--r--libs/gui/Android.bp5
-rw-r--r--libs/gui/BLASTBufferQueue.cpp181
-rw-r--r--libs/gui/BufferItem.cpp65
-rw-r--r--libs/gui/BufferQueueProducer.cpp3
-rw-r--r--libs/gui/BufferReleaseChannel.cpp66
-rw-r--r--libs/gui/DisplayLuts.cpp81
-rw-r--r--libs/gui/IGraphicBufferProducerFlattenables.cpp37
-rw-r--r--libs/gui/LayerState.cpp48
-rw-r--r--libs/gui/Surface.cpp4
-rw-r--r--libs/gui/SurfaceComposerClient.cpp95
-rw-r--r--libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl4
-rw-r--r--libs/gui/aidl/android/gui/FrameRateCategoryRate.aidl (renamed from libs/binder/trusty/ndk/include/android/llndk-versioning.h)13
-rw-r--r--libs/gui/aidl/android/gui/LutProperties.aidl2
-rw-r--r--libs/gui/include/gui/BLASTBufferQueue.h30
-rw-r--r--libs/gui/include/gui/BufferItem.h7
-rw-r--r--libs/gui/include/gui/DisplayLuts.h65
-rw-r--r--libs/gui/include/gui/Flags.h14
-rw-r--r--libs/gui/include/gui/IGraphicBufferProducer.h11
-rw-r--r--libs/gui/include/gui/LayerState.h21
-rw-r--r--libs/gui/include/gui/SurfaceComposerClient.h21
-rw-r--r--libs/gui/include/gui/view/Surface.h10
-rw-r--r--libs/gui/libgui_flags.aconfig8
-rw-r--r--libs/gui/tests/BufferQueue_test.cpp58
-rw-r--r--libs/gui/tests/BufferReleaseChannel_test.cpp47
-rw-r--r--libs/gui/tests/EndToEndNativeInputTest.cpp162
-rw-r--r--libs/gui/view/Surface.cpp32
-rw-r--r--libs/input/Android.bp2
-rw-r--r--libs/input/CoordinateFilter.cpp31
-rw-r--r--libs/input/InputConsumerNoResampling.cpp21
-rw-r--r--libs/input/InputEventLabels.cpp21
-rw-r--r--libs/input/KeyboardClassifier.cpp8
-rw-r--r--libs/input/OneEuroFilter.cpp79
-rw-r--r--libs/input/Resampler.cpp30
-rw-r--r--libs/input/input_flags.aconfig24
-rw-r--r--libs/input/tests/Android.bp1
-rw-r--r--libs/input/tests/InputConsumerResampling_test.cpp187
-rw-r--r--libs/input/tests/InputConsumer_test.cpp3
-rw-r--r--libs/input/tests/OneEuroFilter_test.cpp134
-rw-r--r--libs/input/tests/TestEventMatchers.h16
-rw-r--r--libs/input/tests/TfLiteMotionPredictor_test.cpp16
-rw-r--r--libs/input/tests/TouchResampling_test.cpp5
-rw-r--r--libs/nativewindow/ANativeWindow.cpp10
-rw-r--r--libs/nativewindow/include/android/native_window.h81
-rw-r--r--libs/nativewindow/include/system/window.h17
-rw-r--r--libs/nativewindow/libnativewindow.map.txt1
-rw-r--r--libs/nativewindow/rust/src/handle.rs29
-rw-r--r--libs/nativewindow/rust/src/lib.rs18
-rw-r--r--libs/renderengine/Android.bp1
-rw-r--r--libs/renderengine/include/renderengine/LayerSettings.h5
-rw-r--r--libs/renderengine/skia/GaneshVkRenderEngine.cpp8
-rw-r--r--libs/renderengine/skia/GaneshVkRenderEngine.h1
-rw-r--r--libs/renderengine/skia/GraphiteVkRenderEngine.cpp8
-rw-r--r--libs/renderengine/skia/GraphiteVkRenderEngine.h1
-rw-r--r--libs/renderengine/skia/SkiaGLRenderEngine.cpp2
-rw-r--r--libs/renderengine/skia/SkiaRenderEngine.cpp4
-rw-r--r--libs/renderengine/skia/SkiaRenderEngine.h2
-rw-r--r--libs/renderengine/skia/SkiaVkRenderEngine.cpp16
-rw-r--r--libs/renderengine/skia/SkiaVkRenderEngine.h2
-rw-r--r--libs/renderengine/skia/filters/LutShader.cpp242
-rw-r--r--libs/renderengine/skia/filters/LutShader.h44
-rw-r--r--libs/sensor/Sensor.cpp13
-rw-r--r--libs/tracing_perfetto/Android.bp2
-rw-r--r--libs/tracing_perfetto/tracing_perfetto_internal.cpp57
-rw-r--r--libs/ui/Android.bp1
-rw-r--r--libs/ui/Gralloc5.cpp3
-rw-r--r--libs/ui/PictureProfileHandle.cpp (renamed from libs/gui/aidl/android/gui/Lut.aidl)25
-rw-r--r--libs/ui/include/ui/DynamicDisplayInfo.h4
-rw-r--r--libs/ui/include/ui/FrameRateCategoryRate.h35
-rw-r--r--libs/ui/include/ui/PictureProfileHandle.h60
100 files changed, 2444 insertions, 629 deletions
diff --git a/libs/battery/LongArrayMultiStateCounter.cpp b/libs/battery/LongArrayMultiStateCounter.cpp
index 125cfaffa4..334d84b6b5 100644
--- a/libs/battery/LongArrayMultiStateCounter.cpp
+++ b/libs/battery/LongArrayMultiStateCounter.cpp
@@ -21,58 +21,137 @@
namespace android {
namespace battery {
-template <>
-bool LongArrayMultiStateCounter::delta(const std::vector<uint64_t>& previousValue,
- const std::vector<uint64_t>& newValue,
- std::vector<uint64_t>* outValue) const {
- size_t size = previousValue.size();
- if (newValue.size() != size) {
- ALOGE("Incorrect array size: %d, should be %d", (int)newValue.size(), (int)size);
- return false;
+Uint64ArrayRW::Uint64ArrayRW(const Uint64Array &copy) : Uint64Array(copy.size()) {
+ if (mSize != 0 && copy.data() != nullptr) {
+ mData = new uint64_t[mSize];
+ memcpy(mData, copy.data(), mSize * sizeof(uint64_t));
+ } else {
+ mData = nullptr;
}
+}
- bool is_delta_valid = true;
- for (int i = size - 1; i >= 0; i--) {
- if (newValue[i] >= previousValue[i]) {
- (*outValue)[i] = newValue[i] - previousValue[i];
+uint64_t *Uint64ArrayRW::dataRW() {
+ if (mData == nullptr) {
+ mData = new uint64_t[mSize];
+ memset(mData, 0, mSize * sizeof(uint64_t));
+ }
+ return mData;
+}
+
+Uint64ArrayRW &Uint64ArrayRW::operator=(const Uint64Array &t) {
+ if (t.size() != mSize) {
+ delete[] mData;
+ mSize = t.size();
+ mData = nullptr;
+ }
+ if (mSize != 0) {
+ if (t.data() != nullptr) {
+ if (mData == nullptr) {
+ mData = new uint64_t[mSize];
+ }
+ memcpy(mData, t.data(), mSize * sizeof(uint64_t));
} else {
- (*outValue)[i] = 0;
- is_delta_valid = false;
+ delete[] mData;
+ mData = nullptr;
}
}
- return is_delta_valid;
+ return *this;
+}
+
+std::ostream &operator<<(std::ostream &os, const Uint64Array &v) {
+ os << "{";
+ const uint64_t *data = v.data();
+ if (data != nullptr) {
+ bool first = true;
+ for (size_t i = 0; i < v.size(); i++) {
+ if (!first) {
+ os << ", ";
+ }
+ os << data[i];
+ first = false;
+ }
+ }
+ os << "}";
+ return os;
+}
+
+// Convenience constructor for tests
+Uint64ArrayRW::Uint64ArrayRW(std::initializer_list<uint64_t> init) : Uint64Array(init.size()) {
+ mData = new uint64_t[mSize];
+ memcpy(mData, init.begin(), mSize * sizeof(uint64_t));
+}
+
+// Used in tests only.
+bool Uint64Array::operator==(const Uint64Array &other) const {
+ if (size() != other.size()) {
+ return false;
+ }
+ const uint64_t* thisData = data();
+ const uint64_t* thatData = other.data();
+ for (size_t i = 0; i < mSize; i++) {
+ const uint64_t v1 = thisData != nullptr ? thisData[i] : 0;
+ const uint64_t v2 = thatData != nullptr ? thatData[i] : 0;
+ if (v1 != v2) {
+ return false;
+ }
+ }
+ return true;
}
template <>
-void LongArrayMultiStateCounter::add(std::vector<uint64_t>* value1,
- const std::vector<uint64_t>& value2, const uint64_t numerator,
- const uint64_t denominator) const {
+void LongArrayMultiStateCounter::add(Uint64ArrayRW *value1, const Uint64Array &value2,
+ const uint64_t numerator, const uint64_t denominator) const {
+ const uint64_t* data2 = value2.data();
+ if (data2 == nullptr) {
+ return;
+ }
+
+ uint64_t* data1 = value1->dataRW();
+ size_t size = value2.size();
if (numerator != denominator) {
- for (int i = value2.size() - 1; i >= 0; i--) {
+ for (size_t i = 0; i < size; i++) {
// The caller ensures that denominator != 0
- (*value1)[i] += value2[i] * numerator / denominator;
+ data1[i] += data2[i] * numerator / denominator;
}
} else {
- for (int i = value2.size() - 1; i >= 0; i--) {
- (*value1)[i] += value2[i];
+ for (size_t i = 0; i < size; i++) {
+ data1[i] += data2[i];
}
}
}
-template <>
-std::string LongArrayMultiStateCounter::valueToString(const std::vector<uint64_t>& v) const {
- std::stringstream s;
- s << "{";
- bool first = true;
- for (uint64_t n : v) {
- if (!first) {
- s << ", ";
+template<>
+bool LongArrayMultiStateCounter::delta(const Uint64ArrayRW &previousValue,
+ const Uint64Array &newValue, Uint64ArrayRW *outValue) const {
+ size_t size = previousValue.size();
+ if (newValue.size() != size) {
+ ALOGE("Incorrect array size: %d, should be %d", (int) newValue.size(), (int) size);
+ return false;
+ }
+ if (outValue->size() != size) {
+ ALOGE("Incorrect outValue size: %d, should be %d", (int) outValue->size(), (int) size);
+ return false;
+ }
+
+ bool is_delta_valid = true;
+ const uint64_t *prevData = previousValue.data();
+ const uint64_t *newData = newValue.data();
+ uint64_t *outData = outValue->dataRW();
+ for (size_t i = 0; i < size; i++) {
+ if (prevData == nullptr) {
+ if (newData == nullptr) {
+ outData[i] = 0;
+ } else {
+ outData[i] = newData[i];
+ }
+ } else if (newData == nullptr || newData[i] < prevData[i]) {
+ outData[i] = 0;
+ is_delta_valid = false;
+ } else {
+ outData[i] = newData[i] - prevData[i];
}
- s << n;
- first = false;
}
- s << "}";
- return s.str();
+ return is_delta_valid;
}
} // namespace battery
diff --git a/libs/battery/LongArrayMultiStateCounter.h b/libs/battery/LongArrayMultiStateCounter.h
index f3439f6a0c..e00c96898e 100644
--- a/libs/battery/LongArrayMultiStateCounter.h
+++ b/libs/battery/LongArrayMultiStateCounter.h
@@ -23,7 +23,66 @@
namespace android {
namespace battery {
-typedef MultiStateCounter<std::vector<uint64_t>> LongArrayMultiStateCounter;
+/**
+ * Wrapper for an array of uint64's.
+ */
+class Uint64Array {
+ protected:
+ size_t mSize;
+
+ public:
+ Uint64Array() : Uint64Array(0) {}
+
+ Uint64Array(size_t size) : mSize(size) {}
+
+ virtual ~Uint64Array() {}
+
+ size_t size() const { return mSize; }
+
+ /**
+ * Returns the wrapped array.
+ *
+ * Nullable! Null should be interpreted the same as an array of zeros
+ */
+ virtual const uint64_t *data() const { return nullptr; }
+
+ friend std::ostream &operator<<(std::ostream &os, const Uint64Array &v);
+
+ // Test API
+ bool operator==(const Uint64Array &other) const;
+};
+
+/**
+ * Mutable version of Uint64Array.
+ */
+class Uint64ArrayRW: public Uint64Array {
+ uint64_t* mData;
+
+public:
+ Uint64ArrayRW() : Uint64ArrayRW(0) {}
+
+ Uint64ArrayRW(size_t size) : Uint64Array(size), mData(nullptr) {}
+
+ Uint64ArrayRW(const Uint64Array &copy);
+
+ // Need an explicit copy constructor. In the initialization context C++ does not understand that
+ // a Uint64ArrayRW is a Uint64Array.
+ Uint64ArrayRW(const Uint64ArrayRW &copy) : Uint64ArrayRW((const Uint64Array &) copy) {}
+
+ // Test API
+ Uint64ArrayRW(std::initializer_list<uint64_t> init);
+
+ ~Uint64ArrayRW() override { delete[] mData; }
+
+ const uint64_t *data() const override { return mData; }
+
+ // NonNull. Will initialize the wrapped array if it is null.
+ uint64_t *dataRW();
+
+ Uint64ArrayRW &operator=(const Uint64Array &t);
+};
+
+typedef MultiStateCounter<Uint64ArrayRW, Uint64Array> LongArrayMultiStateCounter;
} // namespace battery
} // namespace android
diff --git a/libs/battery/LongArrayMultiStateCounterTest.cpp b/libs/battery/LongArrayMultiStateCounterTest.cpp
index e4e6b2a49f..1c74e3fd14 100644
--- a/libs/battery/LongArrayMultiStateCounterTest.cpp
+++ b/libs/battery/LongArrayMultiStateCounterTest.cpp
@@ -24,25 +24,25 @@ namespace battery {
class LongArrayMultiStateCounterTest : public testing::Test {};
TEST_F(LongArrayMultiStateCounterTest, stateChange) {
- LongArrayMultiStateCounter testCounter(2, std::vector<uint64_t>(4));
- testCounter.updateValue(std::vector<uint64_t>({0, 0, 0, 0}), 1000);
+ LongArrayMultiStateCounter testCounter(2, Uint64Array(4));
+ testCounter.updateValue(Uint64ArrayRW({0, 0, 0, 0}), 1000);
testCounter.setState(0, 1000);
testCounter.setState(1, 2000);
- testCounter.updateValue(std::vector<uint64_t>({100, 200, 300, 400}), 3000);
+ testCounter.updateValue(Uint64ArrayRW({100, 200, 300, 400}), 3000);
// Time was split in half between the two states, so the counts will be split 50:50 too
- EXPECT_EQ(std::vector<uint64_t>({50, 100, 150, 200}), testCounter.getCount(0));
- EXPECT_EQ(std::vector<uint64_t>({50, 100, 150, 200}), testCounter.getCount(1));
+ EXPECT_EQ(Uint64ArrayRW({50, 100, 150, 200}), testCounter.getCount(0));
+ EXPECT_EQ(Uint64ArrayRW({50, 100, 150, 200}), testCounter.getCount(1));
}
TEST_F(LongArrayMultiStateCounterTest, accumulation) {
- LongArrayMultiStateCounter testCounter(2, std::vector<uint64_t>(4));
- testCounter.updateValue(std::vector<uint64_t>({0, 0, 0, 0}), 1000);
+ LongArrayMultiStateCounter testCounter(2, Uint64Array(4));
+ testCounter.updateValue(Uint64ArrayRW({0, 0, 0, 0}), 1000);
testCounter.setState(0, 1000);
testCounter.setState(1, 2000);
- testCounter.updateValue(std::vector<uint64_t>({100, 200, 300, 400}), 3000);
+ testCounter.updateValue(Uint64ArrayRW({100, 200, 300, 400}), 3000);
testCounter.setState(0, 4000);
- testCounter.updateValue(std::vector<uint64_t>({200, 300, 400, 500}), 8000);
+ testCounter.updateValue(Uint64ArrayRW({200, 300, 400, 500}), 8000);
// The first delta is split 50:50:
// 0: {50, 100, 150, 200}
@@ -50,16 +50,16 @@ TEST_F(LongArrayMultiStateCounterTest, accumulation) {
// The second delta is split 4:1
// 0: {80, 80, 80, 80}
// 1: {20, 20, 20, 20}
- EXPECT_EQ(std::vector<uint64_t>({130, 180, 230, 280}), testCounter.getCount(0));
- EXPECT_EQ(std::vector<uint64_t>({70, 120, 170, 220}), testCounter.getCount(1));
+ EXPECT_EQ(Uint64ArrayRW({130, 180, 230, 280}), testCounter.getCount(0));
+ EXPECT_EQ(Uint64ArrayRW({70, 120, 170, 220}), testCounter.getCount(1));
}
TEST_F(LongArrayMultiStateCounterTest, toString) {
- LongArrayMultiStateCounter testCounter(2, std::vector<uint64_t>(4));
- testCounter.updateValue(std::vector<uint64_t>({0, 0, 0, 0}), 1000);
+ LongArrayMultiStateCounter testCounter(2, Uint64Array(4));
+ testCounter.updateValue(Uint64ArrayRW({0, 0, 0, 0}), 1000);
testCounter.setState(0, 1000);
testCounter.setState(1, 2000);
- testCounter.updateValue(std::vector<uint64_t>({100, 200, 300, 400}), 3000);
+ testCounter.updateValue(Uint64ArrayRW({100, 200, 300, 400}), 3000);
EXPECT_STREQ("[0: {50, 100, 150, 200}, 1: {50, 100, 150, 200}] updated: 3000 currentState: 1",
testCounter.toString().c_str());
diff --git a/libs/battery/MultiStateCounter.h b/libs/battery/MultiStateCounter.h
index 04b718698e..fadc4ffd41 100644
--- a/libs/battery/MultiStateCounter.h
+++ b/libs/battery/MultiStateCounter.h
@@ -35,12 +35,12 @@ namespace battery {
typedef uint16_t state_t;
-template <class T>
+template <class T, class V>
class MultiStateCounter {
- uint16_t stateCount;
+ const uint16_t stateCount;
+ const V emptyValue;
state_t currentState;
time_t lastStateChangeTimestamp;
- T emptyValue;
T lastValue;
time_t lastUpdateTimestamp;
T deltaValue;
@@ -54,7 +54,7 @@ class MultiStateCounter {
State* states;
public:
- MultiStateCounter(uint16_t stateCount, const T& emptyValue);
+ MultiStateCounter(uint16_t stateCount, const V& emptyValue);
virtual ~MultiStateCounter();
@@ -66,35 +66,35 @@ public:
* Copies the current state and accumulated times-in-state from the source. Resets
* the accumulated value.
*/
- void copyStatesFrom(const MultiStateCounter<T>& source);
+ void copyStatesFrom(const MultiStateCounter<T, V> &source);
- void setValue(state_t state, const T& value);
+ void setValue(state_t state, const V& value);
/**
* Updates the value by distributing the delta from the previously set value
* among states according to their respective time-in-state.
* Returns the delta from the previously set value.
*/
- const T& updateValue(const T& value, time_t timestamp);
+ const V& updateValue(const V& value, time_t timestamp);
/**
* Updates the value by distributing the specified increment among states according
* to their respective time-in-state.
*/
- void incrementValue(const T& increment, time_t timestamp);
+ void incrementValue(const V& increment, time_t timestamp);
/**
* Adds the specified increment to the value for the current state, without affecting
* the last updated value or timestamp. Ignores partial time-in-state: the entirety of
* the increment is given to the current state.
*/
- void addValue(const T& increment);
+ void addValue(const V& increment);
void reset();
uint16_t getStateCount();
- const T& getCount(state_t state);
+ const V& getCount(state_t state);
std::string toString();
@@ -104,27 +104,25 @@ private:
* Returns true iff the combination of previousValue and newValue is valid
* (newValue >= prevValue)
*/
- bool delta(const T& previousValue, const T& newValue, T* outValue) const;
+ bool delta(const T& previousValue, const V& newValue, T* outValue) const;
/**
* Adds value2 to value1 and stores the result in value1. Denominator is
* guaranteed to be non-zero.
*/
- void add(T* value1, const T& value2, const uint64_t numerator,
+ void add(T* value1, const V& value2, const uint64_t numerator,
const uint64_t denominator) const;
-
- std::string valueToString(const T& value) const;
};
// ---------------------- MultiStateCounter Implementation -------------------------
// Since MultiStateCounter is a template, the implementation must be inlined.
-template <class T>
-MultiStateCounter<T>::MultiStateCounter(uint16_t stateCount, const T& emptyValue)
+template <class T, class V>
+MultiStateCounter<T, V>::MultiStateCounter(uint16_t stateCount, const V& emptyValue)
: stateCount(stateCount),
+ emptyValue(emptyValue),
currentState(0),
lastStateChangeTimestamp(-1),
- emptyValue(emptyValue),
lastValue(emptyValue),
lastUpdateTimestamp(-1),
deltaValue(emptyValue),
@@ -136,13 +134,13 @@ MultiStateCounter<T>::MultiStateCounter(uint16_t stateCount, const T& emptyValue
}
}
-template <class T>
-MultiStateCounter<T>::~MultiStateCounter() {
+template <class T, class V>
+MultiStateCounter<T, V>::~MultiStateCounter() {
delete[] states;
};
-template <class T>
-void MultiStateCounter<T>::setEnabled(bool enabled, time_t timestamp) {
+template <class T, class V>
+void MultiStateCounter<T, V>::setEnabled(bool enabled, time_t timestamp) {
if (enabled == isEnabled) {
return;
}
@@ -167,8 +165,8 @@ void MultiStateCounter<T>::setEnabled(bool enabled, time_t timestamp) {
}
}
-template <class T>
-void MultiStateCounter<T>::setState(state_t state, time_t timestamp) {
+template <class T, class V>
+void MultiStateCounter<T, V>::setState(state_t state, time_t timestamp) {
if (isEnabled && lastStateChangeTimestamp >= 0 && lastUpdateTimestamp >= 0) {
// If the update arrived out-of-order, just push back the timestamp to
// avoid having the situation where timeInStateSinceUpdate > timeSinceUpdate
@@ -198,8 +196,8 @@ void MultiStateCounter<T>::setState(state_t state, time_t timestamp) {
lastStateChangeTimestamp = timestamp;
}
-template <class T>
-void MultiStateCounter<T>::copyStatesFrom(const MultiStateCounter<T>& source) {
+template <class T, class V>
+void MultiStateCounter<T, V>::copyStatesFrom(const MultiStateCounter<T, V>& source) {
if (stateCount != source.stateCount) {
ALOGE("State count mismatch: %u vs. %u\n", stateCount, source.stateCount);
return;
@@ -214,14 +212,14 @@ void MultiStateCounter<T>::copyStatesFrom(const MultiStateCounter<T>& source) {
lastUpdateTimestamp = source.lastUpdateTimestamp;
}
-template <class T>
-void MultiStateCounter<T>::setValue(state_t state, const T& value) {
+template <class T, class V>
+void MultiStateCounter<T, V>::setValue(state_t state, const V& value) {
states[state].counter = value;
}
-template <class T>
-const T& MultiStateCounter<T>::updateValue(const T& value, time_t timestamp) {
- T* returnValue = &emptyValue;
+template <class T, class V>
+const V& MultiStateCounter<T, V>::updateValue(const V& value, time_t timestamp) {
+ const V* returnValue = &emptyValue;
// If the counter is disabled, we ignore the update, except when the counter got disabled after
// the previous update, in which case we still need to pick up the residual delta.
@@ -250,8 +248,8 @@ const T& MultiStateCounter<T>::updateValue(const T& value, time_t timestamp) {
}
} else {
std::stringstream str;
- str << "updateValue is called with a value " << valueToString(value)
- << ", which is lower than the previous value " << valueToString(lastValue)
+ str << "updateValue is called with a value " << value
+ << ", which is lower than the previous value " << lastValue
<< "\n";
ALOGE("%s", str.str().c_str());
@@ -276,23 +274,25 @@ const T& MultiStateCounter<T>::updateValue(const T& value, time_t timestamp) {
return *returnValue;
}
-template <class T>
-void MultiStateCounter<T>::incrementValue(const T& increment, time_t timestamp) {
+template <class T, class V>
+void MultiStateCounter<T, V>::incrementValue(const V& increment, time_t timestamp) {
+// T newValue;
+// newValue = lastValue; // Copy assignment, not initialization.
T newValue = lastValue;
add(&newValue, increment, 1 /* numerator */, 1 /* denominator */);
updateValue(newValue, timestamp);
}
-template <class T>
-void MultiStateCounter<T>::addValue(const T& value) {
+template <class T, class V>
+void MultiStateCounter<T, V>::addValue(const V& value) {
if (!isEnabled) {
return;
}
add(&states[currentState].counter, value, 1 /* numerator */, 1 /* denominator */);
}
-template <class T>
-void MultiStateCounter<T>::reset() {
+template <class T, class V>
+void MultiStateCounter<T, V>::reset() {
lastStateChangeTimestamp = -1;
lastUpdateTimestamp = -1;
for (int i = 0; i < stateCount; i++) {
@@ -301,25 +301,26 @@ void MultiStateCounter<T>::reset() {
}
}
-template <class T>
-uint16_t MultiStateCounter<T>::getStateCount() {
+template <class T, class V>
+uint16_t MultiStateCounter<T, V>::getStateCount() {
return stateCount;
}
-template <class T>
-const T& MultiStateCounter<T>::getCount(state_t state) {
+template <class T, class V>
+const V& MultiStateCounter<T, V>::getCount(state_t state) {
return states[state].counter;
}
-template <class T>
-std::string MultiStateCounter<T>::toString() {
+template <class T, class V>
+std::string MultiStateCounter<T, V>::toString() {
std::stringstream str;
+// str << "LAST VALUE: " << valueToString(lastValue);
str << "[";
for (int i = 0; i < stateCount; i++) {
if (i != 0) {
str << ", ";
}
- str << i << ": " << valueToString(states[i].counter);
+ str << i << ": " << states[i].counter;
if (states[i].timeInStateSinceUpdate > 0) {
str << " timeInStateSinceUpdate: " << states[i].timeInStateSinceUpdate;
}
diff --git a/libs/battery/MultiStateCounterTest.cpp b/libs/battery/MultiStateCounterTest.cpp
index a51a38a6c7..589b7fe4e3 100644
--- a/libs/battery/MultiStateCounterTest.cpp
+++ b/libs/battery/MultiStateCounterTest.cpp
@@ -21,7 +21,7 @@
namespace android {
namespace battery {
-typedef MultiStateCounter<double> DoubleMultiStateCounter;
+typedef MultiStateCounter<double, double> DoubleMultiStateCounter;
template <>
bool DoubleMultiStateCounter::delta(const double& previousValue, const double& newValue,
@@ -41,11 +41,6 @@ void DoubleMultiStateCounter::add(double* value1, const double& value2, const ui
}
}
-template <>
-std::string DoubleMultiStateCounter::valueToString(const double& v) const {
- return std::to_string(v);
-}
-
class MultiStateCounterTest : public testing::Test {};
TEST_F(MultiStateCounterTest, constructor) {
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 6903cb5d68..2ef642a3a0 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -871,6 +871,10 @@ cc_library {
symbol_file: "libbinder_rpc_unstable.map.txt",
},
+ header_abi_checker: {
+ enabled: false,
+ },
+
// This library is intentionally limited to these targets, and it will be removed later.
// Do not expand the visibility.
visibility: [
diff --git a/libs/binder/BackendUnifiedServiceManager.cpp b/libs/binder/BackendUnifiedServiceManager.cpp
index f7b9f059a5..d32eecdc60 100644
--- a/libs/binder/BackendUnifiedServiceManager.cpp
+++ b/libs/binder/BackendUnifiedServiceManager.cpp
@@ -105,7 +105,8 @@ static const char* kStaticCachableList[] = {
};
bool BinderCacheWithInvalidation::isClientSideCachingEnabled(const std::string& serviceName) {
- if (ProcessState::self()->getThreadPoolMaxTotalThreadCount() <= 0) {
+ sp<ProcessState> self = ProcessState::selfOrNull();
+ if (!self || self->getThreadPoolMaxTotalThreadCount() <= 0) {
ALOGW("Thread Pool max thread count is 0. Cannot cache binder as linkToDeath cannot be "
"implemented. serviceName: %s",
serviceName.c_str());
@@ -172,10 +173,6 @@ BackendUnifiedServiceManager::BackendUnifiedServiceManager(const sp<AidlServiceM
mCacheForGetService = std::make_shared<BinderCacheWithInvalidation>();
}
-sp<AidlServiceManager> BackendUnifiedServiceManager::getImpl() {
- return mTheRealServiceManager;
-}
-
Status BackendUnifiedServiceManager::getService(const ::std::string& name,
sp<IBinder>* _aidl_return) {
os::Service service;
diff --git a/libs/binder/BackendUnifiedServiceManager.h b/libs/binder/BackendUnifiedServiceManager.h
index feb8470032..abc0eda7eb 100644
--- a/libs/binder/BackendUnifiedServiceManager.h
+++ b/libs/binder/BackendUnifiedServiceManager.h
@@ -121,7 +121,6 @@ class BackendUnifiedServiceManager : public android::os::BnServiceManager {
public:
explicit BackendUnifiedServiceManager(const sp<os::IServiceManager>& impl);
- sp<os::IServiceManager> getImpl();
binder::Status getService(const ::std::string& name, sp<IBinder>* _aidl_return) override;
binder::Status getService2(const ::std::string& name, os::Service* out) override;
binder::Status checkService(const ::std::string& name, os::Service* out) override;
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index 32388db076..39d8c2446e 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -561,8 +561,9 @@ sp<IBinder> CppBackendShim::getService(const String16& name) const {
sp<IBinder> svc = checkService(name);
if (svc != nullptr) return svc;
+ sp<ProcessState> self = ProcessState::selfOrNull();
const bool isVendorService =
- strcmp(ProcessState::self()->getDriverName().c_str(), "/dev/vndbinder") == 0;
+ self && strcmp(self->getDriverName().c_str(), "/dev/vndbinder") == 0;
constexpr auto timeout = 5s;
const auto startTime = std::chrono::steady_clock::now();
// Vendor code can't access system properties
@@ -579,7 +580,7 @@ sp<IBinder> CppBackendShim::getService(const String16& name) const {
const useconds_t sleepTime = gSystemBootCompleted ? 1000 : 100;
ALOGI("Waiting for service '%s' on '%s'...", String8(name).c_str(),
- ProcessState::self()->getDriverName().c_str());
+ self ? self->getDriverName().c_str() : "RPC accessors only");
int n = 0;
while (std::chrono::steady_clock::now() - startTime < timeout) {
@@ -661,7 +662,8 @@ sp<IBinder> CppBackendShim::waitForService(const String16& name16) {
if (Status status = realGetService(name, &out); !status.isOk()) {
ALOGW("Failed to getService in waitForService for %s: %s", name.c_str(),
status.toString8().c_str());
- if (0 == ProcessState::self()->getThreadPoolMaxTotalThreadCount()) {
+ sp<ProcessState> self = ProcessState::selfOrNull();
+ if (self && 0 == self->getThreadPoolMaxTotalThreadCount()) {
ALOGW("Got service, but may be racey because we could not wait efficiently for it. "
"Threadpool has 0 guaranteed threads. "
"Is the threadpool configured properly? "
@@ -695,9 +697,10 @@ sp<IBinder> CppBackendShim::waitForService(const String16& name16) {
if (waiter->mBinder != nullptr) return waiter->mBinder;
}
+ sp<ProcessState> self = ProcessState::selfOrNull();
ALOGW("Waited one second for %s (is service started? Number of threads started in the "
"threadpool: %zu. Are binder threads started and available?)",
- name.c_str(), ProcessState::self()->getThreadPoolMaxTotalThreadCount());
+ name.c_str(), self ? self->getThreadPoolMaxTotalThreadCount() : 0);
// Handle race condition for lazy services. Here is what can happen:
// - the service dies (not processed by init yet).
diff --git a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
index 392ebb5b0a..48c0ea636b 100644
--- a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
+++ b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
@@ -37,8 +37,12 @@ enum class ARpcSession_FileDescriptorTransportMode {
// Set `cid` to VMADDR_CID_LOCAL to only bind to the local vsock interface.
// Returns an opaque handle to the running server instance, or null if the server
// could not be started.
+// Set |port| to VMADDR_PORT_ANY to pick an available ephemeral port.
+// |assignedPort| will be set to the assigned port number if it is not null.
+// This will be the provided |port|, or the chosen available ephemeral port when
+// |port| is VMADDR_PORT_ANY.
[[nodiscard]] ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int cid,
- unsigned int port);
+ unsigned int port, unsigned int* assignedPort);
// Starts a Unix domain RPC server with an open raw socket file descriptor
// and a given root IBinder object.
diff --git a/libs/binder/libbinder_rpc_unstable.cpp b/libs/binder/libbinder_rpc_unstable.cpp
index 21537fc50d..a84a0c6e0b 100644
--- a/libs/binder/libbinder_rpc_unstable.cpp
+++ b/libs/binder/libbinder_rpc_unstable.cpp
@@ -81,7 +81,8 @@ RpcSession::FileDescriptorTransportMode toTransportMode(
extern "C" {
#ifndef __TRUSTY__
-ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int cid, unsigned int port) {
+ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int cid, unsigned int port,
+ unsigned int* assignedPort) {
auto server = RpcServer::make();
unsigned int bindCid = VMADDR_CID_ANY; // bind to the remote interface
@@ -90,7 +91,7 @@ ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int cid, unsigned in
cid = VMADDR_CID_ANY; // no need for a connection filter
}
- if (status_t status = server->setupVsockServer(bindCid, port); status != OK) {
+ if (status_t status = server->setupVsockServer(bindCid, port, assignedPort); status != OK) {
ALOGE("Failed to set up vsock server with port %u error: %s", port,
statusToString(status).c_str());
return nullptr;
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp
index a7423b3d2a..5710bbfa9f 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -82,7 +82,6 @@ cc_library {
llndk: {
symbol_file: "libbinder_ndk.map.txt",
- export_llndk_headers: ["libvendorsupport_llndk_headers"],
},
cflags: [
@@ -110,11 +109,9 @@ cc_library {
],
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 886eb4bdf8..53ab68e643 100644
--- a/libs/binder/ndk/binder_rpc.cpp
+++ b/libs/binder/ndk/binder_rpc.cpp
@@ -104,8 +104,8 @@ struct OnDeleteProviderHolder {
};
ABinderRpc_AccessorProvider* ABinderRpc_registerAccessorProvider(
- ABinderRpc_AccessorProvider_getAccessorCallback provider, const char** instances,
- size_t numInstances, void* data,
+ ABinderRpc_AccessorProvider_getAccessorCallback provider,
+ const char* const* const instances, size_t numInstances, void* data,
ABinderRpc_AccessorProviderUserData_deleteCallback onDelete) {
if (provider == nullptr) {
ALOGE("Null provider passed to ABinderRpc_registerAccessorProvider");
diff --git a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
index 0ad110ee83..c6518d816f 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 @@ AIBinder_Class* ICInterface::defineClass(const char* interfaceDescriptor,
#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 83976b3771..f3f3c3802a 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 @@ namespace aidl::android::os {
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 @@ class PersistableBundle {
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 @@ class PersistableBundle {
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 @@ class PersistableBundle {
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 @@ class PersistableBundle {
*/
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 @@ class PersistableBundle {
* 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 @@ class PersistableBundle {
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 @@ class PersistableBundle {
}
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 @@ class PersistableBundle {
}
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 @@ class PersistableBundle {
}
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 @@ class PersistableBundle {
}
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 @@ class PersistableBundle {
}
}
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 @@ class PersistableBundle {
}
}
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 @@ class PersistableBundle {
}
}
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 @@ class PersistableBundle {
}
}
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 @@ class PersistableBundle {
}
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 @@ class PersistableBundle {
}
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 @@ class PersistableBundle {
}
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 @@ class PersistableBundle {
}
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 @@ class PersistableBundle {
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 @@ class PersistableBundle {
}
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 @@ class PersistableBundle {
}
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 @@ class PersistableBundle {
}
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 @@ class PersistableBundle {
}
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 66667d33bd..7d54e2dad9 100644
--- a/libs/binder/ndk/include_platform/android/binder_rpc.h
+++ b/libs/binder/ndk/include_platform/android/binder_rpc.h
@@ -144,8 +144,9 @@ typedef void (*ABinderRpc_AccessorProviderUserData_deleteCallback)(void* _Nullab
*/
ABinderRpc_AccessorProvider* _Nullable ABinderRpc_registerAccessorProvider(
ABinderRpc_AccessorProvider_getAccessorCallback _Nonnull provider,
- const char* _Nullable* _Nonnull instances, size_t numInstances, void* _Nullable data,
- ABinderRpc_AccessorProviderUserData_deleteCallback _Nullable onDelete) __INTRODUCED_IN(36);
+ const char* _Nullable const* const _Nonnull instances, size_t numInstances,
+ void* _Nullable data, ABinderRpc_AccessorProviderUserData_deleteCallback _Nullable onDelete)
+ __INTRODUCED_IN(36);
/**
* Remove an ABinderRpc_AccessorProvider from libbinder. This will remove references
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
index 020ebcce95..8404a48c26 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -139,6 +139,9 @@ rust_bindgen {
"--raw-line",
"use libc::sockaddr;",
],
+ cflags: [
+ "-DANDROID_PLATFORM",
+ ],
shared_libs: [
"libbinder_ndk",
],
@@ -179,6 +182,9 @@ rust_bindgen {
// rustified
"libbinder_ndk_bindgen_flags.txt",
],
+ cflags: [
+ "-DANDROID_PLATFORM",
+ ],
shared_libs: [
"libbinder_ndk_on_trusty_mock",
"libc++",
diff --git a/libs/binder/rust/Cargo.toml b/libs/binder/rust/Cargo.toml
new file mode 100644
index 0000000000..e5738c574a
--- /dev/null
+++ b/libs/binder/rust/Cargo.toml
@@ -0,0 +1,15 @@
+[package]
+name = "android-binder"
+version = "0.1.0"
+edition = "2021"
+description = "Safe bindings to Android Binder, restricted to the NDK"
+license = "Apache-2.0"
+
+[dependencies]
+binder-ndk-sys = { package = "android-binder-ndk-sys", version = "0.1", path = "./sys" }
+downcast-rs = "1.2.1"
+libc = "0.2.159"
+
+[lints.rust.unexpected_cfgs]
+level = "warn"
+check-cfg = ["cfg(android_vendor)", "cfg(android_ndk)", "cfg(android_vndk)", "cfg(trusty)"]
diff --git a/libs/binder/rust/build.rs b/libs/binder/rust/build.rs
new file mode 100644
index 0000000000..f3e6b53778
--- /dev/null
+++ b/libs/binder/rust/build.rs
@@ -0,0 +1,4 @@
+fn main() {
+ // Anything with cargo is NDK only. If you want to access anything else, use Soong.
+ println!("cargo::rustc-cfg=android_ndk");
+}
diff --git a/libs/binder/rust/rpcbinder/src/server/android.rs b/libs/binder/rust/rpcbinder/src/server/android.rs
index 2ab34472a9..74ce315e30 100644
--- a/libs/binder/rust/rpcbinder/src/server/android.rs
+++ b/libs/binder/rust/rpcbinder/src/server/android.rs
@@ -18,7 +18,7 @@ use crate::session::FileDescriptorTransportMode;
use binder::{unstable_api::AsNative, SpIBinder};
use binder_rpc_unstable_bindgen::ARpcServer;
use foreign_types::{foreign_type, ForeignType, ForeignTypeRef};
-use std::ffi::CString;
+use std::ffi::{c_uint, CString};
use std::io::{Error, ErrorKind};
use std::os::unix::io::{IntoRawFd, OwnedFd};
@@ -42,18 +42,29 @@ impl RpcServer {
/// Creates a binder RPC server, serving the supplied binder service implementation on the given
/// vsock port. Only connections from the given CID are accepted.
///
- // Set `cid` to libc::VMADDR_CID_ANY to accept connections from any client.
- // Set `cid` to libc::VMADDR_CID_LOCAL to only bind to the local vsock interface.
- pub fn new_vsock(mut service: SpIBinder, cid: u32, port: u32) -> Result<RpcServer, Error> {
+ /// Set `cid` to [`libc::VMADDR_CID_ANY`] to accept connections from any client.
+ /// Set `cid` to [`libc::VMADDR_CID_LOCAL`] to only bind to the local vsock interface.
+ /// Set `port` to [`libc::VMADDR_PORT_ANY`] to pick an ephemeral port.
+ /// The assigned port is returned with RpcServer.
+ pub fn new_vsock(
+ mut service: SpIBinder,
+ cid: u32,
+ port: u32,
+ ) -> Result<(RpcServer, u32 /* assigned_port */), Error> {
let service = service.as_native_mut();
+ let mut assigned_port: c_uint = 0;
// SAFETY: Service ownership is transferring to the server and won't be valid afterward.
// Plus the binder objects are threadsafe.
- unsafe {
+ let server = unsafe {
Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newVsock(
- service, cid, port,
- ))
- }
+ service,
+ cid,
+ port,
+ &mut assigned_port,
+ ))?
+ };
+ Ok((server, assigned_port as _))
}
/// Creates a binder RPC server, serving the supplied binder service implementation on the given
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index 23026e593c..8c0501ba2f 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -207,8 +207,10 @@ pub const LAST_CALL_TRANSACTION: TransactionCode = sys::LAST_CALL_TRANSACTION;
/// Corresponds to TF_ONE_WAY -- an asynchronous call.
pub const FLAG_ONEWAY: TransactionFlags = sys::FLAG_ONEWAY;
/// Corresponds to TF_CLEAR_BUF -- clear transaction buffers after call is made.
+#[cfg(not(android_ndk))]
pub const FLAG_CLEAR_BUF: TransactionFlags = sys::FLAG_CLEAR_BUF;
/// Set to the vendor flag if we are building for the VNDK, 0 otherwise
+#[cfg(not(android_ndk))]
pub const FLAG_PRIVATE_LOCAL: TransactionFlags = sys::FLAG_PRIVATE_LOCAL;
/// Internal interface of binder local or remote objects for making
@@ -221,7 +223,7 @@ pub trait IBinderInternal: IBinder {
fn is_binder_alive(&self) -> bool;
/// Indicate that the service intends to receive caller security contexts.
- #[cfg(not(android_vndk))]
+ #[cfg(not(any(android_vndk, android_ndk)))]
fn set_requesting_sid(&mut self, enable: bool);
/// Dump this object to the given file handle
@@ -346,7 +348,6 @@ impl InterfaceClass {
panic!("Expected non-null class pointer from AIBinder_Class_define!");
}
sys::AIBinder_Class_setOnDump(class, Some(I::on_dump));
- sys::AIBinder_Class_setHandleShellCommand(class, None);
class
};
InterfaceClass(ptr)
@@ -714,7 +715,7 @@ unsafe impl<T, V: AsNative<T>> AsNative<T> for Option<V> {
pub struct BinderFeatures {
/// Indicates that the service intends to receive caller security contexts. This must be true
/// for `ThreadState::with_calling_sid` to work.
- #[cfg(not(android_vndk))]
+ #[cfg(not(any(android_vndk, android_ndk)))]
pub set_requesting_sid: bool,
// Ensure that clients include a ..BinderFeatures::default() to preserve backwards compatibility
// when new fields are added. #[non_exhaustive] doesn't work because it prevents struct
@@ -916,8 +917,12 @@ macro_rules! declare_binder_interface {
impl $native {
/// Create a new binder service.
pub fn new_binder<T: $interface + Sync + Send + 'static>(inner: T, features: $crate::BinderFeatures) -> $crate::Strong<dyn $interface> {
+ #[cfg(not(android_ndk))]
let mut binder = $crate::binder_impl::Binder::new_with_stability($native(Box::new(inner)), $stability);
- #[cfg(not(android_vndk))]
+ #[cfg(android_ndk)]
+ let mut binder = $crate::binder_impl::Binder::new($native(Box::new(inner)));
+
+ #[cfg(not(any(android_vndk, android_ndk)))]
$crate::binder_impl::IBinderInternal::set_requesting_sid(&mut binder, features.set_requesting_sid);
$crate::Strong::new(Box::new(binder))
}
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
index f7f3f35c9f..14493db262 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -100,11 +100,11 @@ mod error;
mod native;
mod parcel;
mod proxy;
-#[cfg(not(trusty))]
+#[cfg(not(any(trusty, android_ndk)))]
mod service;
-#[cfg(not(trusty))]
+#[cfg(not(any(trusty, android_ndk)))]
mod state;
-#[cfg(not(any(android_vendor, android_vndk)))]
+#[cfg(not(any(android_vendor, android_ndk, android_vndk)))]
mod system_only;
use binder_ndk_sys as sys;
@@ -114,15 +114,18 @@ pub use binder::{BinderFeatures, FromIBinder, IBinder, Interface, Strong, Weak};
pub use error::{ExceptionCode, IntoBinderResult, Status, StatusCode};
pub use parcel::{ParcelFileDescriptor, Parcelable, ParcelableHolder};
pub use proxy::{DeathRecipient, SpIBinder, WpIBinder};
-#[cfg(not(trusty))]
+#[cfg(not(any(trusty, android_ndk)))]
pub use service::{
add_service, check_interface, check_service, force_lazy_services_persist,
- get_declared_instances, get_interface, get_service, is_declared, is_handling_transaction,
- register_lazy_service, wait_for_interface, wait_for_service, LazyServiceGuard,
+ get_declared_instances, is_declared, is_handling_transaction, register_lazy_service,
+ wait_for_interface, wait_for_service, LazyServiceGuard,
};
-#[cfg(not(trusty))]
+#[cfg(not(any(trusty, android_ndk)))]
+#[allow(deprecated)]
+pub use service::{get_interface, get_service};
+#[cfg(not(any(trusty, android_ndk)))]
pub use state::{ProcessState, ThreadState};
-#[cfg(not(any(android_vendor, android_vndk)))]
+#[cfg(not(any(android_vendor, android_vndk, android_ndk)))]
pub use system_only::{delegate_accessor, Accessor, ConnectionInfo};
/// Binder result containing a [`Status`] on error.
@@ -134,9 +137,10 @@ pub mod binder_impl {
pub use crate::binder::{
IBinderInternal, InterfaceClass, LocalStabilityType, Remotable, Stability, StabilityType,
ToAsyncInterface, ToSyncInterface, TransactionCode, TransactionFlags, VintfStabilityType,
- FIRST_CALL_TRANSACTION, FLAG_CLEAR_BUF, FLAG_ONEWAY, FLAG_PRIVATE_LOCAL,
- LAST_CALL_TRANSACTION,
+ FIRST_CALL_TRANSACTION, FLAG_ONEWAY, LAST_CALL_TRANSACTION,
};
+ #[cfg(not(android_ndk))]
+ pub use crate::binder::{FLAG_CLEAR_BUF, FLAG_PRIVATE_LOCAL};
pub use crate::binder_async::BinderAsyncRuntime;
pub use crate::error::status_t;
pub use crate::native::Binder;
diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs
index c87cc94973..9e1cfd6b8f 100644
--- a/libs/binder/rust/src/native.rs
+++ b/libs/binder/rust/src/native.rs
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-use crate::binder::{
- AsNative, Interface, InterfaceClassMethods, Remotable, Stability, TransactionCode,
-};
+#[cfg(not(android_ndk))]
+use crate::binder::Stability;
+use crate::binder::{AsNative, Interface, InterfaceClassMethods, Remotable, TransactionCode};
use crate::error::{status_result, status_t, Result, StatusCode};
use crate::parcel::{BorrowedParcel, Serialize};
use crate::proxy::SpIBinder;
@@ -76,14 +76,32 @@ impl<T: Remotable> Binder<T> {
/// This moves the `rust_object` into an owned [`Box`] and Binder will
/// manage its lifetime.
pub fn new(rust_object: T) -> Binder<T> {
- Self::new_with_stability(rust_object, Stability::default())
+ #[cfg(not(android_ndk))]
+ {
+ Self::new_with_stability(rust_object, Stability::default())
+ }
+ #[cfg(android_ndk)]
+ {
+ Self::new_unmarked(rust_object)
+ }
}
/// Create a new Binder remotable object with the given stability
///
/// This moves the `rust_object` into an owned [`Box`] and Binder will
/// manage its lifetime.
+ #[cfg(not(android_ndk))]
pub fn new_with_stability(rust_object: T, stability: Stability) -> Binder<T> {
+ let mut binder = Self::new_unmarked(rust_object);
+ binder.mark_stability(stability);
+ binder
+ }
+
+ /// Creates a new Binder remotable object with unset stability
+ ///
+ /// This is internal because normally we want to set the stability explicitly,
+ /// however for the NDK variant we cannot mark the stability.
+ fn new_unmarked(rust_object: T) -> Binder<T> {
let class = T::get_class();
let rust_object = Box::into_raw(Box::new(rust_object));
// Safety: `AIBinder_new` expects a valid class pointer (which we
@@ -93,9 +111,7 @@ impl<T: Remotable> Binder<T> {
// decremented via `AIBinder_decStrong` when the reference lifetime
// ends.
let ibinder = unsafe { sys::AIBinder_new(class.into(), rust_object as *mut c_void) };
- let mut binder = Binder { ibinder, rust_object };
- binder.mark_stability(stability);
- binder
+ Binder { ibinder, rust_object }
}
/// Set the extension of a binder interface. This allows a downstream
@@ -189,6 +205,7 @@ impl<T: Remotable> Binder<T> {
}
/// Mark this binder object with the given stability guarantee
+ #[cfg(not(android_ndk))]
fn mark_stability(&mut self, stability: Stability) {
match stability {
Stability::Local => self.mark_local_stability(),
@@ -215,7 +232,7 @@ impl<T: Remotable> Binder<T> {
/// Mark this binder object with local stability, which is vendor if we are
/// building for android_vendor and system otherwise.
- #[cfg(not(android_vendor))]
+ #[cfg(not(any(android_vendor, android_ndk)))]
fn mark_local_stability(&mut self) {
// Safety: Self always contains a valid `AIBinder` pointer, so we can
// always call this C API safely.
diff --git a/libs/binder/rust/src/parcel.rs b/libs/binder/rust/src/parcel.rs
index 3bfc425ee3..485b0bdb0d 100644
--- a/libs/binder/rust/src/parcel.rs
+++ b/libs/binder/rust/src/parcel.rs
@@ -197,6 +197,7 @@ unsafe impl<'a> AsNative<sys::AParcel> for BorrowedParcel<'a> {
// Data serialization methods
impl<'a> BorrowedParcel<'a> {
/// Data written to parcelable is zero'd before being deleted or reallocated.
+ #[cfg(not(android_ndk))]
pub fn mark_sensitive(&mut self) {
// Safety: guaranteed to have a parcel object, and this method never fails
unsafe { sys::AParcel_markSensitive(self.as_native()) }
@@ -342,6 +343,7 @@ impl<'a> WritableSubParcel<'a> {
impl Parcel {
/// Data written to parcelable is zero'd before being deleted or reallocated.
+ #[cfg(not(android_ndk))]
pub fn mark_sensitive(&mut self) {
self.borrowed().mark_sensitive()
}
diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs
index 04f1517556..593d12c1e9 100644
--- a/libs/binder/rust/src/proxy.rs
+++ b/libs/binder/rust/src/proxy.rs
@@ -298,7 +298,7 @@ impl<T: AsNative<sys::AIBinder>> IBinderInternal for T {
unsafe { sys::AIBinder_isAlive(self.as_native()) }
}
- #[cfg(not(android_vndk))]
+ #[cfg(not(any(android_vndk, android_ndk)))]
fn set_requesting_sid(&mut self, enable: bool) {
// Safety: `SpIBinder` guarantees that `self` always contains a valid
// pointer to an `AIBinder`.
diff --git a/libs/binder/rust/src/service.rs b/libs/binder/rust/src/service.rs
index 29dd8e1f58..f4fdcf51c0 100644
--- a/libs/binder/rust/src/service.rs
+++ b/libs/binder/rust/src/service.rs
@@ -176,6 +176,7 @@ pub fn wait_for_service(name: &str) -> Option<SpIBinder> {
/// seconds if it doesn't yet exist.
#[deprecated = "this polls 5s, use wait_for_interface or check_interface"]
pub fn get_interface<T: FromIBinder + ?Sized>(name: &str) -> Result<Strong<T>> {
+ #[allow(deprecated)]
interface_cast(get_service(name))
}
diff --git a/libs/binder/rust/sys/BinderBindings.hpp b/libs/binder/rust/sys/BinderBindings.hpp
index bd666fe0d7..557f0e895d 100644
--- a/libs/binder/rust/sys/BinderBindings.hpp
+++ b/libs/binder/rust/sys/BinderBindings.hpp
@@ -15,15 +15,19 @@
*/
#include <android/binder_ibinder.h>
+#include <android/binder_parcel.h>
+#include <android/binder_status.h>
+
+/* Platform only */
+#if defined(ANDROID_PLATFORM) || defined(__ANDROID_VENDOR__)
#include <android/binder_ibinder_platform.h>
#include <android/binder_manager.h>
-#include <android/binder_parcel.h>
#include <android/binder_parcel_platform.h>
#include <android/binder_process.h>
#include <android/binder_rpc.h>
#include <android/binder_shell.h>
#include <android/binder_stability.h>
-#include <android/binder_status.h>
+#endif
namespace android {
@@ -81,8 +85,10 @@ enum {
enum {
FLAG_ONEWAY = FLAG_ONEWAY,
+#if defined(ANDROID_PLATFORM) || defined(__ANDROID_VENDOR__)
FLAG_CLEAR_BUF = FLAG_CLEAR_BUF,
FLAG_PRIVATE_LOCAL = FLAG_PRIVATE_LOCAL,
+#endif
};
} // namespace consts
diff --git a/libs/binder/rust/sys/Cargo.toml b/libs/binder/rust/sys/Cargo.toml
new file mode 100644
index 0000000000..ad8e9c26f5
--- /dev/null
+++ b/libs/binder/rust/sys/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+name = "android-binder-ndk-sys"
+version = "0.1.0"
+edition = "2021"
+description = "Bindgen bindings to android binder, restricted to the NDK"
+license = "Apache-2.0"
+
+[dependencies]
+
+[lib]
+path = "lib.rs"
+
+[build-dependencies]
+bindgen = "0.70.1"
diff --git a/libs/binder/rust/sys/build.rs b/libs/binder/rust/sys/build.rs
new file mode 100644
index 0000000000..cb9c65ba51
--- /dev/null
+++ b/libs/binder/rust/sys/build.rs
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+use std::env;
+use std::path::PathBuf;
+
+fn main() {
+ let ndk_home = PathBuf::from(env::var("ANDROID_NDK_HOME").unwrap());
+ let toolchain = ndk_home.join("toolchains/llvm/prebuilt/linux-x86_64/");
+ let sysroot = toolchain.join("sysroot");
+ let bindings = bindgen::Builder::default()
+ .clang_arg(format!("--sysroot={}", sysroot.display()))
+ // TODO figure out what the "standard" #define is and use that instead
+ .header("BinderBindings.hpp")
+ .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
+ // Keep in sync with libbinder_ndk_bindgen_flags.txt
+ .default_enum_style(bindgen::EnumVariation::Rust { non_exhaustive: true })
+ .constified_enum("android::c_interface::consts::.*")
+ .allowlist_type("android::c_interface::.*")
+ .allowlist_type("AStatus")
+ .allowlist_type("AIBinder_Class")
+ .allowlist_type("AIBinder")
+ .allowlist_type("AIBinder_Weak")
+ .allowlist_type("AIBinder_DeathRecipient")
+ .allowlist_type("AParcel")
+ .allowlist_type("binder_status_t")
+ .blocklist_function("vprintf")
+ .blocklist_function("strtold")
+ .blocklist_function("_vtlog")
+ .blocklist_function("vscanf")
+ .blocklist_function("vfprintf_worker")
+ .blocklist_function("vsprintf")
+ .blocklist_function("vsnprintf")
+ .blocklist_function("vsnprintf_filtered")
+ .blocklist_function("vfscanf")
+ .blocklist_function("vsscanf")
+ .blocklist_function("vdprintf")
+ .blocklist_function("vasprintf")
+ .blocklist_function("strtold_l")
+ .allowlist_function(".*")
+ .generate()
+ .expect("Couldn't generate bindings");
+ let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
+ bindings.write_to_file(out_path.join("bindings.rs")).expect("Couldn't write bindings.");
+ println!("cargo::rustc-link-lib=binder_ndk");
+}
diff --git a/libs/binder/rust/sys/lib.rs b/libs/binder/rust/sys/lib.rs
index 5352473272..349e5a9cc8 100644
--- a/libs/binder/rust/sys/lib.rs
+++ b/libs/binder/rust/sys/lib.rs
@@ -20,6 +20,7 @@ use std::error::Error;
use std::fmt;
#[cfg(not(target_os = "trusty"))]
+#[allow(bad_style)]
mod bindings {
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
}
diff --git a/libs/binder/tests/binderUtilsHostTest.cpp b/libs/binder/tests/binderUtilsHostTest.cpp
index 6301c74842..a62ad96be1 100644
--- a/libs/binder/tests/binderUtilsHostTest.cpp
+++ b/libs/binder/tests/binderUtilsHostTest.cpp
@@ -56,7 +56,7 @@ TEST(UtilsHost, ExecuteLongRunning) {
});
auto elapsedMs = millisSince(start);
EXPECT_GE(elapsedMs, 1000);
- EXPECT_LT(elapsedMs, 2000);
+ EXPECT_LT(elapsedMs, 3000); // b/377571547: higher to reduce flake
ASSERT_TRUE(result.has_value());
EXPECT_EQ(std::nullopt, result->exitCode);
@@ -65,7 +65,7 @@ TEST(UtilsHost, ExecuteLongRunning) {
// ~CommandResult() called, child process is killed.
// Assert that the second sleep does not finish.
- EXPECT_LT(millisSince(start), 2000);
+ EXPECT_LT(millisSince(start), 3000);
}
TEST(UtilsHost, ExecuteLongRunning2) {
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 1243b214d3..1e33abbdea 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -274,6 +274,7 @@ filegroup {
"LayerMetadata.cpp",
"LayerStatePermissions.cpp",
"LayerState.cpp",
+ "DisplayLuts.cpp",
"OccupancyTracker.cpp",
"StreamSplitter.cpp",
"ScreenCaptureResults.cpp",
@@ -341,6 +342,10 @@ cc_library_shared {
"libgui_aidl_headers",
],
+ static_libs: [
+ "libsurfaceflingerflags",
+ ],
+
afdo: true,
lto: {
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 49f4cba284..7aee90393b 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -52,19 +52,18 @@ using namespace std::chrono_literals;
namespace {
#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
-// RAII wrapper to defer arbitrary work until the Deferred instance is deleted.
-template <class F>
-class Deferred {
+template <class Mutex>
+class UnlockGuard {
public:
- explicit Deferred(F f) : mF{std::move(f)} {}
+ explicit UnlockGuard(Mutex& lock) : mLock{lock} { mLock.unlock(); }
- ~Deferred() { mF(); }
+ ~UnlockGuard() { mLock.lock(); }
- Deferred(const Deferred&) = delete;
- Deferred& operator=(const Deferred&) = delete;
+ UnlockGuard(const UnlockGuard&) = delete;
+ UnlockGuard& operator=(const UnlockGuard&) = delete;
private:
- F mF;
+ Mutex& mLock;
};
#endif
@@ -271,9 +270,6 @@ BLASTBufferQueue::~BLASTBufferQueue() {
void BLASTBufferQueue::onFirstRef() {
// safe default, most producers are expected to override this
mProducer->setMaxDequeuedBufferCount(2);
-#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
- mBufferReleaseThread.emplace(sp<BLASTBufferQueue>::fromExisting(this));
-#endif
}
void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height,
@@ -290,18 +286,23 @@ void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width,
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) {
- t.setFlags(mSurfaceControl, layer_state_t::eEnableBackpressure,
- layer_state_t::eEnableBackpressure);
#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
- t.setBufferReleaseChannel(mSurfaceControl, mBufferReleaseProducer);
+ 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();
@@ -325,7 +326,7 @@ void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width,
}
if (applyTransaction) {
// All transactions on our apply token are one-way. See comment on mAppliedLastTransaction
- t.setApplyToken(mApplyToken).apply(false, true);
+ t.setApplyToken(mApplyToken).apply(false /* synchronous */, true /* oneWay */);
}
}
@@ -419,7 +420,6 @@ void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence
stat.latchTime,
stat.frameEventStats.dequeueReadyTime);
}
-#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
auto currFrameNumber = stat.frameEventStats.frameNumber;
std::vector<ReleaseCallbackId> staleReleases;
for (const auto& [key, value]: mSubmitted) {
@@ -435,7 +435,6 @@ void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence
stat.currentMaxAcquiredBufferCount,
true /* fakeRelease */);
}
-#endif
} else {
BQA_LOGE("Failed to find matching SurfaceControl in transactionCallback");
}
@@ -469,6 +468,9 @@ ReleaseBufferCallback BLASTBufferQueue::makeReleaseBufferCallbackThunk() {
return;
}
bbq->releaseBufferCallback(id, releaseFence, currentMaxAcquiredBufferCount);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+ bbq->drainBufferReleaseConsumer();
+#endif
};
}
@@ -535,8 +537,6 @@ void BLASTBufferQueue::releaseBuffer(const ReleaseCallbackId& callbackId,
const sp<Fence>& releaseFence) {
auto it = mSubmitted.find(callbackId);
if (it == mSubmitted.end()) {
- BQA_LOGE("ERROR: releaseBufferCallback without corresponding submitted buffer %s",
- callbackId.to_string().c_str());
return;
}
mNumAcquired--;
@@ -646,12 +646,7 @@ status_t BLASTBufferQueue::acquireNextBufferLocked(
bufferItem.mGraphicBuffer->getHeight(), bufferItem.mTransform,
bufferItem.mScalingMode, crop);
-#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
- ReleaseBufferCallback releaseBufferCallback =
- applyTransaction ? nullptr : makeReleaseBufferCallbackThunk();
-#else
auto releaseBufferCallback = makeReleaseBufferCallbackThunk();
-#endif
sp<Fence> fence = bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE;
nsecs_t dequeueTime = -1;
@@ -689,6 +684,17 @@ status_t BLASTBufferQueue::acquireNextBufferLocked(
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() &&
@@ -1230,12 +1236,7 @@ public:
// we want to ignore it. This must be done before unlocking the BufferQueue lock to ensure
// we don't miss an interrupt.
bbq->mBufferReleaseReader->clearInterrupts();
- bbq->mThreadsBlockingOnDequeue++;
- bufferQueueLock.unlock();
- Deferred cleanup{[&]() {
- bufferQueueLock.lock();
- bbq->mThreadsBlockingOnDequeue--;
- }};
+ UnlockGuard unlockGuard{bufferQueueLock};
ATRACE_FORMAT("waiting for free buffer");
ReleaseCallbackId id;
@@ -1345,6 +1346,35 @@ void BLASTBufferQueue::setApplyToken(sp<IBinder> applyToken) {
#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+void BLASTBufferQueue::updateBufferReleaseProducer() {
+ // SELinux policy may prevent this process from sending the BufferReleaseChannel's file
+ // descriptor to SurfaceFlinger, causing the entire transaction to be dropped. We send this
+ // transaction independently of any other updates to ensure those updates aren't lost.
+ SurfaceComposerClient::Transaction t;
+ status_t status = t.setApplyToken(mApplyToken)
+ .setBufferReleaseChannel(mSurfaceControl, mBufferReleaseProducer)
+ .apply(false /* synchronous */, true /* oneWay */);
+ if (status != OK) {
+ ALOGW("[%s] %s - failed to set buffer release channel on %s", mName.c_str(),
+ statusToString(status).c_str(), mSurfaceControl->getName().c_str());
+ }
+}
+
+void BLASTBufferQueue::drainBufferReleaseConsumer() {
+ ATRACE_CALL();
+ while (true) {
+ ReleaseCallbackId id;
+ sp<Fence> fence;
+ uint32_t maxAcquiredBufferCount;
+ status_t status =
+ mBufferReleaseConsumer->readReleaseFence(id, fence, maxAcquiredBufferCount);
+ if (status != OK) {
+ return;
+ }
+ releaseBufferCallback(id, fence, maxAcquiredBufferCount);
+ }
+}
+
BLASTBufferQueue::BufferReleaseReader::BufferReleaseReader(BLASTBufferQueue& bbq) : mBbq{bbq} {
mEpollFd = android::base::unique_fd{epoll_create1(EPOLL_CLOEXEC)};
LOG_ALWAYS_FATAL_IF(!mEpollFd.ok(),
@@ -1438,95 +1468,6 @@ void BLASTBufferQueue::BufferReleaseReader::clearInterrupts() {
}
}
-BLASTBufferQueue::BufferReleaseThread::BufferReleaseThread(const sp<BLASTBufferQueue>& bbq) {
- android::base::unique_fd epollFd{epoll_create1(EPOLL_CLOEXEC)};
- LOG_ALWAYS_FATAL_IF(!epollFd.ok(),
- "Failed to create buffer release background thread epoll file descriptor. "
- "errno=%d message='%s'",
- errno, strerror(errno));
-
- epoll_event registerEndpointFd{};
- registerEndpointFd.events = EPOLLIN;
- registerEndpointFd.data.fd = bbq->mBufferReleaseConsumer->getFd();
- status_t status = epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, bbq->mBufferReleaseConsumer->getFd(),
- &registerEndpointFd);
- LOG_ALWAYS_FATAL_IF(status == -1,
- "Failed to register background thread buffer release consumer file "
- "descriptor with epoll. errno=%d message='%s'",
- errno, strerror(errno));
-
- // EventFd is used to break the background thread's loop.
- android::base::unique_fd eventFd{eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK)};
- LOG_ALWAYS_FATAL_IF(!eventFd.ok(),
- "Failed to create background thread buffer release event file descriptor. "
- "errno=%d message='%s'",
- errno, strerror(errno));
-
- epoll_event registerEventFd{};
- registerEventFd.events = EPOLLIN;
- registerEventFd.data.fd = eventFd.get();
- status = epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd.get(), &registerEventFd);
- LOG_ALWAYS_FATAL_IF(status == -1,
- "Failed to register background thread event file descriptor with epoll. "
- "errno=%d message='%s'",
- errno, strerror(errno));
-
- mEventFd = eventFd.get();
-
- std::thread([epollFd = std::move(epollFd), eventFd = std::move(eventFd),
- weakBbq = wp<BLASTBufferQueue>(bbq)]() {
- pthread_setname_np(pthread_self(), "BufferReleaseThread");
- while (true) {
- epoll_event event{};
- int eventCount;
- do {
- eventCount = epoll_wait(epollFd.get(), &event, 1 /*maxevents*/, -1 /*timeout*/);
- } while (eventCount == -1 && errno != EINTR);
-
- if (eventCount == -1) {
- ALOGE("epoll_wait error while waiting for buffer release in background thread. "
- "errno=%d message='%s'",
- errno, strerror(errno));
- continue;
- }
-
- // EventFd is used to join this thread.
- if (event.data.fd == eventFd.get()) {
- return;
- }
-
- sp<BLASTBufferQueue> bbq = weakBbq.promote();
- if (!bbq) {
- return;
- }
-
- // If there are threads blocking on dequeue, give those threads priority for handling
- // the release.
- if (bbq->mThreadsBlockingOnDequeue > 0) {
- std::this_thread::sleep_for(0ms);
- continue;
- }
-
- ReleaseCallbackId id;
- sp<Fence> fence;
- uint32_t maxAcquiredBufferCount;
- status_t status = bbq->mBufferReleaseConsumer->readReleaseFence(id, fence,
- maxAcquiredBufferCount);
- if (status != OK) {
- ALOGE("failed to read from buffer release consumer in background thread. errno=%d "
- "message='%s'",
- errno, strerror(errno));
- continue;
- }
- bbq->releaseBufferCallback(id, fence, maxAcquiredBufferCount);
- }
- }).detach();
-}
-
-BLASTBufferQueue::BufferReleaseThread::~BufferReleaseThread() {
- eventfd_write(mEventFd, 1);
-}
-
#endif
} // namespace android
diff --git a/libs/gui/BufferItem.cpp b/libs/gui/BufferItem.cpp
index 5beba02e63..3b2d337a21 100644
--- a/libs/gui/BufferItem.cpp
+++ b/libs/gui/BufferItem.cpp
@@ -38,26 +38,25 @@ static inline constexpr T to64(const uint32_t lo, const uint32_t hi) {
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 @@ size_t BufferItem::getPodSize() const {
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 @@ status_t BufferItem::flatten(
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 @@ status_t BufferItem::unflatten(
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 @@ status_t BufferItem::unflatten(
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 473a374a59..39209f9745 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -938,6 +938,8 @@ status_t BufferQueueProducer::queueBuffer(int slot,
&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 @@ status_t BufferQueueProducer::queueBuffer(int slot,
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/BufferReleaseChannel.cpp b/libs/gui/BufferReleaseChannel.cpp
index e9c6ef3512..e9cb013baf 100644
--- a/libs/gui/BufferReleaseChannel.cpp
+++ b/libs/gui/BufferReleaseChannel.cpp
@@ -35,35 +35,35 @@ namespace android::gui {
namespace {
template <typename T>
-static void readAligned(const void*& buffer, size_t& size, T& value) {
+void readAligned(const void*& buffer, size_t& size, T& value) {
size -= FlattenableUtils::align<alignof(T)>(buffer);
FlattenableUtils::read(buffer, size, value);
}
template <typename T>
-static void writeAligned(void*& buffer, size_t& size, T value) {
+void writeAligned(void*& buffer, size_t& size, T value) {
size -= FlattenableUtils::align<alignof(T)>(buffer);
FlattenableUtils::write(buffer, size, value);
}
template <typename T>
-static void addAligned(size_t& size, T /* value */) {
+void addAligned(size_t& size, T /* value */) {
size = FlattenableUtils::align<sizeof(T)>(size);
size += sizeof(T);
}
template <typename T>
-static inline constexpr uint32_t low32(const T n) {
+inline constexpr uint32_t low32(const T n) {
return static_cast<uint32_t>(static_cast<uint64_t>(n));
}
template <typename T>
-static inline constexpr uint32_t high32(const T n) {
+inline constexpr uint32_t high32(const T n) {
return static_cast<uint32_t>(static_cast<uint64_t>(n) >> 32);
}
template <typename T>
-static inline constexpr T to64(const uint32_t lo, const uint32_t hi) {
+inline constexpr T to64(const uint32_t lo, const uint32_t hi) {
return static_cast<T>(static_cast<uint64_t>(hi) << 32 | lo);
}
@@ -139,19 +139,18 @@ status_t BufferReleaseChannel::ConsumerEndpoint::readReleaseFence(
std::lock_guard lock{mMutex};
Message message;
mFlattenedBuffer.resize(message.getFlattenedSize());
- std::array<uint8_t, CMSG_SPACE(sizeof(int))> controlMessageBuffer;
+ std::array<uint8_t, CMSG_SPACE(sizeof(int))> controlMessageBuffer{};
iovec iov{
.iov_base = mFlattenedBuffer.data(),
.iov_len = mFlattenedBuffer.size(),
};
- msghdr msg{
- .msg_iov = &iov,
- .msg_iovlen = 1,
- .msg_control = controlMessageBuffer.data(),
- .msg_controllen = controlMessageBuffer.size(),
- };
+ msghdr msg{};
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = controlMessageBuffer.data();
+ msg.msg_controllen = controlMessageBuffer.size();
ssize_t result;
do {
@@ -161,7 +160,7 @@ status_t BufferReleaseChannel::ConsumerEndpoint::readReleaseFence(
if (errno == EWOULDBLOCK || errno == EAGAIN) {
return WOULD_BLOCK;
}
- ALOGE("Error reading release fence from socket: error %#x (%s)", errno, strerror(errno));
+ ALOGE("Error reading release fence from socket: error %d (%s)", errno, strerror(errno));
return UNKNOWN_ERROR;
}
@@ -200,9 +199,9 @@ status_t BufferReleaseChannel::ConsumerEndpoint::readReleaseFence(
return OK;
}
-int BufferReleaseChannel::ProducerEndpoint::writeReleaseFence(const ReleaseCallbackId& callbackId,
- const sp<Fence>& fence,
- uint32_t maxAcquiredBufferCount) {
+status_t BufferReleaseChannel::ProducerEndpoint::writeReleaseFence(
+ const ReleaseCallbackId& callbackId, const sp<Fence>& fence,
+ uint32_t maxAcquiredBufferCount) {
Message message{callbackId, fence ? fence : Fence::NO_FENCE, maxAcquiredBufferCount};
mFlattenedBuffer.resize(message.getFlattenedSize());
int flattenedFd;
@@ -213,25 +212,22 @@ int BufferReleaseChannel::ProducerEndpoint::writeReleaseFence(const ReleaseCallb
size_t flattenedBufferSize = mFlattenedBuffer.size();
int* flattenedFdPtr = &flattenedFd;
size_t flattenedFdCount = 1;
- if (status_t err = message.flatten(flattenedBufferPtr, flattenedBufferSize, flattenedFdPtr,
- flattenedFdCount);
- err != OK) {
- ALOGE("Failed to flatten BufferReleaseChannel message.");
- return err;
+ if (status_t status = message.flatten(flattenedBufferPtr, flattenedBufferSize,
+ flattenedFdPtr, flattenedFdCount);
+ status != OK) {
+ return status;
}
}
- iovec iov{
- .iov_base = mFlattenedBuffer.data(),
- .iov_len = mFlattenedBuffer.size(),
- };
+ iovec iov{};
+ iov.iov_base = mFlattenedBuffer.data();
+ iov.iov_len = mFlattenedBuffer.size();
- msghdr msg{
- .msg_iov = &iov,
- .msg_iovlen = 1,
- };
+ msghdr msg{};
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
- std::array<uint8_t, CMSG_SPACE(sizeof(int))> controlMessageBuffer;
+ std::array<uint8_t, CMSG_SPACE(sizeof(int))> controlMessageBuffer{};
if (fence && fence->isValid()) {
msg.msg_control = controlMessageBuffer.data();
msg.msg_controllen = controlMessageBuffer.size();
@@ -248,7 +244,6 @@ int BufferReleaseChannel::ProducerEndpoint::writeReleaseFence(const ReleaseCallb
result = sendmsg(mFd, &msg, 0);
} while (result == -1 && errno == EINTR);
if (result == -1) {
- ALOGD("Error writing release fence to socket: error %#x (%s)", errno, strerror(errno));
return -errno;
}
@@ -344,13 +339,6 @@ status_t BufferReleaseChannel::open(std::string name,
return -errno;
}
- // Make the producer write-only
- if (shutdown(producerFd.get(), SHUT_RD) == -1) {
- ALOGE("[%s] Failed to shutdown reading on producer socket. errno=%d message='%s'",
- name.c_str(), errno, strerror(errno));
- return -errno;
- }
-
outConsumer = std::make_unique<ConsumerEndpoint>(name, std::move(consumerFd));
outProducer = std::make_shared<ProducerEndpoint>(std::move(name), std::move(producerFd));
return STATUS_OK;
diff --git a/libs/gui/DisplayLuts.cpp b/libs/gui/DisplayLuts.cpp
new file mode 100644
index 0000000000..80429765be
--- /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 c8b9b6751d..4e92a39973 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 @@ status_t IGraphicBufferProducer::QueueBufferInput::flatten(
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 @@ status_t IGraphicBufferProducer::QueueBufferInput::unflatten(
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 422c57bc87..c1a03fcfea 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 @@ layer_state_t::layer_state_t()
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 @@ status_t layer_state_t::write(Parcel& output) const
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 @@ status_t layer_state_t::read(const Parcel& input)
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;
}
@@ -664,6 +692,10 @@ void layer_state_t::merge(const layer_state_t& other) {
what |= eShadowRadiusChanged;
shadowRadius = other.shadowRadius;
}
+ if (other.what & eLutsChanged) {
+ what |= eLutsChanged;
+ luts = other.luts;
+ }
if (other.what & eDefaultFrameRateCompatibilityChanged) {
what |= eDefaultFrameRateCompatibilityChanged;
defaultFrameRateCompatibility = other.defaultFrameRateCompatibility;
@@ -741,6 +773,16 @@ void layer_state_t::merge(const layer_state_t& other) {
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,
@@ -821,6 +863,10 @@ uint64_t layer_state_t::diff(const layer_state_t& other) const {
CHECK_DIFF(diff, eColorSpaceAgnosticChanged, other, colorSpaceAgnostic);
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 66e7ddd915..e41f9bbf43 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -2735,8 +2735,8 @@ status_t Surface::unlockAndPost()
bool Surface::waitForNextFrame(uint64_t lastFrame, nsecs_t timeout) {
Mutex::Autolock lock(mMutex);
- if (mNextFrameNumber > lastFrame) {
- return true;
+ if (mLastFrameNumber > lastFrame) {
+ return true;
}
return mQueueBufferCondition.waitRelative(mMutex, timeout) == OK;
}
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index eeea80fa82..61aabaa7f6 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,8 @@
#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>
#include <utils/Errors.h>
@@ -56,6 +56,7 @@
#include <ui/DisplayMode.h>
#include <ui/DisplayState.h>
#include <ui/DynamicDisplayInfo.h>
+#include <ui/FrameRateCategoryRate.h>
#include <android-base/thread_annotations.h>
#include <gui/LayerStatePermissions.h>
@@ -90,6 +91,7 @@ int64_t generateId() {
}
constexpr int64_t INVALID_VSYNC = -1;
+const constexpr char* LOG_SURFACE_CONTROL_REGISTRY = "SurfaceControlRegistry";
} // namespace
@@ -871,6 +873,7 @@ status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel
const bool earlyWakeupEnd = parcel->readBool();
const int64_t desiredPresentTime = parcel->readInt64();
const bool isAutoTimestamp = parcel->readBool();
+ const bool logCallPoints = parcel->readBool();
FrameTimelineInfo frameTimelineInfo;
frameTimelineInfo.readFromParcel(parcel);
@@ -998,6 +1001,7 @@ status_t SurfaceComposerClient::Transaction::writeToParcel(Parcel* parcel) const
parcel->writeBool(mEarlyWakeupEnd);
parcel->writeInt64(mDesiredPresentTime);
parcel->writeBool(mIsAutoTimestamp);
+ parcel->writeBool(mLogCallPoints);
mFrameTimelineInfo.writeToParcel(parcel);
parcel->writeStrongBinder(mApplyToken);
parcel->writeUint32(static_cast<uint32_t>(mDisplayStates.size()));
@@ -1133,6 +1137,12 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::merge(Tr
mergeFrameTimelineInfo(mFrameTimelineInfo, other.mFrameTimelineInfo);
+ mLogCallPoints |= other.mLogCallPoints;
+ if (mLogCallPoints) {
+ ALOG(LOG_DEBUG, LOG_SURFACE_CONTROL_REGISTRY,
+ "Transaction %" PRIu64 " merged with transaction %" PRIu64, other.getId(), mId);
+ }
+
other.clear();
return *this;
}
@@ -1152,6 +1162,7 @@ void SurfaceComposerClient::Transaction::clear() {
mFrameTimelineInfo = {};
mApplyToken = nullptr;
mMergedTransactionIds.clear();
+ mLogCallPoints = false;
}
uint64_t SurfaceComposerClient::Transaction::getId() {
@@ -1346,21 +1357,26 @@ status_t SurfaceComposerClient::Transaction::apply(bool synchronous, bool oneWay
sp<IBinder> applyToken = mApplyToken ? mApplyToken : getDefaultApplyToken();
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
- sf->setTransactionState(mFrameTimelineInfo, composerStates, displayStates, flags, applyToken,
- mInputWindowCommands, mDesiredPresentTime, mIsAutoTimestamp,
- mUncacheBuffers, hasListenerCallbacks, listenerCallbacks, mId,
- mMergedTransactionIds);
+ status_t binderStatus =
+ sf->setTransactionState(mFrameTimelineInfo, composerStates, displayStates, flags,
+ applyToken, mInputWindowCommands, mDesiredPresentTime,
+ mIsAutoTimestamp, mUncacheBuffers, hasListenerCallbacks,
+ listenerCallbacks, mId, mMergedTransactionIds);
mId = generateId();
// Clear the current states and flags
clear();
- if (synchronous) {
+ if (synchronous && binderStatus == OK) {
syncCallback->wait();
}
+ if (mLogCallPoints) {
+ ALOG(LOG_DEBUG, LOG_SURFACE_CONTROL_REGISTRY, "Transaction %" PRIu64 " applied", mId);
+ }
+
mStatus = NO_ERROR;
- return NO_ERROR;
+ return binderStatus;
}
sp<IBinder> SurfaceComposerClient::Transaction::sApplyToken = new BBinder();
@@ -1374,7 +1390,7 @@ sp<IBinder> SurfaceComposerClient::Transaction::getDefaultApplyToken() {
void SurfaceComposerClient::Transaction::setDefaultApplyToken(sp<IBinder> applyToken) {
std::scoped_lock lock{sApplyTokenMutex};
- sApplyToken = applyToken;
+ sApplyToken = std::move(applyToken);
}
status_t SurfaceComposerClient::Transaction::sendSurfaceFlushJankDataTransaction(
@@ -1389,6 +1405,11 @@ status_t SurfaceComposerClient::Transaction::sendSurfaceFlushJankDataTransaction
t.registerSurfaceControlForCallback(sc);
return t.apply(/*sync=*/false, /* oneWay=*/true);
}
+
+void SurfaceComposerClient::Transaction::enableDebugLogCallPoints() {
+ mLogCallPoints = true;
+}
+
// ---------------------------------------------------------------------------
sp<IBinder> SurfaceComposerClient::createVirtualDisplay(const std::string& displayName,
@@ -1940,15 +1961,23 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setDesir
}
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setLuts(
- const sp<SurfaceControl>& sc, const base::unique_fd& /*lutFd*/,
- const std::vector<int32_t>& /*offsets*/, const std::vector<int32_t>& /*dimensions*/,
- const std::vector<int32_t>& /*sizes*/, const std::vector<int32_t>& /*samplingKeys*/) {
+ const sp<SurfaceControl>& sc, const base::unique_fd& lutFd,
+ const std::vector<int32_t>& offsets, const std::vector<int32_t>& dimensions,
+ const std::vector<int32_t>& sizes, const std::vector<int32_t>& samplingKeys) {
layer_state_t* s = getLayerState(sc);
if (!s) {
mStatus = BAD_INDEX;
return *this;
}
- // TODO (b/329472856): update layer_state_t for lut(s)
+
+ 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;
}
@@ -2103,13 +2132,13 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::notifyPr
}
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setInputWindowInfo(
- const sp<SurfaceControl>& sc, const WindowInfo& info) {
+ const sp<SurfaceControl>& sc, sp<WindowInfoHandle> info) {
layer_state_t* s = getLayerState(sc);
if (!s) {
mStatus = BAD_INDEX;
return *this;
}
- s->windowInfoHandle = new WindowInfoHandle(info);
+ s->windowInfoHandle = std::move(info);
s->what |= layer_state_t::eInputInfoChanged;
return *this;
}
@@ -2421,6 +2450,40 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBuffe
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) {
@@ -2808,6 +2871,8 @@ void SurfaceComposerClient::getDynamicDisplayInfoInternal(gui::DynamicDisplayInf
outInfo->gameContentTypeSupported = ginfo.gameContentTypeSupported;
outInfo->preferredBootDisplayMode = ginfo.preferredBootDisplayMode;
outInfo->hasArrSupport = ginfo.hasArrSupport;
+ outInfo->frameRateCategoryRate = ui::FrameRateCategoryRate(ginfo.frameRateCategoryRate.normal,
+ ginfo.frameRateCategoryRate.high);
}
status_t SurfaceComposerClient::getDynamicDisplayInfoFromId(int64_t displayId,
diff --git a/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl b/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl
index 70873b001b..67cc273fce 100644
--- a/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl
+++ b/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl
@@ -17,6 +17,7 @@
package android.gui;
import android.gui.DisplayMode;
+import android.gui.FrameRateCategoryRate;
import android.gui.HdrCapabilities;
// Information about a physical display which may change on hotplug reconnect.
@@ -46,4 +47,7 @@ parcelable DynamicDisplayInfo {
// Represents whether display supports ARR.
boolean hasArrSupport;
+
+ // Represents frame rate for FrameRateCategory Normal and High.
+ FrameRateCategoryRate frameRateCategoryRate;
}
diff --git a/libs/binder/trusty/ndk/include/android/llndk-versioning.h b/libs/gui/aidl/android/gui/FrameRateCategoryRate.aidl
index e955a34bdf..f30280139f 100644
--- a/libs/binder/trusty/ndk/include/android/llndk-versioning.h
+++ b/libs/gui/aidl/android/gui/FrameRateCategoryRate.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,7 +13,12 @@
* 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)
+package android.gui;
+
+/** @hide */
+// Represents frame rate for FrameRateCategory Normal and High.
+parcelable FrameRateCategoryRate {
+ float normal;
+ float high;
+} \ No newline at end of file
diff --git a/libs/gui/aidl/android/gui/LutProperties.aidl b/libs/gui/aidl/android/gui/LutProperties.aidl
index 561e0c069c..87b878c1ca 100644
--- a/libs/gui/aidl/android/gui/LutProperties.aidl
+++ b/libs/gui/aidl/android/gui/LutProperties.aidl
@@ -25,7 +25,7 @@ parcelable LutProperties {
enum Dimension { ONE_D = 1, THREE_D = 3 }
Dimension dimension;
- long size;
+ int size;
@Backing(type="int")
enum SamplingKey { RGB, MAX_RGB }
SamplingKey[] samplingKeys;
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 99c64daa15..07558aa49d 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 @@ private:
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;
@@ -325,6 +330,14 @@ private:
std::unique_ptr<gui::BufferReleaseChannel::ConsumerEndpoint> mBufferReleaseConsumer;
std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint> mBufferReleaseProducer;
+ void updateBufferReleaseProducer() REQUIRES(mMutex);
+ void drainBufferReleaseConsumer();
+
+ // BufferReleaseReader is used to do blocking but interruptible reads from the buffer
+ // release channel. To implement this, BufferReleaseReader owns an epoll file descriptor that
+ // is configured to wake up when either the BufferReleaseReader::ConsumerEndpoint or an eventfd
+ // becomes readable. Interrupts are necessary because a free buffer may become available for
+ // reasons other than a buffer release from the producer.
class BufferReleaseReader {
public:
explicit BufferReleaseReader(BLASTBufferQueue&);
@@ -353,19 +366,6 @@ private:
};
std::optional<BufferReleaseReader> mBufferReleaseReader;
-
- std::atomic<int> mThreadsBlockingOnDequeue = 0;
-
- class BufferReleaseThread {
- public:
- BufferReleaseThread(const sp<BLASTBufferQueue>&);
- ~BufferReleaseThread();
-
- private:
- int mEventFd;
- };
-
- std::optional<BufferReleaseThread> mBufferReleaseThread;
#endif
};
diff --git a/libs/gui/include/gui/BufferItem.h b/libs/gui/include/gui/BufferItem.h
index 218bb424fb..2f85c62a54 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 @@ class BufferItem : public Flattenable<BufferItem> {
// 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
new file mode 100644
index 0000000000..ab86ac4af8
--- /dev/null
+++ b/libs/gui/include/gui/DisplayLuts.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <android-base/unique_fd.h>
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <vector>
+
+namespace android::gui {
+
+struct DisplayLuts : public Parcelable {
+public:
+ 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() {}
+
+ DisplayLuts(base::unique_fd lutfd, std::vector<int32_t> lutoffsets,
+ std::vector<int32_t> lutdimensions, std::vector<int32_t> lutsizes,
+ std::vector<int32_t> lutsamplingKeys) {
+ fd = std::move(lutfd);
+ offsets = lutoffsets;
+ lutProperties.reserve(offsets.size());
+ for (size_t i = 0; i < lutoffsets.size(); i++) {
+ Entry entry{lutdimensions[i], lutsizes[i], lutsamplingKeys[i]};
+ lutProperties.emplace_back(entry);
+ }
+ }
+
+ 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;
+
+private:
+ base::unique_fd fd;
+}; // struct DisplayLuts
+
+} // namespace android::gui \ No newline at end of file
diff --git a/libs/gui/include/gui/Flags.h b/libs/gui/include/gui/Flags.h
index 735375a1e7..34350d2c91 100644
--- a/libs/gui/include/gui/Flags.h
+++ b/libs/gui/include/gui/Flags.h
@@ -17,8 +17,20 @@
#pragma once
#include <com_android_graphics_libgui_flags.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/Surface.h>
#define WB_CAMERA3_AND_PROCESSORS_WITH_DEPENDENCIES \
(COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CAMERA3_AND_PROCESSORS) && \
COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) && \
- COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)) \ No newline at end of file
+ COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS))
+
+#define WB_LIBCAMERASERVICE_WITH_DEPENDENCIES \
+ (WB_CAMERA3_AND_PROCESSORS_WITH_DEPENDENCIES && \
+ COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_LIBCAMERASERVICE))
+
+#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES
+typedef android::Surface SurfaceType;
+#else
+typedef android::IGraphicBufferProducer SurfaceType;
+#endif \ No newline at end of file
diff --git a/libs/gui/include/gui/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h
index 3aac457a09..001e570982 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 @@ public:
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 @@ public:
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 00065c81d8..9098dffa8e 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -26,6 +26,7 @@
#include <android/gui/LayerCaptureArgs.h>
#include <android/gui/TrustedPresentationThresholds.h>
#include <android/native_window.h>
+#include <gui/DisplayLuts.h>
#include <gui/IGraphicBufferProducer.h>
#include <gui/ITransactionCompletedListener.h>
#include <math/mat4.h>
@@ -46,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>
@@ -184,6 +186,7 @@ struct layer_state_t {
eCachingHintChanged = 0x00000200,
eDimmingEnabledChanged = 0x00000400,
eShadowRadiusChanged = 0x00000800,
+ eLutsChanged = 0x00001000,
eBufferCropChanged = 0x00002000,
eRelativeLayerChanged = 0x00004000,
eReparent = 0x00008000,
@@ -222,6 +225,8 @@ struct layer_state_t {
eExtendedRangeBrightnessChanged = 0x10000'00000000,
eEdgeExtensionChanged = 0x20000'00000000,
eBufferReleaseChannelChanged = 0x40000'00000000,
+ ePictureProfileHandleChanged = 0x80000'00000000,
+ eAppContentPriorityChanged = 0x100000'00000000,
};
layer_state_t();
@@ -255,7 +260,7 @@ struct layer_state_t {
layer_state_t::eTransformToDisplayInverseChanged |
layer_state_t::eTransparentRegionChanged |
layer_state_t::eExtendedRangeBrightnessChanged |
- layer_state_t::eDesiredHdrHeadroomChanged;
+ layer_state_t::eDesiredHdrHeadroomChanged | layer_state_t::eLutsChanged;
// Content updates.
static constexpr uint64_t CONTENT_CHANGES = layer_state_t::BUFFER_CHANGES |
@@ -265,7 +270,8 @@ struct layer_state_t {
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 |
@@ -410,12 +416,23 @@ struct layer_state_t {
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;
TrustedPresentationListener trustedPresentationListener;
std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint> bufferReleaseChannel;
+
+ std::shared_ptr<gui::DisplayLuts> luts;
};
class ComposerState {
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 5ea0c1619b..0d7f8c2824 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 @@ public:
static void mergeFrameTimelineInfo(FrameTimelineInfo& t, const FrameTimelineInfo& other);
// Tracks registered callbacks
sp<TransactionCompletedListener> mTransactionCompletedListener = nullptr;
+ // Prints debug logs when enabled.
+ bool mLogCallPoints = false;
protected:
std::unordered_map<sp<IBinder>, ComposerState, IBinderHash> mComposerStates;
@@ -685,7 +688,8 @@ public:
// ONLY FOR BLAST ADAPTER
Transaction& notifyProducerDisconnect(const sp<SurfaceControl>& sc);
- Transaction& setInputWindowInfo(const sp<SurfaceControl>& sc, const gui::WindowInfo& info);
+ Transaction& setInputWindowInfo(const sp<SurfaceControl>& sc,
+ sp<gui::WindowInfoHandle> info);
Transaction& setFocusedWindow(const gui::FocusRequest& request);
Transaction& addWindowInfosReportedListener(
@@ -773,6 +777,20 @@ public:
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 @@ public:
static void setDefaultApplyToken(sp<IBinder> applyToken);
static status_t sendSurfaceFlushJankDataTransaction(const sp<SurfaceControl>& sc);
+ void enableDebugLogCallPoints();
};
status_t clearLayerFrameStats(const sp<IBinder>& token) const;
diff --git a/libs/gui/include/gui/view/Surface.h b/libs/gui/include/gui/view/Surface.h
index 7ddac8139a..7c762d3869 100644
--- a/libs/gui/include/gui/view/Surface.h
+++ b/libs/gui/include/gui/view/Surface.h
@@ -24,7 +24,9 @@
#include <binder/IBinder.h>
#include <binder/Parcelable.h>
+#include <gui/Flags.h>
#include <gui/IGraphicBufferProducer.h>
+#include <gui/Surface.h>
namespace android {
@@ -46,6 +48,14 @@ class Surface : public Parcelable {
sp<IGraphicBufferProducer> graphicBufferProducer;
sp<IBinder> surfaceControlHandle;
+#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES
+ // functions used to convert to a parcelable Surface so it can be passed over binder.
+ static Surface fromSurface(const sp<android::Surface>& surface);
+ sp<android::Surface> toSurface() const;
+
+ status_t getUniqueId(/* out */ uint64_t* id) const;
+#endif
+
virtual status_t writeToParcel(Parcel* parcel) const override;
virtual status_t readFromParcel(const Parcel* parcel) override;
diff --git a/libs/gui/libgui_flags.aconfig b/libs/gui/libgui_flags.aconfig
index 1c7e0e439c..22d32e9769 100644
--- a/libs/gui/libgui_flags.aconfig
+++ b/libs/gui/libgui_flags.aconfig
@@ -2,6 +2,14 @@ package: "com.android.graphics.libgui.flags"
container: "system"
flag {
+ name: "apply_picture_profiles"
+ namespace: "tv_os_media"
+ description: "This flag controls sending picture profiles from BBQ to Composer HAL"
+ bug: "337330263"
+ is_fixed_read_only: true
+} # apply_picture_profiles
+
+flag {
name: "bq_setframerate"
namespace: "core_graphics"
description: "This flag controls plumbing setFrameRate thru BufferQueue"
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index 2e6ffcb57f..b026e640aa 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 @@ TEST_F(BufferQueueTest, TestAdditionalOptions) {
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/BufferReleaseChannel_test.cpp b/libs/gui/tests/BufferReleaseChannel_test.cpp
index 11d122b525..74f69e1ff0 100644
--- a/libs/gui/tests/BufferReleaseChannel_test.cpp
+++ b/libs/gui/tests/BufferReleaseChannel_test.cpp
@@ -29,11 +29,11 @@ namespace {
// Helper function to check if two file descriptors point to the same file.
bool is_same_file(int fd1, int fd2) {
- struct stat stat1;
+ struct stat stat1 {};
if (fstat(fd1, &stat1) != 0) {
return false;
}
- struct stat stat2;
+ struct stat stat2 {};
if (fstat(fd2, &stat2) != 0) {
return false;
}
@@ -42,7 +42,18 @@ bool is_same_file(int fd1, int fd2) {
} // namespace
-TEST(BufferReleaseChannelTest, MessageFlattenable) {
+class BufferReleaseChannelTest : public testing::Test {
+protected:
+ std::unique_ptr<BufferReleaseChannel::ConsumerEndpoint> mConsumer;
+ std::shared_ptr<BufferReleaseChannel::ProducerEndpoint> mProducer;
+
+ void SetUp() override {
+ ASSERT_EQ(OK,
+ BufferReleaseChannel::open("BufferReleaseChannelTest"s, mConsumer, mProducer));
+ }
+};
+
+TEST_F(BufferReleaseChannelTest, MessageFlattenable) {
ReleaseCallbackId releaseCallbackId{1, 2};
sp<Fence> releaseFence = sp<Fence>::make(memfd_create("fake-fence-fd", 0));
uint32_t maxAcquiredBufferCount = 5;
@@ -92,31 +103,23 @@ TEST(BufferReleaseChannelTest, MessageFlattenable) {
// Verify that the BufferReleaseChannel consume returns WOULD_BLOCK when there's no message
// available.
-TEST(BufferReleaseChannelTest, ConsumerEndpointIsNonBlocking) {
- std::unique_ptr<BufferReleaseChannel::ConsumerEndpoint> consumer;
- std::shared_ptr<BufferReleaseChannel::ProducerEndpoint> producer;
- ASSERT_EQ(OK, BufferReleaseChannel::open("test-channel"s, consumer, producer));
-
+TEST_F(BufferReleaseChannelTest, ConsumerEndpointIsNonBlocking) {
ReleaseCallbackId releaseCallbackId;
sp<Fence> releaseFence;
uint32_t maxAcquiredBufferCount;
ASSERT_EQ(WOULD_BLOCK,
- consumer->readReleaseFence(releaseCallbackId, releaseFence, maxAcquiredBufferCount));
+ mConsumer->readReleaseFence(releaseCallbackId, releaseFence, maxAcquiredBufferCount));
}
// Verify that we can write a message to the BufferReleaseChannel producer and read that message
// using the BufferReleaseChannel consumer.
-TEST(BufferReleaseChannelTest, ProduceAndConsume) {
- std::unique_ptr<BufferReleaseChannel::ConsumerEndpoint> consumer;
- std::shared_ptr<BufferReleaseChannel::ProducerEndpoint> producer;
- ASSERT_EQ(OK, BufferReleaseChannel::open("test-channel"s, consumer, producer));
-
+TEST_F(BufferReleaseChannelTest, ProduceAndConsume) {
sp<Fence> fence = sp<Fence>::make(memfd_create("fake-fence-fd", 0));
for (uint64_t i = 0; i < 64; i++) {
ReleaseCallbackId producerId{i, i + 1};
uint32_t maxAcquiredBufferCount = i + 2;
- ASSERT_EQ(OK, producer->writeReleaseFence(producerId, fence, maxAcquiredBufferCount));
+ ASSERT_EQ(OK, mProducer->writeReleaseFence(producerId, fence, maxAcquiredBufferCount));
}
for (uint64_t i = 0; i < 64; i++) {
@@ -127,7 +130,7 @@ TEST(BufferReleaseChannelTest, ProduceAndConsume) {
sp<Fence> consumerFence;
uint32_t maxAcquiredBufferCount;
ASSERT_EQ(OK,
- consumer->readReleaseFence(consumerId, consumerFence, maxAcquiredBufferCount));
+ mConsumer->readReleaseFence(consumerId, consumerFence, maxAcquiredBufferCount));
ASSERT_EQ(expectedId, consumerId);
ASSERT_TRUE(is_same_file(fence->get(), consumerFence->get()));
@@ -135,4 +138,16 @@ TEST(BufferReleaseChannelTest, ProduceAndConsume) {
}
}
+// Verify that BufferReleaseChannel::ConsumerEndpoint's socket can't be written to.
+TEST_F(BufferReleaseChannelTest, ConsumerSocketReadOnly) {
+ uint64_t data = 0;
+ ASSERT_EQ(-1, write(mConsumer->getFd().get(), &data, sizeof(uint64_t)));
+ ASSERT_EQ(errno, EPIPE);
+}
+
+// Verify that BufferReleaseChannel::ProducerEndpoint's socket can't be read from.
+TEST_F(BufferReleaseChannelTest, ProducerSocketWriteOnly) {
+ ASSERT_EQ(0, read(mProducer->getFd().get(), nullptr, sizeof(uint64_t)));
+}
+
} // namespace android \ No newline at end of file
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index 7d0b512cb4..0e84d68eec 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -112,7 +112,7 @@ public:
mInputFlinger = getInputFlinger();
if (noInputChannel) {
- mInputInfo.setInputConfig(WindowInfo::InputConfig::NO_INPUT_CHANNEL, true);
+ mInputInfo->editInfo()->setInputConfig(WindowInfo::InputConfig::NO_INPUT_CHANNEL, true);
} else {
android::os::InputChannelCore tempChannel;
android::binder::Status result =
@@ -121,21 +121,21 @@ public:
ADD_FAILURE() << "binder call to createInputChannel failed";
}
mClientChannel = InputChannel::create(std::move(tempChannel));
- mInputInfo.token = mClientChannel->getConnectionToken();
+ mInputInfo->editInfo()->token = mClientChannel->getConnectionToken();
mInputConsumer = new InputConsumer(mClientChannel);
}
- mInputInfo.name = "Test info";
- mInputInfo.dispatchingTimeout = 5s;
- mInputInfo.globalScaleFactor = 1.0;
- mInputInfo.touchableRegion.orSelf(Rect(0, 0, width, height));
+ mInputInfo->editInfo()->name = "Test info";
+ mInputInfo->editInfo()->dispatchingTimeout = 5s;
+ mInputInfo->editInfo()->globalScaleFactor = 1.0;
+ mInputInfo->editInfo()->touchableRegion.orSelf(Rect(0, 0, width, height));
InputApplicationInfo aInfo;
aInfo.token = new BBinder();
aInfo.name = "Test app info";
aInfo.dispatchingTimeoutMillis =
std::chrono::duration_cast<std::chrono::milliseconds>(DISPATCHING_TIMEOUT).count();
- mInputInfo.applicationInfo = aInfo;
+ mInputInfo->editInfo()->applicationInfo = aInfo;
}
static std::unique_ptr<InputSurface> makeColorInputSurface(const sp<SurfaceComposerClient>& scc,
@@ -183,20 +183,6 @@ public:
return std::make_unique<InputSurface>(surfaceControl, width, height);
}
- InputEvent* consumeEvent(std::chrono::milliseconds timeout = 3000ms) {
- mClientChannel->waitForMessage(timeout);
-
- InputEvent* ev;
- uint32_t seqId;
- status_t consumed = mInputConsumer->consume(&mInputEventFactory, true, -1, &seqId, &ev);
- if (consumed != OK) {
- return nullptr;
- }
- status_t status = mInputConsumer->sendFinishedSignal(seqId, true);
- EXPECT_EQ(OK, status) << "Could not send finished signal";
- return ev;
- }
-
void assertFocusChange(bool hasFocus) {
InputEvent* ev = consumeEvent();
ASSERT_NE(ev, nullptr);
@@ -294,7 +280,7 @@ public:
transactionBody) {
SurfaceComposerClient::Transaction t;
transactionBody(t, mSurfaceControl);
- t.apply(true);
+ t.apply(/*synchronously=*/true);
}
virtual void showAt(int x, int y, Rect crop = Rect(0, 0, 100, 100)) {
@@ -307,30 +293,46 @@ public:
t.setAlpha(mSurfaceControl, 1);
auto reportedListener = sp<SynchronousWindowInfosReportedListener>::make();
t.addWindowInfosReportedListener(reportedListener);
- t.apply();
+ t.apply(/*synchronously=*/true);
reportedListener->wait();
}
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);
- t.apply(true);
+ t.apply(/*synchronously=*/true);
}
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 {
@@ -363,7 +365,7 @@ public:
transactionBody) override {
SurfaceComposerClient::Transaction t;
transactionBody(t, mParentSurfaceControl);
- t.apply(true);
+ t.apply(/*synchronously=*/true);
}
void showAt(int x, int y, Rect crop = Rect(0, 0, 100, 100)) override {
@@ -377,7 +379,7 @@ public:
t.setInputWindowInfo(mSurfaceControl, mInputInfo);
t.setCrop(mSurfaceControl, crop);
t.setAlpha(mSurfaceControl, 1);
- t.apply(true);
+ t.apply(/*synchronously=*/true);
}
private:
@@ -417,7 +419,7 @@ public:
BufferUsage::COMPOSER_OVERLAY | BufferUsage::GPU_TEXTURE;
sp<GraphicBuffer> buffer =
new GraphicBuffer(w, h, PIXEL_FORMAT_RGBA_8888, 1, usageFlags, "test");
- Transaction().setBuffer(layer, buffer).apply(true);
+ Transaction().setBuffer(layer, buffer).apply(/*synchronously=*/true);
usleep(mBufferPostDelay);
}
@@ -458,7 +460,7 @@ TEST_F(InputSurfacesTest, can_receive_input) {
injectTap(101, 101);
- EXPECT_NE(surface->consumeEvent(), nullptr);
+ surface->expectTap(1, 1);
}
/**
@@ -521,7 +523,7 @@ TEST_F(InputSurfacesTest, input_respects_surface_insets) {
std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
bgSurface->showAt(100, 100);
- fgSurface->mInputInfo.surfaceInset = 5;
+ fgSurface->mInputInfo->editInfo()->surfaceInset = 5;
fgSurface->showAt(100, 100);
injectTap(106, 106);
@@ -536,8 +538,8 @@ TEST_F(InputSurfacesTest, input_respects_surface_insets_with_replaceTouchableReg
std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
bgSurface->showAt(100, 100);
- fgSurface->mInputInfo.surfaceInset = 5;
- fgSurface->mInputInfo.replaceTouchableRegionWithCrop = true;
+ fgSurface->mInputInfo->editInfo()->surfaceInset = 5;
+ fgSurface->mInputInfo->editInfo()->replaceTouchableRegionWithCrop = true;
fgSurface->showAt(100, 100);
injectTap(106, 106);
@@ -553,7 +555,7 @@ TEST_F(InputSurfacesTest, input_respects_cropped_surface_insets) {
std::unique_ptr<InputSurface> childSurface = makeSurface(100, 100);
parentSurface->showAt(100, 100);
- childSurface->mInputInfo.surfaceInset = 10;
+ childSurface->mInputInfo->editInfo()->surfaceInset = 10;
childSurface->showAt(100, 100);
childSurface->doTransaction([&](auto& t, auto& sc) {
@@ -574,7 +576,7 @@ TEST_F(InputSurfacesTest, input_respects_scaled_surface_insets) {
std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
bgSurface->showAt(100, 100);
- fgSurface->mInputInfo.surfaceInset = 5;
+ fgSurface->mInputInfo->editInfo()->surfaceInset = 5;
fgSurface->showAt(100, 100);
fgSurface->doTransaction([&](auto& t, auto& sc) { t.setMatrix(sc, 2.0, 0, 0, 4.0); });
@@ -593,7 +595,7 @@ TEST_F(InputSurfacesTest, input_respects_scaled_surface_insets_overflow) {
bgSurface->showAt(100, 100);
// In case we pass the very big inset without any checking.
- fgSurface->mInputInfo.surfaceInset = INT32_MAX;
+ fgSurface->mInputInfo->editInfo()->surfaceInset = INT32_MAX;
fgSurface->showAt(100, 100);
fgSurface->doTransaction([&](auto& t, auto& sc) { t.setMatrix(sc, 2.0, 0, 0, 2.0); });
@@ -606,13 +608,13 @@ TEST_F(InputSurfacesTest, input_respects_scaled_surface_insets_overflow) {
TEST_F(InputSurfacesTest, touchable_region) {
std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
- surface->mInputInfo.touchableRegion.set(Rect{19, 29, 21, 31});
+ surface->mInputInfo->editInfo()->touchableRegion.set(Rect{19, 29, 21, 31});
surface->showAt(11, 22);
// A tap within the surface but outside the touchable region should not be sent to the surface.
injectTap(20, 30);
- EXPECT_EQ(surface->consumeEvent(/*timeout=*/200ms), nullptr);
+ surface->assertNoEvent();
injectTap(31, 52);
surface->expectTap(20, 30);
@@ -627,7 +629,8 @@ TEST_F(InputSurfacesTest, input_respects_touchable_region_offset_overflow) {
// Since the surface is offset from the origin, the touchable region will be transformed into
// display space, which would trigger an overflow or an underflow. Ensure that we are protected
// against such a situation.
- fgSurface->mInputInfo.touchableRegion.orSelf(Rect{INT32_MIN, INT32_MIN, INT32_MAX, INT32_MAX});
+ fgSurface->mInputInfo->editInfo()->touchableRegion.orSelf(
+ Rect{INT32_MIN, INT32_MIN, INT32_MAX, INT32_MAX});
fgSurface->showAt(100, 100);
@@ -642,7 +645,8 @@ TEST_F(InputSurfacesTest, input_respects_scaled_touchable_region_overflow) {
std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
bgSurface->showAt(0, 0);
- fgSurface->mInputInfo.touchableRegion.orSelf(Rect{INT32_MIN, INT32_MIN, INT32_MAX, INT32_MAX});
+ fgSurface->mInputInfo->editInfo()->touchableRegion.orSelf(
+ Rect{INT32_MIN, INT32_MIN, INT32_MAX, INT32_MAX});
fgSurface->showAt(0, 0);
fgSurface->doTransaction([&](auto& t, auto& sc) { t.setMatrix(sc, 2.0, 0, 0, 2.0); });
@@ -812,7 +816,7 @@ TEST_F(InputSurfacesTest, rotate_surface_with_scale) {
TEST_F(InputSurfacesTest, rotate_surface_with_scale_and_insets) {
std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
- surface->mInputInfo.surfaceInset = 5;
+ surface->mInputInfo->editInfo()->surfaceInset = 5;
surface->showAt(100, 100);
surface->doTransaction([](auto& t, auto& sc) {
@@ -841,11 +845,12 @@ TEST_F(InputSurfacesTest, touch_flag_obscured) {
// Add non touchable window to fully cover touchable window. Window behind gets touch, but
// with flag AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED
std::unique_ptr<InputSurface> nonTouchableSurface = makeSurface(100, 100);
- nonTouchableSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
- nonTouchableSurface->mInputInfo.ownerUid = gui::Uid{22222};
+ nonTouchableSurface->mInputInfo->editInfo()
+ ->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
+ nonTouchableSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222};
// Overriding occlusion mode otherwise the touch would be discarded at InputDispatcher by
// the default obscured/untrusted touch filter introduced in S.
- nonTouchableSurface->mInputInfo.touchOcclusionMode = TouchOcclusionMode::ALLOW;
+ nonTouchableSurface->mInputInfo->editInfo()->touchOcclusionMode = TouchOcclusionMode::ALLOW;
nonTouchableSurface->showAt(100, 100);
injectTap(190, 199);
@@ -861,10 +866,12 @@ TEST_F(InputSurfacesTest, touch_flag_partially_obscured_with_crop) {
// AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED
std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100);
std::unique_ptr<InputSurface> nonTouchableSurface = makeSurface(100, 100);
- nonTouchableSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
- parentSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
- nonTouchableSurface->mInputInfo.ownerUid = gui::Uid{22222};
- parentSurface->mInputInfo.ownerUid = gui::Uid{22222};
+ nonTouchableSurface->mInputInfo->editInfo()
+ ->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
+ parentSurface->mInputInfo->editInfo()->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE,
+ true);
+ nonTouchableSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222};
+ parentSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222};
nonTouchableSurface->showAt(0, 0);
parentSurface->showAt(100, 100);
@@ -885,10 +892,12 @@ TEST_F(InputSurfacesTest, touch_not_obscured_with_crop) {
// the touchable window. Window behind gets touch with no obscured flags.
std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100);
std::unique_ptr<InputSurface> nonTouchableSurface = makeSurface(100, 100);
- nonTouchableSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
- parentSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
- nonTouchableSurface->mInputInfo.ownerUid = gui::Uid{22222};
- parentSurface->mInputInfo.ownerUid = gui::Uid{22222};
+ nonTouchableSurface->mInputInfo->editInfo()
+ ->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
+ parentSurface->mInputInfo->editInfo()->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE,
+ true);
+ nonTouchableSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222};
+ parentSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222};
nonTouchableSurface->showAt(0, 0);
parentSurface->showAt(50, 50);
@@ -906,8 +915,9 @@ TEST_F(InputSurfacesTest, touch_not_obscured_with_zero_sized_bql) {
std::unique_ptr<InputSurface> bufferSurface =
InputSurface::makeBufferInputSurface(mComposerClient, 0, 0);
- bufferSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
- bufferSurface->mInputInfo.ownerUid = gui::Uid{22222};
+ bufferSurface->mInputInfo->editInfo()->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE,
+ true);
+ bufferSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222};
surface->showAt(10, 10);
bufferSurface->showAt(50, 50, Rect::EMPTY_RECT);
@@ -921,8 +931,9 @@ TEST_F(InputSurfacesTest, touch_not_obscured_with_zero_sized_blast) {
std::unique_ptr<BlastInputSurface> bufferSurface =
BlastInputSurface::makeBlastInputSurface(mComposerClient, 0, 0);
- bufferSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
- bufferSurface->mInputInfo.ownerUid = gui::Uid{22222};
+ bufferSurface->mInputInfo->editInfo()->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE,
+ true);
+ bufferSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222};
surface->showAt(10, 10);
bufferSurface->showAt(50, 50, Rect::EMPTY_RECT);
@@ -965,13 +976,14 @@ TEST_F(InputSurfacesTest, strict_unobscured_input_scaled_without_crop_window) {
TEST_F(InputSurfacesTest, strict_unobscured_input_obscured_window) {
std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
- surface->mInputInfo.ownerUid = gui::Uid{11111};
+ surface->mInputInfo->editInfo()->ownerUid = gui::Uid{11111};
surface->doTransaction(
[&](auto& t, auto& sc) { t.setDropInputMode(sc, gui::DropInputMode::OBSCURED); });
surface->showAt(100, 100);
std::unique_ptr<InputSurface> obscuringSurface = makeSurface(100, 100);
- obscuringSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
- obscuringSurface->mInputInfo.ownerUid = gui::Uid{22222};
+ obscuringSurface->mInputInfo->editInfo()->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE,
+ true);
+ obscuringSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222};
obscuringSurface->showAt(100, 100);
injectTap(101, 101);
surface->assertNoEvent();
@@ -984,13 +996,14 @@ TEST_F(InputSurfacesTest, strict_unobscured_input_obscured_window) {
TEST_F(InputSurfacesTest, strict_unobscured_input_partially_obscured_window) {
std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
- surface->mInputInfo.ownerUid = gui::Uid{11111};
+ surface->mInputInfo->editInfo()->ownerUid = gui::Uid{11111};
surface->doTransaction(
[&](auto& t, auto& sc) { t.setDropInputMode(sc, gui::DropInputMode::OBSCURED); });
surface->showAt(100, 100);
std::unique_ptr<InputSurface> obscuringSurface = makeSurface(100, 100);
- obscuringSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
- obscuringSurface->mInputInfo.ownerUid = gui::Uid{22222};
+ obscuringSurface->mInputInfo->editInfo()->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE,
+ true);
+ obscuringSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222};
obscuringSurface->showAt(190, 190);
injectTap(101, 101);
@@ -1054,7 +1067,7 @@ TEST_F(InputSurfacesTest, ignore_touch_region_with_zero_sized_blast) {
BlastInputSurface::makeBlastInputSurface(mComposerClient, 0, 0);
surface->showAt(100, 100);
- bufferSurface->mInputInfo.touchableRegion.orSelf(Rect(0, 0, 200, 200));
+ bufferSurface->mInputInfo->editInfo()->touchableRegion.orSelf(Rect(0, 0, 200, 200));
bufferSurface->showAt(100, 100, Rect::EMPTY_RECT);
injectTap(101, 101);
@@ -1097,8 +1110,8 @@ TEST_F(InputSurfacesTest, cropped_container_replaces_touchable_region_with_null_
InputSurface::makeContainerInputSurface(mComposerClient, 100, 100);
containerSurface->doTransaction(
[&](auto& t, auto& sc) { t.reparent(sc, parentContainer->mSurfaceControl); });
- containerSurface->mInputInfo.replaceTouchableRegionWithCrop = true;
- containerSurface->mInputInfo.touchableRegionCropHandle = nullptr;
+ containerSurface->mInputInfo->editInfo()->replaceTouchableRegionWithCrop = true;
+ containerSurface->mInputInfo->editInfo()->touchableRegionCropHandle = nullptr;
parentContainer->showAt(10, 10, Rect(0, 0, 20, 20));
containerSurface->showAt(10, 10, Rect(0, 0, 5, 5));
@@ -1116,14 +1129,19 @@ TEST_F(InputSurfacesTest, cropped_container_replaces_touchable_region_with_null_
* in its parent's touchable region. The input events should be in the layer's coordinate space.
*/
TEST_F(InputSurfacesTest, uncropped_container_replaces_touchable_region_with_null_crop) {
+ std::unique_ptr<InputSurface> bgContainer =
+ InputSurface::makeContainerInputSurface(mComposerClient, 0, 0);
std::unique_ptr<InputSurface> parentContainer =
InputSurface::makeContainerInputSurface(mComposerClient, 0, 0);
std::unique_ptr<InputSurface> containerSurface =
InputSurface::makeContainerInputSurface(mComposerClient, 100, 100);
containerSurface->doTransaction(
[&](auto& t, auto& sc) { t.reparent(sc, parentContainer->mSurfaceControl); });
- containerSurface->mInputInfo.replaceTouchableRegionWithCrop = true;
- containerSurface->mInputInfo.touchableRegionCropHandle = nullptr;
+ containerSurface->mInputInfo->editInfo()->replaceTouchableRegionWithCrop = true;
+ containerSurface->mInputInfo->editInfo()->touchableRegionCropHandle = nullptr;
+ parentContainer->doTransaction(
+ [&](auto& t, auto& sc) { t.reparent(sc, bgContainer->mSurfaceControl); });
+ bgContainer->showAt(0, 0, Rect(0, 0, 100, 100));
parentContainer->showAt(10, 10, Rect(0, 0, 20, 20));
containerSurface->showAt(10, 10, Rect::INVALID_RECT);
@@ -1147,8 +1165,8 @@ TEST_F(InputSurfacesTest, replace_touchable_region_with_crop) {
std::unique_ptr<InputSurface> containerSurface =
InputSurface::makeContainerInputSurface(mComposerClient, 100, 100);
- containerSurface->mInputInfo.replaceTouchableRegionWithCrop = true;
- containerSurface->mInputInfo.touchableRegionCropHandle =
+ containerSurface->mInputInfo->editInfo()->replaceTouchableRegionWithCrop = true;
+ containerSurface->mInputInfo->editInfo()->touchableRegionCropHandle =
cropLayer->mSurfaceControl->getHandle();
containerSurface->showAt(10, 10, Rect::INVALID_RECT);
@@ -1207,7 +1225,7 @@ public:
t.setDisplayLayerStack(token, layerStack);
t.setDisplayProjection(token, ui::ROTATION_0, {0, 0, width, height},
{offsetX, offsetY, offsetX + width, offsetY + height});
- t.apply(true);
+ t.apply(/*synchronously=*/true);
mVirtualDisplays.push_back(token);
}
diff --git a/libs/gui/view/Surface.cpp b/libs/gui/view/Surface.cpp
index 84c2a6ac71..9f57923886 100644
--- a/libs/gui/view/Surface.cpp
+++ b/libs/gui/view/Surface.cpp
@@ -121,6 +121,38 @@ String16 Surface::readMaybeEmptyString16(const Parcel* parcel) {
return str.value_or(String16());
}
+#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES
+Surface Surface::fromSurface(const sp<android::Surface>& surface) {
+ if (surface == nullptr) {
+ ALOGE("%s: Error: view::Surface::fromSurface failed due to null surface.", __FUNCTION__);
+ return Surface();
+ }
+ Surface s;
+ s.name = String16(surface->getConsumerName());
+ s.graphicBufferProducer = surface->getIGraphicBufferProducer();
+ s.surfaceControlHandle = surface->getSurfaceControlHandle();
+ return s;
+}
+
+sp<android::Surface> Surface::toSurface() const {
+ if (graphicBufferProducer == nullptr) return nullptr;
+ return new android::Surface(graphicBufferProducer, false, surfaceControlHandle);
+}
+
+status_t Surface::getUniqueId(uint64_t* out_id) const {
+ if (graphicBufferProducer == nullptr) {
+ ALOGE("android::viewSurface::getUniqueId() failed because it's not initialized.");
+ return UNEXPECTED_NULL;
+ }
+ status_t status = graphicBufferProducer->getUniqueId(out_id);
+ if (status != OK) {
+ ALOGE("android::viewSurface::getUniqueId() failed.");
+ return status;
+ }
+ return OK;
+}
+#endif
+
std::string Surface::toString() const {
std::stringstream out;
out << name;
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index e4e81adf58..a4ae54b351 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -217,6 +217,7 @@ cc_library {
],
srcs: [
"AccelerationCurve.cpp",
+ "CoordinateFilter.cpp",
"Input.cpp",
"InputConsumer.cpp",
"InputConsumerNoResampling.cpp",
@@ -230,6 +231,7 @@ cc_library {
"KeyLayoutMap.cpp",
"MotionPredictor.cpp",
"MotionPredictorMetricsManager.cpp",
+ "OneEuroFilter.cpp",
"PrintTools.cpp",
"PropertyMap.cpp",
"Resampler.cpp",
diff --git a/libs/input/CoordinateFilter.cpp b/libs/input/CoordinateFilter.cpp
new file mode 100644
index 0000000000..d231474577
--- /dev/null
+++ b/libs/input/CoordinateFilter.cpp
@@ -0,0 +1,31 @@
+/**
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "CoordinateFilter"
+
+#include <input/CoordinateFilter.h>
+
+namespace android {
+
+CoordinateFilter::CoordinateFilter(float minCutoffFreq, float beta)
+ : mXFilter{minCutoffFreq, beta}, mYFilter{minCutoffFreq, beta} {}
+
+void CoordinateFilter::filter(std::chrono::duration<float> timestamp, PointerCoords& coords) {
+ coords.setAxisValue(AMOTION_EVENT_AXIS_X, mXFilter.filter(timestamp, coords.getX()));
+ coords.setAxisValue(AMOTION_EVENT_AXIS_Y, mYFilter.filter(timestamp, coords.getY()));
+}
+
+} // namespace android
diff --git a/libs/input/InputConsumerNoResampling.cpp b/libs/input/InputConsumerNoResampling.cpp
index 9665de799f..d3653cfc45 100644
--- a/libs/input/InputConsumerNoResampling.cpp
+++ b/libs/input/InputConsumerNoResampling.cpp
@@ -357,7 +357,8 @@ void InputConsumerNoResampling::handleMessages(std::vector<InputMessage>&& messa
mBatches[deviceId].emplace(msg);
} else {
// consume all pending batches for this device immediately
- consumeBatchedInputEvents(deviceId, /*requestedFrameTime=*/std::nullopt);
+ consumeBatchedInputEvents(deviceId, /*requestedFrameTime=*/
+ std::numeric_limits<nsecs_t>::max());
if (canResample &&
(action == AMOTION_EVENT_ACTION_UP || action == AMOTION_EVENT_ACTION_CANCEL)) {
LOG_IF(INFO, mResamplers.erase(deviceId) == 0)
@@ -480,7 +481,7 @@ void InputConsumerNoResampling::handleMessage(const InputMessage& msg) const {
}
std::pair<std::unique_ptr<MotionEvent>, std::optional<uint32_t>>
-InputConsumerNoResampling::createBatchedMotionEvent(const nsecs_t requestedFrameTime,
+InputConsumerNoResampling::createBatchedMotionEvent(const std::optional<nsecs_t> requestedFrameTime,
std::queue<InputMessage>& messages) {
std::unique_ptr<MotionEvent> motionEvent;
std::optional<uint32_t> firstSeqForBatch;
@@ -491,7 +492,11 @@ InputConsumerNoResampling::createBatchedMotionEvent(const nsecs_t requestedFrame
const nanoseconds resampleLatency = (resampler != mResamplers.cend())
? resampler->second->getResampleLatency()
: nanoseconds{0};
- const nanoseconds adjustedFrameTime = nanoseconds{requestedFrameTime} - resampleLatency;
+ // When batching is not enabled, we want to consume all events. That's equivalent to having an
+ // infinite requestedFrameTime.
+ const nanoseconds adjustedFrameTime = (requestedFrameTime.has_value())
+ ? (nanoseconds{*requestedFrameTime} - resampleLatency)
+ : nanoseconds{std::numeric_limits<nsecs_t>::max()};
while (!messages.empty() &&
(messages.front().body.motion.eventTime <= adjustedFrameTime.count())) {
@@ -513,8 +518,9 @@ InputConsumerNoResampling::createBatchedMotionEvent(const nsecs_t requestedFrame
if (!messages.empty()) {
futureSample = &messages.front();
}
- if ((motionEvent != nullptr) && (resampler != mResamplers.cend())) {
- resampler->second->resampleMotionEvent(nanoseconds{requestedFrameTime}, *motionEvent,
+ if ((motionEvent != nullptr) && (resampler != mResamplers.cend()) &&
+ (requestedFrameTime.has_value())) {
+ resampler->second->resampleMotionEvent(nanoseconds{*requestedFrameTime}, *motionEvent,
futureSample);
}
@@ -524,16 +530,13 @@ InputConsumerNoResampling::createBatchedMotionEvent(const nsecs_t requestedFrame
bool InputConsumerNoResampling::consumeBatchedInputEvents(
std::optional<DeviceId> deviceId, std::optional<nsecs_t> requestedFrameTime) {
ensureCalledOnLooperThread(__func__);
- // When batching is not enabled, we want to consume all events. That's equivalent to having an
- // infinite requestedFrameTime.
- requestedFrameTime = requestedFrameTime.value_or(std::numeric_limits<nsecs_t>::max());
bool producedEvents = false;
for (auto deviceIdIter = (deviceId.has_value()) ? (mBatches.find(*deviceId))
: (mBatches.begin());
deviceIdIter != mBatches.cend(); ++deviceIdIter) {
std::queue<InputMessage>& messages = deviceIdIter->second;
- auto [motion, firstSeqForBatch] = createBatchedMotionEvent(*requestedFrameTime, messages);
+ auto [motion, firstSeqForBatch] = createBatchedMotionEvent(requestedFrameTime, messages);
if (motion != nullptr) {
LOG_ALWAYS_FATAL_IF(!firstSeqForBatch.has_value());
mCallbacks.onMotionEvent(std::move(motion), *firstSeqForBatch);
diff --git a/libs/input/InputEventLabels.cpp b/libs/input/InputEventLabels.cpp
index 8db0ca588b..b537feb68f 100644
--- a/libs/input/InputEventLabels.cpp
+++ b/libs/input/InputEventLabels.cpp
@@ -350,7 +350,26 @@ namespace android {
DEFINE_KEYCODE(MACRO_3), \
DEFINE_KEYCODE(MACRO_4), \
DEFINE_KEYCODE(EMOJI_PICKER), \
- DEFINE_KEYCODE(SCREENSHOT)
+ DEFINE_KEYCODE(SCREENSHOT), \
+ DEFINE_KEYCODE(DICTATE), \
+ DEFINE_KEYCODE(NEW), \
+ DEFINE_KEYCODE(CLOSE), \
+ DEFINE_KEYCODE(DO_NOT_DISTURB), \
+ DEFINE_KEYCODE(PRINT), \
+ DEFINE_KEYCODE(LOCK), \
+ DEFINE_KEYCODE(FULLSCREEN), \
+ DEFINE_KEYCODE(F13), \
+ DEFINE_KEYCODE(F14), \
+ DEFINE_KEYCODE(F15), \
+ DEFINE_KEYCODE(F16), \
+ DEFINE_KEYCODE(F17), \
+ DEFINE_KEYCODE(F18), \
+ DEFINE_KEYCODE(F19),\
+ DEFINE_KEYCODE(F20), \
+ DEFINE_KEYCODE(F21), \
+ DEFINE_KEYCODE(F22), \
+ DEFINE_KEYCODE(F23), \
+ DEFINE_KEYCODE(F24)
// NOTE: If you add a new axis here you must also add it to several other files.
// Refer to frameworks/base/core/java/android/view/MotionEvent.java for the full list.
diff --git a/libs/input/KeyboardClassifier.cpp b/libs/input/KeyboardClassifier.cpp
index 0c2c7be582..2a83919283 100644
--- a/libs/input/KeyboardClassifier.cpp
+++ b/libs/input/KeyboardClassifier.cpp
@@ -57,14 +57,14 @@ void KeyboardClassifier::notifyKeyboardChanged(DeviceId deviceId,
uint32_t deviceClasses) {
if (mRustClassifier) {
RustInputDeviceIdentifier rustIdentifier;
- rustIdentifier.name = identifier.name;
- rustIdentifier.location = identifier.location;
- rustIdentifier.unique_id = identifier.uniqueId;
+ rustIdentifier.name = rust::String::lossy(identifier.name);
+ rustIdentifier.location = rust::String::lossy(identifier.location);
+ rustIdentifier.unique_id = rust::String::lossy(identifier.uniqueId);
rustIdentifier.bus = identifier.bus;
rustIdentifier.vendor = identifier.vendor;
rustIdentifier.product = identifier.product;
rustIdentifier.version = identifier.version;
- rustIdentifier.descriptor = identifier.descriptor;
+ rustIdentifier.descriptor = rust::String::lossy(identifier.descriptor);
android::input::keyboardClassifier::notifyKeyboardChanged(**mRustClassifier, deviceId,
rustIdentifier, deviceClasses);
} else {
diff --git a/libs/input/OneEuroFilter.cpp b/libs/input/OneEuroFilter.cpp
new file mode 100644
index 0000000000..400d7c9ab0
--- /dev/null
+++ b/libs/input/OneEuroFilter.cpp
@@ -0,0 +1,79 @@
+/**
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "OneEuroFilter"
+
+#include <chrono>
+#include <cmath>
+
+#include <android-base/logging.h>
+#include <input/CoordinateFilter.h>
+
+namespace android {
+namespace {
+
+inline float cutoffFreq(float minCutoffFreq, float beta, float filteredSpeed) {
+ return minCutoffFreq + beta * std::abs(filteredSpeed);
+}
+
+inline float smoothingFactor(std::chrono::duration<float> samplingPeriod, float cutoffFreq) {
+ return samplingPeriod.count() / (samplingPeriod.count() + (1.0 / (2.0 * M_PI * cutoffFreq)));
+}
+
+inline float lowPassFilter(float rawPosition, float prevFilteredPosition, float smoothingFactor) {
+ return smoothingFactor * rawPosition + (1 - smoothingFactor) * prevFilteredPosition;
+}
+
+} // namespace
+
+OneEuroFilter::OneEuroFilter(float minCutoffFreq, float beta, float speedCutoffFreq)
+ : mMinCutoffFreq{minCutoffFreq}, mBeta{beta}, mSpeedCutoffFreq{speedCutoffFreq} {}
+
+float OneEuroFilter::filter(std::chrono::duration<float> timestamp, float rawPosition) {
+ LOG_IF(FATAL, mPrevFilteredPosition.has_value() && (timestamp <= *mPrevTimestamp))
+ << "Timestamp must be greater than mPrevTimestamp";
+
+ const std::chrono::duration<float> samplingPeriod = (mPrevTimestamp.has_value())
+ ? (timestamp - *mPrevTimestamp)
+ : std::chrono::duration<float>{1.0};
+
+ const float rawVelocity = (mPrevFilteredPosition.has_value())
+ ? ((rawPosition - *mPrevFilteredPosition) / samplingPeriod.count())
+ : 0.0;
+
+ const float speedSmoothingFactor = smoothingFactor(samplingPeriod, mSpeedCutoffFreq);
+
+ const float filteredVelocity = (mPrevFilteredVelocity.has_value())
+ ? lowPassFilter(rawVelocity, *mPrevFilteredVelocity, speedSmoothingFactor)
+ : rawVelocity;
+
+ const float positionCutoffFreq = cutoffFreq(mMinCutoffFreq, mBeta, filteredVelocity);
+
+ const float positionSmoothingFactor = smoothingFactor(samplingPeriod, positionCutoffFreq);
+
+ const float filteredPosition = (mPrevFilteredPosition.has_value())
+ ? lowPassFilter(rawPosition, *mPrevFilteredPosition, positionSmoothingFactor)
+ : rawPosition;
+
+ mPrevTimestamp = timestamp;
+ mPrevRawPosition = rawPosition;
+ mPrevFilteredVelocity = filteredVelocity;
+ mPrevFilteredPosition = filteredPosition;
+
+ return filteredPosition;
+}
+
+} // namespace android
diff --git a/libs/input/Resampler.cpp b/libs/input/Resampler.cpp
index 056db093d1..3ab132d550 100644
--- a/libs/input/Resampler.cpp
+++ b/libs/input/Resampler.cpp
@@ -389,4 +389,34 @@ void LegacyResampler::resampleMotionEvent(nanoseconds frameTime, MotionEvent& mo
mLastRealSample = *(mLatestSamples.end() - 1);
}
+// --- FilteredLegacyResampler ---
+
+FilteredLegacyResampler::FilteredLegacyResampler(float minCutoffFreq, float beta)
+ : mResampler{}, mMinCutoffFreq{minCutoffFreq}, mBeta{beta} {}
+
+void FilteredLegacyResampler::resampleMotionEvent(std::chrono::nanoseconds requestedFrameTime,
+ MotionEvent& motionEvent,
+ const InputMessage* futureSample) {
+ mResampler.resampleMotionEvent(requestedFrameTime, motionEvent, futureSample);
+ const size_t numSamples = motionEvent.getHistorySize() + 1;
+ for (size_t sampleIndex = 0; sampleIndex < numSamples; ++sampleIndex) {
+ for (size_t pointerIndex = 0; pointerIndex < motionEvent.getPointerCount();
+ ++pointerIndex) {
+ const int32_t pointerId = motionEvent.getPointerProperties(pointerIndex)->id;
+ const nanoseconds eventTime =
+ nanoseconds{motionEvent.getHistoricalEventTime(sampleIndex)};
+ // Refer to the static function `setMotionEventPointerCoords` for a justification of
+ // casting away const.
+ PointerCoords& pointerCoords = const_cast<PointerCoords&>(
+ *(motionEvent.getHistoricalRawPointerCoords(pointerIndex, sampleIndex)));
+ const auto& [iter, _] = mFilteredPointers.try_emplace(pointerId, mMinCutoffFreq, mBeta);
+ iter->second.filter(eventTime, pointerCoords);
+ }
+ }
+}
+
+std::chrono::nanoseconds FilteredLegacyResampler::getResampleLatency() const {
+ return mResampler.getResampleLatency();
+}
+
} // namespace android
diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig
index e93c04df6d..fd7704815f 100644
--- a/libs/input/input_flags.aconfig
+++ b/libs/input/input_flags.aconfig
@@ -94,13 +94,6 @@ flag {
}
flag {
- name: "enable_new_mouse_pointer_ballistics"
- namespace: "input"
- description: "Change the acceleration curves for mouse pointer movements to match the touchpad ones"
- bug: "315313622"
-}
-
-flag {
name: "rate_limit_user_activity_poke_in_dispatcher"
namespace: "input"
description: "Move user-activity poke rate-limiting from PowerManagerService to InputDispatcher."
@@ -221,3 +214,20 @@ flag {
description: "Set input device's power/wakeup sysfs node"
bug: "372812925"
}
+
+flag {
+ name: "enable_alphabetic_keyboard_wake"
+ namespace: "input"
+ description: "Enable wake from alphabetic keyboards."
+ bug: "352856881"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "connected_displays_cursor"
+ namespace: "lse_desktop_experience"
+ description: "Allow cursor to transition across multiple connected displays"
+ bug: "362719483"
+}
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index 661c9f739f..46e819061f 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -25,6 +25,7 @@ cc_test {
"InputVerifier_test.cpp",
"MotionPredictor_test.cpp",
"MotionPredictorMetricsManager_test.cpp",
+ "OneEuroFilter_test.cpp",
"Resampler_test.cpp",
"RingBuffer_test.cpp",
"TestInputChannel.cpp",
diff --git a/libs/input/tests/InputConsumerResampling_test.cpp b/libs/input/tests/InputConsumerResampling_test.cpp
index 883ca82fe0..97688a83ae 100644
--- a/libs/input/tests/InputConsumerResampling_test.cpp
+++ b/libs/input/tests/InputConsumerResampling_test.cpp
@@ -38,6 +38,8 @@ namespace {
using std::chrono::nanoseconds;
using namespace std::chrono_literals;
+const std::chrono::milliseconds RESAMPLE_LATENCY{5};
+
struct Pointer {
int32_t id{0};
float x{0.0f};
@@ -440,7 +442,7 @@ TEST_F(InputConsumerResamplingTest, SampleTimeEqualsEventTime) {
{20ms, {Pointer{.id = 0, .x = 30.0f, .y = 30.0f}}, AMOTION_EVENT_ACTION_MOVE}));
invokeLooperCallback();
- mConsumer->consumeBatchedInputEvents(nanoseconds{20ms + 5ms /*RESAMPLE_LATENCY*/}.count());
+ mConsumer->consumeBatchedInputEvents(nanoseconds{20ms + RESAMPLE_LATENCY}.count());
// MotionEvent should not resampled because the resample time falls exactly on the existing
// event time.
@@ -496,14 +498,15 @@ TEST_F(InputConsumerResamplingTest, ResampledValueIsUsedForIdenticalCoordinates)
{40ms, {Pointer{.id = 0, .x = 30.0f, .y = 30.0f}}, AMOTION_EVENT_ACTION_MOVE}));
invokeLooperCallback();
- mConsumer->consumeBatchedInputEvents(nanoseconds{45ms + 5ms /*RESAMPLE_LATENCY*/}.count());
+ mConsumer->consumeBatchedInputEvents(nanoseconds{45ms + RESAMPLE_LATENCY}.count());
+ // Original and resampled event should be both overwritten.
assertReceivedMotionEvent(
{InputEventEntry{40ms,
{Pointer{.id = 0, .x = 35.0f, .y = 30.0f, .isResampled = true}},
- AMOTION_EVENT_ACTION_MOVE}, // original event, rewritten
+ AMOTION_EVENT_ACTION_MOVE},
InputEventEntry{45ms,
{Pointer{.id = 0, .x = 35.0f, .y = 30.0f, .isResampled = true}},
- AMOTION_EVENT_ACTION_MOVE}}); // resampled event, rewritten
+ AMOTION_EVENT_ACTION_MOVE}});
mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true);
mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true);
@@ -552,13 +555,14 @@ TEST_F(InputConsumerResamplingTest, OldEventReceivedAfterResampleOccurs) {
invokeLooperCallback();
mConsumer->consumeBatchedInputEvents(nanoseconds{50ms}.count());
+ // Original and resampled event should be both overwritten.
assertReceivedMotionEvent(
{InputEventEntry{24ms,
{Pointer{.id = 0, .x = 35.0f, .y = 30.0f, .isResampled = true}},
- AMOTION_EVENT_ACTION_MOVE}, // original event, rewritten
+ AMOTION_EVENT_ACTION_MOVE},
InputEventEntry{26ms,
{Pointer{.id = 0, .x = 45.0f, .y = 30.0f, .isResampled = true}},
- AMOTION_EVENT_ACTION_MOVE}}); // resampled event, rewritten
+ AMOTION_EVENT_ACTION_MOVE}});
mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true);
mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true);
@@ -566,4 +570,175 @@ TEST_F(InputConsumerResamplingTest, OldEventReceivedAfterResampleOccurs) {
mClientTestChannel->assertFinishMessage(/*seq=*/4, /*handled=*/true);
}
+TEST_F(InputConsumerResamplingTest, DoNotResampleWhenFrameTimeIsNotAvailable) {
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {0ms, {Pointer{.id = 0, .x = 10.0f, .y = 20.0f}}, AMOTION_EVENT_ACTION_DOWN}));
+
+ invokeLooperCallback();
+ assertReceivedMotionEvent({InputEventEntry{0ms,
+ {Pointer{.id = 0, .x = 10.0f, .y = 20.0f}},
+ AMOTION_EVENT_ACTION_DOWN}});
+
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {10ms, {Pointer{.id = 0, .x = 20.0f, .y = 30.0f}}, AMOTION_EVENT_ACTION_MOVE}));
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {20ms, {Pointer{.id = 0, .x = 30.0f, .y = 30.0f}}, AMOTION_EVENT_ACTION_MOVE}));
+
+ invokeLooperCallback();
+ mConsumer->consumeBatchedInputEvents(std::nullopt);
+ assertReceivedMotionEvent({InputEventEntry{10ms,
+ {Pointer{.id = 0, .x = 20.0f, .y = 30.0f}},
+ AMOTION_EVENT_ACTION_MOVE},
+ InputEventEntry{20ms,
+ {Pointer{.id = 0, .x = 30.0f, .y = 30.0f}},
+ AMOTION_EVENT_ACTION_MOVE}});
+
+ mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/3, /*handled=*/true);
+}
+
+TEST_F(InputConsumerResamplingTest, TwoPointersAreResampledIndependently) {
+ // Full action for when a pointer with index=1 appears (some other pointer must already be
+ // present)
+ const int32_t actionPointer1Down =
+ AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+
+ // Full action for when a pointer with index=0 disappears (some other pointer must still remain)
+ const int32_t actionPointer0Up =
+ AMOTION_EVENT_ACTION_POINTER_UP + (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {0ms, {Pointer{.id = 0, .x = 100.0f, .y = 100.0f}}, AMOTION_EVENT_ACTION_DOWN}));
+
+ mClientTestChannel->assertNoSentMessages();
+
+ invokeLooperCallback();
+ assertReceivedMotionEvent({InputEventEntry{0ms,
+ {Pointer{.id = 0, .x = 100.0f, .y = 100.0f}},
+ AMOTION_EVENT_ACTION_DOWN}});
+
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {10ms, {Pointer{.id = 0, .x = 100.0f, .y = 100.0f}}, AMOTION_EVENT_ACTION_MOVE}));
+
+ invokeLooperCallback();
+ mConsumer->consumeBatchedInputEvents(nanoseconds{10ms + RESAMPLE_LATENCY}.count());
+ // Not resampled value because requestedFrameTime - RESAMPLE_LATENCY == eventTime
+ assertReceivedMotionEvent({InputEventEntry{10ms,
+ {Pointer{.id = 0, .x = 100.0f, .y = 100.0f}},
+ AMOTION_EVENT_ACTION_MOVE}});
+
+ // Second pointer id=1 appears
+ mClientTestChannel->enqueueMessage(
+ nextPointerMessage({15ms,
+ {Pointer{.id = 0, .x = 100.0f, .y = 100.0f},
+ Pointer{.id = 1, .x = 500.0f, .y = 500.0f}},
+ actionPointer1Down}));
+
+ invokeLooperCallback();
+ mConsumer->consumeBatchedInputEvents(nanoseconds{20ms + RESAMPLE_LATENCY}.count());
+ // Not resampled value because requestedFrameTime - RESAMPLE_LATENCY == eventTime.
+ assertReceivedMotionEvent({InputEventEntry{15ms,
+ {Pointer{.id = 0, .x = 100.0f, .y = 100.0f},
+ Pointer{.id = 1, .x = 500.0f, .y = 500.0f}},
+ actionPointer1Down}});
+
+ // Both pointers move
+ mClientTestChannel->enqueueMessage(
+ nextPointerMessage({30ms,
+ {Pointer{.id = 0, .x = 100.0f, .y = 100.0f},
+ Pointer{.id = 1, .x = 500.0f, .y = 500.0f}},
+ AMOTION_EVENT_ACTION_MOVE}));
+ mClientTestChannel->enqueueMessage(
+ nextPointerMessage({40ms,
+ {Pointer{.id = 0, .x = 120.0f, .y = 120.0f},
+ Pointer{.id = 1, .x = 600.0f, .y = 600.0f}},
+ AMOTION_EVENT_ACTION_MOVE}));
+
+ invokeLooperCallback();
+ mConsumer->consumeBatchedInputEvents(nanoseconds{45ms + RESAMPLE_LATENCY}.count());
+ assertReceivedMotionEvent(
+ {InputEventEntry{30ms,
+ {Pointer{.id = 0, .x = 100.0f, .y = 100.0f},
+ Pointer{.id = 1, .x = 500.0f, .y = 500.0f}},
+ AMOTION_EVENT_ACTION_MOVE},
+ InputEventEntry{40ms,
+ {Pointer{.id = 0, .x = 120.0f, .y = 120.0f},
+ Pointer{.id = 1, .x = 600.0f, .y = 600.0f}},
+ AMOTION_EVENT_ACTION_MOVE},
+ InputEventEntry{45ms,
+ {Pointer{.id = 0, .x = 130.0f, .y = 130.0f, .isResampled = true},
+ Pointer{.id = 1, .x = 650.0f, .y = 650.0f, .isResampled = true}},
+ AMOTION_EVENT_ACTION_MOVE}});
+
+ // Both pointers move again
+ mClientTestChannel->enqueueMessage(
+ nextPointerMessage({60ms,
+ {Pointer{.id = 0, .x = 120.0f, .y = 120.0f},
+ Pointer{.id = 1, .x = 600.0f, .y = 600.0f}},
+ AMOTION_EVENT_ACTION_MOVE}));
+ mClientTestChannel->enqueueMessage(
+ nextPointerMessage({70ms,
+ {Pointer{.id = 0, .x = 130.0f, .y = 130.0f},
+ Pointer{.id = 1, .x = 700.0f, .y = 700.0f}},
+ AMOTION_EVENT_ACTION_MOVE}));
+
+ invokeLooperCallback();
+ mConsumer->consumeBatchedInputEvents(nanoseconds{75ms + RESAMPLE_LATENCY}.count());
+
+ /*
+ * The pointer id 0 at t = 60 should not be equal to 120 because the value was received twice,
+ * and resampled to 130. Therefore, if we reported 130, then we should continue to report it as
+ * such. Likewise, with pointer id 1.
+ */
+
+ // Not 120 because it matches a previous real event.
+ assertReceivedMotionEvent(
+ {InputEventEntry{60ms,
+ {Pointer{.id = 0, .x = 130.0f, .y = 130.0f, .isResampled = true},
+ Pointer{.id = 1, .x = 650.0f, .y = 650.0f, .isResampled = true}},
+ AMOTION_EVENT_ACTION_MOVE},
+ InputEventEntry{70ms,
+ {Pointer{.id = 0, .x = 130.0f, .y = 130.0f},
+ Pointer{.id = 1, .x = 700.0f, .y = 700.0f}},
+ AMOTION_EVENT_ACTION_MOVE},
+ InputEventEntry{75ms,
+ {Pointer{.id = 0, .x = 135.0f, .y = 135.0f, .isResampled = true},
+ Pointer{.id = 1, .x = 750.0f, .y = 750.0f, .isResampled = true}},
+ AMOTION_EVENT_ACTION_MOVE}});
+
+ // First pointer id=0 leaves the screen
+ mClientTestChannel->enqueueMessage(
+ nextPointerMessage({80ms,
+ {Pointer{.id = 0, .x = 120.0f, .y = 120.0f},
+ Pointer{.id = 1, .x = 600.0f, .y = 600.0f}},
+ actionPointer0Up}));
+
+ invokeLooperCallback();
+ // Not resampled event for ACTION_POINTER_UP
+ assertReceivedMotionEvent({InputEventEntry{80ms,
+ {Pointer{.id = 0, .x = 120.0f, .y = 120.0f},
+ Pointer{.id = 1, .x = 600.0f, .y = 600.0f}},
+ actionPointer0Up}});
+
+ // Remaining pointer id=1 is still present, but doesn't move
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {90ms, {Pointer{.id = 1, .x = 600.0f, .y = 600.0f}}, AMOTION_EVENT_ACTION_MOVE}));
+
+ invokeLooperCallback();
+ mConsumer->consumeBatchedInputEvents(nanoseconds{100ms}.count());
+
+ /*
+ * The latest event with ACTION_MOVE was at t = 70 with value = 700. Thus, the resampled value
+ * is 700 + ((95 - 70)/(90 - 70))*(600 - 700) = 575.
+ */
+ assertReceivedMotionEvent(
+ {InputEventEntry{90ms,
+ {Pointer{.id = 1, .x = 600.0f, .y = 600.0f}},
+ AMOTION_EVENT_ACTION_MOVE},
+ InputEventEntry{95ms,
+ {Pointer{.id = 1, .x = 575.0f, .y = 575.0f, .isResampled = true}},
+ AMOTION_EVENT_ACTION_MOVE}});
+}
+
} // namespace android
diff --git a/libs/input/tests/InputConsumer_test.cpp b/libs/input/tests/InputConsumer_test.cpp
index 6a3bbe5b36..06e19bb644 100644
--- a/libs/input/tests/InputConsumer_test.cpp
+++ b/libs/input/tests/InputConsumer_test.cpp
@@ -194,7 +194,7 @@ TEST_F(InputConsumerTest, MessageStreamBatchedInMotionEvent) {
std::unique_ptr<MotionEvent> moveMotionEvent =
assertReceivedMotionEvent(WithMotionAction(ACTION_MOVE));
ASSERT_NE(moveMotionEvent, nullptr);
- EXPECT_EQ(moveMotionEvent->getHistorySize() + 1, 3UL);
+ EXPECT_EQ(moveMotionEvent->getHistorySize() + 1, 2UL);
mClientTestChannel->assertFinishMessage(/*seq=*/0, /*handled=*/true);
mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true);
@@ -443,4 +443,5 @@ TEST_F(InputConsumerTest, MultiDeviceResampling) {
mClientTestChannel->assertFinishMessage(/*seq=*/8, /*handled=*/true);
mClientTestChannel->assertFinishMessage(/*seq=*/10, /*handled=*/true);
}
+
} // namespace android
diff --git a/libs/input/tests/OneEuroFilter_test.cpp b/libs/input/tests/OneEuroFilter_test.cpp
new file mode 100644
index 0000000000..270e789c84
--- /dev/null
+++ b/libs/input/tests/OneEuroFilter_test.cpp
@@ -0,0 +1,134 @@
+/**
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <input/OneEuroFilter.h>
+
+#include <algorithm>
+#include <chrono>
+#include <cmath>
+#include <numeric>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <input/Input.h>
+
+namespace android {
+namespace {
+
+using namespace std::literals::chrono_literals;
+using std::chrono::duration;
+
+struct Sample {
+ duration<double> timestamp{};
+ double value{};
+
+ friend bool operator<(const Sample& lhs, const Sample& rhs) { return lhs.value < rhs.value; }
+};
+
+/**
+ * Generates a sinusoidal signal with the passed frequency and amplitude.
+ */
+std::vector<Sample> generateSinusoidalSignal(duration<double> signalDuration,
+ double samplingFrequency, double signalFrequency,
+ double amplitude) {
+ std::vector<Sample> signal;
+ const duration<double> samplingPeriod{1.0 / samplingFrequency};
+ for (duration<double> timestamp{0.0}; timestamp < signalDuration; timestamp += samplingPeriod) {
+ signal.push_back(
+ Sample{timestamp,
+ amplitude * std::sin(2.0 * M_PI * signalFrequency * timestamp.count())});
+ }
+ return signal;
+}
+
+double meanAbsoluteError(const std::vector<Sample>& filteredSignal,
+ const std::vector<Sample>& signal) {
+ if (filteredSignal.size() != signal.size()) {
+ ADD_FAILURE() << "filteredSignal and signal do not have equal number of samples";
+ return std::numeric_limits<double>::max();
+ }
+ std::vector<double> absoluteError;
+ for (size_t sampleIndex = 0; sampleIndex < signal.size(); ++sampleIndex) {
+ absoluteError.push_back(
+ std::abs(filteredSignal[sampleIndex].value - signal[sampleIndex].value));
+ }
+ if (absoluteError.empty()) {
+ ADD_FAILURE() << "Zero division. absoluteError is empty";
+ return std::numeric_limits<double>::max();
+ }
+ return std::accumulate(absoluteError.begin(), absoluteError.end(), 0.0) / absoluteError.size();
+}
+
+double maxAbsoluteAmplitude(const std::vector<Sample>& signal) {
+ if (signal.empty()) {
+ ADD_FAILURE() << "Max absolute value amplitude does not exist. Signal is empty";
+ return std::numeric_limits<double>::max();
+ }
+ std::vector<Sample> absoluteSignal;
+ for (const Sample& sample : signal) {
+ absoluteSignal.push_back(Sample{sample.timestamp, std::abs(sample.value)});
+ }
+ return std::max_element(absoluteSignal.begin(), absoluteSignal.end())->value;
+}
+
+} // namespace
+
+class OneEuroFilterTest : public ::testing::Test {
+protected:
+ // The constructor's parameters are the ones that Chromium's using. The tuning was based on a 60
+ // Hz sampling frequency. Refer to their one_euro_filter.h header for additional information
+ // about these parameters.
+ OneEuroFilterTest() : mFilter{/*minCutoffFreq=*/4.7, /*beta=*/0.01} {}
+
+ std::vector<Sample> filterSignal(const std::vector<Sample>& signal) {
+ std::vector<Sample> filteredSignal;
+ for (const Sample& sample : signal) {
+ filteredSignal.push_back(
+ Sample{sample.timestamp, mFilter.filter(sample.timestamp, sample.value)});
+ }
+ return filteredSignal;
+ }
+
+ OneEuroFilter mFilter;
+};
+
+TEST_F(OneEuroFilterTest, PassLowFrequencySignal) {
+ const std::vector<Sample> signal =
+ generateSinusoidalSignal(1s, /*samplingFrequency=*/60, /*signalFrequency=*/1,
+ /*amplitude=*/1);
+
+ const std::vector<Sample> filteredSignal = filterSignal(signal);
+
+ // The reason behind using the mean absolute error as a metric is that, ideally, a low frequency
+ // filtered signal is expected to be almost identical to the raw one. Therefore, the error
+ // between them should be minimal. The constant is heuristically chosen.
+ EXPECT_LT(meanAbsoluteError(filteredSignal, signal), 0.25);
+}
+
+TEST_F(OneEuroFilterTest, RejectHighFrequencySignal) {
+ const std::vector<Sample> signal =
+ generateSinusoidalSignal(1s, /*samplingFrequency=*/60, /*signalFrequency=*/22.5,
+ /*amplitude=*/1);
+
+ const std::vector<Sample> filteredSignal = filterSignal(signal);
+
+ // The filtered signal should consist of values that are much closer to zero. The comparison
+ // constant is heuristically chosen.
+ EXPECT_LT(maxAbsoluteAmplitude(filteredSignal), 0.25);
+}
+
+} // namespace android
diff --git a/libs/input/tests/TestEventMatchers.h b/libs/input/tests/TestEventMatchers.h
index 3589de599f..290a97d736 100644
--- a/libs/input/tests/TestEventMatchers.h
+++ b/libs/input/tests/TestEventMatchers.h
@@ -75,12 +75,18 @@ public:
using is_gtest_matcher = void;
explicit WithMotionActionMatcher(int32_t action) : mAction(action) {}
- bool MatchAndExplain(const MotionEvent& event, std::ostream*) const {
- bool matches = mAction == event.getAction();
- if (event.getAction() == AMOTION_EVENT_ACTION_CANCEL) {
- matches &= (event.getFlags() & AMOTION_EVENT_FLAG_CANCELED) != 0;
+ bool MatchAndExplain(const MotionEvent& event, testing::MatchResultListener* listener) const {
+ if (mAction != event.getAction()) {
+ *listener << "expected " << MotionEvent::actionToString(mAction) << ", but got "
+ << MotionEvent::actionToString(event.getAction());
+ return false;
+ }
+ if (event.getAction() == AMOTION_EVENT_ACTION_CANCEL &&
+ (event.getFlags() & AMOTION_EVENT_FLAG_CANCELED) == 0) {
+ *listener << "event with CANCEL action is missing FLAG_CANCELED";
+ return false;
}
- return matches;
+ return true;
}
void DescribeTo(std::ostream* os) const {
diff --git a/libs/input/tests/TfLiteMotionPredictor_test.cpp b/libs/input/tests/TfLiteMotionPredictor_test.cpp
index c3ac0b7cfb..0c19ebe651 100644
--- a/libs/input/tests/TfLiteMotionPredictor_test.cpp
+++ b/libs/input/tests/TfLiteMotionPredictor_test.cpp
@@ -89,23 +89,23 @@ TEST(TfLiteMotionPredictorTest, BuffersCopyTo) {
buffers.pushSample(/*timestamp=*/1,
{.position = {.x = 10, .y = 10},
.pressure = 0,
- .orientation = 0,
- .tilt = 0.2});
+ .tilt = 0.2,
+ .orientation = 0});
buffers.pushSample(/*timestamp=*/2,
{.position = {.x = 10, .y = 50},
.pressure = 0.4,
- .orientation = M_PI / 4,
- .tilt = 0.3});
+ .tilt = 0.3,
+ .orientation = M_PI / 4});
buffers.pushSample(/*timestamp=*/3,
{.position = {.x = 30, .y = 50},
.pressure = 0.5,
- .orientation = -M_PI / 4,
- .tilt = 0.4});
+ .tilt = 0.4,
+ .orientation = -M_PI / 4});
buffers.pushSample(/*timestamp=*/3,
{.position = {.x = 30, .y = 60},
.pressure = 0,
- .orientation = 0,
- .tilt = 0.5});
+ .tilt = 0.5,
+ .orientation = 0});
buffers.copyTo(*model);
const int zeroPadding = model->inputLength() - 3;
diff --git a/libs/input/tests/TouchResampling_test.cpp b/libs/input/tests/TouchResampling_test.cpp
index 8d8b5300c1..9841c03826 100644
--- a/libs/input/tests/TouchResampling_test.cpp
+++ b/libs/input/tests/TouchResampling_test.cpp
@@ -571,11 +571,12 @@ TEST_F(TouchResamplingTest, TwoPointersAreResampledIndependently) {
std::chrono::nanoseconds frameTime;
std::vector<InputEventEntry> entries, expectedEntries;
- // full action for when a pointer with id=1 appears (some other pointer must already be present)
+ // full action for when a pointer with index=1 appears (some other pointer must already be
+ // present)
constexpr int32_t actionPointer1Down =
AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
- // full action for when a pointer with id=0 disappears (some other pointer must still remain)
+ // full action for when a pointer with index=0 disappears (some other pointer must still remain)
constexpr int32_t actionPointer0Up =
AMOTION_EVENT_ACTION_POINTER_UP + (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp
index f97eed5db3..ac3a832168 100644
--- a/libs/nativewindow/ANativeWindow.cpp
+++ b/libs/nativewindow/ANativeWindow.cpp
@@ -263,6 +263,16 @@ int32_t ANativeWindow_setFrameRateWithChangeStrategy(ANativeWindow* window, floa
return native_window_set_frame_rate(window, frameRate, compatibility, changeFrameRateStrategy);
}
+int32_t ANativeWindow_setFrameRateParams(
+ ANativeWindow* window, float desiredMinRate, float desiredMaxRate, float fixedSourceRate,
+ ANativeWindow_ChangeFrameRateStrategy changeFrameRateStrategy) {
+ if (!window || !query(window, NATIVE_WINDOW_IS_VALID)) {
+ return -EINVAL;
+ }
+ return native_window_set_frame_rate_params(window, desiredMinRate, desiredMaxRate,
+ fixedSourceRate, changeFrameRateStrategy);
+}
+
/**************************************************************************************************
* vndk-stable
**************************************************************************************************/
diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h
index be6623ee75..bd8d67a649 100644
--- a/libs/nativewindow/include/android/native_window.h
+++ b/libs/nativewindow/include/android/native_window.h
@@ -33,8 +33,8 @@
#ifndef ANDROID_NATIVE_WINDOW_H
#define ANDROID_NATIVE_WINDOW_H
-#include <stdint.h>
#include <stdbool.h>
+#include <stdint.h>
#include <sys/cdefs.h>
#include <android/data_space.h>
@@ -282,7 +282,7 @@ int32_t ANativeWindow_setFrameRate(ANativeWindow* window, float frameRate, int8_
void ANativeWindow_tryAllocateBuffers(ANativeWindow* window) __INTRODUCED_IN(30);
/** Change frame rate strategy value for ANativeWindow_setFrameRate. */
-enum ANativeWindow_ChangeFrameRateStrategy {
+typedef enum ANativeWindow_ChangeFrameRateStrategy : int8_t {
/**
* Change the frame rate only if the transition is going to be seamless.
*/
@@ -292,7 +292,7 @@ enum ANativeWindow_ChangeFrameRateStrategy {
* i.e. with visual interruptions for the user.
*/
ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS = 1
-} __INTRODUCED_IN(31);
+} ANativeWindow_ChangeFrameRateStrategy __INTRODUCED_IN(31);
/**
* Sets the intended frame rate for this window.
@@ -345,6 +345,76 @@ int32_t ANativeWindow_setFrameRateWithChangeStrategy(ANativeWindow* window, floa
__INTRODUCED_IN(31);
/**
+ * Sets the intended frame rate for this window.
+ *
+ * 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.
+ *
+ * Note that this only has an effect for surfaces presented on the display. If this
+ * surface is consumed by something other than the system compositor, e.g. a media
+ * codec, this call has no effect.
+ *
+ * You can register for changes in the refresh rate using
+ * \a AChoreographer_registerRefreshRateCallback.
+ *
+ * See ANativeWindow_clearFrameRate().
+ *
+ * Available since API level 36.
+ *
+ * \param window pointer to an ANativeWindow object.
+ *
+ * \param desiredMinRate The desired minimum frame rate (inclusive) for the window, 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 window 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 window, 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 window 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 window 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 window'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 window 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.
+ *
+ * \return 0 for success, -EINVAL if the arguments are invalid.
+ */
+int32_t ANativeWindow_setFrameRateParams(
+ ANativeWindow* window, float desiredMinRate, float desiredMaxRate, float fixedSourceRate,
+ ANativeWindow_ChangeFrameRateStrategy changeFrameRateStrategy) __INTRODUCED_IN(36);
+
+/**
* Clears the frame rate which is set for this window.
*
* This is equivalent to calling
@@ -366,14 +436,13 @@ int32_t ANativeWindow_setFrameRateWithChangeStrategy(ANativeWindow* window, floa
*
* See ANativeWindow_setFrameRateWithChangeStrategy().
*
- * Available since API level 34.
+ * Available since API level 31.
*
* \param window pointer to an ANativeWindow object.
*
* \return 0 for success, -EINVAL if the window value is invalid.
*/
-inline int32_t ANativeWindow_clearFrameRate(ANativeWindow* window)
- __INTRODUCED_IN(__ANDROID_API_U__) {
+inline int32_t ANativeWindow_clearFrameRate(ANativeWindow* window) __INTRODUCED_IN(31) {
return ANativeWindow_setFrameRateWithChangeStrategy(window, 0,
ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index 33c303ae71..05f49ad25f 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -1156,6 +1156,23 @@ static inline int native_window_set_frame_rate(struct ANativeWindow* window, flo
(int)compatibility, (int)changeFrameRateStrategy);
}
+static inline int native_window_set_frame_rate_params(struct ANativeWindow* window,
+ float desiredMinRate, float desiredMaxRate,
+ float fixedSourceRate,
+ int8_t changeFrameRateStrategy) {
+ // TODO(b/362798998): Fix plumbing to send whole params
+ int compatibility = fixedSourceRate == 0 ? ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT
+ : ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
+ double frameRate = compatibility == ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
+ ? fixedSourceRate
+ : desiredMinRate;
+ if (desiredMaxRate < desiredMinRate) {
+ return -EINVAL;
+ }
+ return window->perform(window, NATIVE_WINDOW_SET_FRAME_RATE, frameRate, compatibility,
+ changeFrameRateStrategy);
+}
+
struct ANativeWindowFrameTimelineInfo {
// Frame Id received from ANativeWindow_getNextFrameId.
uint64_t frameNumber;
diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt
index e29d5a6bb4..071e3548d0 100644
--- a/libs/nativewindow/libnativewindow.map.txt
+++ b/libs/nativewindow/libnativewindow.map.txt
@@ -53,6 +53,7 @@ LIBNATIVEWINDOW {
ANativeWindow_setBuffersTransform;
ANativeWindow_setDequeueTimeout; # systemapi introduced=30
ANativeWindow_setFrameRate; # introduced=30
+ ANativeWindow_setFrameRateParams; # introduced=36
ANativeWindow_setFrameRateWithChangeStrategy; # introduced=31
ANativeWindow_setSharedBufferMode; # llndk
ANativeWindow_setSwapInterval; # llndk
diff --git a/libs/nativewindow/rust/src/handle.rs b/libs/nativewindow/rust/src/handle.rs
index ee70e3f538..2b08c1bcb9 100644
--- a/libs/nativewindow/rust/src/handle.rs
+++ b/libs/nativewindow/rust/src/handle.rs
@@ -85,6 +85,12 @@ impl NativeHandle {
/// Destroys the `NativeHandle`, taking ownership of the file descriptors it contained.
pub fn into_fds(self) -> Vec<OwnedFd> {
+ // Unset FDSan tag since this `native_handle_t` is no longer the owner of the file
+ // descriptors after this function.
+ // SAFETY: Our wrapped `native_handle_t` pointer is always valid.
+ unsafe {
+ ffi::native_handle_unset_fdsan_tag(self.as_ref());
+ }
let fds = self.data()[..self.fd_count()]
.iter()
.map(|fd| {
@@ -261,6 +267,29 @@ mod test {
}
#[test]
+ fn to_fds() {
+ let file = File::open("/dev/null").unwrap();
+ let original = NativeHandle::new(vec![file.into()], &[42]).unwrap();
+ assert_eq!(original.ints(), &[42]);
+ assert_eq!(original.fds().len(), 1);
+
+ let fds = original.into_fds();
+ assert_eq!(fds.len(), 1);
+ }
+
+ #[test]
+ fn to_aidl() {
+ let file = File::open("/dev/null").unwrap();
+ let original = NativeHandle::new(vec![file.into()], &[42]).unwrap();
+ assert_eq!(original.ints(), &[42]);
+ assert_eq!(original.fds().len(), 1);
+
+ let aidl = AidlNativeHandle::from(original);
+ assert_eq!(&aidl.ints, &[42]);
+ assert_eq!(aidl.fds.len(), 1);
+ }
+
+ #[test]
fn to_from_aidl() {
let file = File::open("/dev/null").unwrap();
let original = NativeHandle::new(vec![file.into()], &[42]).unwrap();
diff --git a/libs/nativewindow/rust/src/lib.rs b/libs/nativewindow/rust/src/lib.rs
index a1d986ed2b..014c912a67 100644
--- a/libs/nativewindow/rust/src/lib.rs
+++ b/libs/nativewindow/rust/src/lib.rs
@@ -203,8 +203,8 @@ impl HardwareBuffer {
Self(buffer_ptr)
}
- /// Creates a new Rust HardwareBuffer to wrap the given AHardwareBuffer without taking ownership
- /// of it.
+ /// Creates a new Rust HardwareBuffer to wrap the given `AHardwareBuffer` without taking
+ /// ownership of it.
///
/// Unlike [`from_raw`](Self::from_raw) this method will increment the refcount on the buffer.
/// This means that the caller can continue to use the raw buffer it passed in, and must call
@@ -220,14 +220,20 @@ impl HardwareBuffer {
Self(buffer)
}
- /// Get the internal `AHardwareBuffer` pointer that is only valid when this `HardwareBuffer`
- /// exists. This can be used to provide a pointer to the AHB for a C/C++ API over the FFI.
+ /// Returns the internal `AHardwareBuffer` pointer.
+ ///
+ /// This is only valid as long as this `HardwareBuffer` exists, so shouldn't be stored. It can
+ /// be used to provide a pointer for a C/C++ API over FFI.
pub fn as_raw(&self) -> NonNull<AHardwareBuffer> {
self.0
}
- /// Get the internal `AHardwareBuffer` pointer without decrementing the refcount. This can
- /// be used to provide a pointer to the AHB for a C/C++ API over the FFI.
+ /// Gets the internal `AHardwareBuffer` pointer without decrementing the refcount. This can
+ /// be used for a C/C++ API which takes ownership of the pointer.
+ ///
+ /// The caller is responsible for releasing the `AHardwareBuffer` pointer by calling
+ /// `AHardwareBuffer_release` when it is finished with it, or may convert it back to a Rust
+ /// `HardwareBuffer` by calling [`HardwareBuffer::from_raw`].
pub fn into_raw(self) -> NonNull<AHardwareBuffer> {
let buffer = ManuallyDrop::new(self);
buffer.0
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index d248ea0b84..7f207f0670 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -105,6 +105,7 @@ filegroup {
"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/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index 859ae8b6e2..ac43da8dcf 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -16,6 +16,7 @@
#pragma once
+#include <gui/DisplayLuts.h>
#include <math/mat4.h>
#include <math/vec3.h>
#include <renderengine/ExternalTexture.h>
@@ -145,6 +146,8 @@ struct LayerSettings {
// If white point nits are unknown, then this layer is assumed to have the
// same luminance as the brightest layer in the scene.
float whitePointNits = -1.f;
+
+ std::shared_ptr<gui::DisplayLuts> luts;
};
// Keep in sync with custom comparison function in
@@ -187,7 +190,7 @@ static inline bool operator==(const LayerSettings& lhs, const LayerSettings& rhs
lhs.blurRegionTransform == rhs.blurRegionTransform &&
lhs.stretchEffect == rhs.stretchEffect &&
lhs.edgeExtensionEffect == rhs.edgeExtensionEffect &&
- lhs.whitePointNits == rhs.whitePointNits;
+ lhs.whitePointNits == rhs.whitePointNits && lhs.luts == rhs.luts;
}
static inline void PrintTo(const Buffer& settings, ::std::ostream* os) {
diff --git a/libs/renderengine/skia/GaneshVkRenderEngine.cpp b/libs/renderengine/skia/GaneshVkRenderEngine.cpp
index a3a43e20be..cc73f405a6 100644
--- a/libs/renderengine/skia/GaneshVkRenderEngine.cpp
+++ b/libs/renderengine/skia/GaneshVkRenderEngine.cpp
@@ -21,12 +21,15 @@
#include <include/gpu/ganesh/vk/GrVkBackendSemaphore.h>
+#include <android-base/stringprintf.h>
#include <common/trace.h>
#include <log/log_main.h>
#include <sync/sync.h>
namespace android::renderengine::skia {
+using base::StringAppendF;
+
std::unique_ptr<GaneshVkRenderEngine> GaneshVkRenderEngine::create(
const RenderEngineCreationArgs& args) {
std::unique_ptr<GaneshVkRenderEngine> engine(new GaneshVkRenderEngine(args));
@@ -111,4 +114,9 @@ base::unique_fd GaneshVkRenderEngine::flushAndSubmit(SkiaGpuContext* context,
return res;
}
+void GaneshVkRenderEngine::appendBackendSpecificInfoToDump(std::string& result) {
+ StringAppendF(&result, "\n ------------RE Vulkan (Ganesh)----------\n");
+ SkiaVkRenderEngine::appendBackendSpecificInfoToDump(result);
+}
+
} // namespace android::renderengine::skia
diff --git a/libs/renderengine/skia/GaneshVkRenderEngine.h b/libs/renderengine/skia/GaneshVkRenderEngine.h
index e6123c21bf..ba17f71201 100644
--- a/libs/renderengine/skia/GaneshVkRenderEngine.h
+++ b/libs/renderengine/skia/GaneshVkRenderEngine.h
@@ -28,6 +28,7 @@ protected:
std::unique_ptr<SkiaGpuContext> createContext(VulkanInterface& vulkanInterface) override;
void waitFence(SkiaGpuContext* context, base::borrowed_fd fenceFd) override;
base::unique_fd flushAndSubmit(SkiaGpuContext* context, sk_sp<SkSurface> dstSurface) override;
+ void appendBackendSpecificInfoToDump(std::string& result) override;
private:
GaneshVkRenderEngine(const RenderEngineCreationArgs& args) : SkiaVkRenderEngine(args) {}
diff --git a/libs/renderengine/skia/GraphiteVkRenderEngine.cpp b/libs/renderengine/skia/GraphiteVkRenderEngine.cpp
index 390ad6efd1..a9332fa4e1 100644
--- a/libs/renderengine/skia/GraphiteVkRenderEngine.cpp
+++ b/libs/renderengine/skia/GraphiteVkRenderEngine.cpp
@@ -25,6 +25,7 @@
#include <include/gpu/graphite/Recording.h>
#include <include/gpu/graphite/vk/VulkanGraphiteTypes.h>
+#include <android-base/stringprintf.h>
#include <log/log_main.h>
#include <sync/sync.h>
@@ -33,6 +34,8 @@
namespace android::renderengine::skia {
+using base::StringAppendF;
+
std::unique_ptr<GraphiteVkRenderEngine> GraphiteVkRenderEngine::create(
const RenderEngineCreationArgs& args) {
std::unique_ptr<GraphiteVkRenderEngine> engine(new GraphiteVkRenderEngine(args));
@@ -139,4 +142,9 @@ base::unique_fd GraphiteVkRenderEngine::flushAndSubmit(SkiaGpuContext* context,
return drawFenceFd;
}
+void GraphiteVkRenderEngine::appendBackendSpecificInfoToDump(std::string& result) {
+ StringAppendF(&result, "\n ------------RE Vulkan (Graphite)----------\n");
+ SkiaVkRenderEngine::appendBackendSpecificInfoToDump(result);
+}
+
} // namespace android::renderengine::skia
diff --git a/libs/renderengine/skia/GraphiteVkRenderEngine.h b/libs/renderengine/skia/GraphiteVkRenderEngine.h
index cf24a3b756..33a47f1a7f 100644
--- a/libs/renderengine/skia/GraphiteVkRenderEngine.h
+++ b/libs/renderengine/skia/GraphiteVkRenderEngine.h
@@ -30,6 +30,7 @@ protected:
std::unique_ptr<SkiaGpuContext> createContext(VulkanInterface& vulkanInterface) override;
void waitFence(SkiaGpuContext* context, base::borrowed_fd fenceFd) override;
base::unique_fd flushAndSubmit(SkiaGpuContext* context, sk_sp<SkSurface> dstSurface) override;
+ void appendBackendSpecificInfoToDump(std::string& result) override;
private:
GraphiteVkRenderEngine(const RenderEngineCreationArgs& args) : SkiaVkRenderEngine(args) {}
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 4ef7d5bccb..ddae9fc78f 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -541,7 +541,7 @@ int SkiaGLRenderEngine::getContextPriority() {
void SkiaGLRenderEngine::appendBackendSpecificInfoToDump(std::string& result) {
const GLExtensions& extensions = GLExtensions::getInstance();
- StringAppendF(&result, "\n ------------RE GLES------------\n");
+ StringAppendF(&result, "\n ------------RE GLES (Ganesh)------------\n");
StringAppendF(&result, "EGL implementation : %s\n", extensions.getEGLVersion());
StringAppendF(&result, "%s\n", extensions.getEGLExtensions());
StringAppendF(&result, "GLES: %s, %s, %s\n", extensions.getVendor(), extensions.getRenderer(),
diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
index ec9d3efb88..5c46c9168d 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -543,6 +543,10 @@ sk_sp<SkShader> SkiaRenderEngine::createRuntimeEffectShader(
}
}
+ 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 b5f8898263..7be4c253e7 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 @@ private:
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 677a2b63b2..177abe6c9f 100644
--- a/libs/renderengine/skia/SkiaVkRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
@@ -169,24 +169,26 @@ int SkiaVkRenderEngine::getContextPriority() {
}
void SkiaVkRenderEngine::appendBackendSpecificInfoToDump(std::string& result) {
- StringAppendF(&result, "\n ------------RE Vulkan----------\n");
- StringAppendF(&result, "\n Vulkan device initialized: %d\n", sVulkanInterface.isInitialized());
- StringAppendF(&result, "\n Vulkan protected device initialized: %d\n",
+ // Subclasses will prepend a backend-specific name / section header
+ StringAppendF(&result, "Vulkan device initialized: %d\n", sVulkanInterface.isInitialized());
+ StringAppendF(&result, "Vulkan protected device initialized: %d\n",
sProtectedContentVulkanInterface.isInitialized());
if (!sVulkanInterface.isInitialized()) {
return;
}
- StringAppendF(&result, "\n Instance extensions:\n");
+ StringAppendF(&result, "Instance extensions: [\n");
for (const auto& name : sVulkanInterface.getInstanceExtensionNames()) {
- StringAppendF(&result, "\n %s\n", name.c_str());
+ StringAppendF(&result, " %s\n", name.c_str());
}
+ StringAppendF(&result, "]\n");
- StringAppendF(&result, "\n Device extensions:\n");
+ StringAppendF(&result, "Device extensions: [\n");
for (const auto& name : sVulkanInterface.getDeviceExtensionNames()) {
- StringAppendF(&result, "\n %s\n", name.c_str());
+ StringAppendF(&result, " %s\n", name.c_str());
}
+ StringAppendF(&result, "]\n");
}
} // namespace skia
diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.h b/libs/renderengine/skia/SkiaVkRenderEngine.h
index d2bb3d53cf..88b04df58d 100644
--- a/libs/renderengine/skia/SkiaVkRenderEngine.h
+++ b/libs/renderengine/skia/SkiaVkRenderEngine.h
@@ -81,7 +81,7 @@ protected:
SkiaRenderEngine::Contexts createContexts() override;
bool supportsProtectedContentImpl() const override;
bool useProtectedContextImpl(GrProtected isProtected) override;
- void appendBackendSpecificInfoToDump(std::string& result) override;
+ virtual void appendBackendSpecificInfoToDump(std::string& result) override;
// TODO: b/300533018 - refactor this to be non-static
static VulkanInterface& getVulkanInterface(bool protectedContext);
diff --git a/libs/renderengine/skia/filters/LutShader.cpp b/libs/renderengine/skia/filters/LutShader.cpp
new file mode 100644
index 0000000000..cea46ef40e
--- /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 0000000000..c157904b63
--- /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 eddd568fb5..797efbe5df 100644
--- a/libs/sensor/Sensor.cpp
+++ b/libs/sensor/Sensor.cpp
@@ -306,7 +306,18 @@ Sensor::Sensor(struct sensor_t const& hwSensor, const uuid_t& uuid, int halVersi
}
if (halVersion > SENSORS_DEVICE_API_VERSION_1_0 && hwSensor.requiredPermission) {
mRequiredPermission = hwSensor.requiredPermission;
- if (!strcmp(mRequiredPermission, SENSOR_PERMISSION_BODY_SENSORS)) {
+ bool requiresBodySensorPermission =
+ !strcmp(mRequiredPermission, SENSOR_PERMISSION_BODY_SENSORS);
+ if (android::permission::flags::replace_body_sensor_permission_enabled()) {
+ if (requiresBodySensorPermission) {
+ ALOGE("Sensor %s using deprecated Body Sensor permission", mName.c_str());
+ }
+
+ AppOpsManager appOps;
+ // Lookup to see if an AppOp exists for the permission. If none
+ // does, the default value of -1 is used.
+ mRequiredAppOp = appOps.permissionToOpCode(String16(mRequiredPermission));
+ } else if (requiresBodySensorPermission) {
AppOpsManager appOps;
mRequiredAppOp = appOps.permissionToOpCode(String16(SENSOR_PERMISSION_BODY_SENSORS));
}
diff --git a/libs/tracing_perfetto/Android.bp b/libs/tracing_perfetto/Android.bp
index b5c56c5c52..9a2d4f7463 100644
--- a/libs/tracing_perfetto/Android.bp
+++ b/libs/tracing_perfetto/Android.bp
@@ -47,4 +47,6 @@ cc_library_shared {
],
host_supported: true,
+ // for vndbinder
+ vendor_available: true,
}
diff --git a/libs/tracing_perfetto/tracing_perfetto_internal.cpp b/libs/tracing_perfetto/tracing_perfetto_internal.cpp
index 9a0042aee5..c4f866338a 100644
--- a/libs/tracing_perfetto/tracing_perfetto_internal.cpp
+++ b/libs/tracing_perfetto/tracing_perfetto_internal.cpp
@@ -253,15 +253,31 @@ void perfettoTraceEnd(const struct PerfettoTeCategory& category) {
void perfettoTraceAsyncBeginForTrack(const struct PerfettoTeCategory& category, const char* name,
const char* trackName, uint64_t cookie) {
PERFETTO_TE(
- category, PERFETTO_TE_SLICE_BEGIN(name),
- PERFETTO_TE_NAMED_TRACK(trackName, cookie, PerfettoTeProcessTrackUuid()));
+ category, PERFETTO_TE_SLICE_BEGIN(name),
+ PERFETTO_TE_PROTO_TRACK(
+ PerfettoTeNamedTrackUuid(trackName, cookie,
+ PerfettoTeProcessTrackUuid()),
+ PERFETTO_TE_PROTO_FIELD_CSTR(
+ perfetto_protos_TrackDescriptor_atrace_name_field_number,
+ trackName),
+ PERFETTO_TE_PROTO_FIELD_VARINT(
+ perfetto_protos_TrackDescriptor_parent_uuid_field_number,
+ PerfettoTeProcessTrackUuid())));
}
void perfettoTraceAsyncEndForTrack(const struct PerfettoTeCategory& category,
const char* trackName, uint64_t cookie) {
- PERFETTO_TE(
- category, PERFETTO_TE_SLICE_END(),
- PERFETTO_TE_NAMED_TRACK(trackName, cookie, PerfettoTeProcessTrackUuid()));
+ PERFETTO_TE(
+ category, PERFETTO_TE_SLICE_END(),
+ PERFETTO_TE_PROTO_TRACK(
+ PerfettoTeNamedTrackUuid(trackName, cookie,
+ PerfettoTeProcessTrackUuid()),
+ PERFETTO_TE_PROTO_FIELD_CSTR(
+ perfetto_protos_TrackDescriptor_atrace_name_field_number,
+ trackName),
+ PERFETTO_TE_PROTO_FIELD_VARINT(
+ perfetto_protos_TrackDescriptor_parent_uuid_field_number,
+ PerfettoTeProcessTrackUuid())));
}
void perfettoTraceAsyncBegin(const struct PerfettoTeCategory& category, const char* name,
@@ -281,14 +297,35 @@ void perfettoTraceInstant(const struct PerfettoTeCategory& category, const char*
void perfettoTraceInstantForTrack(const struct PerfettoTeCategory& category,
const char* trackName, const char* name) {
PERFETTO_TE(
- category, PERFETTO_TE_INSTANT(name),
- PERFETTO_TE_NAMED_TRACK(trackName, 1, PerfettoTeProcessTrackUuid()));
+ category, PERFETTO_TE_INSTANT(name),
+ PERFETTO_TE_PROTO_TRACK(
+ PerfettoTeNamedTrackUuid(trackName, 1,
+ PerfettoTeProcessTrackUuid()),
+ PERFETTO_TE_PROTO_FIELD_CSTR(
+ perfetto_protos_TrackDescriptor_atrace_name_field_number,
+ trackName),
+ PERFETTO_TE_PROTO_FIELD_VARINT(
+ perfetto_protos_TrackDescriptor_parent_uuid_field_number,
+ PerfettoTeProcessTrackUuid())));
}
void perfettoTraceCounter(const struct PerfettoTeCategory& category,
- [[maybe_unused]] const char* name, int64_t value) {
- PERFETTO_TE(category, PERFETTO_TE_COUNTER(),
- PERFETTO_TE_INT_COUNTER(value));
+ const char* name, int64_t value) {
+ PERFETTO_TE(
+ category, PERFETTO_TE_COUNTER(),
+ PERFETTO_TE_PROTO_TRACK(
+ PerfettoTeCounterTrackUuid(name,
+ PerfettoTeProcessTrackUuid()),
+ PERFETTO_TE_PROTO_FIELD_CSTR(
+ perfetto_protos_TrackDescriptor_atrace_name_field_number,
+ name),
+ PERFETTO_TE_PROTO_FIELD_VARINT(
+ perfetto_protos_TrackDescriptor_parent_uuid_field_number,
+ PerfettoTeProcessTrackUuid()),
+ PERFETTO_TE_PROTO_FIELD_BYTES(
+ perfetto_protos_TrackDescriptor_counter_field_number,
+ PERFETTO_NULL, 0)),
+ PERFETTO_TE_INT_COUNTER(value));
}
} // namespace internal
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index 12230f99d2..87e213e394 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -136,6 +136,7 @@ cc_library_shared {
"GraphicBuffer.cpp",
"GraphicBufferAllocator.cpp",
"GraphicBufferMapper.cpp",
+ "PictureProfileHandle.cpp",
"PixelFormat.cpp",
"PublicFormat.cpp",
"StaticAsserts.cpp",
diff --git a/libs/ui/Gralloc5.cpp b/libs/ui/Gralloc5.cpp
index c9ec036186..2143f79f11 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 @@ static void *loadIMapperLibrary() {
}
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/libs/gui/aidl/android/gui/Lut.aidl b/libs/ui/PictureProfileHandle.cpp
index a06e521789..0701e906f0 100644
--- a/libs/gui/aidl/android/gui/Lut.aidl
+++ b/libs/ui/PictureProfileHandle.cpp
@@ -1,11 +1,11 @@
/*
- * Copyright (C) 2024 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.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * 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,
@@ -14,17 +14,16 @@
* limitations under the License.
*/
-package android.gui;
+#include <ui/PictureProfileHandle.h>
-import android.gui.LutProperties;
-import android.os.ParcelFileDescriptor;
+#include <format>
-/**
- * This mirrors aidl::android::hardware::graphics::composer3::Lut definition
- * @hide
- */
-parcelable Lut {
- @nullable ParcelFileDescriptor pfd;
+namespace android {
+
+const PictureProfileHandle PictureProfileHandle::NONE(0);
+
+::std::string toString(const PictureProfileHandle& handle) {
+ return std::format("{:#010x}", handle.getId());
+}
- LutProperties lutProperties;
-} \ No newline at end of file
+} // namespace android
diff --git a/libs/ui/include/ui/DynamicDisplayInfo.h b/libs/ui/include/ui/DynamicDisplayInfo.h
index 25a2b6ee53..af494dcf39 100644
--- a/libs/ui/include/ui/DynamicDisplayInfo.h
+++ b/libs/ui/include/ui/DynamicDisplayInfo.h
@@ -22,6 +22,7 @@
#include <optional>
#include <vector>
+#include <ui/FrameRateCategoryRate.h>
#include <ui/GraphicTypes.h>
#include <ui/HdrCapabilities.h>
@@ -55,6 +56,9 @@ struct DynamicDisplayInfo {
std::optional<ui::DisplayMode> getActiveDisplayMode() const;
bool hasArrSupport;
+
+ // Represents frame rate for FrameRateCategory Normal and High.
+ ui::FrameRateCategoryRate frameRateCategoryRate;
};
} // namespace android::ui
diff --git a/libs/ui/include/ui/FrameRateCategoryRate.h b/libs/ui/include/ui/FrameRateCategoryRate.h
new file mode 100644
index 0000000000..9c392d9bc8
--- /dev/null
+++ b/libs/ui/include/ui/FrameRateCategoryRate.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+namespace android::ui {
+
+// Represents frame rate for FrameRateCategory Normal and High.
+class FrameRateCategoryRate {
+public:
+ FrameRateCategoryRate(float normal = 0, float high = 0) : mNormal(normal), mHigh(high) {}
+
+ float getNormal() const { return mNormal; }
+
+ float getHigh() const { return mHigh; }
+
+private:
+ float mNormal;
+ float mHigh;
+};
+
+} // namespace android::ui \ No newline at end of file
diff --git a/libs/ui/include/ui/PictureProfileHandle.h b/libs/ui/include/ui/PictureProfileHandle.h
new file mode 100644
index 0000000000..f8406501b4
--- /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