diff options
Diffstat (limited to 'libs')
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 ©) : Uint64Array(copy.size()) { + if (mSize != 0 && copy.data() != nullptr) { + mData = new uint64_t[mSize]; + memcpy(mData, copy.data(), mSize * sizeof(uint64_t)); + } else { + mData = nullptr; } +} - bool is_delta_valid = true; - for (int i = size - 1; i >= 0; i--) { - if (newValue[i] >= previousValue[i]) { - (*outValue)[i] = newValue[i] - previousValue[i]; +uint64_t *Uint64ArrayRW::dataRW() { + if (mData == nullptr) { + mData = new uint64_t[mSize]; + memset(mData, 0, mSize * sizeof(uint64_t)); + } + return mData; +} + +Uint64ArrayRW &Uint64ArrayRW::operator=(const Uint64Array &t) { + if (t.size() != mSize) { + delete[] mData; + mSize = t.size(); + mData = nullptr; + } + if (mSize != 0) { + if (t.data() != nullptr) { + if (mData == nullptr) { + mData = new uint64_t[mSize]; + } + memcpy(mData, t.data(), mSize * sizeof(uint64_t)); } else { - (*outValue)[i] = 0; - is_delta_valid = false; + delete[] mData; + mData = nullptr; } } - return is_delta_valid; + return *this; +} + +std::ostream &operator<<(std::ostream &os, const Uint64Array &v) { + os << "{"; + const uint64_t *data = v.data(); + if (data != nullptr) { + bool first = true; + for (size_t i = 0; i < v.size(); i++) { + if (!first) { + os << ", "; + } + os << data[i]; + first = false; + } + } + os << "}"; + return os; +} + +// Convenience constructor for tests +Uint64ArrayRW::Uint64ArrayRW(std::initializer_list<uint64_t> init) : Uint64Array(init.size()) { + mData = new uint64_t[mSize]; + memcpy(mData, init.begin(), mSize * sizeof(uint64_t)); +} + +// Used in tests only. +bool Uint64Array::operator==(const Uint64Array &other) const { + if (size() != other.size()) { + return false; + } + const uint64_t* thisData = data(); + const uint64_t* thatData = other.data(); + for (size_t i = 0; i < mSize; i++) { + const uint64_t v1 = thisData != nullptr ? thisData[i] : 0; + const uint64_t v2 = thatData != nullptr ? thatData[i] : 0; + if (v1 != v2) { + return false; + } + } + return true; } template <> -void LongArrayMultiStateCounter::add(std::vector<uint64_t>* value1, - const std::vector<uint64_t>& value2, const uint64_t numerator, - const uint64_t denominator) const { +void LongArrayMultiStateCounter::add(Uint64ArrayRW *value1, const Uint64Array &value2, + const uint64_t numerator, const uint64_t denominator) const { + const uint64_t* data2 = value2.data(); + if (data2 == nullptr) { + return; + } + + uint64_t* data1 = value1->dataRW(); + size_t size = value2.size(); if (numerator != denominator) { - for (int i = value2.size() - 1; i >= 0; i--) { + for (size_t i = 0; i < size; i++) { // The caller ensures that denominator != 0 - (*value1)[i] += value2[i] * numerator / denominator; + data1[i] += data2[i] * numerator / denominator; } } else { - for (int i = value2.size() - 1; i >= 0; i--) { - (*value1)[i] += value2[i]; + for (size_t i = 0; i < size; i++) { + data1[i] += data2[i]; } } } -template <> -std::string LongArrayMultiStateCounter::valueToString(const std::vector<uint64_t>& v) const { - std::stringstream s; - s << "{"; - bool first = true; - for (uint64_t n : v) { - if (!first) { - s << ", "; +template<> +bool LongArrayMultiStateCounter::delta(const Uint64ArrayRW &previousValue, + const Uint64Array &newValue, Uint64ArrayRW *outValue) const { + size_t size = previousValue.size(); + if (newValue.size() != size) { + ALOGE("Incorrect array size: %d, should be %d", (int) newValue.size(), (int) size); + return false; + } + if (outValue->size() != size) { + ALOGE("Incorrect outValue size: %d, should be %d", (int) outValue->size(), (int) size); + return false; + } + + bool is_delta_valid = true; + const uint64_t *prevData = previousValue.data(); + const uint64_t *newData = newValue.data(); + uint64_t *outData = outValue->dataRW(); + for (size_t i = 0; i < size; i++) { + if (prevData == nullptr) { + if (newData == nullptr) { + outData[i] = 0; + } else { + outData[i] = newData[i]; + } + } else if (newData == nullptr || newData[i] < prevData[i]) { + outData[i] = 0; + is_delta_valid = false; + } else { + outData[i] = newData[i] - prevData[i]; } - s << n; - first = false; } - s << "}"; - return s.str(); + return is_delta_valid; } } // namespace battery diff --git a/libs/battery/LongArrayMultiStateCounter.h b/libs/battery/LongArrayMultiStateCounter.h index f3439f6a0c..e00c96898e 100644 --- a/libs/battery/LongArrayMultiStateCounter.h +++ b/libs/battery/LongArrayMultiStateCounter.h @@ -23,7 +23,66 @@ namespace android { namespace battery { -typedef MultiStateCounter<std::vector<uint64_t>> LongArrayMultiStateCounter; +/** + * Wrapper for an array of uint64's. + */ +class Uint64Array { + protected: + size_t mSize; + + public: + Uint64Array() : Uint64Array(0) {} + + Uint64Array(size_t size) : mSize(size) {} + + virtual ~Uint64Array() {} + + size_t size() const { return mSize; } + + /** + * Returns the wrapped array. + * + * Nullable! Null should be interpreted the same as an array of zeros + */ + virtual const uint64_t *data() const { return nullptr; } + + friend std::ostream &operator<<(std::ostream &os, const Uint64Array &v); + + // Test API + bool operator==(const Uint64Array &other) const; +}; + +/** + * Mutable version of Uint64Array. + */ +class Uint64ArrayRW: public Uint64Array { + uint64_t* mData; + +public: + Uint64ArrayRW() : Uint64ArrayRW(0) {} + + Uint64ArrayRW(size_t size) : Uint64Array(size), mData(nullptr) {} + + Uint64ArrayRW(const Uint64Array ©); + + // Need an explicit copy constructor. In the initialization context C++ does not understand that + // a Uint64ArrayRW is a Uint64Array. + Uint64ArrayRW(const Uint64ArrayRW ©) : Uint64ArrayRW((const Uint64Array &) copy) {} + + // Test API + Uint64ArrayRW(std::initializer_list<uint64_t> init); + + ~Uint64ArrayRW() override { delete[] mData; } + + const uint64_t *data() const override { return mData; } + + // NonNull. Will initialize the wrapped array if it is null. + uint64_t *dataRW(); + + Uint64ArrayRW &operator=(const Uint64Array &t); +}; + +typedef MultiStateCounter<Uint64ArrayRW, Uint64Array> LongArrayMultiStateCounter; } // namespace battery } // namespace android diff --git a/libs/battery/LongArrayMultiStateCounterTest.cpp b/libs/battery/LongArrayMultiStateCounterTest.cpp index e4e6b2a49f..1c74e3fd14 100644 --- a/libs/battery/LongArrayMultiStateCounterTest.cpp +++ b/libs/battery/LongArrayMultiStateCounterTest.cpp @@ -24,25 +24,25 @@ namespace battery { class LongArrayMultiStateCounterTest : public testing::Test {}; TEST_F(LongArrayMultiStateCounterTest, stateChange) { - LongArrayMultiStateCounter testCounter(2, std::vector<uint64_t>(4)); - testCounter.updateValue(std::vector<uint64_t>({0, 0, 0, 0}), 1000); + LongArrayMultiStateCounter testCounter(2, Uint64Array(4)); + testCounter.updateValue(Uint64ArrayRW({0, 0, 0, 0}), 1000); testCounter.setState(0, 1000); testCounter.setState(1, 2000); - testCounter.updateValue(std::vector<uint64_t>({100, 200, 300, 400}), 3000); + testCounter.updateValue(Uint64ArrayRW({100, 200, 300, 400}), 3000); // Time was split in half between the two states, so the counts will be split 50:50 too - EXPECT_EQ(std::vector<uint64_t>({50, 100, 150, 200}), testCounter.getCount(0)); - EXPECT_EQ(std::vector<uint64_t>({50, 100, 150, 200}), testCounter.getCount(1)); + EXPECT_EQ(Uint64ArrayRW({50, 100, 150, 200}), testCounter.getCount(0)); + EXPECT_EQ(Uint64ArrayRW({50, 100, 150, 200}), testCounter.getCount(1)); } TEST_F(LongArrayMultiStateCounterTest, accumulation) { - LongArrayMultiStateCounter testCounter(2, std::vector<uint64_t>(4)); - testCounter.updateValue(std::vector<uint64_t>({0, 0, 0, 0}), 1000); + LongArrayMultiStateCounter testCounter(2, Uint64Array(4)); + testCounter.updateValue(Uint64ArrayRW({0, 0, 0, 0}), 1000); testCounter.setState(0, 1000); testCounter.setState(1, 2000); - testCounter.updateValue(std::vector<uint64_t>({100, 200, 300, 400}), 3000); + testCounter.updateValue(Uint64ArrayRW({100, 200, 300, 400}), 3000); testCounter.setState(0, 4000); - testCounter.updateValue(std::vector<uint64_t>({200, 300, 400, 500}), 8000); + testCounter.updateValue(Uint64ArrayRW({200, 300, 400, 500}), 8000); // The first delta is split 50:50: // 0: {50, 100, 150, 200} @@ -50,16 +50,16 @@ TEST_F(LongArrayMultiStateCounterTest, accumulation) { // The second delta is split 4:1 // 0: {80, 80, 80, 80} // 1: {20, 20, 20, 20} - EXPECT_EQ(std::vector<uint64_t>({130, 180, 230, 280}), testCounter.getCount(0)); - EXPECT_EQ(std::vector<uint64_t>({70, 120, 170, 220}), testCounter.getCount(1)); + EXPECT_EQ(Uint64ArrayRW({130, 180, 230, 280}), testCounter.getCount(0)); + EXPECT_EQ(Uint64ArrayRW({70, 120, 170, 220}), testCounter.getCount(1)); } TEST_F(LongArrayMultiStateCounterTest, toString) { - LongArrayMultiStateCounter testCounter(2, std::vector<uint64_t>(4)); - testCounter.updateValue(std::vector<uint64_t>({0, 0, 0, 0}), 1000); + LongArrayMultiStateCounter testCounter(2, Uint64Array(4)); + testCounter.updateValue(Uint64ArrayRW({0, 0, 0, 0}), 1000); testCounter.setState(0, 1000); testCounter.setState(1, 2000); - testCounter.updateValue(std::vector<uint64_t>({100, 200, 300, 400}), 3000); + testCounter.updateValue(Uint64ArrayRW({100, 200, 300, 400}), 3000); EXPECT_STREQ("[0: {50, 100, 150, 200}, 1: {50, 100, 150, 200}] updated: 3000 currentState: 1", testCounter.toString().c_str()); diff --git a/libs/battery/MultiStateCounter.h b/libs/battery/MultiStateCounter.h index 04b718698e..fadc4ffd41 100644 --- a/libs/battery/MultiStateCounter.h +++ b/libs/battery/MultiStateCounter.h @@ -35,12 +35,12 @@ namespace battery { typedef uint16_t state_t; -template <class T> +template <class T, class V> class MultiStateCounter { - uint16_t stateCount; + const uint16_t stateCount; + const V emptyValue; state_t currentState; time_t lastStateChangeTimestamp; - T emptyValue; T lastValue; time_t lastUpdateTimestamp; T deltaValue; @@ -54,7 +54,7 @@ class MultiStateCounter { State* states; public: - MultiStateCounter(uint16_t stateCount, const T& emptyValue); + MultiStateCounter(uint16_t stateCount, const V& emptyValue); virtual ~MultiStateCounter(); @@ -66,35 +66,35 @@ public: * Copies the current state and accumulated times-in-state from the source. Resets * the accumulated value. */ - void copyStatesFrom(const MultiStateCounter<T>& source); + void copyStatesFrom(const MultiStateCounter<T, V> &source); - void setValue(state_t state, const T& value); + void setValue(state_t state, const V& value); /** * Updates the value by distributing the delta from the previously set value * among states according to their respective time-in-state. * Returns the delta from the previously set value. */ - const T& updateValue(const T& value, time_t timestamp); + const V& updateValue(const V& value, time_t timestamp); /** * Updates the value by distributing the specified increment among states according * to their respective time-in-state. */ - void incrementValue(const T& increment, time_t timestamp); + void incrementValue(const V& increment, time_t timestamp); /** * Adds the specified increment to the value for the current state, without affecting * the last updated value or timestamp. Ignores partial time-in-state: the entirety of * the increment is given to the current state. */ - void addValue(const T& increment); + void addValue(const V& increment); void reset(); uint16_t getStateCount(); - const T& getCount(state_t state); + const V& getCount(state_t state); std::string toString(); @@ -104,27 +104,25 @@ private: * Returns true iff the combination of previousValue and newValue is valid * (newValue >= prevValue) */ - bool delta(const T& previousValue, const T& newValue, T* outValue) const; + bool delta(const T& previousValue, const V& newValue, T* outValue) const; /** * Adds value2 to value1 and stores the result in value1. Denominator is * guaranteed to be non-zero. */ - void add(T* value1, const T& value2, const uint64_t numerator, + void add(T* value1, const V& value2, const uint64_t numerator, const uint64_t denominator) const; - - std::string valueToString(const T& value) const; }; // ---------------------- MultiStateCounter Implementation ------------------------- // Since MultiStateCounter is a template, the implementation must be inlined. -template <class T> -MultiStateCounter<T>::MultiStateCounter(uint16_t stateCount, const T& emptyValue) +template <class T, class V> +MultiStateCounter<T, V>::MultiStateCounter(uint16_t stateCount, const V& emptyValue) : stateCount(stateCount), + emptyValue(emptyValue), currentState(0), lastStateChangeTimestamp(-1), - emptyValue(emptyValue), lastValue(emptyValue), lastUpdateTimestamp(-1), deltaValue(emptyValue), @@ -136,13 +134,13 @@ MultiStateCounter<T>::MultiStateCounter(uint16_t stateCount, const T& emptyValue } } -template <class T> -MultiStateCounter<T>::~MultiStateCounter() { +template <class T, class V> +MultiStateCounter<T, V>::~MultiStateCounter() { delete[] states; }; -template <class T> -void MultiStateCounter<T>::setEnabled(bool enabled, time_t timestamp) { +template <class T, class V> +void MultiStateCounter<T, V>::setEnabled(bool enabled, time_t timestamp) { if (enabled == isEnabled) { return; } @@ -167,8 +165,8 @@ void MultiStateCounter<T>::setEnabled(bool enabled, time_t timestamp) { } } -template <class T> -void MultiStateCounter<T>::setState(state_t state, time_t timestamp) { +template <class T, class V> +void MultiStateCounter<T, V>::setState(state_t state, time_t timestamp) { if (isEnabled && lastStateChangeTimestamp >= 0 && lastUpdateTimestamp >= 0) { // If the update arrived out-of-order, just push back the timestamp to // avoid having the situation where timeInStateSinceUpdate > timeSinceUpdate @@ -198,8 +196,8 @@ void MultiStateCounter<T>::setState(state_t state, time_t timestamp) { lastStateChangeTimestamp = timestamp; } -template <class T> -void MultiStateCounter<T>::copyStatesFrom(const MultiStateCounter<T>& source) { +template <class T, class V> +void MultiStateCounter<T, V>::copyStatesFrom(const MultiStateCounter<T, V>& source) { if (stateCount != source.stateCount) { ALOGE("State count mismatch: %u vs. %u\n", stateCount, source.stateCount); return; @@ -214,14 +212,14 @@ void MultiStateCounter<T>::copyStatesFrom(const MultiStateCounter<T>& source) { lastUpdateTimestamp = source.lastUpdateTimestamp; } -template <class T> -void MultiStateCounter<T>::setValue(state_t state, const T& value) { +template <class T, class V> +void MultiStateCounter<T, V>::setValue(state_t state, const V& value) { states[state].counter = value; } -template <class T> -const T& MultiStateCounter<T>::updateValue(const T& value, time_t timestamp) { - T* returnValue = &emptyValue; +template <class T, class V> +const V& MultiStateCounter<T, V>::updateValue(const V& value, time_t timestamp) { + const V* returnValue = &emptyValue; // If the counter is disabled, we ignore the update, except when the counter got disabled after // the previous update, in which case we still need to pick up the residual delta. @@ -250,8 +248,8 @@ const T& MultiStateCounter<T>::updateValue(const T& value, time_t timestamp) { } } else { std::stringstream str; - str << "updateValue is called with a value " << valueToString(value) - << ", which is lower than the previous value " << valueToString(lastValue) + str << "updateValue is called with a value " << value + << ", which is lower than the previous value " << lastValue << "\n"; ALOGE("%s", str.str().c_str()); @@ -276,23 +274,25 @@ const T& MultiStateCounter<T>::updateValue(const T& value, time_t timestamp) { return *returnValue; } -template <class T> -void MultiStateCounter<T>::incrementValue(const T& increment, time_t timestamp) { +template <class T, class V> +void MultiStateCounter<T, V>::incrementValue(const V& increment, time_t timestamp) { +// T newValue; +// newValue = lastValue; // Copy assignment, not initialization. T newValue = lastValue; add(&newValue, increment, 1 /* numerator */, 1 /* denominator */); updateValue(newValue, timestamp); } -template <class T> -void MultiStateCounter<T>::addValue(const T& value) { +template <class T, class V> +void MultiStateCounter<T, V>::addValue(const V& value) { if (!isEnabled) { return; } add(&states[currentState].counter, value, 1 /* numerator */, 1 /* denominator */); } -template <class T> -void MultiStateCounter<T>::reset() { +template <class T, class V> +void MultiStateCounter<T, V>::reset() { lastStateChangeTimestamp = -1; lastUpdateTimestamp = -1; for (int i = 0; i < stateCount; i++) { @@ -301,25 +301,26 @@ void MultiStateCounter<T>::reset() { } } -template <class T> -uint16_t MultiStateCounter<T>::getStateCount() { +template <class T, class V> +uint16_t MultiStateCounter<T, V>::getStateCount() { return stateCount; } -template <class T> -const T& MultiStateCounter<T>::getCount(state_t state) { +template <class T, class V> +const V& MultiStateCounter<T, V>::getCount(state_t state) { return states[state].counter; } -template <class T> -std::string MultiStateCounter<T>::toString() { +template <class T, class V> +std::string MultiStateCounter<T, V>::toString() { std::stringstream str; +// str << "LAST VALUE: " << valueToString(lastValue); str << "["; for (int i = 0; i < stateCount; i++) { if (i != 0) { str << ", "; } - str << i << ": " << valueToString(states[i].counter); + str << i << ": " << states[i].counter; if (states[i].timeInStateSinceUpdate > 0) { str << " timeInStateSinceUpdate: " << states[i].timeInStateSinceUpdate; } diff --git a/libs/battery/MultiStateCounterTest.cpp b/libs/battery/MultiStateCounterTest.cpp index a51a38a6c7..589b7fe4e3 100644 --- a/libs/battery/MultiStateCounterTest.cpp +++ b/libs/battery/MultiStateCounterTest.cpp @@ -21,7 +21,7 @@ namespace android { namespace battery { -typedef MultiStateCounter<double> DoubleMultiStateCounter; +typedef MultiStateCounter<double, double> DoubleMultiStateCounter; template <> bool DoubleMultiStateCounter::delta(const double& previousValue, const double& newValue, @@ -41,11 +41,6 @@ void DoubleMultiStateCounter::add(double* value1, const double& value2, const ui } } -template <> -std::string DoubleMultiStateCounter::valueToString(const double& v) const { - return std::to_string(v); -} - class MultiStateCounterTest : public testing::Test {}; TEST_F(MultiStateCounterTest, constructor) { diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index 6903cb5d68..2ef642a3a0 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -871,6 +871,10 @@ cc_library { symbol_file: "libbinder_rpc_unstable.map.txt", }, + header_abi_checker: { + enabled: false, + }, + // This library is intentionally limited to these targets, and it will be removed later. // Do not expand the visibility. visibility: [ diff --git a/libs/binder/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(), - ®isterEndpointFd); - 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(), ®isterEventFd); - 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 |