diff options
Diffstat (limited to 'libs')
231 files changed, 6317 insertions, 2594 deletions
diff --git a/libs/attestation/OWNERS b/libs/attestation/OWNERS index 4dbb0eae5c..76811f2551 100644 --- a/libs/attestation/OWNERS +++ b/libs/attestation/OWNERS @@ -1,2 +1 @@ -chaviw@google.com -svv@google.com
\ No newline at end of file +svv@google.com diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index aac369ddd1..a3499c1963 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -269,6 +269,7 @@ cc_defaults { "-Wzero-as-null-pointer-constant", "-Wreorder-init-list", "-Wunused-const-variable", + "-Wunused-result", "-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION", "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION", // Hide symbols by default and set the BUILDING_LIBBINDER macro so that @@ -276,15 +277,6 @@ cc_defaults { "-fvisibility=hidden", "-DBUILDING_LIBBINDER", ], - - target: { - vendor: { - // Trimming the exported symbols reveals a bug in vendor code, so - // disable it for the vendor variant for now. http://b/349657329 - // TODO: Fix the issue and remove this override. - cflags: ["-fvisibility=default"], - }, - }, } cc_defaults { @@ -826,6 +818,7 @@ cc_library { // so we restrict its visibility to the Trusty-specific packages. visibility: [ ":__subpackages__", + "//hardware/interfaces/security/see:__subpackages__", "//system/core/trusty:__subpackages__", "//vendor:__subpackages__", ], diff --git a/libs/binder/BackendUnifiedServiceManager.cpp b/libs/binder/BackendUnifiedServiceManager.cpp index ee3d6af742..b1c8994b05 100644 --- a/libs/binder/BackendUnifiedServiceManager.cpp +++ b/libs/binder/BackendUnifiedServiceManager.cpp @@ -130,7 +130,13 @@ os::ServiceWithMetadata createServiceWithMetadata(const sp<IBinder>& service, bo bool BinderCacheWithInvalidation::isClientSideCachingEnabled(const std::string& serviceName) { sp<ProcessState> self = ProcessState::selfOrNull(); - if (!self || self->getThreadPoolMaxTotalThreadCount() <= 0) { + // Should not cache if process state could not be found, or if thread pool + // max could is not greater than zero. + if (!self) { + ALOGW("Service retrieved before binder threads started. If they are to be started, " + "consider starting binder threads earlier."); + return false; + } else if (self->getThreadPoolMaxTotalThreadCount() <= 0) { ALOGW("Thread Pool max thread count is 0. Cannot cache binder as linkToDeath cannot be " "implemented. serviceName: %s", serviceName.c_str()); @@ -215,7 +221,9 @@ Status BackendUnifiedServiceManager::getService(const ::std::string& name, sp<IBinder>* _aidl_return) { os::Service service; Status status = getService2(name, &service); - *_aidl_return = service.get<os::Service::Tag::serviceWithMetadata>().service; + if (status.isOk()) { + *_aidl_return = service.get<os::Service::Tag::serviceWithMetadata>().service; + } return status; } @@ -238,7 +246,17 @@ Status BackendUnifiedServiceManager::getService2(const ::std::string& name, os:: return status; } -Status BackendUnifiedServiceManager::checkService(const ::std::string& name, os::Service* _out) { +Status BackendUnifiedServiceManager::checkService(const ::std::string& name, + sp<IBinder>* _aidl_return) { + os::Service service; + Status status = checkService2(name, &service); + if (status.isOk()) { + *_aidl_return = service.get<os::Service::Tag::serviceWithMetadata>().service; + } + return status; +} + +Status BackendUnifiedServiceManager::checkService2(const ::std::string& name, os::Service* _out) { os::Service service; if (returnIfCached(name, _out)) { return Status::ok(); @@ -246,7 +264,7 @@ Status BackendUnifiedServiceManager::checkService(const ::std::string& name, os: Status status = Status::ok(); if (mTheRealServiceManager) { - status = mTheRealServiceManager->checkService(name, &service); + status = mTheRealServiceManager->checkService2(name, &service); } if (status.isOk()) { status = toBinderService(name, service, _out); diff --git a/libs/binder/BackendUnifiedServiceManager.h b/libs/binder/BackendUnifiedServiceManager.h index 2496f62503..c14f28063f 100644 --- a/libs/binder/BackendUnifiedServiceManager.h +++ b/libs/binder/BackendUnifiedServiceManager.h @@ -122,7 +122,8 @@ public: 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; + binder::Status checkService(const ::std::string& name, sp<IBinder>* _aidl_return) override; + binder::Status checkService2(const ::std::string& name, os::Service* out) override; binder::Status addService(const ::std::string& name, const sp<IBinder>& service, bool allowIsolated, int32_t dumpPriority) override; binder::Status listServices(int32_t dumpPriority, diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp index 53bd08d420..bc7ae37ff0 100644 --- a/libs/binder/Binder.cpp +++ b/libs/binder/Binder.cpp @@ -38,6 +38,7 @@ #endif #include "BuildFlags.h" +#include "Constants.h" #include "OS.h" #include "RpcState.h" @@ -70,8 +71,6 @@ constexpr bool kEnableRecording = true; constexpr bool kEnableRecording = false; #endif -// Log any reply transactions for which the data exceeds this size -#define LOG_REPLIES_OVER_SIZE (300 * 1024) // --------------------------------------------------------------------------- IBinder::IBinder() @@ -288,7 +287,7 @@ public: // for below objects RpcMutex mLock; std::set<sp<RpcServerLink>> mRpcServerLinks; - BpBinder::ObjectManager mObjects; + BpBinder::ObjectManager mObjectMgr; unique_fd mRecordingFd; }; @@ -412,7 +411,7 @@ status_t BBinder::transact( // In case this is being transacted on in the same process. if (reply != nullptr) { reply->setDataPosition(0); - if (reply->dataSize() > LOG_REPLIES_OVER_SIZE) { + if (reply->dataSize() > binder::kLogTransactionsOverBytes) { ALOGW("Large reply transaction of %zu bytes, interface descriptor %s, code %d", reply->dataSize(), String8(getInterfaceDescriptor()).c_str(), code); } @@ -468,7 +467,7 @@ void* BBinder::attachObject(const void* objectID, void* object, void* cleanupCoo LOG_ALWAYS_FATAL_IF(!e, "no memory"); RpcMutexUniqueLock _l(e->mLock); - return e->mObjects.attach(objectID, object, cleanupCookie, func); + return e->mObjectMgr.attach(objectID, object, cleanupCookie, func); } void* BBinder::findObject(const void* objectID) const @@ -477,7 +476,7 @@ void* BBinder::findObject(const void* objectID) const if (!e) return nullptr; RpcMutexUniqueLock _l(e->mLock); - return e->mObjects.find(objectID); + return e->mObjectMgr.find(objectID); } void* BBinder::detachObject(const void* objectID) { @@ -485,7 +484,7 @@ void* BBinder::detachObject(const void* objectID) { if (!e) return nullptr; RpcMutexUniqueLock _l(e->mLock); - return e->mObjects.detach(objectID); + return e->mObjectMgr.detach(objectID); } void BBinder::withLock(const std::function<void()>& doWithLock) { @@ -501,7 +500,7 @@ sp<IBinder> BBinder::lookupOrCreateWeak(const void* objectID, object_make_func m Extras* e = getOrCreateExtras(); LOG_ALWAYS_FATAL_IF(!e, "no memory"); RpcMutexUniqueLock _l(e->mLock); - return e->mObjects.lookupOrCreateWeak(objectID, make, makeArgs); + return e->mObjectMgr.lookupOrCreateWeak(objectID, make, makeArgs); } BBinder* BBinder::localBinder() diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp index 3758b6521c..c13e0f9e9c 100644 --- a/libs/binder/BpBinder.cpp +++ b/libs/binder/BpBinder.cpp @@ -28,6 +28,7 @@ #include <stdio.h> #include "BuildFlags.h" +#include "Constants.h" #include "file.h" //#undef ALOGV @@ -63,9 +64,6 @@ std::atomic<uint32_t> BpBinder::sBinderProxyCountWarned(0); static constexpr uint32_t kBinderProxyCountWarnInterval = 5000; -// Log any transactions for which the data exceeds this size -#define LOG_TRANSACTIONS_OVER_SIZE (300 * 1024) - enum { LIMIT_REACHED_MASK = 0x80000000, // A flag denoting that the limit has been reached WARNING_REACHED_MASK = 0x40000000, // A flag denoting that the warning has been reached @@ -78,7 +76,16 @@ BpBinder::ObjectManager::ObjectManager() BpBinder::ObjectManager::~ObjectManager() { - kill(); + const size_t N = mObjects.size(); + ALOGV("Killing %zu objects in manager %p", N, this); + for (auto i : mObjects) { + const entry_t& e = i.second; + if (e.func != nullptr) { + e.func(i.first, e.object, e.cleanupCookie); + } + } + + mObjects.clear(); } void* BpBinder::ObjectManager::attach(const void* objectID, void* object, void* cleanupCookie, @@ -144,20 +151,6 @@ sp<IBinder> BpBinder::ObjectManager::lookupOrCreateWeak(const void* objectID, ob return newObj; } -void BpBinder::ObjectManager::kill() -{ - const size_t N = mObjects.size(); - ALOGV("Killing %zu objects in manager %p", N, this); - for (auto i : mObjects) { - const entry_t& e = i.second; - if (e.func != nullptr) { - e.func(i.first, e.object, e.cleanupCookie); - } - } - - mObjects.clear(); -} - // --------------------------------------------------------------------------- sp<BpBinder> BpBinder::create(int32_t handle, std::function<void()>* postTask) { @@ -408,9 +401,11 @@ status_t BpBinder::transact( status = IPCThreadState::self()->transact(binderHandle(), code, data, reply, flags); } - if (data.dataSize() > LOG_TRANSACTIONS_OVER_SIZE) { + + if (data.dataSize() > binder::kLogTransactionsOverBytes) { RpcMutexUniqueLock _l(mLock); - ALOGW("Large outgoing transaction of %zu bytes, interface descriptor %s, code %d", + ALOGW("Large outgoing transaction of %zu bytes, interface descriptor %s, code %d was " + "sent", data.dataSize(), String8(mDescriptorCache).c_str(), code); } @@ -697,19 +692,19 @@ void BpBinder::reportOneDeath(const Obituary& obit) void* BpBinder::attachObject(const void* objectID, void* object, void* cleanupCookie, object_cleanup_func func) { RpcMutexUniqueLock _l(mLock); - ALOGV("Attaching object %p to binder %p (manager=%p)", object, this, &mObjects); - return mObjects.attach(objectID, object, cleanupCookie, func); + ALOGV("Attaching object %p to binder %p (manager=%p)", object, this, &mObjectMgr); + return mObjectMgr.attach(objectID, object, cleanupCookie, func); } void* BpBinder::findObject(const void* objectID) const { RpcMutexUniqueLock _l(mLock); - return mObjects.find(objectID); + return mObjectMgr.find(objectID); } void* BpBinder::detachObject(const void* objectID) { RpcMutexUniqueLock _l(mLock); - return mObjects.detach(objectID); + return mObjectMgr.detach(objectID); } void BpBinder::withLock(const std::function<void()>& doWithLock) { @@ -720,7 +715,7 @@ void BpBinder::withLock(const std::function<void()>& doWithLock) { sp<IBinder> BpBinder::lookupOrCreateWeak(const void* objectID, object_make_func make, const void* makeArgs) { RpcMutexUniqueLock _l(mLock); - return mObjects.lookupOrCreateWeak(objectID, make, makeArgs); + return mObjectMgr.lookupOrCreateWeak(objectID, make, makeArgs); } BpBinder* BpBinder::remoteBinder() diff --git a/libs/binder/Constants.h b/libs/binder/Constants.h new file mode 100644 index 0000000000..b75493cb8a --- /dev/null +++ b/libs/binder/Constants.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2025 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::binder { + +/** + * See also BINDER_VM_SIZE. In kernel binder, the sum of all transactions must be allocated in this + * space. Large transactions are very error prone. In general, we should work to reduce this limit. + * The same limit is used in RPC binder for consistency. + */ +constexpr size_t kLogTransactionsOverBytes = 300 * 1024; + +/** + * See b/392575419 - this limit is chosen for a specific usecase, because RPC binder does not have + * support for shared memory in the Android Baklava timeframe. This was 100 KB during and before + * Android V. + * + * Keeping this low helps preserve overall system performance. Transactions of this size are far too + * expensive to make multiple copies over binder or sockets, and they should be avoided if at all + * possible and transition to shared memory. + */ +constexpr size_t kRpcTransactionLimitBytes = 600 * 1024; + +} // namespace android::binder diff --git a/libs/binder/IActivityManager.cpp b/libs/binder/IActivityManager.cpp index 152c815ec3..83f4719de2 100644 --- a/libs/binder/IActivityManager.cpp +++ b/libs/binder/IActivityManager.cpp @@ -147,9 +147,11 @@ public: data.writeInterfaceToken(IActivityManager::getInterfaceDescriptor()); data.writeInt32(uid); data.writeString16(callingPackage); - remote()->transact(IS_UID_ACTIVE_TRANSACTION, data, &reply); + status_t err = remote()->transact(IS_UID_ACTIVE_TRANSACTION, data, &reply); // fail on exception - if (reply.readExceptionCode() != 0) return false; + if (err != NO_ERROR || ((err = reply.readExceptionCode()) != NO_ERROR)) { + return false; + } return reply.readInt32() == 1; } @@ -159,9 +161,9 @@ public: data.writeInterfaceToken(IActivityManager::getInterfaceDescriptor()); data.writeInt32(uid); data.writeString16(callingPackage); - remote()->transact(GET_UID_PROCESS_STATE_TRANSACTION, data, &reply); + status_t err = remote()->transact(GET_UID_PROCESS_STATE_TRANSACTION, data, &reply); // fail on exception - if (reply.readExceptionCode() != 0) { + if (err != NO_ERROR || ((err = reply.readExceptionCode()) != NO_ERROR)) { return ActivityManager::PROCESS_STATE_UNKNOWN; } return reply.readInt32(); @@ -192,7 +194,7 @@ public: data.writeInt32(appPid); status_t err = remote()->transact(LOG_FGS_API_BEGIN_TRANSACTION, data, &reply, IBinder::FLAG_ONEWAY); - if (err != NO_ERROR || ((err = reply.readExceptionCode()) != NO_ERROR)) { + if (err != NO_ERROR) { ALOGD("%s: FGS Logger Transaction failed, %d", __func__, err); return err; } @@ -207,7 +209,7 @@ public: data.writeInt32(appPid); status_t err = remote()->transact(LOG_FGS_API_END_TRANSACTION, data, &reply, IBinder::FLAG_ONEWAY); - if (err != NO_ERROR || ((err = reply.readExceptionCode()) != NO_ERROR)) { + if (err != NO_ERROR) { ALOGD("%s: FGS Logger Transaction failed, %d", __func__, err); return err; } @@ -224,7 +226,7 @@ public: data.writeInt32(appPid); status_t err = remote()->transact(LOG_FGS_API_STATE_CHANGED_TRANSACTION, data, &reply, IBinder::FLAG_ONEWAY); - if (err != NO_ERROR || ((err = reply.readExceptionCode()) != NO_ERROR)) { + if (err != NO_ERROR) { ALOGD("%s: FGS Logger Transaction failed, %d", __func__, err); return err; } diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp index 623e7b9139..1c1b6f30a4 100644 --- a/libs/binder/IPCThreadState.cpp +++ b/libs/binder/IPCThreadState.cpp @@ -38,6 +38,10 @@ #include "Utils.h" #include "binder_module.h" +#if (defined(__ANDROID__) || defined(__Fuchsia__)) && !defined(BINDER_WITH_KERNEL_IPC) +#error Android and Fuchsia are expected to have BINDER_WITH_KERNEL_IPC +#endif + #if LOG_NDEBUG #define IF_LOG_TRANSACTIONS() if (false) @@ -626,12 +630,22 @@ void IPCThreadState::flushCommands() { if (mProcess->mDriverFD < 0) return; - talkWithDriver(false); + + if (status_t res = talkWithDriver(false); res != OK) { + // TODO: we may want to abort for some of these cases + ALOGW("1st call to talkWithDriver returned error in flushCommands: %s", + statusToString(res).c_str()); + } + // The flush could have caused post-write refcount decrements to have // been executed, which in turn could result in BC_RELEASE/BC_DECREFS // being queued in mOut. So flush again, if we need to. if (mOut.dataSize() > 0) { - talkWithDriver(false); + if (status_t res = talkWithDriver(false); res != OK) { + // TODO: we may want to abort for some of these cases + ALOGW("2nd call to talkWithDriver returned error in flushCommands: %s", + statusToString(res).c_str()); + } } if (mOut.dataSize() > 0) { ALOGW("mOut.dataSize() > 0 after flushCommands()"); @@ -803,7 +817,11 @@ void IPCThreadState::joinThreadPool(bool isMain) mOut.writeInt32(BC_EXIT_LOOPER); mIsLooper = false; - talkWithDriver(false); + if (status_t res = talkWithDriver(false); res != OK) { + // TODO: we may want to abort for some of these cases + ALOGW("call to talkWithDriver in joinThreadPool returned error: %s, FD: %d", + statusToString(res).c_str(), mProcess->mDriverFD); + } size_t oldCount = mProcess->mCurrentThreads.fetch_sub(1); LOG_ALWAYS_FATAL_IF(oldCount == 0, "Threadpool thread count underflowed. Thread cannot exist and exit in " @@ -840,7 +858,7 @@ status_t IPCThreadState::handlePolledCommands() void IPCThreadState::stopProcess(bool /*immediate*/) { //ALOGI("**** STOPPING PROCESS"); - flushCommands(); + (void)flushCommands(); int fd = mProcess->mDriverFD; mProcess->mDriverFD = -1; close(fd); @@ -1215,7 +1233,7 @@ status_t IPCThreadState::talkWithDriver(bool doReceive) std::string message = logStream.str(); ALOGI("%s", message.c_str()); } -#if defined(__ANDROID__) +#if defined(BINDER_WITH_KERNEL_IPC) if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0) err = NO_ERROR; else @@ -1494,7 +1512,14 @@ status_t IPCThreadState::executeCommand(int32_t cmd) buffer.setDataSize(0); constexpr uint32_t kForwardReplyFlags = TF_CLEAR_BUF; - sendReply(reply, (tr.flags & kForwardReplyFlags)); + + // TODO: we may want to abort if there is an error here, or return as 'error' + // from this function, but the impact needs to be measured + status_t error2 = sendReply(reply, (tr.flags & kForwardReplyFlags)); + if (error2 != OK) { + ALOGE("error in sendReply for synchronous call: %s", + statusToString(error2).c_str()); + } } else { if (error != OK) { std::ostringstream logStream; @@ -1604,7 +1629,7 @@ void IPCThreadState::threadDestructor(void *st) IPCThreadState* const self = static_cast<IPCThreadState*>(st); if (self) { self->flushCommands(); -#if defined(__ANDROID__) +#if defined(BINDER_WITH_KERNEL_IPC) if (self->mProcess->mDriverFD >= 0) { ioctl(self->mProcess->mDriverFD, BINDER_THREAD_EXIT, 0); } @@ -1620,7 +1645,7 @@ status_t IPCThreadState::getProcessFreezeInfo(pid_t pid, uint32_t *sync_received binder_frozen_status_info info = {}; info.pid = pid; -#if defined(__ANDROID__) +#if defined(BINDER_WITH_KERNEL_IPC) if (ioctl(self()->mProcess->mDriverFD, BINDER_GET_FROZEN_INFO, &info) < 0) ret = -errno; #endif @@ -1639,7 +1664,7 @@ status_t IPCThreadState::freeze(pid_t pid, bool enable, uint32_t timeout_ms) { info.timeout_ms = timeout_ms; -#if defined(__ANDROID__) +#if defined(BINDER_WITH_KERNEL_IPC) if (ioctl(self()->mProcess->mDriverFD, BINDER_FREEZE, &info) < 0) ret = -errno; #endif @@ -1657,7 +1682,7 @@ void IPCThreadState::logExtendedError() { if (!ProcessState::isDriverFeatureEnabled(ProcessState::DriverFeature::EXTENDED_ERROR)) return; -#if defined(__ANDROID__) +#if defined(BINDER_WITH_KERNEL_IPC) if (ioctl(self()->mProcess->mDriverFD, BINDER_GET_EXTENDED_ERROR, &ee) < 0) { ALOGE("Failed to get extended error: %s", strerror(errno)); return; diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp index 5c72ed3197..c9ca646472 100644 --- a/libs/binder/IServiceManager.cpp +++ b/libs/binder/IServiceManager.cpp @@ -43,7 +43,11 @@ #include <binder/IPermissionController.h> #endif -#ifdef __ANDROID__ +#if !(defined(__ANDROID__) || defined(__FUCHSIA)) +#define BINDER_SERVICEMANAGEMENT_DELEGATION_SUPPORT +#endif + +#if !defined(BINDER_SERVICEMANAGEMENT_DELEGATION_SUPPORT) #include <cutils/properties.h> #else #include "ServiceManagerHost.h" @@ -624,7 +628,7 @@ sp<IBinder> CppBackendShim::getService(const String16& name) const { sp<IBinder> CppBackendShim::checkService(const String16& name) const { Service ret; - if (!mUnifiedServiceManager->checkService(String8(name).c_str(), &ret).isOk()) { + if (!mUnifiedServiceManager->checkService2(String8(name).c_str(), &ret).isOk()) { return nullptr; } return ret.get<Service::Tag::serviceWithMetadata>().service; @@ -902,7 +906,7 @@ std::vector<IServiceManager::ServiceDebugInfo> CppBackendShim::getServiceDebugIn return ret; } -#ifndef __ANDROID__ +#if defined(BINDER_SERVICEMANAGEMENT_DELEGATION_SUPPORT) // CppBackendShim for host. Implements the old libbinder android::IServiceManager API. // The internal implementation of the AIDL interface android::os::IServiceManager calls into // on-device service manager. diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index 0b7cd8154d..777c22a63e 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -299,8 +299,13 @@ status_t Parcel::flattenBinder(const sp<IBinder>& binder) { obj.handle = handle; obj.cookie = 0; } else { +#if __linux__ int policy = local->getMinSchedulerPolicy(); int priority = local->getMinSchedulerPriority(); +#else + int policy = 0; + int priority = 0; +#endif if (policy != 0 || priority != 0) { // override value, since it is set explicitly @@ -616,6 +621,7 @@ status_t Parcel::appendFrom(const Parcel* parcel, size_t offset, size_t len) { } #else LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + (void)kernelFields; return INVALID_OPERATION; #endif // BINDER_WITH_KERNEL_IPC } else { @@ -797,6 +803,7 @@ std::vector<int> Parcel::debugReadAllFileDescriptors() const { setDataPosition(initPosition); #else LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + (void)kernelFields; #endif } else if (const auto* rpcFields = maybeRpcFields(); rpcFields && rpcFields->mFds) { for (const auto& fd : *rpcFields->mFds) { @@ -839,9 +846,10 @@ status_t Parcel::hasBindersInRange(size_t offset, size_t len, bool* result) cons } #else LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + (void)kernelFields; return INVALID_OPERATION; #endif // BINDER_WITH_KERNEL_IPC - } else if (const auto* rpcFields = maybeRpcFields()) { + } else if (maybeRpcFields()) { return INVALID_OPERATION; } return NO_ERROR; @@ -879,6 +887,7 @@ status_t Parcel::hasFileDescriptorsInRange(size_t offset, size_t len, bool* resu } #else LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + (void)kernelFields; return INVALID_OPERATION; #endif // BINDER_WITH_KERNEL_IPC } else if (const auto* rpcFields = maybeRpcFields()) { @@ -971,6 +980,7 @@ status_t Parcel::writeInterfaceToken(const char16_t* str, size_t len) { writeInt32(kHeader); #else // BINDER_WITH_KERNEL_IPC LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + (void)kernelFields; return INVALID_OPERATION; #endif // BINDER_WITH_KERNEL_IPC } @@ -1061,6 +1071,7 @@ bool Parcel::enforceInterface(const char16_t* interface, #else // BINDER_WITH_KERNEL_IPC LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); (void)threadState; + (void)kernelFields; return false; #endif // BINDER_WITH_KERNEL_IPC } @@ -1293,10 +1304,6 @@ status_t Parcel::writeUniqueFileDescriptorVector(const std::vector<unique_fd>& v status_t Parcel::writeUniqueFileDescriptorVector(const std::optional<std::vector<unique_fd>>& val) { return writeData(val); } -status_t Parcel::writeUniqueFileDescriptorVector( - const std::unique_ptr<std::vector<unique_fd>>& val) { - return writeData(val); -} status_t Parcel::writeStrongBinderVector(const std::vector<sp<IBinder>>& val) { return writeData(val); } status_t Parcel::writeStrongBinderVector(const std::optional<std::vector<sp<IBinder>>>& val) { return writeData(val); } @@ -1352,10 +1359,6 @@ status_t Parcel::readUtf8VectorFromUtf16Vector(std::vector<std::string>* val) co status_t Parcel::readUniqueFileDescriptorVector(std::optional<std::vector<unique_fd>>* val) const { return readData(val); } -status_t Parcel::readUniqueFileDescriptorVector( - std::unique_ptr<std::vector<unique_fd>>* val) const { - return readData(val); -} status_t Parcel::readUniqueFileDescriptorVector(std::vector<unique_fd>* val) const { return readData(val); } @@ -2696,6 +2699,7 @@ void Parcel::closeFileDescriptors(size_t newObjectsSize) { #else // BINDER_WITH_KERNEL_IPC (void)newObjectsSize; LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + (void)kernelFields; #endif // BINDER_WITH_KERNEL_IPC } else if (auto* rpcFields = maybeRpcFields()) { rpcFields->mFds.reset(); @@ -3397,14 +3401,6 @@ void Parcel::scanForFds() const { } #ifdef BINDER_WITH_KERNEL_IPC -size_t Parcel::getBlobAshmemSize() const -{ - // This used to return the size of all blobs that were written to ashmem, now we're returning - // the ashmem currently referenced by this Parcel, which should be equivalent. - // TODO(b/202029388): Remove method once ABI can be changed. - return getOpenAshmemSize(); -} - size_t Parcel::getOpenAshmemSize() const { auto* kernelFields = maybeKernelFields(); diff --git a/libs/binder/PersistableBundle.cpp b/libs/binder/PersistableBundle.cpp index abb6612a1c..99f9726c5e 100644 --- a/libs/binder/PersistableBundle.cpp +++ b/libs/binder/PersistableBundle.cpp @@ -119,6 +119,9 @@ status_t PersistableBundle::writeToParcel(Parcel* parcel) const { } RETURN_IF_FAILED(parcel->writeInt32(static_cast<int32_t>(length))); parcel->setDataPosition(end_pos); + // write mHasIntent to be consistent with BaseBundle.writeToBundle. But it would always be + // false since PersistableBundle won't contain an intent. + RETURN_IF_FAILED(parcel->writeBool(false)); return NO_ERROR; } @@ -473,6 +476,8 @@ status_t PersistableBundle::readFromParcelInner(const Parcel* parcel, size_t len } } } + // result intentional ignored since it will always be false; + RETURN_IF_FAILED(parcel->readBool()); return NO_ERROR; } diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp index 0e1e9b4127..0bec37999b 100644 --- a/libs/binder/ProcessState.cpp +++ b/libs/binder/ProcessState.cpp @@ -48,6 +48,10 @@ #define DEFAULT_MAX_BINDER_THREADS 15 #define DEFAULT_ENABLE_ONEWAY_SPAM_DETECTION 1 +#if defined(__ANDROID__) || defined(__Fuchsia__) +#define EXPECT_BINDER_OPEN_SUCCESS +#endif + #ifdef __ANDROID_VNDK__ const char* kDefaultDriver = "/dev/vndbinder"; #else @@ -613,7 +617,7 @@ ProcessState::ProcessState(const char* driver) } } -#ifdef __ANDROID__ +#if defined(EXPECT_BINDER_OPEN_SUCCESS) LOG_ALWAYS_FATAL_IF(!opened.ok(), "Binder driver '%s' could not be opened. Error: %s. Terminating.", driver, error.c_str()); diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp index 16023ffa82..1f3a45a6df 100644 --- a/libs/binder/RpcSession.cpp +++ b/libs/binder/RpcSession.cpp @@ -188,7 +188,9 @@ status_t RpcSession::setupInetClient(const char* addr, unsigned int port) { } status_t RpcSession::setupPreconnectedClient(unique_fd fd, std::function<unique_fd()>&& request) { - return setupClient([&](const std::vector<uint8_t>& sessionId, bool incoming) -> status_t { + return setupClient([&, fd = std::move(fd), + request = std::move(request)](const std::vector<uint8_t>& sessionId, + bool incoming) mutable -> status_t { if (!fd.ok()) { fd = request(); if (!fd.ok()) return BAD_VALUE; @@ -476,8 +478,10 @@ sp<RpcServer> RpcSession::server() { return server; } -status_t RpcSession::setupClient(const std::function<status_t(const std::vector<uint8_t>& sessionId, - bool incoming)>& connectAndInit) { +template <typename Fn, + typename /* = std::enable_if_t<std::is_invocable_r_v< + status_t, Fn, const std::vector<uint8_t>&, bool>> */> +status_t RpcSession::setupClient(Fn&& connectAndInit) { { RpcMutexLockGuard _l(mMutex); LOG_ALWAYS_FATAL_IF(mStartedSetup, "Must only setup session once"); diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp index fe6e1a3318..03d974d186 100644 --- a/libs/binder/RpcState.cpp +++ b/libs/binder/RpcState.cpp @@ -23,6 +23,7 @@ #include <binder/IPCThreadState.h> #include <binder/RpcServer.h> +#include "Constants.h" #include "Debug.h" #include "RpcWireFormat.h" #include "Utils.h" @@ -337,6 +338,8 @@ std::string RpcState::BinderNode::toString() const { } RpcState::CommandData::CommandData(size_t size) : mSize(size) { + if (size == 0) return; + // The maximum size for regular binder is 1MB for all concurrent // transactions. A very small proportion of transactions are even // larger than a page, but we need to avoid allocating too much @@ -348,11 +351,11 @@ RpcState::CommandData::CommandData(size_t size) : mSize(size) { // transaction (in some cases, additional fixed size amounts are added), // though for rough consistency, we should avoid cases where this data type // is used for multiple dynamic allocations for a single transaction. - constexpr size_t kMaxTransactionAllocation = 100 * 1000; - if (size == 0) return; - if (size > kMaxTransactionAllocation) { - ALOGW("Transaction requested too much data allocation %zu", size); + if (size > binder::kRpcTransactionLimitBytes) { + ALOGE("Transaction requested too much data allocation: %zu bytes, failing.", size); return; + } else if (size > binder::kLogTransactionsOverBytes) { + ALOGW("Transaction too large: inefficient and in danger of breaking: %zu bytes.", size); } mData.reset(new (std::nothrow) uint8_t[size]); } diff --git a/libs/binder/RpcTransportTipcAndroid.cpp b/libs/binder/RpcTransportTipcAndroid.cpp index 3819fb6472..14c0bde7c5 100644 --- a/libs/binder/RpcTransportTipcAndroid.cpp +++ b/libs/binder/RpcTransportTipcAndroid.cpp @@ -21,6 +21,7 @@ #include <log/log.h> #include <poll.h> #include <trusty/tipc.h> +#include <type_traits> #include "FdTrigger.h" #include "RpcState.h" @@ -32,6 +33,9 @@ using android::binder::unique_fd; namespace android { +// Corresponds to IPC_MAX_MSG_HANDLES in the Trusty kernel +constexpr size_t kMaxTipcHandles = 8; + // RpcTransport for writing Trusty IPC clients in Android. class RpcTransportTipcAndroid : public RpcTransport { public: @@ -78,12 +82,28 @@ public: FdTrigger* fdTrigger, iovec* iovs, int niovs, const std::optional<SmallFunction<status_t()>>& altPoll, const std::vector<std::variant<unique_fd, borrowed_fd>>* ancillaryFds) override { + bool sentFds = false; auto writeFn = [&](iovec* iovs, size_t niovs) -> ssize_t { - // TODO: send ancillaryFds. For now, we just abort if anyone tries - // to send any. - LOG_ALWAYS_FATAL_IF(ancillaryFds != nullptr && !ancillaryFds->empty(), - "File descriptors are not supported on Trusty yet"); - return TEMP_FAILURE_RETRY(tipc_send(mSocket.fd.get(), iovs, niovs, nullptr, 0)); + trusty_shm shms[kMaxTipcHandles] = {{0}}; + ssize_t shm_count = 0; + + if (!sentFds && ancillaryFds != nullptr && !ancillaryFds->empty()) { + if (ancillaryFds->size() > kMaxTipcHandles) { + ALOGE("Too many file descriptors for TIPC: %zu", ancillaryFds->size()); + errno = EINVAL; + return -1; + } + for (const auto& fdVariant : *ancillaryFds) { + shms[shm_count++] = {std::visit([](const auto& fd) { return fd.get(); }, + fdVariant), + TRUSTY_SEND_SECURE_OR_SHARE}; + } + } + + auto ret = TEMP_FAILURE_RETRY(tipc_send(mSocket.fd.get(), iovs, niovs, + (shm_count == 0) ? nullptr : shms, shm_count)); + sentFds |= ret >= 0; + return ret; }; status_t status = interruptableReadOrWrite(mSocket, fdTrigger, iovs, niovs, writeFn, diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING index 9e5e79f89b..4332f8ae79 100644 --- a/libs/binder/TEST_MAPPING +++ b/libs/binder/TEST_MAPPING @@ -37,6 +37,9 @@ "name": "binderStabilityTest" }, { + "name": "binderStabilityIntegrationTest" + }, + { "name": "binderRpcWireProtocolTest" }, { diff --git a/libs/binder/aidl/android/content/pm/OWNERS b/libs/binder/aidl/android/content/pm/OWNERS index 31005184bf..2617a16f69 100644 --- a/libs/binder/aidl/android/content/pm/OWNERS +++ b/libs/binder/aidl/android/content/pm/OWNERS @@ -1,5 +1,4 @@ +michaelwr@google.com narayan@google.com patb@google.com -svetoslavganov@google.com -toddke@google.com -patb@google.com
\ No newline at end of file +schfan@google.com diff --git a/libs/binder/aidl/android/os/IServiceManager.aidl b/libs/binder/aidl/android/os/IServiceManager.aidl index 69edef86aa..6539238ce7 100644 --- a/libs/binder/aidl/android/os/IServiceManager.aidl +++ b/libs/binder/aidl/android/os/IServiceManager.aidl @@ -83,11 +83,20 @@ interface IServiceManager { /** * Retrieve an existing service called @a name from the service + * manager. Non-blocking. Returns null if the service does not exist. + * + * @deprecated TODO(b/355394904): Use checkService2 instead. This does not + * return metadata that is included in ServiceWithMetadata + */ + @UnsupportedAppUsage + @nullable IBinder checkService(@utf8InCpp String name); + + /** + * Retrieve an existing service called @a name from the service * manager. Non-blocking. Returns null if the service does not * exist. */ - @UnsupportedAppUsage - Service checkService(@utf8InCpp String name); + Service checkService2(@utf8InCpp String name); /** * Place a new @a service called @a name into the service diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h index 7518044ce6..935bd8dbc6 100644 --- a/libs/binder/include/binder/BpBinder.h +++ b/libs/binder/include/binder/BpBinder.h @@ -104,6 +104,7 @@ public: // Stop the current recording. LIBBINDER_EXPORTED status_t stopRecordingBinder(); + // Note: This class is not thread safe so protect uses of it when necessary class ObjectManager { public: ObjectManager(); @@ -116,8 +117,6 @@ public: sp<IBinder> lookupOrCreateWeak(const void* objectID, IBinder::object_make_func make, const void* makeArgs); - void kill(); - private: ObjectManager(const ObjectManager&); ObjectManager& operator=(const ObjectManager&); @@ -224,7 +223,7 @@ private: volatile int32_t mObitsSent; Vector<Obituary>* mObituaries; std::unique_ptr<FrozenStateChange> mFrozen; - ObjectManager mObjects; + ObjectManager mObjectMgr; mutable String16 mDescriptorCache; int32_t mTrackedUid; diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h index bb45ad2ad5..993ad82b96 100644 --- a/libs/binder/include/binder/IInterface.h +++ b/libs/binder/include/binder/IInterface.h @@ -263,7 +263,6 @@ constexpr const char* const kManualInterfaces[] = { "android.utils.IMemory", "android.utils.IMemoryHeap", "com.android.car.procfsinspector.IProcfsInspector", - "com.android.internal.app.IAppOpsCallback", "com.android.internal.app.IAppOpsService", "com.android.internal.app.IBatteryStats", "com.android.internal.os.IResultReceiver", diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h index 69a73d415e..6c4c6fe573 100644 --- a/libs/binder/include/binder/Parcel.h +++ b/libs/binder/include/binder/Parcel.h @@ -383,9 +383,6 @@ public: LIBBINDER_EXPORTED status_t writeUniqueFileDescriptorVector(const std::optional<std::vector<binder::unique_fd>>& val); LIBBINDER_EXPORTED status_t - writeUniqueFileDescriptorVector(const std::unique_ptr<std::vector<binder::unique_fd>>& val) - __attribute__((deprecated("use std::optional version instead"))); - LIBBINDER_EXPORTED status_t writeUniqueFileDescriptorVector(const std::vector<binder::unique_fd>& val); // WARNING: deprecated and incompatible with AIDL. You should use Parcelable @@ -630,9 +627,6 @@ public: LIBBINDER_EXPORTED status_t readUniqueFileDescriptorVector(std::optional<std::vector<binder::unique_fd>>* val) const; LIBBINDER_EXPORTED status_t - readUniqueFileDescriptorVector(std::unique_ptr<std::vector<binder::unique_fd>>* val) const - __attribute__((deprecated("use std::optional version instead"))); - LIBBINDER_EXPORTED status_t readUniqueFileDescriptorVector(std::vector<binder::unique_fd>* val) const; // WARNING: deprecated and incompatible with AIDL. You should use Parcelable @@ -1494,14 +1488,15 @@ public: * Note: for historical reasons, this does not include ashmem memory which * is referenced by this Parcel, but which this parcel doesn't own (e.g. * writeFileDescriptor is called without 'takeOwnership' true). + * + * WARNING: you should not use this, but rather, unparcel, and inspect + * each FD independently. This counts ashmem size, but there may be + * other resources used for non-ashmem FDs, such as other types of + * shared memory, files, etc.. */ LIBBINDER_EXPORTED size_t getOpenAshmemSize() const; private: - // TODO(b/202029388): Remove 'getBlobAshmemSize' once no prebuilts reference - // this - LIBBINDER_EXPORTED size_t getBlobAshmemSize() const; - // Needed so that we can save object metadata to the disk friend class android::binder::debug::RecordedTransaction; }; diff --git a/libs/binder/include/binder/RpcSession.h b/libs/binder/include/binder/RpcSession.h index af37bf29a4..c9f92da009 100644 --- a/libs/binder/include/binder/RpcSession.h +++ b/libs/binder/include/binder/RpcSession.h @@ -25,6 +25,7 @@ #include <map> #include <optional> +#include <type_traits> #include <vector> namespace android { @@ -291,9 +292,14 @@ private: // join on thread passed to preJoinThreadOwnership static void join(sp<RpcSession>&& session, PreJoinSetupResult&& result); - [[nodiscard]] status_t setupClient( - const std::function<status_t(const std::vector<uint8_t>& sessionId, bool incoming)>& - connectAndInit); + // This is a workaround to support move-only functors. + // TODO: use std::move_only_function when it becomes available. + template <typename Fn, + // Fn must be a callable type taking (const std::vector<uint8_t>&, bool) and returning + // status_t + typename = std::enable_if_t< + std::is_invocable_r_v<status_t, Fn, const std::vector<uint8_t>&, bool>>> + [[nodiscard]] status_t setupClient(Fn&& connectAndInit); [[nodiscard]] status_t setupSocketClient(const RpcSocketAddress& address); [[nodiscard]] status_t setupOneSocketConnection(const RpcSocketAddress& address, const std::vector<uint8_t>& sessionId, diff --git a/libs/binder/include/binder/RpcThreads.h b/libs/binder/include/binder/RpcThreads.h index 99fa6b8b29..51b9716bb1 100644 --- a/libs/binder/include/binder/RpcThreads.h +++ b/libs/binder/include/binder/RpcThreads.h @@ -20,6 +20,7 @@ #include <condition_variable> #include <functional> #include <memory> +#include <mutex> #include <thread> #include <binder/Common.h> diff --git a/libs/binder/include/binder/SafeInterface.h b/libs/binder/include/binder/SafeInterface.h index bcbd14f9d4..e848385418 100644 --- a/libs/binder/include/binder/SafeInterface.h +++ b/libs/binder/include/binder/SafeInterface.h @@ -79,7 +79,7 @@ public: template <typename T> typename std::enable_if<std::is_base_of<Flattenable<T>, T>::value, status_t>::type read( const Parcel& parcel, sp<T>* t) const { - *t = new T{}; + *t = sp<T>::make(); return callParcel("read(sp<Flattenable>)", [&]() { return parcel.read(*(t->get())); }); } template <typename T> diff --git a/libs/binder/include/binder/Stability.h b/libs/binder/include/binder/Stability.h index cafb8aa04b..bfe0a5af9c 100644 --- a/libs/binder/include/binder/Stability.h +++ b/libs/binder/include/binder/Stability.h @@ -20,6 +20,8 @@ #include <binder/IBinder.h> #include <string> +class BinderStabilityIntegrationTest_ExpectedStabilityForItsPartition_Test; + namespace android { class BpBinder; @@ -127,6 +129,8 @@ private: // through Parcel) friend ::android::ProcessState; + friend ::BinderStabilityIntegrationTest_ExpectedStabilityForItsPartition_Test; + static void tryMarkCompilationUnit(IBinder* binder); // Currently, we use int16_t for Level so that it can fit in BBinder. @@ -156,11 +160,11 @@ private: uint32_t flags); // get stability information as encoded on the wire - static int16_t getRepr(IBinder* binder); + LIBBINDER_EXPORTED static int16_t getRepr(IBinder* binder); // whether a transaction on binder is allowed, if the transaction // is done from a context with a specific stability level - static bool check(int16_t provided, Level required); + LIBBINDER_EXPORTED static bool check(int16_t provided, Level required); static bool isDeclaredLevel(int32_t level); static std::string levelString(int32_t level); diff --git a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp index 48c0ea636b..1949cbb039 100644 --- a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp +++ b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp @@ -134,9 +134,14 @@ AIBinder* ARpcSession_setupInet(ARpcSession* session, const char* address, unsig // // param will be passed to requestFd. Callers can use param to pass contexts to // the requestFd function. +// +// paramDeleteFd will be called when requestFd and param are no longer in use. +// Callers can pass a function deleting param if necessary. Callers can set +// paramDeleteFd to null if callers doesn't need to clean up param. AIBinder* ARpcSession_setupPreconnectedClient(ARpcSession* session, int (*requestFd)(void* param), - void* param); + void* param, + void (*paramDeleteFd)(void* param)); // Sets the file descriptor transport mode for this session. void ARpcSession_setFileDescriptorTransportMode(ARpcSession* session, diff --git a/libs/binder/libbinder_rpc_unstable.cpp b/libs/binder/libbinder_rpc_unstable.cpp index 64b1be25d2..8177fb07db 100644 --- a/libs/binder/libbinder_rpc_unstable.cpp +++ b/libs/binder/libbinder_rpc_unstable.cpp @@ -248,9 +248,18 @@ AIBinder* ARpcSession_setupInet(ARpcSession* handle, const char* address, unsign #endif // __TRUSTY__ AIBinder* ARpcSession_setupPreconnectedClient(ARpcSession* handle, int (*requestFd)(void* param), - void* param) { + void* param, void (*paramDeleteFd)(void* param)) { auto session = handleToStrongPointer<RpcSession>(handle); - auto request = [=] { return unique_fd{requestFd(param)}; }; + auto deleter = [=](void* param) { + if (paramDeleteFd) { + paramDeleteFd(param); + } + }; + // TODO: use unique_ptr once setupPreconnectedClient uses std::move_only_function. + std::shared_ptr<void> sharedParam(param, deleter); + auto request = [=, sharedParam = std::move(sharedParam)] { + return unique_fd{requestFd(sharedParam.get())}; + }; if (status_t status = session->setupPreconnectedClient(unique_fd{}, request); status != OK) { ALOGE("Failed to set up preconnected client. error: %s", statusToString(status).c_str()); return nullptr; diff --git a/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h b/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h index 89f21dda21..783e11f0e9 100644 --- a/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h +++ b/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h @@ -62,6 +62,8 @@ __attribute__((weak, warn_unused_result)) const char* AIBinder_getCallingSid() _ * This must be called before the object is sent to another process. * Aborts on invalid values. Not thread safe. * + * This overrides the setting in ABinderProcess_disableBackgroundScheduling. + * * \param binder local server binder to set the policy for * \param policy scheduler policy as defined in linux UAPI * \param priority priority. [-20..19] for SCHED_NORMAL, [1..99] for RT diff --git a/libs/binder/ndk/include_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h index f5df8d5c2f..2c2e2c8856 100644 --- a/libs/binder/ndk/include_platform/android/binder_manager.h +++ b/libs/binder/ndk/include_platform/android/binder_manager.h @@ -30,10 +30,14 @@ enum AServiceManager_AddServiceFlag : uint32_t { * not be added with this flag for privacy concerns. */ ADD_SERVICE_ALLOW_ISOLATED = 1 << 0, + /** + * Allows services to dump sections according to priorities and format + */ ADD_SERVICE_DUMP_FLAG_PRIORITY_CRITICAL = 1 << 1, ADD_SERVICE_DUMP_FLAG_PRIORITY_HIGH = 1 << 2, ADD_SERVICE_DUMP_FLAG_PRIORITY_NORMAL = 1 << 3, ADD_SERVICE_DUMP_FLAG_PRIORITY_DEFAULT = 1 << 4, + ADD_SERVICE_DUMP_FLAG_PROTO = 1 << 5, // All other bits are reserved for internal usage }; diff --git a/libs/binder/ndk/include_platform/android/binder_process.h b/libs/binder/ndk/include_platform/android/binder_process.h index 6aff994a15..2432099d6e 100644 --- a/libs/binder/ndk/include_platform/android/binder_process.h +++ b/libs/binder/ndk/include_platform/android/binder_process.h @@ -75,6 +75,19 @@ bool ABinderProcess_isThreadPoolStarted(void); void ABinderProcess_joinThreadPool(void); /** + * Disables (or enables) background scheduling. + * + * By default, binder threads execute at a lower priority. However, this can cause + * priority inversion, so it is recommended to be disabled in high priority + * or in system processes. + * + * See also AIBinder_setMinSchedulerPolicy, which overrides this setting. + * + * \param disable whether to disable background scheduling + */ +void ABinderProcess_disableBackgroundScheduling(bool disable); + +/** * This gives you an fd to wait on. Whenever data is available on the fd, * ABinderProcess_handlePolledCommands can be called to handle binder queries. * This is expected to be used in a single threaded process which waits on diff --git a/libs/binder/ndk/include_platform/android/binder_stability.h b/libs/binder/ndk/include_platform/android/binder_stability.h index 089c775eca..80502056ee 100644 --- a/libs/binder/ndk/include_platform/android/binder_stability.h +++ b/libs/binder/ndk/include_platform/android/binder_stability.h @@ -27,6 +27,10 @@ constexpr binder_flags_t FLAG_PRIVATE_VENDOR = 0x10000000; #if defined(__ANDROID_VENDOR__) +#if defined(__ANDROID_PRODUCT__) +#error "build bug: product is not part of the vendor half of the Treble system/vendor split" +#endif + /** * Private addition to binder_flag_t. */ diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt index a63716522e..d4eb8c7579 100644 --- a/libs/binder/ndk/libbinder_ndk.map.txt +++ b/libs/binder/ndk/libbinder_ndk.map.txt @@ -229,6 +229,7 @@ LIBBINDER_NDK_PLATFORM { AIBinder_fromPlatformBinder*; AIBinder_toPlatformBinder*; AParcel_viewPlatformParcel*; + ABinderProcess_disableBackgroundScheduling; }; local: *; diff --git a/libs/binder/ndk/process.cpp b/libs/binder/ndk/process.cpp index 0072ac3d3e..bcdb9594fa 100644 --- a/libs/binder/ndk/process.cpp +++ b/libs/binder/ndk/process.cpp @@ -36,6 +36,10 @@ void ABinderProcess_joinThreadPool(void) { IPCThreadState::self()->joinThreadPool(); } +void ABinderProcess_disableBackgroundScheduling(bool disable) { + IPCThreadState::disableBackgroundScheduling(disable); +} + binder_status_t ABinderProcess_setupPolling(int* fd) { return IPCThreadState::self()->setupPolling(fd); } diff --git a/libs/binder/ndk/service_manager.cpp b/libs/binder/ndk/service_manager.cpp index d6ac4acc29..14bc5d2b75 100644 --- a/libs/binder/ndk/service_manager.cpp +++ b/libs/binder/ndk/service_manager.cpp @@ -63,6 +63,9 @@ binder_exception_t AServiceManager_addServiceWithFlags(AIBinder* binder, const c if (flags & AServiceManager_AddServiceFlag::ADD_SERVICE_DUMP_FLAG_PRIORITY_DEFAULT) { dumpFlags |= IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT; } + if (flags & AServiceManager_AddServiceFlag::ADD_SERVICE_DUMP_FLAG_PROTO) { + dumpFlags |= IServiceManager::DUMP_FLAG_PROTO; + } if (dumpFlags == 0) { dumpFlags = IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT; } diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp index 8404a48c26..adef9ea64b 100644 --- a/libs/binder/rust/Android.bp +++ b/libs/binder/rust/Android.bp @@ -16,6 +16,7 @@ rust_library { "libdowncast_rs", "liblibc", "liblog_rust", + "libzerocopy", ], host_supported: true, vendor_available: true, @@ -205,6 +206,7 @@ rust_test { "libdowncast_rs", "liblibc", "liblog_rust", + "libzerocopy", ], } diff --git a/libs/binder/rust/rpcbinder/Android.bp b/libs/binder/rust/rpcbinder/Android.bp index 4036551ef3..46651ceb48 100644 --- a/libs/binder/rust/rpcbinder/Android.bp +++ b/libs/binder/rust/rpcbinder/Android.bp @@ -26,6 +26,7 @@ rust_library { ], visibility: [ "//device/google/cuttlefish/shared/minidroid/sample", + "//hardware/interfaces/security/see:__subpackages__", "//packages/modules/Virtualization:__subpackages__", ], apex_available: [ diff --git a/libs/binder/rust/rpcbinder/src/lib.rs b/libs/binder/rust/rpcbinder/src/lib.rs index 7e5c9ddc35..774bb21efe 100644 --- a/libs/binder/rust/rpcbinder/src/lib.rs +++ b/libs/binder/rust/rpcbinder/src/lib.rs @@ -20,6 +20,8 @@ mod server; mod session; pub use server::RpcServer; +#[cfg(target_os = "trusty")] +pub use server::RpcServerConnection; #[cfg(not(target_os = "trusty"))] pub use server::RpcServerRef; pub use session::{FileDescriptorTransportMode, RpcSession, RpcSessionRef}; diff --git a/libs/binder/rust/rpcbinder/src/session.rs b/libs/binder/rust/rpcbinder/src/session.rs index 09688a21a7..411b9de849 100644 --- a/libs/binder/rust/rpcbinder/src/session.rs +++ b/libs/binder/rust/rpcbinder/src/session.rs @@ -195,11 +195,13 @@ impl RpcSessionRef { /// take ownership of) file descriptors already connected to it. pub fn setup_preconnected_client<T: FromIBinder + ?Sized>( &self, - mut request_fd: impl FnMut() -> Option<RawFd>, + request_fd: impl FnMut() -> Option<RawFd>, ) -> Result<Strong<T>, StatusCode> { - // Double reference the factory because trait objects aren't FFI safe. - let mut request_fd_ref: RequestFd = &mut request_fd; - let param = &mut request_fd_ref as *mut RequestFd as *mut c_void; + // Trait objects aren't FFI safe, so *mut c_void can't be converted back to + // *mut dyn FnMut() -> Option<RawFd>>. Double box the factory to make it possible to get + // the factory from *mut c_void (to *mut Box<dyn<...>>) in the callbacks. + let request_fd_box: Box<dyn FnMut() -> Option<RawFd>> = Box::new(request_fd); + let param = Box::into_raw(Box::new(request_fd_box)) as *mut c_void; // SAFETY: AIBinder returned by RpcPreconnectedClient has correct reference count, and the // ownership can be safely taken by new_spibinder. RpcPreconnectedClient does not take ownership @@ -209,6 +211,7 @@ impl RpcSessionRef { self.as_ptr(), Some(request_fd_wrapper), param, + Some(param_delete_fd_wrapper), )) }; Self::get_interface(service) @@ -225,13 +228,18 @@ impl RpcSessionRef { } } -type RequestFd<'a> = &'a mut dyn FnMut() -> Option<RawFd>; - unsafe extern "C" fn request_fd_wrapper(param: *mut c_void) -> c_int { - let request_fd_ptr = param as *mut RequestFd; + let request_fd_ptr = param as *mut Box<dyn FnMut() -> Option<RawFd>>; // SAFETY: This is only ever called by RpcPreconnectedClient, within the lifetime of the // BinderFdFactory reference, with param being a properly aligned non-null pointer to an // initialized instance. let request_fd = unsafe { request_fd_ptr.as_mut().unwrap() }; request_fd().unwrap_or(-1) } + +unsafe extern "C" fn param_delete_fd_wrapper(param: *mut c_void) { + // SAFETY: This is only ever called by RpcPreconnectedClient, with param being the + // pointer returned from Box::into_raw. + let request_fd_box = unsafe { Box::from_raw(param as *mut Box<dyn FnMut() -> Option<RawFd>>) }; + drop(request_fd_box); +} diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs index 8c0501ba2f..6a8a69843a 100644 --- a/libs/binder/rust/src/binder.rs +++ b/libs/binder/rust/src/binder.rs @@ -1201,5 +1201,45 @@ macro_rules! declare_binder_enum { Ok(v.map(|v| v.into_iter().map(Self).collect())) } } + + impl std::ops::BitOr for $enum { + type Output = Self; + fn bitor(self, rhs: Self) -> Self { + Self(self.0 | rhs.0) + } + } + + impl std::ops::BitOrAssign for $enum { + fn bitor_assign(&mut self, rhs: Self) { + self.0 = self.0 | rhs.0; + } + } + + impl std::ops::BitAnd for $enum { + type Output = Self; + fn bitand(self, rhs: Self) -> Self { + Self(self.0 & rhs.0) + } + } + + impl std::ops::BitAndAssign for $enum { + fn bitand_assign(&mut self, rhs: Self) { + self.0 = self.0 & rhs.0; + } + } + + impl std::ops::BitXor for $enum { + type Output = Self; + fn bitxor(self, rhs: Self) -> Self { + Self(self.0 ^ rhs.0) + } + } + + impl std::ops::BitXorAssign for $enum { + fn bitxor_assign(&mut self, rhs: Self) { + self.0 = self.0 ^ rhs.0; + } + } + }; } diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs index e08a76312d..0026f213f2 100644 --- a/libs/binder/rust/src/lib.rs +++ b/libs/binder/rust/src/lib.rs @@ -99,6 +99,8 @@ mod binder_async; mod error; mod native; mod parcel; +#[cfg(not(trusty))] +mod persistable_bundle; mod proxy; #[cfg(not(any(trusty, android_ndk)))] mod service; @@ -113,6 +115,8 @@ pub use crate::binder_async::{BinderAsyncPool, BoxFuture}; pub use binder::{BinderFeatures, FromIBinder, IBinder, Interface, Strong, Weak}; pub use error::{ExceptionCode, IntoBinderResult, Status, StatusCode}; pub use parcel::{ParcelFileDescriptor, Parcelable, ParcelableHolder}; +#[cfg(not(trusty))] +pub use persistable_bundle::{PersistableBundle, ValueType}; pub use proxy::{DeathRecipient, SpIBinder, WpIBinder}; #[cfg(not(any(trusty, android_ndk)))] pub use service::{ diff --git a/libs/binder/rust/src/parcel.rs b/libs/binder/rust/src/parcel.rs index 485b0bdb0d..2d40ced2fd 100644 --- a/libs/binder/rust/src/parcel.rs +++ b/libs/binder/rust/src/parcel.rs @@ -184,7 +184,7 @@ unsafe impl AsNative<sys::AParcel> for Parcel { /// Safety: The `BorrowedParcel` constructors guarantee that a `BorrowedParcel` /// object will always contain a valid pointer to an `AParcel`. -unsafe impl<'a> AsNative<sys::AParcel> for BorrowedParcel<'a> { +unsafe impl AsNative<sys::AParcel> for BorrowedParcel<'_> { fn as_native(&self) -> *const sys::AParcel { self.ptr.as_ptr() } @@ -195,7 +195,7 @@ unsafe impl<'a> AsNative<sys::AParcel> for BorrowedParcel<'a> { } // Data serialization methods -impl<'a> BorrowedParcel<'a> { +impl BorrowedParcel<'_> { /// Data written to parcelable is zero'd before being deleted or reallocated. #[cfg(not(android_ndk))] pub fn mark_sensitive(&mut self) { @@ -334,7 +334,7 @@ impl<'a> BorrowedParcel<'a> { /// A segment of a writable parcel, used for [`BorrowedParcel::sized_write`]. pub struct WritableSubParcel<'a>(BorrowedParcel<'a>); -impl<'a> WritableSubParcel<'a> { +impl WritableSubParcel<'_> { /// Write a type that implements [`Serialize`] to the sub-parcel. pub fn write<S: Serialize + ?Sized>(&mut self, parcelable: &S) -> Result<()> { parcelable.serialize(&mut self.0) @@ -440,7 +440,7 @@ impl Parcel { } // Data deserialization methods -impl<'a> BorrowedParcel<'a> { +impl BorrowedParcel<'_> { /// Attempt to read a type that implements [`Deserialize`] from this parcel. pub fn read<D: Deserialize>(&self) -> Result<D> { D::deserialize(self) @@ -565,7 +565,7 @@ pub struct ReadableSubParcel<'a> { end_position: i32, } -impl<'a> ReadableSubParcel<'a> { +impl ReadableSubParcel<'_> { /// Read a type that implements [`Deserialize`] from the sub-parcel. pub fn read<D: Deserialize>(&self) -> Result<D> { D::deserialize(&self.parcel) @@ -649,7 +649,7 @@ impl Parcel { } // Internal APIs -impl<'a> BorrowedParcel<'a> { +impl BorrowedParcel<'_> { pub(crate) fn write_binder(&mut self, binder: Option<&SpIBinder>) -> Result<()> { // Safety: `BorrowedParcel` always contains a valid pointer to an // `AParcel`. `AsNative` for `Option<SpIBinder`> will either return @@ -702,7 +702,7 @@ impl fmt::Debug for Parcel { } } -impl<'a> fmt::Debug for BorrowedParcel<'a> { +impl fmt::Debug for BorrowedParcel<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("BorrowedParcel").finish() } diff --git a/libs/binder/rust/src/persistable_bundle.rs b/libs/binder/rust/src/persistable_bundle.rs new file mode 100644 index 0000000000..8639c0d08c --- /dev/null +++ b/libs/binder/rust/src/persistable_bundle.rs @@ -0,0 +1,1076 @@ +/* + * Copyright (C) 2025 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 crate::{ + binder::AsNative, + error::{status_result, StatusCode}, + impl_deserialize_for_unstructured_parcelable, impl_serialize_for_unstructured_parcelable, + parcel::{BorrowedParcel, UnstructuredParcelable}, +}; +use binder_ndk_sys::{ + APersistableBundle, APersistableBundle_delete, APersistableBundle_dup, + APersistableBundle_erase, APersistableBundle_getBoolean, APersistableBundle_getBooleanKeys, + APersistableBundle_getBooleanVector, APersistableBundle_getBooleanVectorKeys, + APersistableBundle_getDouble, APersistableBundle_getDoubleKeys, + APersistableBundle_getDoubleVector, APersistableBundle_getDoubleVectorKeys, + APersistableBundle_getInt, APersistableBundle_getIntKeys, APersistableBundle_getIntVector, + APersistableBundle_getIntVectorKeys, APersistableBundle_getLong, + APersistableBundle_getLongKeys, APersistableBundle_getLongVector, + APersistableBundle_getLongVectorKeys, APersistableBundle_getPersistableBundle, + APersistableBundle_getPersistableBundleKeys, APersistableBundle_getString, + APersistableBundle_getStringKeys, APersistableBundle_getStringVector, + APersistableBundle_getStringVectorKeys, APersistableBundle_isEqual, APersistableBundle_new, + APersistableBundle_putBoolean, APersistableBundle_putBooleanVector, + APersistableBundle_putDouble, APersistableBundle_putDoubleVector, APersistableBundle_putInt, + APersistableBundle_putIntVector, APersistableBundle_putLong, APersistableBundle_putLongVector, + APersistableBundle_putPersistableBundle, APersistableBundle_putString, + APersistableBundle_putStringVector, APersistableBundle_readFromParcel, APersistableBundle_size, + APersistableBundle_writeToParcel, APERSISTABLEBUNDLE_ALLOCATOR_FAILED, + APERSISTABLEBUNDLE_KEY_NOT_FOUND, +}; +use std::ffi::{c_char, c_void, CStr, CString, NulError}; +use std::ptr::{null_mut, slice_from_raw_parts_mut, NonNull}; +use zerocopy::FromZeros; + +/// A mapping from string keys to values of various types. +#[derive(Debug)] +pub struct PersistableBundle(NonNull<APersistableBundle>); + +impl PersistableBundle { + /// Creates a new `PersistableBundle`. + pub fn new() -> Self { + // SAFETY: APersistableBundle_new doesn't actually have any safety requirements. + let bundle = unsafe { APersistableBundle_new() }; + Self(NonNull::new(bundle).expect("Allocated APersistableBundle was null")) + } + + /// Returns the number of mappings in the bundle. + pub fn size(&self) -> usize { + // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the + // lifetime of the `PersistableBundle`. + unsafe { APersistableBundle_size(self.0.as_ptr()) } + .try_into() + .expect("APersistableBundle_size returned a negative size") + } + + /// Removes any entry with the given key. + /// + /// Returns an error if the given key contains a NUL character, otherwise returns whether there + /// was any entry to remove. + pub fn remove(&mut self, key: &str) -> Result<bool, NulError> { + let key = CString::new(key)?; + // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the + // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed + // to be valid for the duration of this call. + Ok(unsafe { APersistableBundle_erase(self.0.as_ptr(), key.as_ptr()) != 0 }) + } + + /// Inserts a key-value pair into the bundle. + /// + /// If the key is already present then its value will be overwritten by the given value. + /// + /// Returns an error if the key contains a NUL character. + pub fn insert_bool(&mut self, key: &str, value: bool) -> Result<(), NulError> { + let key = CString::new(key)?; + // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the + // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed + // to be valid for the duration of this call. + unsafe { + APersistableBundle_putBoolean(self.0.as_ptr(), key.as_ptr(), value); + } + Ok(()) + } + + /// Inserts a key-value pair into the bundle. + /// + /// If the key is already present then its value will be overwritten by the given value. + /// + /// Returns an error if the key contains a NUL character. + pub fn insert_int(&mut self, key: &str, value: i32) -> Result<(), NulError> { + let key = CString::new(key)?; + // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the + // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed + // to be valid for the duration of this call. + unsafe { + APersistableBundle_putInt(self.0.as_ptr(), key.as_ptr(), value); + } + Ok(()) + } + + /// Inserts a key-value pair into the bundle. + /// + /// If the key is already present then its value will be overwritten by the given value. + /// + /// Returns an error if the key contains a NUL character. + pub fn insert_long(&mut self, key: &str, value: i64) -> Result<(), NulError> { + let key = CString::new(key)?; + // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the + // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed + // to be valid for the duration of this call. + unsafe { + APersistableBundle_putLong(self.0.as_ptr(), key.as_ptr(), value); + } + Ok(()) + } + + /// Inserts a key-value pair into the bundle. + /// + /// If the key is already present then its value will be overwritten by the given value. + /// + /// Returns an error if the key contains a NUL character. + pub fn insert_double(&mut self, key: &str, value: f64) -> Result<(), NulError> { + let key = CString::new(key)?; + // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the + // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed + // to be valid for the duration of this call. + unsafe { + APersistableBundle_putDouble(self.0.as_ptr(), key.as_ptr(), value); + } + Ok(()) + } + + /// Inserts a key-value pair into the bundle. + /// + /// If the key is already present then its value will be overwritten by the given value. + /// + /// Returns an error if the key or value contains a NUL character. + pub fn insert_string(&mut self, key: &str, value: &str) -> Result<(), NulError> { + let key = CString::new(key)?; + let value = CString::new(value)?; + // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the + // lifetime of the `PersistableBundle`. The pointer returned by `CStr::as_ptr` is guaranteed + // to be valid for the duration of this call. + unsafe { + APersistableBundle_putString(self.0.as_ptr(), key.as_ptr(), value.as_ptr()); + } + Ok(()) + } + + /// Inserts a key-value pair into the bundle. + /// + /// If the key is already present then its value will be overwritten by the given value. + /// + /// Returns an error if the key contains a NUL character. + pub fn insert_bool_vec(&mut self, key: &str, value: &[bool]) -> Result<(), NulError> { + let key = CString::new(key)?; + // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the + // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed + // to be valid for the duration of this call, and likewise the pointer returned by + // `value.as_ptr()` is guaranteed to be valid for at least `value.len()` values for the + // duration of the call. + unsafe { + APersistableBundle_putBooleanVector( + self.0.as_ptr(), + key.as_ptr(), + value.as_ptr(), + value.len().try_into().unwrap(), + ); + } + Ok(()) + } + + /// Inserts a key-value pair into the bundle. + /// + /// If the key is already present then its value will be overwritten by the given value. + /// + /// Returns an error if the key contains a NUL character. + pub fn insert_int_vec(&mut self, key: &str, value: &[i32]) -> Result<(), NulError> { + let key = CString::new(key)?; + // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the + // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed + // to be valid for the duration of this call, and likewise the pointer returned by + // `value.as_ptr()` is guaranteed to be valid for at least `value.len()` values for the + // duration of the call. + unsafe { + APersistableBundle_putIntVector( + self.0.as_ptr(), + key.as_ptr(), + value.as_ptr(), + value.len().try_into().unwrap(), + ); + } + Ok(()) + } + + /// Inserts a key-value pair into the bundle. + /// + /// If the key is already present then its value will be overwritten by the given value. + /// + /// Returns an error if the key contains a NUL character. + pub fn insert_long_vec(&mut self, key: &str, value: &[i64]) -> Result<(), NulError> { + let key = CString::new(key)?; + // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the + // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed + // to be valid for the duration of this call, and likewise the pointer returned by + // `value.as_ptr()` is guaranteed to be valid for at least `value.len()` values for the + // duration of the call. + unsafe { + APersistableBundle_putLongVector( + self.0.as_ptr(), + key.as_ptr(), + value.as_ptr(), + value.len().try_into().unwrap(), + ); + } + Ok(()) + } + + /// Inserts a key-value pair into the bundle. + /// + /// If the key is already present then its value will be overwritten by the given value. + /// + /// Returns an error if the key contains a NUL character. + pub fn insert_double_vec(&mut self, key: &str, value: &[f64]) -> Result<(), NulError> { + let key = CString::new(key)?; + // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the + // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed + // to be valid for the duration of this call, and likewise the pointer returned by + // `value.as_ptr()` is guaranteed to be valid for at least `value.len()` values for the + // duration of the call. + unsafe { + APersistableBundle_putDoubleVector( + self.0.as_ptr(), + key.as_ptr(), + value.as_ptr(), + value.len().try_into().unwrap(), + ); + } + Ok(()) + } + + /// Inserts a key-value pair into the bundle. + /// + /// If the key is already present then its value will be overwritten by the given value. + /// + /// Returns an error if the key contains a NUL character. + pub fn insert_string_vec<'a, T: ToString + 'a>( + &mut self, + key: &str, + value: impl IntoIterator<Item = &'a T>, + ) -> Result<(), NulError> { + let key = CString::new(key)?; + // We need to collect the new `CString`s into something first so that they live long enough + // for their pointers to be valid for the `APersistableBundle_putStringVector` call below. + let c_strings = value + .into_iter() + .map(|s| CString::new(s.to_string())) + .collect::<Result<Vec<_>, NulError>>()?; + let char_pointers = c_strings.iter().map(|s| s.as_ptr()).collect::<Vec<_>>(); + // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the + // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed + // to be valid for the duration of this call, and likewise the pointer returned by + // `value.as_ptr()` is guaranteed to be valid for at least `value.len()` values for the + // duration of the call. + unsafe { + APersistableBundle_putStringVector( + self.0.as_ptr(), + key.as_ptr(), + char_pointers.as_ptr(), + char_pointers.len().try_into().unwrap(), + ); + } + Ok(()) + } + + /// Inserts a key-value pair into the bundle. + /// + /// If the key is already present then its value will be overwritten by the given value. + /// + /// Returns an error if the key contains a NUL character. + pub fn insert_persistable_bundle( + &mut self, + key: &str, + value: &PersistableBundle, + ) -> Result<(), NulError> { + let key = CString::new(key)?; + // SAFETY: The wrapped `APersistableBundle` pointers are guaranteed to be valid for the + // lifetime of the `PersistableBundle`s. The pointer returned by `CStr::as_ptr` is + // guaranteed to be valid for the duration of this call, and + // `APersistableBundle_putPersistableBundle` does a deep copy so that is all that is + // required. + unsafe { + APersistableBundle_putPersistableBundle( + self.0.as_ptr(), + key.as_ptr(), + value.0.as_ptr(), + ); + } + Ok(()) + } + + /// Gets the boolean value associated with the given key. + /// + /// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist + /// in the bundle. + pub fn get_bool(&self, key: &str) -> Result<Option<bool>, NulError> { + let key = CString::new(key)?; + let mut value = false; + // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the + // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed + // to be valid for the duration of this call. The value pointer must be valid because it + // comes from a reference. + if unsafe { APersistableBundle_getBoolean(self.0.as_ptr(), key.as_ptr(), &mut value) } { + Ok(Some(value)) + } else { + Ok(None) + } + } + + /// Gets the i32 value associated with the given key. + /// + /// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist + /// in the bundle. + pub fn get_int(&self, key: &str) -> Result<Option<i32>, NulError> { + let key = CString::new(key)?; + let mut value = 0; + // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the + // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed + // to be valid for the duration of this call. The value pointer must be valid because it + // comes from a reference. + if unsafe { APersistableBundle_getInt(self.0.as_ptr(), key.as_ptr(), &mut value) } { + Ok(Some(value)) + } else { + Ok(None) + } + } + + /// Gets the i64 value associated with the given key. + /// + /// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist + /// in the bundle. + pub fn get_long(&self, key: &str) -> Result<Option<i64>, NulError> { + let key = CString::new(key)?; + let mut value = 0; + // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the + // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed + // to be valid for the duration of this call. The value pointer must be valid because it + // comes from a reference. + if unsafe { APersistableBundle_getLong(self.0.as_ptr(), key.as_ptr(), &mut value) } { + Ok(Some(value)) + } else { + Ok(None) + } + } + + /// Gets the f64 value associated with the given key. + /// + /// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist + /// in the bundle. + pub fn get_double(&self, key: &str) -> Result<Option<f64>, NulError> { + let key = CString::new(key)?; + let mut value = 0.0; + // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the + // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed + // to be valid for the duration of this call. The value pointer must be valid because it + // comes from a reference. + if unsafe { APersistableBundle_getDouble(self.0.as_ptr(), key.as_ptr(), &mut value) } { + Ok(Some(value)) + } else { + Ok(None) + } + } + + /// Gets the string value associated with the given key. + /// + /// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist + /// in the bundle. + pub fn get_string(&self, key: &str) -> Result<Option<String>, NulError> { + let key = CString::new(key)?; + let mut value = null_mut(); + let mut allocated_size: usize = 0; + // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the + // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed + // to be valid for the lifetime of `key`. The value pointer must be valid because it comes + // from a reference. + let value_size_bytes = unsafe { + APersistableBundle_getString( + self.0.as_ptr(), + key.as_ptr(), + &mut value, + Some(string_allocator), + (&raw mut allocated_size).cast(), + ) + }; + match value_size_bytes { + APERSISTABLEBUNDLE_KEY_NOT_FOUND => Ok(None), + APERSISTABLEBUNDLE_ALLOCATOR_FAILED => { + panic!("APersistableBundle_getString failed to allocate string"); + } + _ => { + let raw_slice = slice_from_raw_parts_mut(value.cast(), allocated_size); + // SAFETY: The pointer was returned from string_allocator, which used + // `Box::into_raw`, and we've got the appropriate size back from allocated_size. + let boxed_slice: Box<[u8]> = unsafe { Box::from_raw(raw_slice) }; + assert_eq!( + allocated_size, + usize::try_from(value_size_bytes) + .expect("APersistableBundle_getString returned negative value size") + + 1 + ); + let c_string = CString::from_vec_with_nul(boxed_slice.into()) + .expect("APersistableBundle_getString returned string missing NUL byte"); + let string = c_string + .into_string() + .expect("APersistableBundle_getString returned invalid UTF-8"); + Ok(Some(string)) + } + } + } + + /// Gets the vector of `T` associated with the given key. + /// + /// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist + /// in the bundle. + /// + /// `get_func` should be one of the `APersistableBundle_get*Vector` functions from + /// `binder_ndk_sys`. + /// + /// # Safety + /// + /// `get_func` must only require that the pointers it takes are valid for the duration of the + /// call. It must allow a null pointer for the buffer, and must return the size in bytes of + /// buffer it requires. If it is given a non-null buffer pointer it must write that number of + /// bytes to the buffer, which must be a whole number of valid `T` values. + unsafe fn get_vec<T: Clone>( + &self, + key: &str, + default: T, + get_func: unsafe extern "C" fn( + *const APersistableBundle, + *const c_char, + *mut T, + i32, + ) -> i32, + ) -> Result<Option<Vec<T>>, NulError> { + let key = CString::new(key)?; + // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the + // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed + // to be valid for the lifetime of `key`. A null pointer is allowed for the buffer. + match unsafe { get_func(self.0.as_ptr(), key.as_ptr(), null_mut(), 0) } { + APERSISTABLEBUNDLE_KEY_NOT_FOUND => Ok(None), + APERSISTABLEBUNDLE_ALLOCATOR_FAILED => { + panic!("APersistableBundle_getStringVector failed to allocate string"); + } + required_buffer_size => { + let mut value = vec![ + default; + usize::try_from(required_buffer_size).expect( + "APersistableBundle_get*Vector returned invalid size" + ) / size_of::<T>() + ]; + // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for + // the lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` + // is guaranteed to be valid for the lifetime of `key`. The value buffer pointer is + // valid as it comes from the Vec we just allocated. + match unsafe { + get_func( + self.0.as_ptr(), + key.as_ptr(), + value.as_mut_ptr(), + (value.len() * size_of::<T>()).try_into().unwrap(), + ) + } { + APERSISTABLEBUNDLE_KEY_NOT_FOUND => { + panic!("APersistableBundle_get*Vector failed to find key after first finding it"); + } + APERSISTABLEBUNDLE_ALLOCATOR_FAILED => { + panic!("APersistableBundle_getStringVector failed to allocate string"); + } + _ => Ok(Some(value)), + } + } + } + } + + /// Gets the boolean vector value associated with the given key. + /// + /// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist + /// in the bundle. + pub fn get_bool_vec(&self, key: &str) -> Result<Option<Vec<bool>>, NulError> { + // SAFETY: APersistableBundle_getBooleanVector fulfils all the safety requirements of + // `get_vec`. + unsafe { self.get_vec(key, Default::default(), APersistableBundle_getBooleanVector) } + } + + /// Gets the i32 vector value associated with the given key. + /// + /// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist + /// in the bundle. + pub fn get_int_vec(&self, key: &str) -> Result<Option<Vec<i32>>, NulError> { + // SAFETY: APersistableBundle_getIntVector fulfils all the safety requirements of + // `get_vec`. + unsafe { self.get_vec(key, Default::default(), APersistableBundle_getIntVector) } + } + + /// Gets the i64 vector value associated with the given key. + /// + /// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist + /// in the bundle. + pub fn get_long_vec(&self, key: &str) -> Result<Option<Vec<i64>>, NulError> { + // SAFETY: APersistableBundle_getLongVector fulfils all the safety requirements of + // `get_vec`. + unsafe { self.get_vec(key, Default::default(), APersistableBundle_getLongVector) } + } + + /// Gets the f64 vector value associated with the given key. + /// + /// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist + /// in the bundle. + pub fn get_double_vec(&self, key: &str) -> Result<Option<Vec<f64>>, NulError> { + // SAFETY: APersistableBundle_getDoubleVector fulfils all the safety requirements of + // `get_vec`. + unsafe { self.get_vec(key, Default::default(), APersistableBundle_getDoubleVector) } + } + + /// Gets the string vector value associated with the given key. + /// + /// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist + /// in the bundle. + pub fn get_string_vec(&self, key: &str) -> Result<Option<Vec<String>>, NulError> { + if let Some(value) = + // SAFETY: `get_string_vector_with_allocator` fulfils all the safety requirements of + // `get_vec`. + unsafe { self.get_vec(key, null_mut(), get_string_vector_with_allocator) }? + { + Ok(Some( + value + .into_iter() + .map(|s| { + // SAFETY: The pointer was returned from `string_allocator`, which used + // `Box::into_raw`, and `APersistableBundle_getStringVector` should have + // written valid bytes to it including a NUL terminator in the last + // position. + let string_length = unsafe { CStr::from_ptr(s) }.count_bytes(); + let raw_slice = slice_from_raw_parts_mut(s.cast(), string_length + 1); + // SAFETY: The pointer was returned from `string_allocator`, which used + // `Box::into_raw`, and we've got the appropriate size back by checking the + // length of the string. + let boxed_slice: Box<[u8]> = unsafe { Box::from_raw(raw_slice) }; + let c_string = CString::from_vec_with_nul(boxed_slice.into()).expect( + "APersistableBundle_getStringVector returned string missing NUL byte", + ); + c_string + .into_string() + .expect("APersistableBundle_getStringVector returned invalid UTF-8") + }) + .collect(), + )) + } else { + Ok(None) + } + } + + /// Gets the `PersistableBundle` value associated with the given key. + /// + /// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist + /// in the bundle. + pub fn get_persistable_bundle(&self, key: &str) -> Result<Option<Self>, NulError> { + let key = CString::new(key)?; + let mut value = null_mut(); + // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the + // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed + // to be valid for the lifetime of `key`. The value pointer must be valid because it comes + // from a reference. + if unsafe { + APersistableBundle_getPersistableBundle(self.0.as_ptr(), key.as_ptr(), &mut value) + } { + Ok(Some(Self(NonNull::new(value).expect( + "APersistableBundle_getPersistableBundle returned true but didn't set outBundle", + )))) + } else { + Ok(None) + } + } + + /// Calls the appropriate `APersistableBundle_get*Keys` function for the given `value_type`, + /// with our `string_allocator` and a null context pointer. + /// + /// # Safety + /// + /// `out_keys` must either be null or point to a buffer of at least `buffer_size_bytes` bytes, + /// properly aligned for `T`, and not otherwise accessed for the duration of the call. + unsafe fn get_keys_raw( + &self, + value_type: ValueType, + out_keys: *mut *mut c_char, + buffer_size_bytes: i32, + ) -> i32 { + // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the + // lifetime of the `PersistableBundle`. Our caller guarantees an appropriate value for + // `out_keys` and `buffer_size_bytes`. + unsafe { + match value_type { + ValueType::Boolean => APersistableBundle_getBooleanKeys( + self.0.as_ptr(), + out_keys, + buffer_size_bytes, + Some(string_allocator), + null_mut(), + ), + ValueType::Integer => APersistableBundle_getIntKeys( + self.0.as_ptr(), + out_keys, + buffer_size_bytes, + Some(string_allocator), + null_mut(), + ), + ValueType::Long => APersistableBundle_getLongKeys( + self.0.as_ptr(), + out_keys, + buffer_size_bytes, + Some(string_allocator), + null_mut(), + ), + ValueType::Double => APersistableBundle_getDoubleKeys( + self.0.as_ptr(), + out_keys, + buffer_size_bytes, + Some(string_allocator), + null_mut(), + ), + ValueType::String => APersistableBundle_getStringKeys( + self.0.as_ptr(), + out_keys, + buffer_size_bytes, + Some(string_allocator), + null_mut(), + ), + ValueType::BooleanVector => APersistableBundle_getBooleanVectorKeys( + self.0.as_ptr(), + out_keys, + buffer_size_bytes, + Some(string_allocator), + null_mut(), + ), + ValueType::IntegerVector => APersistableBundle_getIntVectorKeys( + self.0.as_ptr(), + out_keys, + buffer_size_bytes, + Some(string_allocator), + null_mut(), + ), + ValueType::LongVector => APersistableBundle_getLongVectorKeys( + self.0.as_ptr(), + out_keys, + buffer_size_bytes, + Some(string_allocator), + null_mut(), + ), + ValueType::DoubleVector => APersistableBundle_getDoubleVectorKeys( + self.0.as_ptr(), + out_keys, + buffer_size_bytes, + Some(string_allocator), + null_mut(), + ), + ValueType::StringVector => APersistableBundle_getStringVectorKeys( + self.0.as_ptr(), + out_keys, + buffer_size_bytes, + Some(string_allocator), + null_mut(), + ), + ValueType::PersistableBundle => APersistableBundle_getPersistableBundleKeys( + self.0.as_ptr(), + out_keys, + buffer_size_bytes, + Some(string_allocator), + null_mut(), + ), + } + } + } + + /// Gets all the keys associated with values of the given type. + pub fn keys_for_type(&self, value_type: ValueType) -> Vec<String> { + // SAFETY: A null pointer is allowed for the buffer. + match unsafe { self.get_keys_raw(value_type, null_mut(), 0) } { + APERSISTABLEBUNDLE_ALLOCATOR_FAILED => { + panic!("APersistableBundle_get*Keys failed to allocate string"); + } + required_buffer_size => { + let required_buffer_size_usize = usize::try_from(required_buffer_size) + .expect("APersistableBundle_get*Keys returned invalid size"); + assert_eq!(required_buffer_size_usize % size_of::<*mut c_char>(), 0); + let mut keys = + vec![null_mut(); required_buffer_size_usize / size_of::<*mut c_char>()]; + // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for + // the lifetime of the `PersistableBundle`. The keys buffer pointer is valid as it + // comes from the Vec we just allocated. + if unsafe { self.get_keys_raw(value_type, keys.as_mut_ptr(), required_buffer_size) } + == APERSISTABLEBUNDLE_ALLOCATOR_FAILED + { + panic!("APersistableBundle_get*Keys failed to allocate string"); + } + keys.into_iter() + .map(|key| { + // SAFETY: The pointer was returned from `string_allocator`, which used + // `Box::into_raw`, and `APersistableBundle_getStringVector` should have + // written valid bytes to it including a NUL terminator in the last + // position. + let string_length = unsafe { CStr::from_ptr(key) }.count_bytes(); + let raw_slice = slice_from_raw_parts_mut(key.cast(), string_length + 1); + // SAFETY: The pointer was returned from `string_allocator`, which used + // `Box::into_raw`, and we've got the appropriate size back by checking the + // length of the string. + let boxed_slice: Box<[u8]> = unsafe { Box::from_raw(raw_slice) }; + let c_string = CString::from_vec_with_nul(boxed_slice.into()) + .expect("APersistableBundle_get*Keys returned string missing NUL byte"); + c_string + .into_string() + .expect("APersistableBundle_get*Keys returned invalid UTF-8") + }) + .collect() + } + } + } + + /// Returns an iterator over all keys in the bundle, along with the type of their associated + /// value. + pub fn keys(&self) -> impl Iterator<Item = (String, ValueType)> + use<'_> { + [ + ValueType::Boolean, + ValueType::Integer, + ValueType::Long, + ValueType::Double, + ValueType::String, + ValueType::BooleanVector, + ValueType::IntegerVector, + ValueType::LongVector, + ValueType::DoubleVector, + ValueType::StringVector, + ValueType::PersistableBundle, + ] + .iter() + .flat_map(|value_type| { + self.keys_for_type(*value_type).into_iter().map(|key| (key, *value_type)) + }) + } +} + +/// Wrapper around `APersistableBundle_getStringVector` to pass `string_allocator` and a null +/// context pointer. +/// +/// # Safety +/// +/// * `bundle` must point to a valid `APersistableBundle` which is not modified for the duration of +/// the call. +/// * `key` must point to a valid NUL-terminated C string. +/// * `buffer` must either be null or point to a buffer of at least `buffer_size_bytes` bytes, +/// properly aligned for `T`, and not otherwise accessed for the duration of the call. +unsafe extern "C" fn get_string_vector_with_allocator( + bundle: *const APersistableBundle, + key: *const c_char, + buffer: *mut *mut c_char, + buffer_size_bytes: i32, +) -> i32 { + // SAFETY: The safety requirements are all guaranteed by our caller according to the safety + // documentation above. + unsafe { + APersistableBundle_getStringVector( + bundle, + key, + buffer, + buffer_size_bytes, + Some(string_allocator), + null_mut(), + ) + } +} + +// SAFETY: The underlying *APersistableBundle can be moved between threads. +unsafe impl Send for PersistableBundle {} + +// SAFETY: The underlying *APersistableBundle can be read from multiple threads, and we require +// `&mut PersistableBundle` for any operations which mutate it. +unsafe impl Sync for PersistableBundle {} + +impl Default for PersistableBundle { + fn default() -> Self { + Self::new() + } +} + +impl Drop for PersistableBundle { + fn drop(&mut self) { + // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the + // lifetime of this `PersistableBundle`. + unsafe { APersistableBundle_delete(self.0.as_ptr()) }; + } +} + +impl Clone for PersistableBundle { + fn clone(&self) -> Self { + // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the + // lifetime of the `PersistableBundle`. + let duplicate = unsafe { APersistableBundle_dup(self.0.as_ptr()) }; + Self(NonNull::new(duplicate).expect("Duplicated APersistableBundle was null")) + } +} + +impl PartialEq for PersistableBundle { + fn eq(&self, other: &Self) -> bool { + // SAFETY: The wrapped `APersistableBundle` pointers are guaranteed to be valid for the + // lifetime of the `PersistableBundle`s. + unsafe { APersistableBundle_isEqual(self.0.as_ptr(), other.0.as_ptr()) } + } +} + +impl UnstructuredParcelable for PersistableBundle { + fn write_to_parcel(&self, parcel: &mut BorrowedParcel) -> Result<(), StatusCode> { + let status = + // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the + // lifetime of the `PersistableBundle`. `parcel.as_native_mut()` always returns a valid + // parcel pointer. + unsafe { APersistableBundle_writeToParcel(self.0.as_ptr(), parcel.as_native_mut()) }; + status_result(status) + } + + fn from_parcel(parcel: &BorrowedParcel) -> Result<Self, StatusCode> { + let mut bundle = null_mut(); + + // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the + // lifetime of the `PersistableBundle`. `parcel.as_native()` always returns a valid parcel + // pointer. + let status = unsafe { APersistableBundle_readFromParcel(parcel.as_native(), &mut bundle) }; + status_result(status)?; + + Ok(Self(NonNull::new(bundle).expect( + "APersistableBundle_readFromParcel returned success but didn't allocate bundle", + ))) + } +} + +/// Allocates a boxed slice of the given size in bytes, returns a pointer to it and writes its size +/// to `*context`. +/// +/// # Safety +/// +/// `context` must either be null or point to a `usize` to which we can write. +unsafe extern "C" fn string_allocator(size: i32, context: *mut c_void) -> *mut c_char { + let Ok(size) = size.try_into() else { + return null_mut(); + }; + let Ok(boxed_slice) = <[c_char]>::new_box_zeroed_with_elems(size) else { + return null_mut(); + }; + if !context.is_null() { + // SAFETY: The caller promised that `context` is either null or points to a `usize` to which + // we can write, and we just checked that it's not null. + unsafe { + *context.cast::<usize>() = size; + } + } + Box::into_raw(boxed_slice).cast() +} + +impl_deserialize_for_unstructured_parcelable!(PersistableBundle); +impl_serialize_for_unstructured_parcelable!(PersistableBundle); + +/// The types which may be stored as values in a [`PersistableBundle`]. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum ValueType { + /// A `bool`. + Boolean, + /// An `i32`. + Integer, + /// An `i64`. + Long, + /// An `f64`. + Double, + /// A string. + String, + /// A vector of `bool`s. + BooleanVector, + /// A vector of `i32`s. + IntegerVector, + /// A vector of `i64`s. + LongVector, + /// A vector of `f64`s. + DoubleVector, + /// A vector of strings. + StringVector, + /// A nested `PersistableBundle`. + PersistableBundle, +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn create_delete() { + let bundle = PersistableBundle::new(); + drop(bundle); + } + + #[test] + fn duplicate_equal() { + let bundle = PersistableBundle::new(); + let duplicate = bundle.clone(); + assert_eq!(bundle, duplicate); + } + + #[test] + fn get_empty() { + let bundle = PersistableBundle::new(); + assert_eq!(bundle.get_bool("foo"), Ok(None)); + assert_eq!(bundle.get_int("foo"), Ok(None)); + assert_eq!(bundle.get_long("foo"), Ok(None)); + assert_eq!(bundle.get_double("foo"), Ok(None)); + assert_eq!(bundle.get_bool_vec("foo"), Ok(None)); + assert_eq!(bundle.get_int_vec("foo"), Ok(None)); + assert_eq!(bundle.get_long_vec("foo"), Ok(None)); + assert_eq!(bundle.get_double_vec("foo"), Ok(None)); + assert_eq!(bundle.get_string("foo"), Ok(None)); + } + + #[test] + fn remove_empty() { + let mut bundle = PersistableBundle::new(); + assert_eq!(bundle.remove("foo"), Ok(false)); + } + + #[test] + fn insert_get_primitives() { + let mut bundle = PersistableBundle::new(); + + assert_eq!(bundle.insert_bool("bool", true), Ok(())); + assert_eq!(bundle.insert_int("int", 42), Ok(())); + assert_eq!(bundle.insert_long("long", 66), Ok(())); + assert_eq!(bundle.insert_double("double", 123.4), Ok(())); + + assert_eq!(bundle.get_bool("bool"), Ok(Some(true))); + assert_eq!(bundle.get_int("int"), Ok(Some(42))); + assert_eq!(bundle.get_long("long"), Ok(Some(66))); + assert_eq!(bundle.get_double("double"), Ok(Some(123.4))); + assert_eq!(bundle.size(), 4); + + // Getting the wrong type should return nothing. + assert_eq!(bundle.get_int("bool"), Ok(None)); + assert_eq!(bundle.get_long("bool"), Ok(None)); + assert_eq!(bundle.get_double("bool"), Ok(None)); + assert_eq!(bundle.get_bool("int"), Ok(None)); + assert_eq!(bundle.get_long("int"), Ok(None)); + assert_eq!(bundle.get_double("int"), Ok(None)); + assert_eq!(bundle.get_bool("long"), Ok(None)); + assert_eq!(bundle.get_int("long"), Ok(None)); + assert_eq!(bundle.get_double("long"), Ok(None)); + assert_eq!(bundle.get_bool("double"), Ok(None)); + assert_eq!(bundle.get_int("double"), Ok(None)); + assert_eq!(bundle.get_long("double"), Ok(None)); + + // If they are removed they should no longer be present. + assert_eq!(bundle.remove("bool"), Ok(true)); + assert_eq!(bundle.remove("int"), Ok(true)); + assert_eq!(bundle.remove("long"), Ok(true)); + assert_eq!(bundle.remove("double"), Ok(true)); + assert_eq!(bundle.get_bool("bool"), Ok(None)); + assert_eq!(bundle.get_int("int"), Ok(None)); + assert_eq!(bundle.get_long("long"), Ok(None)); + assert_eq!(bundle.get_double("double"), Ok(None)); + assert_eq!(bundle.size(), 0); + } + + #[test] + fn insert_get_string() { + let mut bundle = PersistableBundle::new(); + + assert_eq!(bundle.insert_string("string", "foo"), Ok(())); + assert_eq!(bundle.insert_string("empty", ""), Ok(())); + assert_eq!(bundle.size(), 2); + + assert_eq!(bundle.get_string("string"), Ok(Some("foo".to_string()))); + assert_eq!(bundle.get_string("empty"), Ok(Some("".to_string()))); + } + + #[test] + fn insert_get_vec() { + let mut bundle = PersistableBundle::new(); + + assert_eq!(bundle.insert_bool_vec("bool", &[]), Ok(())); + assert_eq!(bundle.insert_int_vec("int", &[42]), Ok(())); + assert_eq!(bundle.insert_long_vec("long", &[66, 67, 68]), Ok(())); + assert_eq!(bundle.insert_double_vec("double", &[123.4]), Ok(())); + assert_eq!(bundle.insert_string_vec("string", &["foo", "bar", "baz"]), Ok(())); + assert_eq!( + bundle.insert_string_vec( + "string", + &[&"foo".to_string(), &"bar".to_string(), &"baz".to_string()] + ), + Ok(()) + ); + assert_eq!( + bundle.insert_string_vec( + "string", + &["foo".to_string(), "bar".to_string(), "baz".to_string()] + ), + Ok(()) + ); + + assert_eq!(bundle.size(), 5); + + assert_eq!(bundle.get_bool_vec("bool"), Ok(Some(vec![]))); + assert_eq!(bundle.get_int_vec("int"), Ok(Some(vec![42]))); + assert_eq!(bundle.get_long_vec("long"), Ok(Some(vec![66, 67, 68]))); + assert_eq!(bundle.get_double_vec("double"), Ok(Some(vec![123.4]))); + assert_eq!( + bundle.get_string_vec("string"), + Ok(Some(vec!["foo".to_string(), "bar".to_string(), "baz".to_string()])) + ); + } + + #[test] + fn insert_get_bundle() { + let mut bundle = PersistableBundle::new(); + + let mut sub_bundle = PersistableBundle::new(); + assert_eq!(sub_bundle.insert_int("int", 42), Ok(())); + assert_eq!(sub_bundle.size(), 1); + assert_eq!(bundle.insert_persistable_bundle("bundle", &sub_bundle), Ok(())); + + assert_eq!(bundle.get_persistable_bundle("bundle"), Ok(Some(sub_bundle))); + } + + #[test] + fn get_keys() { + let mut bundle = PersistableBundle::new(); + + assert_eq!(bundle.keys_for_type(ValueType::Boolean), Vec::<String>::new()); + assert_eq!(bundle.keys_for_type(ValueType::Integer), Vec::<String>::new()); + assert_eq!(bundle.keys_for_type(ValueType::StringVector), Vec::<String>::new()); + + assert_eq!(bundle.insert_bool("bool1", false), Ok(())); + assert_eq!(bundle.insert_bool("bool2", true), Ok(())); + assert_eq!(bundle.insert_int("int", 42), Ok(())); + + assert_eq!( + bundle.keys_for_type(ValueType::Boolean), + vec!["bool1".to_string(), "bool2".to_string()] + ); + assert_eq!(bundle.keys_for_type(ValueType::Integer), vec!["int".to_string()]); + assert_eq!(bundle.keys_for_type(ValueType::StringVector), Vec::<String>::new()); + + assert_eq!( + bundle.keys().collect::<Vec<_>>(), + vec![ + ("bool1".to_string(), ValueType::Boolean), + ("bool2".to_string(), ValueType::Boolean), + ("int".to_string(), ValueType::Integer), + ] + ); + } +} diff --git a/libs/binder/rust/src/state.rs b/libs/binder/rust/src/state.rs index c0cac830d7..609334eb53 100644 --- a/libs/binder/rust/src/state.rs +++ b/libs/binder/rust/src/state.rs @@ -28,8 +28,9 @@ impl ProcessState { /// `num_threads` additional threads as specified by /// [`set_thread_pool_max_thread_count`](Self::set_thread_pool_max_thread_count). /// - /// This should be done before creating any Binder client or server. If - /// neither this nor [`join_thread_pool`](Self::join_thread_pool) are + /// If this is called, it must be done before creating any Binder client or server. + /// + /// If neither this nor [`join_thread_pool`](Self::join_thread_pool) are /// called, then some things (such as callbacks and /// [`IBinder::link_to_death`](crate::IBinder::link_to_death)) will silently /// not work: the callbacks will be queued but never called as there is no diff --git a/libs/binder/rust/sys/BinderBindings.hpp b/libs/binder/rust/sys/BinderBindings.hpp index 557f0e895d..c19e375a97 100644 --- a/libs/binder/rust/sys/BinderBindings.hpp +++ b/libs/binder/rust/sys/BinderBindings.hpp @@ -17,6 +17,7 @@ #include <android/binder_ibinder.h> #include <android/binder_parcel.h> #include <android/binder_status.h> +#include <android/persistable_bundle.h> /* Platform only */ #if defined(ANDROID_PLATFORM) || defined(__ANDROID_VENDOR__) @@ -91,6 +92,11 @@ enum { #endif }; +enum { + APERSISTABLEBUNDLE_KEY_NOT_FOUND = APERSISTABLEBUNDLE_KEY_NOT_FOUND, + APERSISTABLEBUNDLE_ALLOCATOR_FAILED = APERSISTABLEBUNDLE_ALLOCATOR_FAILED, +}; + } // namespace consts } // namespace c_interface diff --git a/libs/binder/servicedispatcher.cpp b/libs/binder/servicedispatcher.cpp index be990657d5..78fe2a8714 100644 --- a/libs/binder/servicedispatcher.cpp +++ b/libs/binder/servicedispatcher.cpp @@ -127,7 +127,12 @@ public: // We can't send BpBinder for regular binder over RPC. return android::binder::Status::fromStatusT(android::INVALID_OPERATION); } - android::binder::Status checkService(const std::string&, android::os::Service*) override { + android::binder::Status checkService(const std::string&, + android::sp<android::IBinder>*) override { + // We can't send BpBinder for regular binder over RPC. + return android::binder::Status::fromStatusT(android::INVALID_OPERATION); + } + android::binder::Status checkService2(const std::string&, android::os::Service*) override { // We can't send BpBinder for regular binder over RPC. return android::binder::Status::fromStatusT(android::INVALID_OPERATION); } diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp index c21d7c61cd..f412dfb6f4 100644 --- a/libs/binder/tests/Android.bp +++ b/libs/binder/tests/Android.bp @@ -803,6 +803,28 @@ cc_test { } cc_test { + name: "binderStabilityIntegrationTest", + defaults: ["binder_test_defaults"], + srcs: [ + "binderStabilityIntegrationTest.cpp", + ], + + shared_libs: [ + "libbinder", + "libutils", + ], + static_libs: [ + "libprocpartition", + ], + + test_suites: [ + "general-tests", + "vts", + ], + require_root: true, +} + +cc_test { name: "binderAllocationLimits", defaults: ["binder_test_defaults"], srcs: ["binderAllocationLimits.cpp"], diff --git a/libs/binder/tests/IBinderRpcTest.aidl b/libs/binder/tests/IBinderRpcTest.aidl index 116476765a..dcd6461b53 100644 --- a/libs/binder/tests/IBinderRpcTest.aidl +++ b/libs/binder/tests/IBinderRpcTest.aidl @@ -34,6 +34,8 @@ interface IBinderRpcTest { void holdBinder(@nullable IBinder binder); @nullable IBinder getHeldBinder(); + byte[] repeatBytes(in byte[] bytes); + // Idea is client creates its own instance of IBinderRpcTest and calls this, // and the server calls 'binder' with (calls - 1) passing itself as 'binder', // going back and forth until calls = 0 diff --git a/libs/binder/tests/binderAllocationLimits.cpp b/libs/binder/tests/binderAllocationLimits.cpp index c0c0aae80b..339ce4bdf8 100644 --- a/libs/binder/tests/binderAllocationLimits.cpp +++ b/libs/binder/tests/binderAllocationLimits.cpp @@ -22,17 +22,33 @@ #include <binder/RpcServer.h> #include <binder/RpcSession.h> #include <cutils/trace.h> +#include <gtest/gtest-spi.h> #include <gtest/gtest.h> #include <utils/CallStack.h> #include <malloc.h> +#include <atomic> #include <functional> +#include <numeric> #include <vector> using namespace android::binder::impl; static android::String8 gEmpty(""); // make sure first allocation from optimization runs +struct State { + State(std::vector<size_t>&& expectedMallocs) : expectedMallocs(std::move(expectedMallocs)) {} + ~State() { + size_t num = numMallocs.load(); + if (expectedMallocs.size() != num) { + ADD_FAILURE() << "Expected " << expectedMallocs.size() << " allocations, but got " + << num; + } + } + const std::vector<size_t> expectedMallocs; + std::atomic<size_t> numMallocs; +}; + struct DestructionAction { DestructionAction(std::function<void()> f) : mF(std::move(f)) {} ~DestructionAction() { mF(); }; @@ -95,8 +111,7 @@ namespace LambdaHooks { // Action to execute when malloc is hit. Supports nesting. Malloc is not // restricted when the allocation hook is being processed. -__attribute__((warn_unused_result)) -DestructionAction OnMalloc(LambdaHooks::AllocationHook f) { +__attribute__((warn_unused_result)) DestructionAction OnMalloc(LambdaHooks::AllocationHook f) { MallocHooks before = MallocHooks::save(); LambdaHooks::lambdas.emplace_back(std::move(f)); LambdaHooks::lambda_malloc_hooks.overwrite(); @@ -106,6 +121,22 @@ DestructionAction OnMalloc(LambdaHooks::AllocationHook f) { }); } +DestructionAction setExpectedMallocs(std::vector<size_t>&& expected) { + auto state = std::make_shared<State>(std::move(expected)); + return OnMalloc([state = state](size_t bytes) { + size_t num = state->numMallocs.fetch_add(1); + if (num >= state->expectedMallocs.size() || state->expectedMallocs[num] != bytes) { + ADD_FAILURE() << "Unexpected allocation number " << num << " of size " << bytes + << " bytes" << std::endl + << android::CallStack::stackToString("UNEXPECTED ALLOCATION", + android::CallStack::getCurrent( + 4 /*ignoreDepth*/) + .get()) + << std::endl; + } + }); +} + // exported symbol, to force compiler not to optimize away pointers we set here const void* imaginary_use; @@ -119,16 +150,53 @@ TEST(TestTheTest, OnMalloc) { imaginary_use = new int[10]; } + delete[] reinterpret_cast<const int*>(imaginary_use); EXPECT_EQ(mallocs, 1u); } +TEST(TestTheTest, OnMallocWithExpectedMallocs) { + std::vector<size_t> expectedMallocs = { + 4, + 16, + 8, + }; + { + const auto on_malloc = setExpectedMallocs(std::move(expectedMallocs)); + imaginary_use = new int32_t[1]; + delete[] reinterpret_cast<const int*>(imaginary_use); + imaginary_use = new int32_t[4]; + delete[] reinterpret_cast<const int*>(imaginary_use); + imaginary_use = new int32_t[2]; + delete[] reinterpret_cast<const int*>(imaginary_use); + } +} + +TEST(TestTheTest, OnMallocWithExpectedMallocsWrongSize) { + std::vector<size_t> expectedMallocs = { + 4, + 16, + 100000, + }; + EXPECT_NONFATAL_FAILURE( + { + const auto on_malloc = setExpectedMallocs(std::move(expectedMallocs)); + imaginary_use = new int32_t[1]; + delete[] reinterpret_cast<const int*>(imaginary_use); + imaginary_use = new int32_t[4]; + delete[] reinterpret_cast<const int*>(imaginary_use); + imaginary_use = new int32_t[2]; + delete[] reinterpret_cast<const int*>(imaginary_use); + }, + "Unexpected allocation number 2 of size 8 bytes"); +} __attribute__((warn_unused_result)) DestructionAction ScopeDisallowMalloc() { return OnMalloc([&](size_t bytes) { - ADD_FAILURE() << "Unexpected allocation: " << bytes; + FAIL() << "Unexpected allocation: " << bytes; using android::CallStack; - std::cout << CallStack::stackToString("UNEXPECTED ALLOCATION", CallStack::getCurrent(4 /*ignoreDepth*/).get()) + std::cout << CallStack::stackToString("UNEXPECTED ALLOCATION", + CallStack::getCurrent(4 /*ignoreDepth*/).get()) << std::endl; }); } @@ -224,6 +292,51 @@ TEST(BinderAllocation, SmallTransaction) { EXPECT_EQ(mallocs, 1u); } +TEST(BinderAccessorAllocation, AddAccessorCheckService) { + // Need to call defaultServiceManager() before checking malloc because it + // will allocate an instance in the call_once + const auto sm = defaultServiceManager(); + const std::string kInstanceName1 = "foo.bar.IFoo/default"; + const std::string kInstanceName2 = "foo.bar.IFoo2/default"; + const String16 kInstanceName16(kInstanceName1.c_str()); + std::vector<size_t> expectedMallocs = { + // addAccessorProvider + 112, // new AccessorProvider + 16, // new AccessorProviderEntry + // checkService + 45, // String8 from String16 in CppShim::checkService + 128, // writeInterfaceToken + 16, // getInjectedAccessor, new AccessorProviderEntry + 66, // getInjectedAccessor, String16 + 45, // String8 from String16 in AccessorProvider::provide + }; + std::set<std::string> supportedInstances = {kInstanceName1, kInstanceName2}; + auto onMalloc = setExpectedMallocs(std::move(expectedMallocs)); + + auto receipt = + android::addAccessorProvider(std::move(supportedInstances), + [&](const String16&) -> sp<IBinder> { return nullptr; }); + EXPECT_FALSE(receipt.expired()); + + sp<IBinder> binder = sm->checkService(kInstanceName16); + + status_t status = android::removeAccessorProvider(receipt); +} + +TEST(BinderAccessorAllocation, AddAccessorEmpty) { + std::vector<size_t> expectedMallocs = { + 48, // From ALOGE with empty set of instances + }; + std::set<std::string> supportedInstances = {}; + auto onMalloc = setExpectedMallocs(std::move(expectedMallocs)); + + auto receipt = + android::addAccessorProvider(std::move(supportedInstances), + [&](const String16&) -> sp<IBinder> { return nullptr; }); + + EXPECT_TRUE(receipt.expired()); +} + TEST(RpcBinderAllocation, SetupRpcServer) { std::string tmp = getenv("TMPDIR") ?: "/tmp"; std::string addr = tmp + "/binderRpcBenchmark"; @@ -255,6 +368,7 @@ TEST(RpcBinderAllocation, SetupRpcServer) { } int main(int argc, char** argv) { + LOG(INFO) << "Priming static log variables for binderAllocationLimits."; if (getenv("LIBC_HOOKS_ENABLE") == nullptr) { CHECK(0 == setenv("LIBC_HOOKS_ENABLE", "1", true /*overwrite*/)); execv(argv[0], argv); diff --git a/libs/binder/tests/binderCacheUnitTest.cpp b/libs/binder/tests/binderCacheUnitTest.cpp index 19395c2dcd..121e5ae5e9 100644 --- a/libs/binder/tests/binderCacheUnitTest.cpp +++ b/libs/binder/tests/binderCacheUnitTest.cpp @@ -74,7 +74,7 @@ class MockAidlServiceManager : public os::IServiceManagerDefault { public: MockAidlServiceManager() : innerSm() {} - binder::Status checkService(const ::std::string& name, os::Service* _out) override { + binder::Status checkService2(const ::std::string& name, os::Service* _out) override { os::ServiceWithMetadata serviceWithMetadata = os::ServiceWithMetadata(); serviceWithMetadata.service = innerSm.getService(String16(name.c_str())); serviceWithMetadata.isLazyService = false; @@ -98,7 +98,7 @@ class MockAidlServiceManager2 : public os::IServiceManagerDefault { public: MockAidlServiceManager2() : innerSm() {} - binder::Status checkService(const ::std::string& name, os::Service* _out) override { + binder::Status checkService2(const ::std::string& name, os::Service* _out) override { os::ServiceWithMetadata serviceWithMetadata = os::ServiceWithMetadata(); serviceWithMetadata.service = innerSm.getService(String16(name.c_str())); serviceWithMetadata.isLazyService = true; diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp index 9f656ec96c..170b2adfbb 100644 --- a/libs/binder/tests/binderRpcTest.cpp +++ b/libs/binder/tests/binderRpcTest.cpp @@ -711,6 +711,35 @@ TEST_P(BinderRpc, OnewayCallExhaustion) { proc.proc->sessions.erase(proc.proc->sessions.begin() + 1); } +// TODO(b/392717039): can we move this to universal tests? +TEST_P(BinderRpc, SendTooLargeVector) { + if (GetParam().singleThreaded) { + GTEST_SKIP() << "Requires multi-threaded server to test one of the sessions crashing."; + } + + auto proc = createRpcTestSocketServerProcess({.numSessions = 2}); + + // need a working transaction + EXPECT_EQ(OK, proc.rootBinder->pingBinder()); + + // see libbinder internal Constants.h + const size_t kTooLargeSize = 650 * 1024; + const std::vector<uint8_t> kTestValue(kTooLargeSize / sizeof(uint8_t), 42); + + // TODO(b/392717039): Telling a server to allocate too much data currently causes the session to + // close since RpcServer treats any transaction error as a failure. We likely want to change + // this behavior to be a soft failure, since it isn't hard to keep track of this state. + sp<IBinderRpcTest> rootIface2 = interface_cast<IBinderRpcTest>(proc.proc->sessions.at(1).root); + std::vector<uint8_t> result; + status_t res = rootIface2->repeatBytes(kTestValue, &result).transactionError(); + + // TODO(b/392717039): consistent error results always + EXPECT_TRUE(res == -ECONNRESET || res == DEAD_OBJECT) << statusToString(res); + + // died, so remove it for checks in destructor of proc + proc.proc->sessions.erase(proc.proc->sessions.begin() + 1); +} + TEST_P(BinderRpc, SessionWithIncomingThreadpoolDoesntLeak) { if (clientOrServerSingleThreaded()) { GTEST_SKIP() << "This test requires multiple threads"; @@ -2742,7 +2771,9 @@ INSTANTIATE_TEST_SUITE_P( int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); +#ifndef __ANDROID__ __android_log_set_logger(__android_log_stderr_logger); +#endif return RUN_ALL_TESTS(); } diff --git a/libs/binder/tests/binderRpcTestCommon.h b/libs/binder/tests/binderRpcTestCommon.h index dc22647b85..6e0024628a 100644 --- a/libs/binder/tests/binderRpcTestCommon.h +++ b/libs/binder/tests/binderRpcTestCommon.h @@ -348,6 +348,10 @@ public: *out = binder; return Status::ok(); } + Status repeatBytes(const std::vector<uint8_t>& bytes, std::vector<uint8_t>* out) override { + *out = bytes; + return Status::ok(); + } static sp<IBinder> mHeldBinder; Status holdBinder(const sp<IBinder>& binder) override { mHeldBinder = binder; diff --git a/libs/binder/tests/binderRpcTestService.cpp b/libs/binder/tests/binderRpcTestService.cpp index aef946481f..0084b9ada2 100644 --- a/libs/binder/tests/binderRpcTestService.cpp +++ b/libs/binder/tests/binderRpcTestService.cpp @@ -100,7 +100,9 @@ public: }; int main(int argc, char* argv[]) { +#ifndef __ANDROID__ __android_log_set_logger(__android_log_stderr_logger); +#endif LOG_ALWAYS_FATAL_IF(argc != 3, "Invalid number of arguments: %d", argc); unique_fd writeEnd(atoi(argv[1])); diff --git a/libs/binder/tests/binderRpcUniversalTests.cpp b/libs/binder/tests/binderRpcUniversalTests.cpp index c6fd4870a0..d227e6eeec 100644 --- a/libs/binder/tests/binderRpcUniversalTests.cpp +++ b/libs/binder/tests/binderRpcUniversalTests.cpp @@ -209,6 +209,18 @@ TEST_P(BinderRpc, RepeatBinder) { EXPECT_EQ(0, MyBinderRpcSession::gNum); } +TEST_P(BinderRpc, SendLargeVector) { + auto proc = createRpcTestSocketServerProcess({}); + + // see libbinder internal Constants.h + const size_t kLargeSize = 550 * 1024; + const std::vector<uint8_t> kTestValue(kLargeSize / sizeof(uint8_t), 42); + + std::vector<uint8_t> result; + EXPECT_OK(proc.rootIface->repeatBytes(kTestValue, &result)); + EXPECT_EQ(result, kTestValue); +} + TEST_P(BinderRpc, RepeatTheirBinder) { auto proc = createRpcTestSocketServerProcess({}); @@ -498,9 +510,9 @@ TEST_P(BinderRpc, Callbacks) { // same thread, everything should have happened in a nested call. Otherwise, // the callback will be processed on another thread. if (callIsOneway || callbackIsOneway || delayed) { - using std::literals::chrono_literals::operator""s; + using std::literals::chrono_literals::operator""ms; RpcMutexUniqueLock _l(cb->mMutex); - cb->mCv.wait_for(_l, 1s, [&] { return !cb->mValues.empty(); }); + cb->mCv.wait_for(_l, 1500ms, [&] { return !cb->mValues.empty(); }); } EXPECT_EQ(cb->mValues.size(), 1UL) diff --git a/libs/binder/tests/binderSafeInterfaceTest.cpp b/libs/binder/tests/binderSafeInterfaceTest.cpp index 7d1556e7d2..45b2103637 100644 --- a/libs/binder/tests/binderSafeInterfaceTest.cpp +++ b/libs/binder/tests/binderSafeInterfaceTest.cpp @@ -858,7 +858,13 @@ TEST_F(SafeInterfaceTest, TestIncrementTwo) { ASSERT_EQ(b + 1, bPlusOne); } -extern "C" int main(int argc, char **argv) { +} // namespace tests +} // namespace android + +int main(int argc, char** argv) { + using namespace android; + using namespace android::tests; + testing::InitGoogleTest(&argc, argv); if (fork() == 0) { @@ -875,6 +881,3 @@ extern "C" int main(int argc, char **argv) { return RUN_ALL_TESTS(); } - -} // namespace tests -} // namespace android diff --git a/libs/binder/tests/binderStabilityIntegrationTest.cpp b/libs/binder/tests/binderStabilityIntegrationTest.cpp new file mode 100644 index 0000000000..cbc4180480 --- /dev/null +++ b/libs/binder/tests/binderStabilityIntegrationTest.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2025 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 <binder/Binder.h> +#include <binder/IServiceManager.h> +#include <binder/Stability.h> +#include <gtest/gtest.h> +#include <procpartition/procpartition.h> + +using namespace android; +using android::internal::Stability; // for testing only! +using android::procpartition::getPartition; +using android::procpartition::Partition; + +class BinderStabilityIntegrationTest : public testing::Test, + public ::testing::WithParamInterface<String16> { +public: + virtual ~BinderStabilityIntegrationTest() {} +}; + +TEST_P(BinderStabilityIntegrationTest, ExpectedStabilityForItsPartition) { + const String16& serviceName = GetParam(); + + sp<IBinder> binder = defaultServiceManager()->checkService(serviceName); + if (!binder) GTEST_SKIP() << "Could not get service, may have gone away."; + + pid_t pid; + status_t res = binder->getDebugPid(&pid); + if (res != OK) { + GTEST_SKIP() << "Could not talk to service to get PID, res: " << statusToString(res); + } + + Partition partition = getPartition(pid); + + Stability::Level level = Stability::Level::UNDECLARED; + switch (partition) { + case Partition::PRODUCT: + case Partition::SYSTEM: + case Partition::SYSTEM_EXT: + level = Stability::Level::SYSTEM; + break; + case Partition::VENDOR: + case Partition::ODM: + level = Stability::Level::VENDOR; + break; + case Partition::UNKNOWN: + GTEST_SKIP() << "Not sure of partition of process."; + return; + default: + ADD_FAILURE() << "Unrecognized partition for service: " << partition; + return; + } + + ASSERT_TRUE(Stability::check(Stability::getRepr(binder.get()), level)) + << "Binder hosted on partition " << partition + << " should have corresponding stability set."; +} + +std::string PrintTestParam( + const testing::TestParamInfo<BinderStabilityIntegrationTest::ParamType>& info) { + std::string name = String8(info.param).c_str(); + for (size_t i = 0; i < name.size(); i++) { + bool alnum = false; + alnum |= (name[i] >= 'a' && name[i] <= 'z'); + alnum |= (name[i] >= 'A' && name[i] <= 'Z'); + alnum |= (name[i] >= '0' && name[i] <= '9'); + alnum |= (name[i] == '_'); + if (!alnum) name[i] = '_'; + } + + // index for uniqueness + return std::to_string(info.index) + "__" + name; +} + +INSTANTIATE_TEST_CASE_P(RegisteredServices, BinderStabilityIntegrationTest, + ::testing::ValuesIn(defaultServiceManager()->listServices()), + PrintTestParam); diff --git a/libs/binder/tests/parcel_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/Android.bp index cac054eb1b..457eaa5a76 100644 --- a/libs/binder/tests/parcel_fuzzer/Android.bp +++ b/libs/binder/tests/parcel_fuzzer/Android.bp @@ -109,6 +109,9 @@ cc_library_static { "libcutils", "libutils", ], + header_libs: [ + "libaidl_transactions", + ], local_include_dirs: ["include_random_parcel"], export_include_dirs: ["include_random_parcel"], } diff --git a/libs/binder/tests/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp index 401c2749ef..b2ba1ae38d 100644 --- a/libs/binder/tests/parcel_fuzzer/binder.cpp +++ b/libs/binder/tests/parcel_fuzzer/binder.cpp @@ -317,8 +317,6 @@ std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS { PARCEL_READ_NO_STATUS(int, readParcelFileDescriptor), PARCEL_READ_WITH_STATUS(unique_fd, readUniqueFileDescriptor), - PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<unique_fd>>, - readUniqueFileDescriptorVector), PARCEL_READ_WITH_STATUS(std::optional<std::vector<unique_fd>>, readUniqueFileDescriptorVector), PARCEL_READ_WITH_STATUS(std::vector<unique_fd>, readUniqueFileDescriptorVector), diff --git a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp index 02e69cc371..11aa76891b 100644 --- a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp +++ b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp @@ -13,6 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +#include <aidl/transaction_ids.h> #include <fuzzbinder/libbinder_driver.h> #include <fuzzbinder/random_parcel.h> @@ -31,6 +33,28 @@ void fuzzService(const sp<IBinder>& binder, FuzzedDataProvider&& provider) { fuzzService(std::vector<sp<IBinder>>{binder}, std::move(provider)); } +uint32_t getCode(FuzzedDataProvider& provider) { + if (provider.ConsumeBool()) { + return provider.ConsumeIntegral<uint32_t>(); + } + + // Most of the AIDL services will have small set of transaction codes. + if (provider.ConsumeBool()) { + return provider.ConsumeIntegralInRange<uint32_t>(0, 100); + } + + if (provider.ConsumeBool()) { + return provider.PickValueInArray<uint32_t>( + {IBinder::DUMP_TRANSACTION, IBinder::PING_TRANSACTION, + IBinder::SHELL_COMMAND_TRANSACTION, IBinder::INTERFACE_TRANSACTION, + IBinder::SYSPROPS_TRANSACTION, IBinder::EXTENSION_TRANSACTION, + IBinder::TWEET_TRANSACTION, IBinder::LIKE_TRANSACTION}); + } + + return provider.ConsumeIntegralInRange<uint32_t>(aidl::kLastMetaMethodId, + aidl::kFirstMetaMethodId); +} + void fuzzService(const std::vector<sp<IBinder>>& binders, FuzzedDataProvider&& provider) { RandomParcelOptions options{ .extraBinders = binders, @@ -61,16 +85,7 @@ void fuzzService(const std::vector<sp<IBinder>>& binders, FuzzedDataProvider&& p } while (provider.remaining_bytes() > 0) { - // Most of the AIDL services will have small set of transaction codes. - // TODO(b/295942369) : Add remaining transact codes from IBinder.h - uint32_t code = provider.ConsumeBool() ? provider.ConsumeIntegral<uint32_t>() - : provider.ConsumeBool() - ? provider.ConsumeIntegralInRange<uint32_t>(0, 100) - : provider.PickValueInArray<uint32_t>( - {IBinder::DUMP_TRANSACTION, IBinder::PING_TRANSACTION, - IBinder::SHELL_COMMAND_TRANSACTION, IBinder::INTERFACE_TRANSACTION, - IBinder::SYSPROPS_TRANSACTION, IBinder::EXTENSION_TRANSACTION, - IBinder::TWEET_TRANSACTION, IBinder::LIKE_TRANSACTION}); + uint32_t code = getCode(provider); uint32_t flags = provider.ConsumeIntegral<uint32_t>(); Parcel data; // for increased fuzz coverage diff --git a/libs/binder/trusty/RpcTransportTipcTrusty.cpp b/libs/binder/trusty/RpcTransportTipcTrusty.cpp index c74ba0a65e..65ad896026 100644 --- a/libs/binder/trusty/RpcTransportTipcTrusty.cpp +++ b/libs/binder/trusty/RpcTransportTipcTrusty.cpp @@ -47,6 +47,71 @@ public: return mHaveMessage ? OK : WOULD_BLOCK; } + void moveMsgStart(ipc_msg_t* msg, size_t msg_size, size_t offset) { + LOG_ALWAYS_FATAL_IF(offset > msg_size, "tried to move message past its end %zd>%zd", offset, + msg_size); + while (true) { + if (offset == 0) { + break; + } + if (offset >= msg->iov[0].iov_len) { + // Move to the next iov, this one was sent already + offset -= msg->iov[0].iov_len; + msg->iov++; + msg->num_iov -= 1; + } else { + // We need to move the base of the current iov + msg->iov[0].iov_len -= offset; + msg->iov[0].iov_base = static_cast<char*>(msg->iov[0].iov_base) + offset; + offset = 0; + } + } + // We only send handles on the first message. This can be changed in the future if we want + // to send more handles than the maximum per message limit (which would require sending + // multiple messages). The current code makes sure that we send less handles than the + // maximum trusty allows. + msg->num_handles = 0; + } + + status_t sendTrustyMsg(ipc_msg_t* msg, size_t msg_size) { + do { + ssize_t rc = send_msg(mSocket.fd.get(), msg); + if (rc == ERR_NOT_ENOUGH_BUFFER) { + // Peer is blocked, wait until it unblocks. + // TODO: when tipc supports a send-unblocked handler, + // save the message here in a queue and retry it asynchronously + // when the handler gets called by the library + uevent uevt; + do { + rc = ::wait(mSocket.fd.get(), &uevt, INFINITE_TIME); + if (rc < 0) { + return statusFromTrusty(rc); + } + if (uevt.event & IPC_HANDLE_POLL_HUP) { + return DEAD_OBJECT; + } + } while (!(uevt.event & IPC_HANDLE_POLL_SEND_UNBLOCKED)); + + // Retry the send, it should go through this time because + // sending is now unblocked + rc = send_msg(mSocket.fd.get(), msg); + } + if (rc < 0) { + return statusFromTrusty(rc); + } + size_t sent_bytes = static_cast<size_t>(rc); + if (sent_bytes < msg_size) { + moveMsgStart(msg, msg_size, static_cast<size_t>(sent_bytes)); + msg_size -= sent_bytes; + } else { + LOG_ALWAYS_FATAL_IF(static_cast<size_t>(rc) != msg_size, + "Sent the wrong number of bytes %zd!=%zu", rc, msg_size); + break; + } + } while (true); + return OK; + } + status_t interruptableWriteFully( FdTrigger* /*fdTrigger*/, iovec* iovs, int niovs, const std::optional<SmallFunction<status_t()>>& /*altPoll*/, @@ -86,34 +151,7 @@ public: msg.handles = msgHandles; } - ssize_t rc = send_msg(mSocket.fd.get(), &msg); - if (rc == ERR_NOT_ENOUGH_BUFFER) { - // Peer is blocked, wait until it unblocks. - // TODO: when tipc supports a send-unblocked handler, - // save the message here in a queue and retry it asynchronously - // when the handler gets called by the library - uevent uevt; - do { - rc = ::wait(mSocket.fd.get(), &uevt, INFINITE_TIME); - if (rc < 0) { - return statusFromTrusty(rc); - } - if (uevt.event & IPC_HANDLE_POLL_HUP) { - return DEAD_OBJECT; - } - } while (!(uevt.event & IPC_HANDLE_POLL_SEND_UNBLOCKED)); - - // Retry the send, it should go through this time because - // sending is now unblocked - rc = send_msg(mSocket.fd.get(), &msg); - } - if (rc < 0) { - return statusFromTrusty(rc); - } - LOG_ALWAYS_FATAL_IF(static_cast<size_t>(rc) != size, - "Sent the wrong number of bytes %zd!=%zu", rc, size); - - return OK; + return sendTrustyMsg(&msg, size); } status_t interruptableReadFully( diff --git a/libs/binder/trusty/binderRpcTest/manifest.json b/libs/binder/trusty/binderRpcTest/manifest.json index 6e20b8a7ad..da0f2ed2d8 100644 --- a/libs/binder/trusty/binderRpcTest/manifest.json +++ b/libs/binder/trusty/binderRpcTest/manifest.json @@ -1,6 +1,6 @@ { "uuid": "9dbe9fb8-60fd-4bdd-af86-03e95d7ad78b", "app_name": "binderRpcTest", - "min_heap": 262144, + "min_heap": 4194304, "min_stack": 20480 } diff --git a/libs/binder/trusty/binderRpcTest/service/manifest.json b/libs/binder/trusty/binderRpcTest/service/manifest.json index d2a1fc0a48..55ff49c0b8 100644 --- a/libs/binder/trusty/binderRpcTest/service/manifest.json +++ b/libs/binder/trusty/binderRpcTest/service/manifest.json @@ -1,7 +1,7 @@ { "uuid": "87e424e5-69d7-4bbd-8b7c-7e24812cbc94", "app_name": "binderRpcTestService", - "min_heap": 65536, + "min_heap": 4194304, "min_stack": 20480, "mgmt_flags": { "restart_on_exit": true, diff --git a/libs/binder/trusty/include/binder/RpcServerTrusty.h b/libs/binder/trusty/include/binder/RpcServerTrusty.h index 583ad015e1..127676bf9a 100644 --- a/libs/binder/trusty/include/binder/RpcServerTrusty.h +++ b/libs/binder/trusty/include/binder/RpcServerTrusty.h @@ -94,9 +94,8 @@ private: static sp<RpcServer> makeRpcServer(std::unique_ptr<RpcTransportCtx> ctx) { auto rpcServer = sp<RpcServer>::make(std::move(ctx)); - // TODO(b/266741352): follow-up to prevent needing this in the future - // Trusty needs to be set to the latest stable version that is in prebuilts there. - LOG_ALWAYS_FATAL_IF(!rpcServer->setProtocolVersion(0)); + // By default we use the latest stable version. + LOG_ALWAYS_FATAL_IF(!rpcServer->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION)); return rpcServer; } diff --git a/libs/binder/trusty/rust/binder_rpc_server_bindgen/cpp/ARpcServerTrusty.cpp b/libs/binder/trusty/rust/binder_rpc_server_bindgen/cpp/ARpcServerTrusty.cpp index 451383a90a..12e347e4f3 100644 --- a/libs/binder/trusty/rust/binder_rpc_server_bindgen/cpp/ARpcServerTrusty.cpp +++ b/libs/binder/trusty/rust/binder_rpc_server_bindgen/cpp/ARpcServerTrusty.cpp @@ -27,6 +27,13 @@ using android::RpcTransportCtxFactoryTipcTrusty; using android::sp; using android::wp; +// The default behavior in trusty is to allow handles to be passed with tipc IPC. +// We add mode NONE so that servers do not reject connections from clients who do +// not change their default transport mode. +static const std::vector<RpcSession::FileDescriptorTransportMode> TRUSTY_SERVER_SUPPORTED_FD_MODES = + {RpcSession::FileDescriptorTransportMode::TRUSTY, + RpcSession::FileDescriptorTransportMode::NONE}; + struct ARpcServerTrusty { sp<RpcServer> mRpcServer; @@ -53,6 +60,8 @@ ARpcServerTrusty* ARpcServerTrusty_newPerSession(AIBinder* (*cb)(const void*, si return nullptr; } + rpcServer->setSupportedFileDescriptorTransportModes(TRUSTY_SERVER_SUPPORTED_FD_MODES); + rpcServer->setPerSessionRootObject( [cb, cbArgSp](wp<RpcSession> /*session*/, const void* addrPtr, size_t len) { auto* aib = (*cb)(addrPtr, len, cbArgSp.get()); diff --git a/libs/binder/trusty/rust/binder_rpc_test/binder_rpc_test_session/lib.rs b/libs/binder/trusty/rust/binder_rpc_test/binder_rpc_test_session/lib.rs index 22cba44975..caf3117195 100644 --- a/libs/binder/trusty/rust/binder_rpc_test/binder_rpc_test_session/lib.rs +++ b/libs/binder/trusty/rust/binder_rpc_test/binder_rpc_test_session/lib.rs @@ -82,6 +82,9 @@ impl IBinderRpcTest for MyBinderRpcSession { fn repeatBinder(&self, _binder: Option<&SpIBinder>) -> Result<Option<SpIBinder>, Status> { todo!() } + fn repeatBytes(&self, _bytes: &[u8]) -> Result<Vec<u8>, Status> { + todo!() + } fn holdBinder(&self, _binder: Option<&SpIBinder>) -> Result<(), Status> { todo!() } diff --git a/libs/binder/trusty/rust/binder_rpc_test/service/main.rs b/libs/binder/trusty/rust/binder_rpc_test/service/main.rs index c4a758a214..6f454be2b6 100644 --- a/libs/binder/trusty/rust/binder_rpc_test/service/main.rs +++ b/libs/binder/trusty/rust/binder_rpc_test/service/main.rs @@ -96,6 +96,9 @@ impl IBinderRpcTest for TestService { None => Err(Status::from(StatusCode::BAD_VALUE)), } } + fn repeatBytes(&self, _bytes: &[u8]) -> Result<Vec<u8>, Status> { + todo!() + } fn holdBinder(&self, binder: Option<&SpIBinder>) -> Result<(), Status> { *HOLD_BINDER.lock().unwrap() = binder.cloned(); Ok(()) diff --git a/libs/binder/trusty/rust/binder_rpc_unstable_bindgen/rules.mk b/libs/binder/trusty/rust/binder_rpc_unstable_bindgen/rules.mk index ef1b7c3cf8..40fc21867d 100644 --- a/libs/binder/trusty/rust/binder_rpc_unstable_bindgen/rules.mk +++ b/libs/binder/trusty/rust/binder_rpc_unstable_bindgen/rules.mk @@ -30,6 +30,8 @@ MODULE_LIBRARY_DEPS += \ trusty/user/base/lib/libstdc++-trusty \ trusty/user/base/lib/trusty-sys \ +MODULE_SRCDEPS := $(LIBBINDER_DIR)/include_rpc_unstable/binder_rpc_unstable.hpp + MODULE_BINDGEN_SRC_HEADER := $(LOCAL_DIR)/BinderBindings.hpp MODULE_BINDGEN_FLAGS += \ diff --git a/libs/binderdebug/stats.cpp b/libs/binderdebug/stats.cpp index 9c26afaa97..972fbd5295 100644 --- a/libs/binderdebug/stats.cpp +++ b/libs/binderdebug/stats.cpp @@ -22,9 +22,9 @@ #include <inttypes.h> -namespace android { +int main() { + using namespace android; -extern "C" int main() { // ignore args - we only print csv // we should use a csv library here for escaping, because @@ -58,5 +58,3 @@ extern "C" int main() { } return 0; } - -} // namespace android diff --git a/libs/binderdebug/tests/binderdebug_test.cpp b/libs/binderdebug/tests/binderdebug_test.cpp index ea799c06a2..ad2b581abc 100644 --- a/libs/binderdebug/tests/binderdebug_test.cpp +++ b/libs/binderdebug/tests/binderdebug_test.cpp @@ -60,8 +60,15 @@ TEST(BinderDebugTests, BinderThreads) { EXPECT_GE(pidInfo.threadCount, 1); } -extern "C" { +} // namespace test +} // namespace binderdebug +} // namespace android + int main(int argc, char** argv) { + using namespace android; + using namespace android::binderdebug; + using namespace android::binderdebug::test; + ::testing::InitGoogleTest(&argc, argv); // Create a child/client process to call into the main process so we can ensure @@ -84,7 +91,3 @@ int main(int argc, char** argv) { return RUN_ALL_TESTS(); } -} // extern "C" -} // namespace test -} // namespace binderdebug -} // namespace android diff --git a/libs/debugstore/rust/src/core.rs b/libs/debugstore/rust/src/core.rs index 6bf79d4e57..16147dd092 100644 --- a/libs/debugstore/rust/src/core.rs +++ b/libs/debugstore/rust/src/core.rs @@ -48,7 +48,7 @@ impl DebugStore { /// /// This constant is used as a part of the debug store's data format, /// allowing for version tracking and compatibility checks. - const ENCODE_VERSION: u32 = 1; + const ENCODE_VERSION: u32 = 3; /// Creates a new instance of `DebugStore` with specified event limit and maximum delay. fn new() -> Self { @@ -123,20 +123,25 @@ impl DebugStore { impl fmt::Display for DebugStore { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // Write the debug store header information let uptime_now = uptimeMillis(); write!(f, "{},{},{}::", Self::ENCODE_VERSION, self.event_store.len(), uptime_now)?; + // Join events with a separator write!( f, "{}", - self.event_store.fold(String::new(), |mut acc, event| { + self.event_store.rfold(String::new(), |mut acc, event| { if !acc.is_empty() { acc.push_str("||"); } acc.push_str(&event.to_string()); acc }) - ) + )?; + + // Write the debug store footer + write!(f, ";;") } } diff --git a/libs/debugstore/rust/src/storage.rs b/libs/debugstore/rust/src/storage.rs index 2ad7f4e0b4..47760f3f21 100644 --- a/libs/debugstore/rust/src/storage.rs +++ b/libs/debugstore/rust/src/storage.rs @@ -32,14 +32,18 @@ impl<T, const N: usize> Storage<T, N> { self.insertion_buffer.force_push(value); } - /// Folds over the elements in the storage using the provided function. - pub fn fold<U, F>(&self, init: U, mut func: F) -> U + /// Folds over the elements in the storage in reverse order using the provided function. + pub fn rfold<U, F>(&self, init: U, mut func: F) -> U where F: FnMut(U, &T) -> U, { - let mut acc = init; + let mut items = Vec::new(); while let Some(value) = self.insertion_buffer.pop() { - acc = func(acc, &value); + items.push(value); + } + let mut acc = init; + for value in items.iter().rev() { + acc = func(acc, value); } acc } @@ -59,18 +63,18 @@ mod tests { let storage = Storage::<i32, 10>::new(); storage.insert(7); - let sum = storage.fold(0, |acc, &x| acc + x); + let sum = storage.rfold(0, |acc, &x| acc + x); assert_eq!(sum, 7, "The sum of the elements should be equal to the inserted value."); } #[test] - fn test_fold_functionality() { + fn test_rfold_functionality() { let storage = Storage::<i32, 5>::new(); storage.insert(1); storage.insert(2); storage.insert(3); - let sum = storage.fold(0, |acc, &x| acc + x); + let sum = storage.rfold(0, |acc, &x| acc + x); assert_eq!( sum, 6, "The sum of the elements should be equal to the sum of inserted values." @@ -84,13 +88,13 @@ mod tests { storage.insert(2); storage.insert(5); - let first_sum = storage.fold(0, |acc, &x| acc + x); + let first_sum = storage.rfold(0, |acc, &x| acc + x); assert_eq!(first_sum, 8, "The sum of the elements should be equal to the inserted values."); storage.insert(30); storage.insert(22); - let second_sum = storage.fold(0, |acc, &x| acc + x); + let second_sum = storage.rfold(0, |acc, &x| acc + x); assert_eq!( second_sum, 52, "The sum of the elements should be equal to the inserted values." @@ -103,7 +107,7 @@ mod tests { storage.insert(1); // This value should overwrite the previously inserted value (1). storage.insert(4); - let sum = storage.fold(0, |acc, &x| acc + x); + let sum = storage.rfold(0, |acc, &x| acc + x); assert_eq!(sum, 4, "The sum of the elements should be equal to the inserted values."); } @@ -128,7 +132,24 @@ mod tests { thread.join().expect("Thread should finish without panicking"); } - let count = storage.fold(0, |acc, _| acc + 1); + let count = storage.rfold(0, |acc, _| acc + 1); assert_eq!(count, 100, "Storage should be filled to its limit with concurrent insertions."); } + + #[test] + fn test_rfold_order() { + let storage = Storage::<i32, 5>::new(); + storage.insert(1); + storage.insert(2); + storage.insert(3); + + let mut result = Vec::new(); + storage.rfold((), |_, &x| result.push(x)); + + assert_eq!( + result, + vec![3, 2, 1], + "Elements should be processed in reverse order of insertion" + ); + } } diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp index a9bd11e41d..43ee33eb86 100644 --- a/libs/dumputils/dump_utils.cpp +++ b/libs/dumputils/dump_utils.cpp @@ -101,7 +101,7 @@ static const std::vector<std::string> aidl_interfaces_to_dump { "android.hardware.automotive.remoteaccess.IRemoteAccess", "android.hardware.automotive.vehicle.IVehicle", "android.hardware.biometrics.face.IBiometricsFace", - "android.hardware.biometrics.fingerprint.IBiometricsFingerprint", + "android.hardware.biometrics.fingerprint.IFingerprint", "android.hardware.camera.provider.ICameraProvider", "android.hardware.drm.IDrmFactory", "android.hardware.graphics.allocator.IAllocator", diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp index 368f5e079c..5244442ff3 100644 --- a/libs/ftl/Android.bp +++ b/libs/ftl/Android.bp @@ -21,10 +21,12 @@ cc_test { "enum_test.cpp", "expected_test.cpp", "fake_guard_test.cpp", + "finalizer_test.cpp", "flags_test.cpp", "function_test.cpp", "future_test.cpp", "hash_test.cpp", + "ignore_test.cpp", "match_test.cpp", "mixins_test.cpp", "non_null_test.cpp", diff --git a/libs/ftl/finalizer_test.cpp b/libs/ftl/finalizer_test.cpp new file mode 100644 index 0000000000..4f5c2258db --- /dev/null +++ b/libs/ftl/finalizer_test.cpp @@ -0,0 +1,209 @@ +/* + * 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 <memory> +#include <type_traits> +#include <utility> + +#include <ftl/finalizer.h> +#include <gtest/gtest.h> + +namespace android::test { + +namespace { + +struct Counter { + constexpr auto increment_fn() { + return [this] { ++value_; }; + } + + auto increment_finalizer() { + return ftl::Finalizer([this] { ++value_; }); + } + + [[nodiscard]] constexpr auto value() const -> int { return value_; } + + private: + int value_ = 0; +}; + +struct CounterPair { + constexpr auto increment_first_fn() { return first.increment_fn(); } + constexpr auto increment_second_fn() { return second.increment_fn(); } + [[nodiscard]] constexpr auto values() const -> std::pair<int, int> { + return {first.value(), second.value()}; + } + + private: + Counter first; + Counter second; +}; + +} // namespace + +TEST(Finalizer, DefaultConstructionAndNoOpDestructionWhenPolymorphicType) { + ftl::FinalizerStd finalizer1; + ftl::FinalizerFtl finalizer2; + ftl::FinalizerFtl1 finalizer3; + ftl::FinalizerFtl2 finalizer4; + ftl::FinalizerFtl3 finalizer5; +} + +TEST(Finalizer, InvokesTheFunctionOnDestruction) { + Counter counter; + { + const auto finalizer = counter.increment_finalizer(); + EXPECT_EQ(counter.value(), 0); + } + EXPECT_EQ(counter.value(), 1); +} + +TEST(Finalizer, InvocationCanBeCanceled) { + Counter counter; + { + auto finalizer = counter.increment_finalizer(); + EXPECT_EQ(counter.value(), 0); + finalizer.cancel(); + EXPECT_EQ(counter.value(), 0); + } + EXPECT_EQ(counter.value(), 0); +} + +TEST(Finalizer, InvokesTheFunctionOnce) { + Counter counter; + { + auto finalizer = counter.increment_finalizer(); + EXPECT_EQ(counter.value(), 0); + finalizer(); + EXPECT_EQ(counter.value(), 1); + finalizer(); + EXPECT_EQ(counter.value(), 1); + } + EXPECT_EQ(counter.value(), 1); +} + +TEST(Finalizer, SelfInvocationIsAllowedAndANoOp) { + Counter counter; + ftl::FinalizerStd finalizer; + finalizer = ftl::Finalizer([&]() { + counter.increment_fn()(); + finalizer(); // recursive invocation should do nothing. + }); + EXPECT_EQ(counter.value(), 0); + finalizer(); + EXPECT_EQ(counter.value(), 1); +} + +TEST(Finalizer, MoveConstruction) { + Counter counter; + { + ftl::FinalizerStd outer_finalizer = counter.increment_finalizer(); + EXPECT_EQ(counter.value(), 0); + { + ftl::FinalizerStd inner_finalizer = std::move(outer_finalizer); + static_assert(std::is_same_v<decltype(inner_finalizer), decltype(outer_finalizer)>); + EXPECT_EQ(counter.value(), 0); + } + EXPECT_EQ(counter.value(), 1); + } + EXPECT_EQ(counter.value(), 1); +} + +TEST(Finalizer, MoveConstructionWithImplicitConversion) { + Counter counter; + { + auto outer_finalizer = counter.increment_finalizer(); + EXPECT_EQ(counter.value(), 0); + { + ftl::FinalizerStd inner_finalizer = std::move(outer_finalizer); + static_assert(!std::is_same_v<decltype(inner_finalizer), decltype(outer_finalizer)>); + EXPECT_EQ(counter.value(), 0); + } + EXPECT_EQ(counter.value(), 1); + } + EXPECT_EQ(counter.value(), 1); +} + +TEST(Finalizer, MoveAssignment) { + CounterPair pair; + { + ftl::FinalizerStd outer_finalizer = ftl::Finalizer(pair.increment_first_fn()); + EXPECT_EQ(pair.values(), std::make_pair(0, 0)); + + { + ftl::FinalizerStd inner_finalizer = ftl::Finalizer(pair.increment_second_fn()); + static_assert(std::is_same_v<decltype(inner_finalizer), decltype(outer_finalizer)>); + EXPECT_EQ(pair.values(), std::make_pair(0, 0)); + inner_finalizer = std::move(outer_finalizer); + EXPECT_EQ(pair.values(), std::make_pair(0, 1)); + } + EXPECT_EQ(pair.values(), std::make_pair(1, 1)); + } + EXPECT_EQ(pair.values(), std::make_pair(1, 1)); +} + +TEST(Finalizer, MoveAssignmentWithImplicitConversion) { + CounterPair pair; + { + auto outer_finalizer = ftl::Finalizer(pair.increment_first_fn()); + EXPECT_EQ(pair.values(), std::make_pair(0, 0)); + + { + ftl::FinalizerStd inner_finalizer = ftl::Finalizer(pair.increment_second_fn()); + static_assert(!std::is_same_v<decltype(inner_finalizer), decltype(outer_finalizer)>); + EXPECT_EQ(pair.values(), std::make_pair(0, 0)); + inner_finalizer = std::move(outer_finalizer); + EXPECT_EQ(pair.values(), std::make_pair(0, 1)); + } + EXPECT_EQ(pair.values(), std::make_pair(1, 1)); + } + EXPECT_EQ(pair.values(), std::make_pair(1, 1)); +} + +TEST(Finalizer, NullifiesTheFunctionWhenInvokedIfPossible) { + auto shared = std::make_shared<int>(0); + std::weak_ptr<int> weak = shared; + + int count = 0; + { + auto lambda = [capture = std::move(shared)]() {}; + auto finalizer = ftl::Finalizer(std::move(lambda)); + EXPECT_FALSE(weak.expired()); + + // A lambda is not nullable. Invoking the finalizer cannot destroy it to destroy the lambda's + // capture. + finalizer(); + EXPECT_FALSE(weak.expired()); + } + // The lambda is only destroyed when the finalizer instance is destroyed. + EXPECT_TRUE(weak.expired()); + + shared = std::make_shared<int>(0); + weak = shared; + + { + auto lambda = [capture = std::move(shared)]() {}; + auto finalizer = ftl::FinalizerStd(std::move(lambda)); + EXPECT_FALSE(weak.expired()); + + // Since std::function is used, and is nullable, invoking the finalizer will destroy the + // contained function, which will destroy the lambda's capture. + finalizer(); + EXPECT_TRUE(weak.expired()); + } +} + +} // namespace android::test diff --git a/libs/ftl/ignore_test.cpp b/libs/ftl/ignore_test.cpp new file mode 100644 index 0000000000..5d5c67b8b1 --- /dev/null +++ b/libs/ftl/ignore_test.cpp @@ -0,0 +1,47 @@ +/* + * Copyright 2025 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 <string> + +#include <ftl/ignore.h> +#include <gtest/gtest.h> + +namespace android::test { +namespace { + +// Keep in sync with the example usage in the header file. + +void ftl_ignore_multiple(int arg1, const char* arg2, std::string arg3) { + // When invoked, all the arguments are ignored. + ftl::ignore(arg1, arg2, arg3); +} + +void ftl_ignore_single(int arg) { + // It can be used like std::ignore to ignore a single value + ftl::ignore = arg; +} + +} // namespace + +TEST(Ignore, Example) { + // The real example test is that there are no compiler warnings for unused arguments above. + + // Use the example functions to avoid a compiler warning about unused functions. + ftl_ignore_multiple(0, "a", "b"); + ftl_ignore_single(0); +} + +} // namespace android::test diff --git a/libs/graphicsenv/Android.bp b/libs/graphicsenv/Android.bp index af50a2980c..dce7778229 100644 --- a/libs/graphicsenv/Android.bp +++ b/libs/graphicsenv/Android.bp @@ -21,10 +21,27 @@ package { default_applicable_licenses: ["frameworks_native_license"], } +aconfig_declarations { + name: "graphicsenv_flags", + package: "com.android.graphics.graphicsenv.flags", + container: "system", + srcs: ["graphicsenv_flags.aconfig"], +} + +cc_aconfig_library { + name: "graphicsenv_flags_c_lib", + aconfig_declarations: "graphicsenv_flags", +} + cc_library_shared { name: "libgraphicsenv", + defaults: [ + "aconfig_lib_cc_static_link.defaults", + ], + srcs: [ + "FeatureOverrides.cpp", "GpuStatsInfo.cpp", "GraphicsEnv.cpp", "IGpuService.cpp", @@ -35,6 +52,10 @@ cc_library_shared { "-Werror", ], + static_libs: [ + "graphicsenv_flags_c_lib", + ], + shared_libs: [ "libbase", "libbinder", @@ -42,6 +63,7 @@ cc_library_shared { "libdl_android", "liblog", "libutils", + "server_configurable_flags", ], header_libs: [ diff --git a/libs/graphicsenv/FeatureOverrides.cpp b/libs/graphicsenv/FeatureOverrides.cpp new file mode 100644 index 0000000000..9e7a4cf4a1 --- /dev/null +++ b/libs/graphicsenv/FeatureOverrides.cpp @@ -0,0 +1,209 @@ +/* + * Copyright 2025 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 <cinttypes> + +#include <android-base/stringprintf.h> +#include <binder/Parcel.h> +#include <graphicsenv/FeatureOverrides.h> + +namespace android { + +using base::StringAppendF; + +status_t FeatureConfig::writeToParcel(Parcel* parcel) const { + status_t status; + + status = parcel->writeUtf8AsUtf16(mFeatureName); + if (status != OK) { + return status; + } + status = parcel->writeBool(mEnabled); + if (status != OK) { + return status; + } + // Number of GPU vendor IDs. + status = parcel->writeVectorSize(mGpuVendorIDs); + if (status != OK) { + return status; + } + // GPU vendor IDs. + for (const auto& vendorID : mGpuVendorIDs) { + status = parcel->writeUint32(vendorID); + if (status != OK) { + return status; + } + } + + return OK; +} + +status_t FeatureConfig::readFromParcel(const Parcel* parcel) { + status_t status; + + status = parcel->readUtf8FromUtf16(&mFeatureName); + if (status != OK) { + return status; + } + status = parcel->readBool(&mEnabled); + if (status != OK) { + return status; + } + // Number of GPU vendor IDs. + int numGpuVendorIDs; + status = parcel->readInt32(&numGpuVendorIDs); + if (status != OK) { + return status; + } + // GPU vendor IDs. + for (int i = 0; i < numGpuVendorIDs; i++) { + uint32_t gpuVendorIdUint; + status = parcel->readUint32(&gpuVendorIdUint); + if (status != OK) { + return status; + } + mGpuVendorIDs.emplace_back(gpuVendorIdUint); + } + + return OK; +} + +std::string FeatureConfig::toString() const { + std::string result; + StringAppendF(&result, "Feature: %s\n", mFeatureName.c_str()); + StringAppendF(&result, " Status: %s\n", mEnabled ? "enabled" : "disabled"); + for (const auto& vendorID : mGpuVendorIDs) { + // vkjson outputs decimal, so print both formats. + StringAppendF(&result, " GPU Vendor ID: 0x%04X (%d)\n", vendorID, vendorID); + } + + return result; +} + +status_t FeatureOverrides::writeToParcel(Parcel* parcel) const { + status_t status; + // Number of global feature configs. + status = parcel->writeVectorSize(mGlobalFeatures); + if (status != OK) { + return status; + } + // Global feature configs. + for (const auto& cfg : mGlobalFeatures) { + status = cfg.writeToParcel(parcel); + if (status != OK) { + return status; + } + } + // Number of package feature overrides. + status = parcel->writeInt32(static_cast<int32_t>(mPackageFeatures.size())); + if (status != OK) { + return status; + } + for (const auto& feature : mPackageFeatures) { + // Package name. + status = parcel->writeUtf8AsUtf16(feature.first); + if (status != OK) { + return status; + } + // Number of package feature configs. + status = parcel->writeVectorSize(feature.second); + if (status != OK) { + return status; + } + // Package feature configs. + for (const auto& cfg : feature.second) { + status = cfg.writeToParcel(parcel); + if (status != OK) { + return status; + } + } + } + + return OK; +} + +status_t FeatureOverrides::readFromParcel(const Parcel* parcel) { + status_t status; + + // Number of global feature configs. + status = parcel->resizeOutVector(&mGlobalFeatures); + if (status != OK) { + return status; + } + // Global feature configs. + for (FeatureConfig& cfg : mGlobalFeatures) { + status = cfg.readFromParcel(parcel); + if (status != OK) { + return status; + } + } + + // Number of package feature overrides. + int numPkgOverrides; + status = parcel->readInt32(&numPkgOverrides); + if (status != OK) { + return status; + } + // Package feature overrides. + for (int i = 0; i < numPkgOverrides; i++) { + // Package name. + std::string name; + status = parcel->readUtf8FromUtf16(&name); + if (status != OK) { + return status; + } + std::vector<FeatureConfig> cfgs; + // Number of package feature configs. + int numCfgs; + status = parcel->readInt32(&numCfgs); + if (status != OK) { + return status; + } + // Package feature configs. + for (int j = 0; j < numCfgs; j++) { + FeatureConfig cfg; + status = cfg.readFromParcel(parcel); + if (status != OK) { + return status; + } + cfgs.emplace_back(cfg); + } + mPackageFeatures[name] = cfgs; + } + + return OK; +} + +std::string FeatureOverrides::toString() const { + std::string result; + result.append("Global Features:\n"); + for (auto& cfg : mGlobalFeatures) { + result.append(" " + cfg.toString()); + } + result.append("\n"); + result.append("Package Features:\n"); + for (const auto& packageFeature : mPackageFeatures) { + result.append(" Package:"); + StringAppendF(&result, " %s\n", packageFeature.first.c_str()); + for (auto& cfg : packageFeature.second) { + result.append(" " + cfg.toString()); + } + } + + return result; +} + +} // namespace android diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp index a8d5fe7371..03e6456ea6 100644 --- a/libs/graphicsenv/GraphicsEnv.cpp +++ b/libs/graphicsenv/GraphicsEnv.cpp @@ -29,6 +29,7 @@ #include <android-base/strings.h> #include <android/dlext.h> #include <binder/IServiceManager.h> +#include <com_android_graphics_graphicsenv_flags.h> #include <graphicsenv/IGpuService.h> #include <log/log.h> #include <nativeloader/dlext_namespaces.h> @@ -70,6 +71,8 @@ static bool isVndkEnabled() { } } // namespace +namespace graphicsenv_flags = com::android::graphics::graphicsenv::flags; + namespace android { enum NativeLibrary { @@ -596,7 +599,7 @@ bool GraphicsEnv::shouldUseAngle() { // If path is set to nonempty and shouldUseNativeDriver is true, ANGLE will be used regardless. void GraphicsEnv::setAngleInfo(const std::string& path, const bool shouldUseNativeDriver, const std::string& packageName, - const std::vector<std::string> eglFeatures) { + const std::vector<std::string>& eglFeatures) { if (mShouldUseAngle) { // ANGLE is already set up for this application process, even if the application // needs to switch from apk to system or vice versa, the application process must @@ -606,11 +609,11 @@ void GraphicsEnv::setAngleInfo(const std::string& path, const bool shouldUseNati return; } - mAngleEglFeatures = std::move(eglFeatures); + mAngleEglFeatures = eglFeatures; ALOGV("setting ANGLE path to '%s'", path.c_str()); - mAnglePath = std::move(path); + mAnglePath = path; ALOGV("setting app package name to '%s'", packageName.c_str()); - mPackageName = std::move(packageName); + mPackageName = packageName; if (mAnglePath == "system") { mShouldUseSystemAngle = true; } @@ -618,16 +621,62 @@ void GraphicsEnv::setAngleInfo(const std::string& path, const bool shouldUseNati mShouldUseAngle = true; } mShouldUseNativeDriver = shouldUseNativeDriver; + + if (mShouldUseAngle) { + updateAngleFeatureOverrides(); + } } std::string& GraphicsEnv::getPackageName() { return mPackageName; } +// List of ANGLE features to enable, specified in the Global.Settings value "angle_egl_features". const std::vector<std::string>& GraphicsEnv::getAngleEglFeatures() { return mAngleEglFeatures; } +// List of ANGLE features to override (enabled or disable). +// The list of overrides is loaded and parsed by GpuService. +void GraphicsEnv::updateAngleFeatureOverrides() { + if (!graphicsenv_flags::angle_feature_overrides()) { + return; + } + + const sp<IGpuService> gpuService = getGpuService(); + if (!gpuService) { + ALOGE("No GPU service"); + return; + } + + mFeatureOverrides = gpuService->getFeatureOverrides(); +} + +void GraphicsEnv::getAngleFeatureOverrides(std::vector<const char*>& enabled, + std::vector<const char*>& disabled) { + if (!graphicsenv_flags::angle_feature_overrides()) { + return; + } + + for (const FeatureConfig& feature : mFeatureOverrides.mGlobalFeatures) { + if (feature.mEnabled) { + enabled.push_back(feature.mFeatureName.c_str()); + } else { + disabled.push_back(feature.mFeatureName.c_str()); + } + } + + if (mFeatureOverrides.mPackageFeatures.count(mPackageName)) { + for (const FeatureConfig& feature : mFeatureOverrides.mPackageFeatures[mPackageName]) { + if (feature.mEnabled) { + enabled.push_back(feature.mFeatureName.c_str()); + } else { + disabled.push_back(feature.mFeatureName.c_str()); + } + } + } +} + android_namespace_t* GraphicsEnv::getAngleNamespace() { std::lock_guard<std::mutex> lock(mNamespaceMutex); diff --git a/libs/graphicsenv/IGpuService.cpp b/libs/graphicsenv/IGpuService.cpp index 42e7c378a9..9a34aff299 100644 --- a/libs/graphicsenv/IGpuService.cpp +++ b/libs/graphicsenv/IGpuService.cpp @@ -119,6 +119,21 @@ public: } return driverPath; } + + FeatureOverrides getFeatureOverrides() override { + Parcel data, reply; + data.writeInterfaceToken(IGpuService::getInterfaceDescriptor()); + + FeatureOverrides featureOverrides; + status_t error = + remote()->transact(BnGpuService::GET_FEATURE_CONFIG_OVERRIDES, data, &reply); + if (error != OK) { + return featureOverrides; + } + + featureOverrides.readFromParcel(&reply); + return featureOverrides; + } }; IMPLEMENT_META_INTERFACE(GpuService, "android.graphicsenv.IGpuService"); @@ -271,6 +286,15 @@ status_t BnGpuService::onTransact(uint32_t code, const Parcel& data, Parcel* rep toggleAngleAsSystemDriver(enableAngleAsSystemDriver); return OK; } + case GET_FEATURE_CONFIG_OVERRIDES: { + CHECK_INTERFACE(IGpuService, data, reply); + + // Get the FeatureOverrides from gpuservice, which implements the IGpuService interface + // with GpuService::getFeatureOverrides(). + FeatureOverrides featureOverrides = getFeatureOverrides(); + featureOverrides.writeToParcel(reply); + return OK; + } default: return BBinder::onTransact(code, data, reply, flags); } diff --git a/libs/graphicsenv/graphicsenv_flags.aconfig b/libs/graphicsenv/graphicsenv_flags.aconfig new file mode 100644 index 0000000000..efa4bca892 --- /dev/null +++ b/libs/graphicsenv/graphicsenv_flags.aconfig @@ -0,0 +1,9 @@ +package: "com.android.graphics.graphicsenv.flags" +container: "system" + +flag { + name: "angle_feature_overrides" + namespace: "gpu" + description: "This flag controls the ANGLE Feature Overrides in GraphicsEnv." + bug: "372694741" +} diff --git a/libs/graphicsenv/include/graphicsenv/FeatureOverrides.h b/libs/graphicsenv/include/graphicsenv/FeatureOverrides.h new file mode 100644 index 0000000000..5dc613b901 --- /dev/null +++ b/libs/graphicsenv/include/graphicsenv/FeatureOverrides.h @@ -0,0 +1,59 @@ +/* + * 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 <map> +#include <string> +#include <vector> + +#include <binder/Parcelable.h> + +namespace android { + +class FeatureConfig : public Parcelable { +public: + FeatureConfig() = default; + FeatureConfig(const FeatureConfig&) = default; + virtual ~FeatureConfig() = default; + virtual status_t writeToParcel(Parcel* parcel) const; + virtual status_t readFromParcel(const Parcel* parcel); + std::string toString() const; + + std::string mFeatureName; + bool mEnabled; + std::vector<uint32_t> mGpuVendorIDs; +}; + +/* + * Class for transporting OpenGL ES Feature configurations from GpuService to authorized + * recipients. + */ +class FeatureOverrides : public Parcelable { +public: + FeatureOverrides() = default; + FeatureOverrides(const FeatureOverrides&) = default; + virtual ~FeatureOverrides() = default; + virtual status_t writeToParcel(Parcel* parcel) const; + virtual status_t readFromParcel(const Parcel* parcel); + std::string toString() const; + + std::vector<FeatureConfig> mGlobalFeatures; + /* Key: Package Name, Value: Package's Feature Configs */ + std::map<std::string, std::vector<FeatureConfig>> mPackageFeatures; +}; + +} // namespace android diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h index b0ab0b9d22..68219008e8 100644 --- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h +++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h @@ -17,6 +17,7 @@ #ifndef ANDROID_UI_GRAPHICS_ENV_H #define ANDROID_UI_GRAPHICS_ENV_H 1 +#include <graphicsenv/FeatureOverrides.h> #include <graphicsenv/GpuStatsInfo.h> #include <mutex> @@ -114,12 +115,15 @@ public: // If shouldUseNativeDriver is true, it means native GLES drivers must be used for the process. // If path is set to nonempty and shouldUseNativeDriver is true, ANGLE will be used regardless. void setAngleInfo(const std::string& path, const bool shouldUseNativeDriver, - const std::string& packageName, const std::vector<std::string> eglFeatures); + const std::string& packageName, const std::vector<std::string>& eglFeatures); // Get the ANGLE driver namespace. android_namespace_t* getAngleNamespace(); // Get the app package name. std::string& getPackageName(); const std::vector<std::string>& getAngleEglFeatures(); + void updateAngleFeatureOverrides(); + void getAngleFeatureOverrides(std::vector<const char*>& enabled, + std::vector<const char*>& disabled); // Set the persist.graphics.egl system property value. void nativeToggleAngleAsSystemDriver(bool enabled); bool shouldUseSystemAngle(); @@ -177,6 +181,7 @@ private: std::string mPackageName; // ANGLE EGL features; std::vector<std::string> mAngleEglFeatures; + FeatureOverrides mFeatureOverrides; // Whether ANGLE should be used. bool mShouldUseAngle = false; // Whether loader should load system ANGLE. diff --git a/libs/graphicsenv/include/graphicsenv/IGpuService.h b/libs/graphicsenv/include/graphicsenv/IGpuService.h index a0d6e37302..442683a3e9 100644 --- a/libs/graphicsenv/include/graphicsenv/IGpuService.h +++ b/libs/graphicsenv/include/graphicsenv/IGpuService.h @@ -18,6 +18,7 @@ #include <binder/IInterface.h> #include <cutils/compiler.h> +#include <graphicsenv/FeatureOverrides.h> #include <graphicsenv/GpuStatsInfo.h> #include <vector> @@ -55,6 +56,9 @@ public: // sets ANGLE as system GLES driver if enabled==true by setting persist.graphics.egl to true. virtual void toggleAngleAsSystemDriver(bool enabled) = 0; + + // Get the list of features to override. + virtual FeatureOverrides getFeatureOverrides() = 0; }; class BnGpuService : public BnInterface<IGpuService> { @@ -67,6 +71,7 @@ public: TOGGLE_ANGLE_AS_SYSTEM_DRIVER, SET_TARGET_STATS_ARRAY, ADD_VULKAN_ENGINE_NAME, + GET_FEATURE_CONFIG_OVERRIDES, // Always append new enum to the end. }; diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp index 052b519db6..b5fa321fc2 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -266,8 +266,6 @@ filegroup { "FenceMonitor.cpp", "Flags.cpp", "GLConsumer.cpp", - "IConsumerListener.cpp", - "IGraphicBufferConsumer.cpp", "IGraphicBufferProducer.cpp", "IProducerListener.cpp", "ISurfaceComposer.cpp", @@ -283,6 +281,7 @@ filegroup { "SurfaceControl.cpp", "SurfaceComposerClient.cpp", "SyncFeatures.cpp", + "TransactionState.cpp", "VsyncEventData.cpp", "view/Surface.cpp", "WindowInfosListenerReporter.cpp", diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index 38465b0de2..1aae13c1f4 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -197,15 +197,15 @@ BLASTBufferQueue::BLASTBufferQueue(const std::string& name, bool updateDestinati mUpdateDestinationFrame(updateDestinationFrame) { createBufferQueue(&mProducer, &mConsumer); #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) - mBufferItemConsumer = new BLASTBufferItemConsumer(mProducer, mConsumer, - GraphicBuffer::USAGE_HW_COMPOSER | - GraphicBuffer::USAGE_HW_TEXTURE, - 1, false, this); + mBufferItemConsumer = sp<BLASTBufferItemConsumer>::make(mProducer, mConsumer, + GraphicBuffer::USAGE_HW_COMPOSER | + GraphicBuffer::USAGE_HW_TEXTURE, + 1, false, this); #else - mBufferItemConsumer = new BLASTBufferItemConsumer(mConsumer, - GraphicBuffer::USAGE_HW_COMPOSER | - GraphicBuffer::USAGE_HW_TEXTURE, - 1, false, this); + mBufferItemConsumer = sp<BLASTBufferItemConsumer>::make(mConsumer, + GraphicBuffer::USAGE_HW_COMPOSER | + GraphicBuffer::USAGE_HW_TEXTURE, + 1, false, this); #endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) // since the adapter is in the client process, set dequeue timeout // explicitly so that dequeueBuffer will block @@ -222,8 +222,6 @@ BLASTBufferQueue::BLASTBufferQueue(const std::string& name, bool updateDestinati ComposerServiceAIDL::getComposerService()->getMaxAcquiredBufferCount(&mMaxAcquiredBuffers); mBufferItemConsumer->setMaxAcquiredBufferCount(mMaxAcquiredBuffers); mCurrentMaxAcquiredBufferCount = mMaxAcquiredBuffers; - mNumAcquired = 0; - mNumFrameAvailable = 0; TransactionCompletedListener::getInstance()->addQueueStallListener( [&](const std::string& reason) { @@ -244,12 +242,6 @@ BLASTBufferQueue::BLASTBufferQueue(const std::string& name, bool updateDestinati BQA_LOGV("BLASTBufferQueue created"); } -BLASTBufferQueue::BLASTBufferQueue(const std::string& name, const sp<SurfaceControl>& surface, - int width, int height, int32_t format) - : BLASTBufferQueue(name) { - update(surface, width, height, format); -} - BLASTBufferQueue::~BLASTBufferQueue() { TransactionCompletedListener::getInstance()->removeQueueStallListener(this); if (mPendingTransactions.empty()) { @@ -421,14 +413,12 @@ void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence stat.frameEventStats.dequeueReadyTime); } auto currFrameNumber = stat.frameEventStats.frameNumber; - std::vector<ReleaseCallbackId> staleReleases; - for (const auto& [key, value]: mSubmitted) { - if (currFrameNumber > key.framenumber) { - staleReleases.push_back(key); + // Release stale buffers. + for (const auto& [key, _] : mSubmitted) { + if (currFrameNumber <= key.framenumber) { + continue; // not stale. } - } - for (const auto& staleRelease : staleReleases) { - releaseBufferCallbackLocked(staleRelease, + releaseBufferCallbackLocked(key, stat.previousReleaseFence ? stat.previousReleaseFence : Fence::NO_FENCE, @@ -447,7 +437,7 @@ void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence void BLASTBufferQueue::flushShadowQueue() { BQA_LOGV("flushShadowQueue"); - int numFramesToFlush = mNumFrameAvailable; + int32_t numFramesToFlush = mNumFrameAvailable; while (numFramesToFlush > 0) { acquireNextBufferLocked(std::nullopt); numFramesToFlush--; @@ -624,7 +614,7 @@ status_t BLASTBufferQueue::acquireNextBufferLocked( mNumAcquired++; mLastAcquiredFrameNumber = bufferItem.mFrameNumber; ReleaseCallbackId releaseCallbackId(buffer->getId(), mLastAcquiredFrameNumber); - mSubmitted[releaseCallbackId] = bufferItem; + mSubmitted.emplace_or_replace(releaseCallbackId, bufferItem); bool needsDisconnect = false; mBufferItemConsumer->getConnectionEvents(bufferItem.mFrameNumber, &needsDisconnect); @@ -647,7 +637,8 @@ status_t BLASTBufferQueue::acquireNextBufferLocked( bufferItem.mScalingMode, crop); auto releaseBufferCallback = makeReleaseBufferCallbackThunk(); - sp<Fence> fence = bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE; + sp<Fence> fence = + bufferItem.mFence ? sp<Fence>::make(bufferItem.mFence->dup()) : Fence::NO_FENCE; nsecs_t dequeueTime = -1; { @@ -857,7 +848,7 @@ void BLASTBufferQueue::onFrameReplaced(const BufferItem& item) { void BLASTBufferQueue::onFrameDequeued(const uint64_t bufferId) { std::lock_guard _lock{mTimestampMutex}; - mDequeueTimestamps[bufferId] = systemTime(); + mDequeueTimestamps.emplace_or_replace(bufferId, systemTime()); }; void BLASTBufferQueue::onFrameCancelled(const uint64_t bufferId) { @@ -946,15 +937,22 @@ public: : Surface(igbp, controlledByApp, scHandle), mBbq(bbq) {} void allocateBuffers() override { + ATRACE_CALL(); uint32_t reqWidth = mReqWidth ? mReqWidth : mUserWidth; uint32_t reqHeight = mReqHeight ? mReqHeight : mUserHeight; auto gbp = getIGraphicBufferProducer(); - std::thread ([reqWidth, reqHeight, gbp=getIGraphicBufferProducer(), - reqFormat=mReqFormat, reqUsage=mReqUsage] () { + std::thread allocateThread([reqWidth, reqHeight, gbp = getIGraphicBufferProducer(), + reqFormat = mReqFormat, reqUsage = mReqUsage]() { + if (com_android_graphics_libgui_flags_allocate_buffer_priority()) { + androidSetThreadName("allocateBuffers"); + pid_t tid = gettid(); + androidSetThreadPriority(tid, ANDROID_PRIORITY_DISPLAY); + } + gbp->allocateBuffers(reqWidth, reqHeight, reqFormat, reqUsage); - - }).detach(); + }); + allocateThread.detach(); } status_t setFrameRate(float frameRate, int8_t compatibility, @@ -1024,7 +1022,7 @@ sp<Surface> BLASTBufferQueue::getSurface(bool includeSurfaceControlHandle) { if (includeSurfaceControlHandle && mSurfaceControl) { scHandle = mSurfaceControl->getHandle(); } - return new BBQSurface(mProducer, true, scHandle, this); + return sp<BBQSurface>::make(mProducer, true, scHandle, this); } void BLASTBufferQueue::mergeWithNextTransaction(SurfaceComposerClient::Transaction* t, @@ -1130,10 +1128,10 @@ ANDROID_SINGLETON_STATIC_INSTANCE(AsyncWorker); class AsyncProducerListener : public BnProducerListener { private: const sp<IProducerListener> mListener; - -public: AsyncProducerListener(const sp<IProducerListener>& listener) : mListener(listener) {} + friend class sp<AsyncProducerListener>; +public: void onBufferReleased() override { AsyncWorker::getInstance().post([listener = mListener]() { listener->onBufferReleased(); }); } @@ -1187,7 +1185,7 @@ public: return BufferQueueProducer::connect(listener, api, producerControlledByApp, output); } - return BufferQueueProducer::connect(new AsyncProducerListener(listener), api, + return BufferQueueProducer::connect(sp<AsyncProducerListener>::make(listener), api, producerControlledByApp, output); } @@ -1227,16 +1225,12 @@ public: #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) status_t waitForBufferRelease(std::unique_lock<std::mutex>& bufferQueueLock, nsecs_t timeout) const override { + const auto startTime = std::chrono::steady_clock::now(); sp<BLASTBufferQueue> bbq = mBLASTBufferQueue.promote(); if (!bbq) { return OK; } - // Provide a callback for Choreographer to start buffer stuffing recovery when blocked - // on buffer release. - std::function<void()> callbackCopy = bbq->getWaitForBufferReleaseCallback(); - if (callbackCopy) callbackCopy(); - // BufferQueue has already checked if we have a free buffer. If there's an unread interrupt, // we want to ignore it. This must be done before unlocking the BufferQueue lock to ensure // we don't miss an interrupt. @@ -1258,6 +1252,14 @@ public: } bbq->releaseBufferCallback(id, fence, maxAcquiredBufferCount); + const nsecs_t durationNanos = std::chrono::duration_cast<std::chrono::nanoseconds>( + std::chrono::steady_clock::now() - startTime) + .count(); + // Provide a callback for Choreographer to start buffer stuffing recovery when blocked + // on buffer release. + std::function<void(const nsecs_t)> callbackCopy = bbq->getWaitForBufferReleaseCallback(); + if (callbackCopy) callbackCopy(durationNanos); + return OK; } #endif @@ -1349,12 +1351,13 @@ void BLASTBufferQueue::setApplyToken(sp<IBinder> applyToken) { mApplyToken = std::move(applyToken); } -void BLASTBufferQueue::setWaitForBufferReleaseCallback(std::function<void()> callback) { +void BLASTBufferQueue::setWaitForBufferReleaseCallback( + std::function<void(const nsecs_t)> callback) { std::lock_guard _lock{mWaitForBufferReleaseMutex}; mWaitForBufferReleaseCallback = std::move(callback); } -std::function<void()> BLASTBufferQueue::getWaitForBufferReleaseCallback() const { +std::function<void(const nsecs_t)> BLASTBufferQueue::getWaitForBufferReleaseCallback() const { std::lock_guard _lock{mWaitForBufferReleaseMutex}; return mWaitForBufferReleaseCallback; } diff --git a/libs/gui/BufferItem.cpp b/libs/gui/BufferItem.cpp index 3b2d337a21..9dcd5dc4c5 100644 --- a/libs/gui/BufferItem.cpp +++ b/libs/gui/BufferItem.cpp @@ -215,14 +215,14 @@ status_t BufferItem::unflatten( FlattenableUtils::read(buffer, size, flags); if (flags & 1) { - mGraphicBuffer = new GraphicBuffer(); + mGraphicBuffer = sp<GraphicBuffer>::make(); status_t err = mGraphicBuffer->unflatten(buffer, size, fds, count); if (err) return err; size -= FlattenableUtils::align<4>(buffer); } if (flags & 2) { - mFence = new Fence(); + mFence = sp<Fence>::make(); status_t err = mFence->unflatten(buffer, size, fds, count); if (err) return err; size -= FlattenableUtils::align<4>(buffer); diff --git a/libs/gui/BufferItemConsumer.cpp b/libs/gui/BufferItemConsumer.cpp index 8566419435..4926ceb619 100644 --- a/libs/gui/BufferItemConsumer.cpp +++ b/libs/gui/BufferItemConsumer.cpp @@ -17,6 +17,7 @@ //#define LOG_NDEBUG 0 #define LOG_TAG "BufferItemConsumer" //#define ATRACE_TAG ATRACE_TAG_GRAPHICS +#include <utils/Errors.h> #include <utils/Log.h> #include <inttypes.h> @@ -24,6 +25,7 @@ #include <com_android_graphics_libgui_flags.h> #include <gui/BufferItem.h> #include <gui/BufferItemConsumer.h> +#include <gui/Surface.h> #include <ui/BufferQueueDefs.h> #include <ui/GraphicBuffer.h> @@ -35,6 +37,30 @@ namespace android { +std::tuple<sp<BufferItemConsumer>, sp<Surface>> BufferItemConsumer::create( + uint64_t consumerUsage, int bufferCount, bool controlledByApp, + bool isConsumerSurfaceFlinger) { +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) + sp<BufferItemConsumer> bufferItemConsumer = + sp<BufferItemConsumer>::make(consumerUsage, bufferCount, controlledByApp, + isConsumerSurfaceFlinger); + return {bufferItemConsumer, bufferItemConsumer->getSurface()}; +#else + sp<IGraphicBufferProducer> igbp; + sp<IGraphicBufferConsumer> igbc; + BufferQueue::createBufferQueue(&igbp, &igbc, isConsumerSurfaceFlinger); + sp<BufferItemConsumer> bufferItemConsumer = + sp<BufferItemConsumer>::make(igbc, consumerUsage, bufferCount, controlledByApp); + return {bufferItemConsumer, sp<Surface>::make(igbp, controlledByApp)}; +#endif +} + +sp<BufferItemConsumer> BufferItemConsumer::create(const sp<IGraphicBufferConsumer>& consumer, + uint64_t consumerUsage, int bufferCount, + bool controlledByApp) { + return sp<BufferItemConsumer>::make(consumer, consumerUsage, bufferCount, controlledByApp); +} + #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) BufferItemConsumer::BufferItemConsumer(uint64_t consumerUsage, int bufferCount, bool controlledByApp, bool isConsumerSurfaceFlinger) @@ -107,13 +133,36 @@ status_t BufferItemConsumer::acquireBuffer(BufferItem *item, return OK; } +status_t BufferItemConsumer::attachBuffer(const sp<GraphicBuffer>& buffer) { + if (!buffer) { + BI_LOGE("BufferItemConsumer::attachBuffer no input buffer specified."); + return BAD_VALUE; + } + + Mutex::Autolock _l(mMutex); + + int slot = INVALID_BUFFER_SLOT; + status_t status = mConsumer->attachBuffer(&slot, buffer); + if (status != OK) { + BI_LOGE("BufferItemConsumer::attachBuffer unable to attach buffer %d", status); + return status; + } + + mSlots[slot] = { + .mGraphicBuffer = buffer, + .mFence = nullptr, + .mFrameNumber = 0, + }; + + return OK; +} + status_t BufferItemConsumer::releaseBuffer(const BufferItem &item, const sp<Fence>& releaseFence) { Mutex::Autolock _l(mMutex); return releaseBufferSlotLocked(item.mSlot, item.mGraphicBuffer, releaseFence); } -#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) status_t BufferItemConsumer::releaseBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence>& releaseFence) { Mutex::Autolock _l(mMutex); @@ -129,7 +178,6 @@ status_t BufferItemConsumer::releaseBuffer(const sp<GraphicBuffer>& buffer, return releaseBufferSlotLocked(slotIndex, buffer, releaseFence); } -#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) status_t BufferItemConsumer::releaseBufferSlotLocked(int slotIndex, const sp<GraphicBuffer>& buffer, const sp<Fence>& releaseFence) { diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp index f1374e23fd..f21ac18f3c 100644 --- a/libs/gui/BufferQueue.cpp +++ b/libs/gui/BufferQueue.cpp @@ -125,15 +125,16 @@ void BufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer, LOG_ALWAYS_FATAL_IF(outConsumer == nullptr, "BufferQueue: outConsumer must not be NULL"); - sp<BufferQueueCore> core(new BufferQueueCore()); + sp<BufferQueueCore> core = sp<BufferQueueCore>::make(); LOG_ALWAYS_FATAL_IF(core == nullptr, "BufferQueue: failed to create BufferQueueCore"); - sp<IGraphicBufferProducer> producer(new BufferQueueProducer(core, consumerIsSurfaceFlinger)); + sp<IGraphicBufferProducer> producer = + sp<BufferQueueProducer>::make(core, consumerIsSurfaceFlinger); LOG_ALWAYS_FATAL_IF(producer == nullptr, "BufferQueue: failed to create BufferQueueProducer"); - sp<IGraphicBufferConsumer> consumer(new BufferQueueConsumer(core)); + sp<IGraphicBufferConsumer> consumer = sp<BufferQueueConsumer>::make(core); LOG_ALWAYS_FATAL_IF(consumer == nullptr, "BufferQueue: failed to create BufferQueueConsumer"); diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp index f0125868ae..4681c9ecbe 100644 --- a/libs/gui/BufferQueueConsumer.cpp +++ b/libs/gui/BufferQueueConsumer.cpp @@ -14,10 +14,6 @@ * limitations under the License. */ -#include <inttypes.h> -#include <pwd.h> -#include <sys/types.h> - #define LOG_TAG "BufferQueueConsumer" #define ATRACE_TAG ATRACE_TAG_GRAPHICS //#define LOG_NDEBUG 0 @@ -48,6 +44,11 @@ #include <com_android_graphics_libgui_flags.h> +#include <inttypes.h> +#include <pwd.h> +#include <sys/types.h> +#include <optional> + namespace android { // Macros for include BufferQueueCore information in log messages @@ -477,9 +478,14 @@ status_t BufferQueueConsumer::attachBuffer(int* outSlot, return NO_ERROR; } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) +status_t BufferQueueConsumer::releaseBuffer(int slot, uint64_t frameNumber, + const sp<Fence>& releaseFence) { +#else status_t BufferQueueConsumer::releaseBuffer(int slot, uint64_t frameNumber, const sp<Fence>& releaseFence, EGLDisplay eglDisplay, EGLSyncKHR eglFence) { +#endif ATRACE_CALL(); ATRACE_BUFFER_INDEX(slot); @@ -493,27 +499,6 @@ status_t BufferQueueConsumer::releaseBuffer(int slot, uint64_t frameNumber, return BAD_VALUE; } -#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) - if (eglFence != EGL_NO_SYNC_KHR) { - // Most platforms will be using native fences, so it's unlikely that we'll ever have to - // process an eglFence. Ideally we can remove this code eventually. In the mean time, do our - // best to wait for it so the buffer stays valid, otherwise return an error to the caller. - // - // EGL_SYNC_FLUSH_COMMANDS_BIT_KHR so that we don't wait forever on a fence that hasn't - // shown up on the GPU yet. - EGLint result = eglClientWaitSyncKHR(eglDisplay, eglFence, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, - 1000000000); - if (result == EGL_FALSE) { - BQ_LOGE("releaseBuffer: error %#x waiting for fence", eglGetError()); - return UNKNOWN_ERROR; - } else if (result == EGL_TIMEOUT_EXPIRED_KHR) { - BQ_LOGE("releaseBuffer: timeout waiting for fence"); - return UNKNOWN_ERROR; - } - eglDestroySyncKHR(eglDisplay, eglFence); - } -#endif - sp<IProducerListener> listener; { // Autolock scope std::lock_guard<std::mutex> lock(mCore->mMutex); @@ -783,11 +768,15 @@ status_t BufferQueueConsumer::setMaxBufferCount(int bufferCount) { return NO_ERROR; } +status_t BufferQueueConsumer::setMaxAcquiredBufferCount(int maxAcquiredBuffers) { + return setMaxAcquiredBufferCount(maxAcquiredBuffers, std::nullopt); +} + status_t BufferQueueConsumer::setMaxAcquiredBufferCount( - int maxAcquiredBuffers) { + int maxAcquiredBuffers, std::optional<OnBufferReleasedCallback> onBuffersReleasedCallback) { ATRACE_FORMAT("%s(%d)", __func__, maxAcquiredBuffers); - sp<IConsumerListener> listener; + std::optional<OnBufferReleasedCallback> callback; { // Autolock scope std::unique_lock<std::mutex> lock(mCore->mMutex); @@ -849,13 +838,20 @@ status_t BufferQueueConsumer::setMaxAcquiredBufferCount( BQ_LOGV("setMaxAcquiredBufferCount: %d", maxAcquiredBuffers); mCore->mMaxAcquiredBufferCount = maxAcquiredBuffers; VALIDATE_CONSISTENCY(); - if (delta < 0 && mCore->mBufferReleasedCbEnabled) { - listener = mCore->mConsumerListener; + if (delta < 0) { + if (onBuffersReleasedCallback) { + callback = std::move(onBuffersReleasedCallback); + } else if (mCore->mBufferReleasedCbEnabled) { + callback = [listener = mCore->mConsumerListener]() { + listener->onBuffersReleased(); + }; + } } } + // Call back without lock held - if (listener != nullptr) { - listener->onBuffersReleased(); + if (callback) { + (*callback)(); } return NO_ERROR; diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp index c241482827..bcf61e45de 100644 --- a/libs/gui/BufferQueueProducer.cpp +++ b/libs/gui/BufferQueueProducer.cpp @@ -364,8 +364,10 @@ status_t BufferQueueProducer::waitForFreeSlotThenRelock(FreeSlotCaller caller, // Producers are not allowed to dequeue more than // mMaxDequeuedBufferCount buffers. // This check is only done if a buffer has already been queued - if (mCore->mBufferHasBeenQueued && - dequeuedCount >= mCore->mMaxDequeuedBufferCount) { + using namespace com::android::graphics::libgui::flags; + bool flagGatedBufferHasBeenQueued = + bq_always_use_max_dequeued_buffer_count() || mCore->mBufferHasBeenQueued; + if (flagGatedBufferHasBeenQueued && dequeuedCount >= mCore->mMaxDequeuedBufferCount) { // Supress error logs when timeout is non-negative. if (mDequeueTimeout < 0) { BQ_LOGE("%s: attempting to exceed the max dequeued buffer " @@ -693,11 +695,11 @@ status_t BufferQueueProducer::dequeueBuffer(int* outSlot, sp<android::Fence>* ou .requestorName = {mConsumerName.c_str(), mConsumerName.size()}, .extras = std::move(tempOptions), }; - sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(allocRequest); + sp<GraphicBuffer> graphicBuffer = sp<GraphicBuffer>::make(allocRequest); #else sp<GraphicBuffer> graphicBuffer = - new GraphicBuffer(width, height, format, BQ_LAYER_COUNT, usage, - {mConsumerName.c_str(), mConsumerName.size()}); + sp<GraphicBuffer>::make(width, height, format, BQ_LAYER_COUNT, usage, + std::string{mConsumerName.c_str(), mConsumerName.size()}); #endif status_t error = graphicBuffer->initCheck(); @@ -1464,7 +1466,7 @@ status_t BufferQueueProducer::connect(const sp<IProducerListener>& listener, #ifndef NO_BINDER if (IInterface::asBinder(listener)->remoteBinder() != nullptr) { status = IInterface::asBinder(listener)->linkToDeath( - static_cast<IBinder::DeathRecipient*>(this)); + sp<IBinder::DeathRecipient>::fromExisting(this)); if (status != NO_ERROR) { BQ_LOGE("connect: linkToDeath failed: %s (%d)", strerror(-status), status); @@ -1553,8 +1555,7 @@ status_t BufferQueueProducer::disconnect(int api, DisconnectMode mode) { IInterface::asBinder(mCore->mLinkedToDeath); // This can fail if we're here because of the death // notification, but we just ignore it - token->unlinkToDeath( - static_cast<IBinder::DeathRecipient*>(this)); + token->unlinkToDeath(static_cast<IBinder::DeathRecipient*>(this)); } #endif mCore->mSharedBufferSlot = @@ -1685,11 +1686,11 @@ void BufferQueueProducer::allocateBuffers(uint32_t width, uint32_t height, #endif #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE) - sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(allocRequest); + sp<GraphicBuffer> graphicBuffer = sp<GraphicBuffer>::make(allocRequest); #else - sp<GraphicBuffer> graphicBuffer = new GraphicBuffer( - allocWidth, allocHeight, allocFormat, BQ_LAYER_COUNT, - allocUsage, allocName); + sp<GraphicBuffer> graphicBuffer = + sp<GraphicBuffer>::make(allocWidth, allocHeight, allocFormat, BQ_LAYER_COUNT, + allocUsage, allocName); #endif status_t result = graphicBuffer->initCheck(); diff --git a/libs/gui/BufferReleaseChannel.cpp b/libs/gui/BufferReleaseChannel.cpp index e9cb013baf..4f495d039d 100644 --- a/libs/gui/BufferReleaseChannel.cpp +++ b/libs/gui/BufferReleaseChannel.cpp @@ -108,7 +108,7 @@ status_t BufferReleaseChannel::Message::flatten(void*& buffer, size_t& size, int status_t BufferReleaseChannel::Message::unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count) { - releaseFence = new Fence(); + releaseFence = sp<Fence>::make(); if (status_t err = releaseFence->unflatten(buffer, size, fds, count); err != OK) { return err; } @@ -344,4 +344,4 @@ status_t BufferReleaseChannel::open(std::string name, return STATUS_OK; } -} // namespace android::gui
\ No newline at end of file +} // namespace android::gui diff --git a/libs/gui/Choreographer.cpp b/libs/gui/Choreographer.cpp index ba50bf83a8..80a35435cb 100644 --- a/libs/gui/Choreographer.cpp +++ b/libs/gui/Choreographer.cpp @@ -20,6 +20,7 @@ #include <gui/Choreographer.h> #include <gui/TraceUtils.h> #include <jni.h> +#include <utils/Looper.h> #undef LOG_TAG #define LOG_TAG "AChoreographer" @@ -69,7 +70,7 @@ namespace android { Choreographer::Context Choreographer::gChoreographers; -static thread_local Choreographer* gChoreographer; +static thread_local sp<Choreographer> gChoreographer; void Choreographer::initJVM(JNIEnv* env) { env->GetJavaVM(&gJni.jvm); @@ -86,14 +87,14 @@ void Choreographer::initJVM(JNIEnv* env) { "()V"); } -Choreographer* Choreographer::getForThread() { +sp<Choreographer> Choreographer::getForThread() { if (gChoreographer == nullptr) { sp<Looper> looper = Looper::getForThread(); if (!looper.get()) { ALOGW("No looper prepared for thread"); return nullptr; } - gChoreographer = new Choreographer(looper); + gChoreographer = sp<Choreographer>::make(looper); status_t result = gChoreographer->initialize(); if (result != OK) { ALOGW("Failed to initialize"); @@ -238,7 +239,7 @@ void Choreographer::scheduleLatestConfigRequest() { // socket should be atomic across processes. DisplayEventReceiver::Event event; event.header = - DisplayEventReceiver::Event::Header{DisplayEventReceiver::DISPLAY_EVENT_NULL, + DisplayEventReceiver::Event::Header{DisplayEventType::DISPLAY_EVENT_NULL, PhysicalDisplayId::fromPort(0), systemTime()}; injectEvent(event); } diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp index 504509dbec..5b89c6e17e 100644 --- a/libs/gui/ConsumerBase.cpp +++ b/libs/gui/ConsumerBase.cpp @@ -31,6 +31,7 @@ #include <gui/BufferItem.h> #include <gui/BufferQueue.h> #include <gui/ConsumerBase.h> +#include <gui/IConsumerListener.h> #include <gui/ISurfaceComposer.h> #include <gui/Surface.h> #include <gui/SurfaceComposerClient.h> @@ -68,8 +69,8 @@ ConsumerBase::ConsumerBase(const sp<IGraphicBufferConsumer>& bufferQueue, bool c #endif mAbandoned(false), mConsumer(bufferQueue), - mPrevFinalReleaseFence(Fence::NO_FENCE) { - initialize(controlledByApp); + mPrevFinalReleaseFence(Fence::NO_FENCE), + mIsControlledByApp(controlledByApp) { } #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) @@ -79,11 +80,11 @@ ConsumerBase::ConsumerBase(bool controlledByApp, bool consumerIsSurfaceFlinger) mSlots(BufferQueueDefs::NUM_BUFFER_SLOTS), #endif mAbandoned(false), - mPrevFinalReleaseFence(Fence::NO_FENCE) { + mPrevFinalReleaseFence(Fence::NO_FENCE), + mIsControlledByApp(controlledByApp) { sp<IGraphicBufferProducer> producer; BufferQueue::createBufferQueue(&producer, &mConsumer, consumerIsSurfaceFlinger); mSurface = sp<Surface>::make(producer, controlledByApp); - initialize(controlledByApp); } ConsumerBase::ConsumerBase(const sp<IGraphicBufferProducer>& producer, @@ -95,24 +96,27 @@ ConsumerBase::ConsumerBase(const sp<IGraphicBufferProducer>& producer, mAbandoned(false), mConsumer(consumer), mSurface(sp<Surface>::make(producer, controlledByApp)), - mPrevFinalReleaseFence(Fence::NO_FENCE) { - initialize(controlledByApp); + mPrevFinalReleaseFence(Fence::NO_FENCE), + mIsControlledByApp(controlledByApp) { } #endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) -void ConsumerBase::initialize(bool controlledByApp) { +void ConsumerBase::onFirstRef() { + ConsumerListener::onFirstRef(); + initialize(); +} + +void ConsumerBase::initialize() { // Choose a name using the PID and a process-unique ID. mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId()); - // Note that we can't create an sp<...>(this) in a ctor that will not keep a - // reference once the ctor ends, as that would cause the refcount of 'this' - // dropping to 0 at the end of the ctor. Since all we need is a wp<...> - // that's what we create. - wp<ConsumerListener> listener = static_cast<ConsumerListener*>(this); - sp<IConsumerListener> proxy = new BufferQueue::ProxyConsumerListener(listener); + // Here we depend on an sp/wp having been created for `this`. For this + // reason, initialize() cannot be called from a ctor. + wp<ConsumerListener> listener = wp<ConsumerListener>::fromExisting(this); + sp<IConsumerListener> proxy = sp<BufferQueue::ProxyConsumerListener>::make(listener); - status_t err = mConsumer->consumerConnect(proxy, controlledByApp); + status_t err = mConsumer->consumerConnect(proxy, mIsControlledByApp); if (err != NO_ERROR) { CB_LOGE("ConsumerBase: error connecting to BufferQueue: %s (%d)", strerror(-err), err); @@ -260,7 +264,10 @@ void ConsumerBase::onFrameReplaced(const BufferItem &item) { void ConsumerBase::onBuffersReleased() { Mutex::Autolock lock(mMutex); + onBuffersReleasedLocked(); +} +void ConsumerBase::onBuffersReleasedLocked() { CB_LOGV("onBuffersReleased"); if (mAbandoned) { @@ -385,6 +392,26 @@ status_t ConsumerBase::detachBuffer(const sp<GraphicBuffer>& buffer) { } #endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) +status_t ConsumerBase::addReleaseFence(const sp<GraphicBuffer> buffer, const sp<Fence>& fence) { + CB_LOGV("addReleaseFence"); + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + CB_LOGE("addReleaseFence: ConsumerBase is abandoned!"); + return NO_INIT; + } + if (buffer == nullptr) { + return BAD_VALUE; + } + + int slotIndex = getSlotForBufferLocked(buffer); + if (slotIndex == BufferQueue::INVALID_BUFFER_SLOT) { + return BAD_VALUE; + } + + return addReleaseFenceLocked(slotIndex, buffer, fence); +} + status_t ConsumerBase::setDefaultBufferSize(uint32_t width, uint32_t height) { Mutex::Autolock _l(mMutex); if (mAbandoned) { @@ -457,10 +484,10 @@ status_t ConsumerBase::setMaxAcquiredBufferCount(int maxAcquiredBuffers) { CB_LOGE("setMaxAcquiredBufferCount: ConsumerBase is abandoned!"); return NO_INIT; } - return mConsumer->setMaxAcquiredBufferCount(maxAcquiredBuffers); + return mConsumer->setMaxAcquiredBufferCount(maxAcquiredBuffers, + {[this]() { onBuffersReleasedLocked(); }}); } -#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) status_t ConsumerBase::setConsumerIsProtected(bool isProtected) { Mutex::Autolock lock(mMutex); if (mAbandoned) { @@ -469,7 +496,6 @@ status_t ConsumerBase::setConsumerIsProtected(bool isProtected) { } return mConsumer->setConsumerIsProtected(isProtected); } -#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) sp<NativeHandle> ConsumerBase::getSidebandStream() const { Mutex::Autolock _l(mMutex); @@ -656,9 +682,13 @@ status_t ConsumerBase::addReleaseFenceLocked(int slot, return OK; } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) +status_t ConsumerBase::releaseBufferLocked(int slot, const sp<GraphicBuffer> graphicBuffer) { +#else status_t ConsumerBase::releaseBufferLocked( int slot, const sp<GraphicBuffer> graphicBuffer, EGLDisplay display, EGLSyncKHR eglFence) { +#endif if (mAbandoned) { CB_LOGE("releaseBufferLocked: ConsumerBase is abandoned!"); return NO_INIT; @@ -675,8 +705,12 @@ status_t ConsumerBase::releaseBufferLocked( CB_LOGV("releaseBufferLocked: slot=%d/%" PRIu64, slot, mSlots[slot].mFrameNumber); +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) + status_t err = mConsumer->releaseBuffer(slot, mSlots[slot].mFrameNumber, mSlots[slot].mFence); +#else status_t err = mConsumer->releaseBuffer(slot, mSlots[slot].mFrameNumber, display, eglFence, mSlots[slot].mFence); +#endif if (err == IGraphicBufferConsumer::STALE_BUFFER_SLOT) { freeBufferLocked(slot); } diff --git a/libs/gui/CpuConsumer.cpp b/libs/gui/CpuConsumer.cpp index 23b432e1f4..ecbceb7a79 100644 --- a/libs/gui/CpuConsumer.cpp +++ b/libs/gui/CpuConsumer.cpp @@ -20,7 +20,11 @@ #include <com_android_graphics_libgui_flags.h> #include <gui/BufferItem.h> +#include <gui/BufferQueue.h> #include <gui/CpuConsumer.h> +#include <gui/IGraphicBufferConsumer.h> +#include <gui/IGraphicBufferProducer.h> +#include <gui/Surface.h> #include <utils/Log.h> #define CC_LOGV(x, ...) ALOGV("[%s] " x, mName.c_str(), ##__VA_ARGS__) @@ -31,6 +35,28 @@ namespace android { +std::tuple<sp<CpuConsumer>, sp<Surface>> CpuConsumer::create(size_t maxLockedBuffers, + bool controlledByApp, + bool isConsumerSurfaceFlinger) { +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) + sp<CpuConsumer> consumer = + sp<CpuConsumer>::make(maxLockedBuffers, controlledByApp, isConsumerSurfaceFlinger); + return {consumer, consumer->getSurface()}; +#else + sp<IGraphicBufferProducer> igbp; + sp<IGraphicBufferConsumer> igbc; + BufferQueue::createBufferQueue(&igbp, &igbc, isConsumerSurfaceFlinger); + + return {sp<CpuConsumer>::make(igbc, maxLockedBuffers, controlledByApp), + sp<Surface>::make(igbp, controlledByApp)}; +#endif +} + +sp<CpuConsumer> CpuConsumer::create(const sp<IGraphicBufferConsumer>& bq, size_t maxLockedBuffers, + bool controlledByApp) { + return sp<CpuConsumer>::make(bq, maxLockedBuffers, controlledByApp); +} + #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) CpuConsumer::CpuConsumer(size_t maxLockedBuffers, bool controlledByApp, bool isConsumerSurfaceFlinger) @@ -230,7 +256,7 @@ status_t CpuConsumer::unlockBuffer(const LockedBuffer &nativeBuffer) { return err; } - sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE); + sp<Fence> fence(fenceFd >= 0 ? sp<Fence>::make(fenceFd) : Fence::NO_FENCE); addReleaseFenceLocked(ab.mSlot, ab.mGraphicBuffer, fence); releaseBufferLocked(ab.mSlot, ab.mGraphicBuffer); diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp index 68f10f4d80..6f238859a4 100644 --- a/libs/gui/DisplayEventDispatcher.cpp +++ b/libs/gui/DisplayEventDispatcher.cpp @@ -167,7 +167,7 @@ bool DisplayEventDispatcher::processPendingEvents(nsecs_t* outTimestamp, for (ssize_t i = 0; i < n; i++) { const DisplayEventReceiver::Event& ev = buf[i]; switch (ev.header.type) { - case DisplayEventReceiver::DISPLAY_EVENT_VSYNC: + case DisplayEventType::DISPLAY_EVENT_VSYNC: // Later vsync events will just overwrite the info from earlier // ones. That's fine, we only care about the most recent. gotVsync = true; @@ -183,7 +183,7 @@ bool DisplayEventDispatcher::processPendingEvents(nsecs_t* outTimestamp, ATRACE_INT("RenderRate", fps); } break; - case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG: + case DisplayEventType::DISPLAY_EVENT_HOTPLUG: if (ev.hotplug.connectionError == 0) { dispatchHotplug(ev.header.timestamp, ev.header.displayId, ev.hotplug.connected); @@ -192,31 +192,28 @@ bool DisplayEventDispatcher::processPendingEvents(nsecs_t* outTimestamp, ev.hotplug.connectionError); } break; - case DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE: + case DisplayEventType::DISPLAY_EVENT_MODE_CHANGE: dispatchModeChanged(ev.header.timestamp, ev.header.displayId, ev.modeChange.modeId, ev.modeChange.vsyncPeriod); break; - case DisplayEventReceiver::DISPLAY_EVENT_NULL: + case DisplayEventType::DISPLAY_EVENT_NULL: dispatchNullEvent(ev.header.timestamp, ev.header.displayId); break; - case DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE: + case DisplayEventType::DISPLAY_EVENT_FRAME_RATE_OVERRIDE: mFrameRateOverrides.emplace_back(ev.frameRateOverride); break; - case DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH: + case DisplayEventType::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH: dispatchFrameRateOverrides(ev.header.timestamp, ev.header.displayId, std::move(mFrameRateOverrides)); break; - case DisplayEventReceiver::DISPLAY_EVENT_HDCP_LEVELS_CHANGE: + case DisplayEventType::DISPLAY_EVENT_HDCP_LEVELS_CHANGE: dispatchHdcpLevelsChanged(ev.header.displayId, ev.hdcpLevelsChange.connectedLevel, ev.hdcpLevelsChange.maxLevel); break; - case DisplayEventReceiver::DISPLAY_EVENT_MODE_REJECTION: + case DisplayEventType::DISPLAY_EVENT_MODE_REJECTION: dispatchModeRejected(ev.header.displayId, ev.modeRejection.modeId); break; - default: - ALOGW("dispatcher %p ~ ignoring unknown event type %#x", this, ev.header.type); - break; } } } diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp index 168129b1f7..2c5770d4c6 100644 --- a/libs/gui/GLConsumer.cpp +++ b/libs/gui/GLConsumer.cpp @@ -37,6 +37,7 @@ #include <gui/DebugEGLImageTracker.h> #include <gui/GLConsumer.h> #include <gui/ISurfaceComposer.h> +#include <gui/Surface.h> #include <gui/SurfaceComposerClient.h> #include <private/gui/ComposerService.h> @@ -101,6 +102,50 @@ static bool hasEglProtectedContent() { return hasIt; } +std::tuple<sp<GLConsumer>, sp<Surface>> GLConsumer::create(uint32_t tex, uint32_t textureTarget, + bool useFenceSync, + bool isControlledByApp) { +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) + sp<GLConsumer> consumer = + sp<GLConsumer>::make(tex, textureTarget, useFenceSync, isControlledByApp); + return {consumer, consumer->getSurface()}; +#else + sp<IGraphicBufferProducer> igbp; + sp<IGraphicBufferConsumer> igbc; + BufferQueue::createBufferQueue(&igbp, &igbc); + + return {sp<GLConsumer>::make(igbc, tex, textureTarget, useFenceSync, isControlledByApp), + sp<Surface>::make(igbp, isControlledByApp)}; +#endif +} + +std::tuple<sp<GLConsumer>, sp<Surface>> GLConsumer::create(uint32_t textureTarget, + bool useFenceSync, + bool isControlledByApp) { +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) + sp<GLConsumer> consumer = sp<GLConsumer>::make(textureTarget, useFenceSync, isControlledByApp); + return {consumer, consumer->getSurface()}; +#else + sp<IGraphicBufferProducer> igbp; + sp<IGraphicBufferConsumer> igbc; + BufferQueue::createBufferQueue(&igbp, &igbc); + + return {sp<GLConsumer>::make(igbc, textureTarget, useFenceSync, isControlledByApp), + sp<Surface>::make(igbp, isControlledByApp)}; +#endif +} + +sp<GLConsumer> GLConsumer::create(const sp<IGraphicBufferConsumer>& bq, uint32_t tex, + uint32_t textureTarget, bool useFenceSync, + bool isControlledByApp) { + return sp<GLConsumer>::make(bq, tex, textureTarget, useFenceSync, isControlledByApp); +} + +sp<GLConsumer> GLConsumer::create(const sp<IGraphicBufferConsumer>& bq, uint32_t textureTarget, + bool useFenceSync, bool isControlledByApp) { + return sp<GLConsumer>::make(bq, textureTarget, useFenceSync, isControlledByApp); +} + #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) GLConsumer::GLConsumer(uint32_t tex, uint32_t texTarget, bool useFenceSync, bool isControlledByApp) : ConsumerBase(isControlledByApp, /* isConsumerSurfaceFlinger */ false), @@ -333,7 +378,7 @@ status_t GLConsumer::releaseTexImage() { } if (mReleasedTexImage == nullptr) { - mReleasedTexImage = new EglImage(getDebugTexImageBuffer()); + mReleasedTexImage = sp<EglImage>::make(getDebugTexImageBuffer()); } mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT; @@ -365,10 +410,10 @@ sp<GraphicBuffer> GLConsumer::getDebugTexImageBuffer() { if (CC_UNLIKELY(sReleasedTexImageBuffer == nullptr)) { // The first time, create the debug texture in case the application // continues to use it. - sp<GraphicBuffer> buffer = new GraphicBuffer( - kDebugData.width, kDebugData.height, PIXEL_FORMAT_RGBA_8888, - DEFAULT_USAGE_FLAGS | GraphicBuffer::USAGE_SW_WRITE_RARELY, - "[GLConsumer debug texture]"); + sp<GraphicBuffer> buffer = + sp<GraphicBuffer>::make(kDebugData.width, kDebugData.height, PIXEL_FORMAT_RGBA_8888, + DEFAULT_USAGE_FLAGS | GraphicBuffer::USAGE_SW_WRITE_RARELY, + "[GLConsumer debug texture]"); uint32_t* bits; buffer->lock(GraphicBuffer::USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&bits)); uint32_t stride = buffer->getStride(); @@ -400,7 +445,7 @@ status_t GLConsumer::acquireBufferLocked(BufferItem *item, // replaces any old EglImage with a new one (using the new buffer). if (item->mGraphicBuffer != nullptr) { int slot = item->mSlot; - mEglSlots[slot].mEglImage = new EglImage(item->mGraphicBuffer); + mEglSlots[slot].mEglImage = sp<EglImage>::make(item->mGraphicBuffer); } return NO_ERROR; @@ -417,18 +462,18 @@ void GLConsumer::onSlotCountChanged(int slotCount) { } #endif -status_t GLConsumer::releaseBufferLocked(int buf, - sp<GraphicBuffer> graphicBuffer, - EGLDisplay display, EGLSyncKHR eglFence) { +#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) +status_t GLConsumer::releaseBufferLocked(int buf, sp<GraphicBuffer> graphicBuffer, + EGLDisplay display, EGLSyncKHR eglFence) { // release the buffer if it hasn't already been discarded by the // BufferQueue. This can happen, for example, when the producer of this // buffer has reallocated the original buffer slot after this buffer // was acquired. - status_t err = ConsumerBase::releaseBufferLocked( - buf, graphicBuffer, display, eglFence); + status_t err = ConsumerBase::releaseBufferLocked(buf, graphicBuffer, display, eglFence); mEglSlots[buf].mEglFence = EGL_NO_SYNC_KHR; return err; } +#endif status_t GLConsumer::updateAndReleaseLocked(const BufferItem& item, PendingRelease* pendingRelease) @@ -490,9 +535,14 @@ status_t GLConsumer::updateAndReleaseLocked(const BufferItem& item, // release old buffer if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { if (pendingRelease == nullptr) { +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) + status_t status = + releaseBufferLocked(mCurrentTexture, mCurrentTextureImage->graphicBuffer()); +#else status_t status = releaseBufferLocked( mCurrentTexture, mCurrentTextureImage->graphicBuffer(), mEglDisplay, mEglSlots[mCurrentTexture].mEglFence); +#endif if (status < NO_ERROR) { GLC_LOGE("updateAndRelease: failed to release buffer: %s (%d)", strerror(-status), status); @@ -501,10 +551,7 @@ status_t GLConsumer::updateAndReleaseLocked(const BufferItem& item, } } else { pendingRelease->currentTexture = mCurrentTexture; - pendingRelease->graphicBuffer = - mCurrentTextureImage->graphicBuffer(); - pendingRelease->display = mEglDisplay; - pendingRelease->fence = mEglSlots[mCurrentTexture].mEglFence; + pendingRelease->graphicBuffer = mCurrentTextureImage->graphicBuffer(); pendingRelease->isPending = true; } } @@ -735,7 +782,7 @@ status_t GLConsumer::syncForReleaseLocked(EGLDisplay dpy) { "fd: %#x", eglGetError()); return UNKNOWN_ERROR; } - sp<Fence> fence(new Fence(fenceFd)); + sp<Fence> fence = sp<Fence>::make(fenceFd); status_t err = addReleaseFenceLocked(mCurrentTexture, mCurrentTextureImage->graphicBuffer(), fence); if (err != OK) { @@ -744,6 +791,11 @@ status_t GLConsumer::syncForReleaseLocked(EGLDisplay dpy) { return err; } } else if (mUseFenceSync && SyncFeatures::getInstance().useFenceSync()) { +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) + // Basically all clients are using native fence syncs. If they aren't, we lose nothing + // by waiting here, because the alternative can cause deadlocks (b/339705065). + glFinish(); +#else EGLSyncKHR fence = mEglSlots[mCurrentTexture].mEglFence; if (fence != EGL_NO_SYNC_KHR) { // There is already a fence for the current slot. We need to @@ -773,6 +825,7 @@ status_t GLConsumer::syncForReleaseLocked(EGLDisplay dpy) { } glFlush(); mEglSlots[mCurrentTexture].mEglFence = fence; +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) } } diff --git a/libs/gui/IConsumerListener.cpp b/libs/gui/IConsumerListener.cpp deleted file mode 100644 index 939db594ec..0000000000 --- a/libs/gui/IConsumerListener.cpp +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (C) 2013 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 <gui/IConsumerListener.h> - -#include <gui/BufferItem.h> - -namespace android { - -namespace { // Anonymous - -enum class Tag : uint32_t { - ON_DISCONNECT = IBinder::FIRST_CALL_TRANSACTION, - ON_FRAME_AVAILABLE, - ON_FRAME_REPLACED, - ON_BUFFERS_RELEASED, - ON_SIDEBAND_STREAM_CHANGED, - ON_FRAME_DEQUEUED, - ON_FRAME_CANCELLED, - ON_FRAME_DETACHED, - ON_SLOT_COUNT_CHANGED, - LAST = ON_SLOT_COUNT_CHANGED, -}; - -} // Anonymous namespace - -class BpConsumerListener : public SafeBpInterface<IConsumerListener> { -public: - explicit BpConsumerListener(const sp<IBinder>& impl) - : SafeBpInterface<IConsumerListener>(impl, "BpConsumerListener") {} - - ~BpConsumerListener() override; - - void onDisconnect() override { - callRemoteAsync<decltype(&IConsumerListener::onDisconnect)>(Tag::ON_DISCONNECT); - } - - void onFrameDequeued(const uint64_t bufferId) override { - callRemoteAsync<decltype(&IConsumerListener::onFrameDequeued)>(Tag::ON_FRAME_DEQUEUED, - bufferId); - } - - void onFrameDetached(const uint64_t bufferId) override { - callRemoteAsync<decltype(&IConsumerListener::onFrameDetached)>(Tag::ON_FRAME_DETACHED, - bufferId); - } - - void onFrameCancelled(const uint64_t bufferId) override { - callRemoteAsync<decltype(&IConsumerListener::onFrameCancelled)>(Tag::ON_FRAME_CANCELLED, - bufferId); - } - - void onFrameAvailable(const BufferItem& item) override { - callRemoteAsync<decltype(&IConsumerListener::onFrameAvailable)>(Tag::ON_FRAME_AVAILABLE, - item); - } - - void onFrameReplaced(const BufferItem& item) override { - callRemoteAsync<decltype(&IConsumerListener::onFrameReplaced)>(Tag::ON_FRAME_REPLACED, - item); - } - - void onBuffersReleased() override { - callRemoteAsync<decltype(&IConsumerListener::onBuffersReleased)>(Tag::ON_BUFFERS_RELEASED); - } - - void onSidebandStreamChanged() override { - callRemoteAsync<decltype(&IConsumerListener::onSidebandStreamChanged)>( - Tag::ON_SIDEBAND_STREAM_CHANGED); - } - - void addAndGetFrameTimestamps(const NewFrameEventsEntry* /*newTimestamps*/, - FrameEventHistoryDelta* /*outDelta*/) override { - LOG_ALWAYS_FATAL("IConsumerListener::addAndGetFrameTimestamps cannot be proxied"); - } - -#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) - void onSlotCountChanged(int slotCount) override { - callRemoteAsync< - decltype(&IConsumerListener::onSlotCountChanged)>(Tag::ON_SLOT_COUNT_CHANGED, - slotCount); - } -#endif -}; - -// Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see -// clang warning -Wweak-vtables) -BpConsumerListener::~BpConsumerListener() = default; - -IMPLEMENT_META_INTERFACE(ConsumerListener, "android.gui.IConsumerListener"); - -status_t BnConsumerListener::onTransact(uint32_t code, const Parcel& data, Parcel* reply, - uint32_t flags) { - if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast<uint32_t>(Tag::LAST)) { - return BBinder::onTransact(code, data, reply, flags); - } - auto tag = static_cast<Tag>(code); - switch (tag) { - case Tag::ON_DISCONNECT: - return callLocalAsync(data, reply, &IConsumerListener::onDisconnect); - case Tag::ON_FRAME_AVAILABLE: - return callLocalAsync(data, reply, &IConsumerListener::onFrameAvailable); - case Tag::ON_FRAME_REPLACED: - return callLocalAsync(data, reply, &IConsumerListener::onFrameReplaced); - case Tag::ON_BUFFERS_RELEASED: - return callLocalAsync(data, reply, &IConsumerListener::onBuffersReleased); - case Tag::ON_SIDEBAND_STREAM_CHANGED: - return callLocalAsync(data, reply, &IConsumerListener::onSidebandStreamChanged); - case Tag::ON_FRAME_DEQUEUED: - return callLocalAsync(data, reply, &IConsumerListener::onFrameDequeued); - case Tag::ON_FRAME_CANCELLED: - return callLocalAsync(data, reply, &IConsumerListener::onFrameCancelled); - case Tag::ON_FRAME_DETACHED: - return callLocalAsync(data, reply, &IConsumerListener::onFrameDetached); - case Tag::ON_SLOT_COUNT_CHANGED: { -#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) - return callLocalAsync(data, reply, &IConsumerListener::onSlotCountChanged); -#else - return INVALID_OPERATION; -#endif - } - } -} - -} // namespace android diff --git a/libs/gui/IGraphicBufferConsumer.cpp b/libs/gui/IGraphicBufferConsumer.cpp deleted file mode 100644 index c1b65689d6..0000000000 --- a/libs/gui/IGraphicBufferConsumer.cpp +++ /dev/null @@ -1,266 +0,0 @@ -/* - * Copyright (C) 2013 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 <gui/IGraphicBufferConsumer.h> - -#include <com_android_graphics_libgui_flags.h> -#include <gui/BufferItem.h> -#include <gui/IConsumerListener.h> - -#include <binder/Parcel.h> - -#include <ui/Fence.h> -#include <ui/GraphicBuffer.h> - -#include <utils/Errors.h> -#include <utils/NativeHandle.h> -#include <utils/String8.h> -#include <cstdint> - -namespace android { - -namespace { // Anonymous namespace - -enum class Tag : uint32_t { - ACQUIRE_BUFFER = IBinder::FIRST_CALL_TRANSACTION, - DETACH_BUFFER, - ATTACH_BUFFER, - RELEASE_BUFFER, - CONSUMER_CONNECT, - CONSUMER_DISCONNECT, - GET_RELEASED_BUFFERS, - SET_DEFAULT_BUFFER_SIZE, - SET_MAX_BUFFER_COUNT, - SET_MAX_ACQUIRED_BUFFER_COUNT, - SET_CONSUMER_NAME, - SET_DEFAULT_BUFFER_FORMAT, - SET_DEFAULT_BUFFER_DATA_SPACE, - SET_CONSUMER_USAGE_BITS, - SET_CONSUMER_IS_PROTECTED, - SET_TRANSFORM_HINT, - GET_SIDEBAND_STREAM, - GET_OCCUPANCY_HISTORY, - DISCARD_FREE_BUFFERS, - DUMP_STATE, - ALLOW_UNLIMITED_SLOTS, - GET_RELEASED_BUFFERS_EXTENDED, - LAST = GET_RELEASED_BUFFERS_EXTENDED, -}; - -} // Anonymous namespace - -class BpGraphicBufferConsumer : public SafeBpInterface<IGraphicBufferConsumer> { -public: - explicit BpGraphicBufferConsumer(const sp<IBinder>& impl) - : SafeBpInterface<IGraphicBufferConsumer>(impl, "BpGraphicBufferConsumer") {} - - ~BpGraphicBufferConsumer() override; - - status_t acquireBuffer(BufferItem* buffer, nsecs_t presentWhen, - uint64_t maxFrameNumber) override { - using Signature = decltype(&IGraphicBufferConsumer::acquireBuffer); - return callRemote<Signature>(Tag::ACQUIRE_BUFFER, buffer, presentWhen, maxFrameNumber); - } - - status_t detachBuffer(int slot) override { - using Signature = decltype(&IGraphicBufferConsumer::detachBuffer); - return callRemote<Signature>(Tag::DETACH_BUFFER, slot); - } - - status_t attachBuffer(int* slot, const sp<GraphicBuffer>& buffer) override { - using Signature = decltype(&IGraphicBufferConsumer::attachBuffer); - return callRemote<Signature>(Tag::ATTACH_BUFFER, slot, buffer); - } - - status_t releaseBuffer(int buf, uint64_t frameNumber, - EGLDisplay display __attribute__((unused)), - EGLSyncKHR fence __attribute__((unused)), - const sp<Fence>& releaseFence) override { - using Signature = status_t (IGraphicBufferConsumer::*)(int, uint64_t, const sp<Fence>&); - return callRemote<Signature>(Tag::RELEASE_BUFFER, buf, frameNumber, releaseFence); - } - - status_t consumerConnect(const sp<IConsumerListener>& consumer, bool controlledByApp) override { - using Signature = decltype(&IGraphicBufferConsumer::consumerConnect); - return callRemote<Signature>(Tag::CONSUMER_CONNECT, consumer, controlledByApp); - } - - status_t consumerDisconnect() override { - return callRemote<decltype(&IGraphicBufferConsumer::consumerDisconnect)>( - Tag::CONSUMER_DISCONNECT); - } - - status_t getReleasedBuffers(uint64_t* slotMask) override { - using Signature = decltype(&IGraphicBufferConsumer::getReleasedBuffers); - return callRemote<Signature>(Tag::GET_RELEASED_BUFFERS, slotMask); - } - -#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) - status_t getReleasedBuffersExtended(std::vector<bool>* slotMask) override { - using Signature = decltype(&IGraphicBufferConsumer::getReleasedBuffersExtended); - return callRemote<Signature>(Tag::GET_RELEASED_BUFFERS_EXTENDED, slotMask); - } -#endif - - status_t setDefaultBufferSize(uint32_t width, uint32_t height) override { - using Signature = decltype(&IGraphicBufferConsumer::setDefaultBufferSize); - return callRemote<Signature>(Tag::SET_DEFAULT_BUFFER_SIZE, width, height); - } - -#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) - status_t allowUnlimitedSlots(bool allowUnlimitedSlots) override { - using Signature = decltype(&IGraphicBufferConsumer::allowUnlimitedSlots); - return callRemote<Signature>(Tag::ALLOW_UNLIMITED_SLOTS, allowUnlimitedSlots); - } -#endif - - status_t setMaxBufferCount(int bufferCount) override { - using Signature = decltype(&IGraphicBufferConsumer::setMaxBufferCount); - return callRemote<Signature>(Tag::SET_MAX_BUFFER_COUNT, bufferCount); - } - - status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) override { - using Signature = decltype(&IGraphicBufferConsumer::setMaxAcquiredBufferCount); - return callRemote<Signature>(Tag::SET_MAX_ACQUIRED_BUFFER_COUNT, maxAcquiredBuffers); - } - - status_t setConsumerName(const String8& name) override { - using Signature = decltype(&IGraphicBufferConsumer::setConsumerName); - return callRemote<Signature>(Tag::SET_CONSUMER_NAME, name); - } - - status_t setDefaultBufferFormat(PixelFormat defaultFormat) override { - using Signature = decltype(&IGraphicBufferConsumer::setDefaultBufferFormat); - return callRemote<Signature>(Tag::SET_DEFAULT_BUFFER_FORMAT, defaultFormat); - } - - status_t setDefaultBufferDataSpace(android_dataspace defaultDataSpace) override { - using Signature = decltype(&IGraphicBufferConsumer::setDefaultBufferDataSpace); - return callRemote<Signature>(Tag::SET_DEFAULT_BUFFER_DATA_SPACE, defaultDataSpace); - } - - status_t setConsumerUsageBits(uint64_t usage) override { - using Signature = decltype(&IGraphicBufferConsumer::setConsumerUsageBits); - return callRemote<Signature>(Tag::SET_CONSUMER_USAGE_BITS, usage); - } - - status_t setConsumerIsProtected(bool isProtected) override { - using Signature = decltype(&IGraphicBufferConsumer::setConsumerIsProtected); - return callRemote<Signature>(Tag::SET_CONSUMER_IS_PROTECTED, isProtected); - } - - status_t setTransformHint(uint32_t hint) override { - using Signature = decltype(&IGraphicBufferConsumer::setTransformHint); - return callRemote<Signature>(Tag::SET_TRANSFORM_HINT, hint); - } - - status_t getSidebandStream(sp<NativeHandle>* outStream) const override { - using Signature = decltype(&IGraphicBufferConsumer::getSidebandStream); - return callRemote<Signature>(Tag::GET_SIDEBAND_STREAM, outStream); - } - - status_t getOccupancyHistory(bool forceFlush, - std::vector<OccupancyTracker::Segment>* outHistory) override { - using Signature = decltype(&IGraphicBufferConsumer::getOccupancyHistory); - return callRemote<Signature>(Tag::GET_OCCUPANCY_HISTORY, forceFlush, outHistory); - } - - status_t discardFreeBuffers() override { - return callRemote<decltype(&IGraphicBufferConsumer::discardFreeBuffers)>( - Tag::DISCARD_FREE_BUFFERS); - } - - status_t dumpState(const String8& prefix, String8* outResult) const override { - using Signature = status_t (IGraphicBufferConsumer::*)(const String8&, String8*) const; - return callRemote<Signature>(Tag::DUMP_STATE, prefix, outResult); - } -}; - -// Out-of-line virtual method definition to trigger vtable emission in this translation unit -// (see clang warning -Wweak-vtables) -BpGraphicBufferConsumer::~BpGraphicBufferConsumer() = default; - -IMPLEMENT_META_INTERFACE(GraphicBufferConsumer, "android.gui.IGraphicBufferConsumer"); - -status_t BnGraphicBufferConsumer::onTransact(uint32_t code, const Parcel& data, Parcel* reply, - uint32_t flags) { - if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast<uint32_t>(Tag::LAST)) { - return BBinder::onTransact(code, data, reply, flags); - } - auto tag = static_cast<Tag>(code); - switch (tag) { - case Tag::ACQUIRE_BUFFER: - return callLocal(data, reply, &IGraphicBufferConsumer::acquireBuffer); - case Tag::DETACH_BUFFER: - return callLocal(data, reply, &IGraphicBufferConsumer::detachBuffer); - case Tag::ATTACH_BUFFER: - return callLocal(data, reply, &IGraphicBufferConsumer::attachBuffer); - case Tag::RELEASE_BUFFER: { - using Signature = status_t (IGraphicBufferConsumer::*)(int, uint64_t, const sp<Fence>&); - return callLocal<Signature>(data, reply, &IGraphicBufferConsumer::releaseBuffer); - } - case Tag::CONSUMER_CONNECT: - return callLocal(data, reply, &IGraphicBufferConsumer::consumerConnect); - case Tag::CONSUMER_DISCONNECT: - return callLocal(data, reply, &IGraphicBufferConsumer::consumerDisconnect); - case Tag::GET_RELEASED_BUFFERS: - return callLocal(data, reply, &IGraphicBufferConsumer::getReleasedBuffers); - case Tag::SET_DEFAULT_BUFFER_SIZE: - return callLocal(data, reply, &IGraphicBufferConsumer::setDefaultBufferSize); - case Tag::SET_MAX_BUFFER_COUNT: - return callLocal(data, reply, &IGraphicBufferConsumer::setMaxBufferCount); - case Tag::SET_MAX_ACQUIRED_BUFFER_COUNT: - return callLocal(data, reply, &IGraphicBufferConsumer::setMaxAcquiredBufferCount); - case Tag::SET_CONSUMER_NAME: - return callLocal(data, reply, &IGraphicBufferConsumer::setConsumerName); - case Tag::SET_DEFAULT_BUFFER_FORMAT: - return callLocal(data, reply, &IGraphicBufferConsumer::setDefaultBufferFormat); - case Tag::SET_DEFAULT_BUFFER_DATA_SPACE: - return callLocal(data, reply, &IGraphicBufferConsumer::setDefaultBufferDataSpace); - case Tag::SET_CONSUMER_USAGE_BITS: - return callLocal(data, reply, &IGraphicBufferConsumer::setConsumerUsageBits); - case Tag::SET_CONSUMER_IS_PROTECTED: - return callLocal(data, reply, &IGraphicBufferConsumer::setConsumerIsProtected); - case Tag::SET_TRANSFORM_HINT: - return callLocal(data, reply, &IGraphicBufferConsumer::setTransformHint); - case Tag::GET_SIDEBAND_STREAM: - return callLocal(data, reply, &IGraphicBufferConsumer::getSidebandStream); - case Tag::GET_OCCUPANCY_HISTORY: - return callLocal(data, reply, &IGraphicBufferConsumer::getOccupancyHistory); - case Tag::DISCARD_FREE_BUFFERS: - return callLocal(data, reply, &IGraphicBufferConsumer::discardFreeBuffers); - case Tag::DUMP_STATE: { - using Signature = status_t (IGraphicBufferConsumer::*)(const String8&, String8*) const; - return callLocal<Signature>(data, reply, &IGraphicBufferConsumer::dumpState); - } - case Tag::GET_RELEASED_BUFFERS_EXTENDED: { -#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) - return callLocal(data, reply, &IGraphicBufferConsumer::getReleasedBuffersExtended); -#else - return INVALID_OPERATION; -#endif - } - case Tag::ALLOW_UNLIMITED_SLOTS: { -#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) - return callLocal(data, reply, &IGraphicBufferConsumer::allowUnlimitedSlots); -#else - return INVALID_OPERATION; -#endif - } - } -} - -} // namespace android diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp index 9f71eb16e7..1d1910eb08 100644 --- a/libs/gui/IGraphicBufferProducer.cpp +++ b/libs/gui/IGraphicBufferProducer.cpp @@ -104,7 +104,7 @@ public: } bool nonNull = reply.readInt32(); if (nonNull) { - *buf = new GraphicBuffer(); + *buf = sp<GraphicBuffer>::make(); result = reply.read(**buf); if(result != NO_ERROR) { (*buf).clear(); @@ -197,7 +197,7 @@ public: } *buf = reply.readInt32(); - *fence = new Fence(); + *fence = sp<Fence>::make(); result = reply.read(**fence); if (result != NO_ERROR) { fence->clear(); @@ -293,7 +293,7 @@ public: if (result == NO_ERROR) { bool nonNull = reply.readInt32(); if (nonNull) { - *outBuffer = new GraphicBuffer; + *outBuffer = sp<GraphicBuffer>::make(); result = reply.read(**outBuffer); if (result != NO_ERROR) { outBuffer->clear(); @@ -302,7 +302,7 @@ public: } nonNull = reply.readInt32(); if (nonNull) { - *outFence = new Fence; + *outFence = sp<Fence>::make(); result = reply.read(**outFence); if (result != NO_ERROR) { outBuffer->clear(); @@ -640,7 +640,7 @@ public: bool hasBuffer = reply.readBool(); sp<GraphicBuffer> buffer; if (hasBuffer) { - buffer = new GraphicBuffer(); + buffer = sp<GraphicBuffer>::make(); result = reply.read(*buffer); if (result == NO_ERROR) { result = reply.read(outTransformMatrix, sizeof(float) * 16); @@ -650,7 +650,7 @@ public: ALOGE("getLastQueuedBuffer failed to read buffer: %d", result); return result; } - sp<Fence> fence(new Fence); + sp<Fence> fence = sp<Fence>::make(); result = reply.read(*fence); if (result != NO_ERROR) { ALOGE("getLastQueuedBuffer failed to read fence: %d", result); @@ -687,7 +687,7 @@ public: } sp<GraphicBuffer> buffer; if (hasBuffer) { - buffer = new GraphicBuffer(); + buffer = sp<GraphicBuffer>::make(); result = reply.read(*buffer); if (result == NO_ERROR) { result = reply.read(*outRect); @@ -700,7 +700,7 @@ public: ALOGE("getLastQueuedBuffer failed to read buffer: %d", result); return result; } - sp<Fence> fence(new Fence); + sp<Fence> fence = sp<Fence>::make(); result = reply.read(*fence); if (result != NO_ERROR) { ALOGE("getLastQueuedBuffer failed to read fence: %d", result); @@ -1232,7 +1232,7 @@ status_t BnGraphicBufferProducer::onTransact( } case ATTACH_BUFFER: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); - sp<GraphicBuffer> buffer = new GraphicBuffer(); + sp<GraphicBuffer> buffer = sp<GraphicBuffer>::make(); status_t result = data.read(*buffer.get()); int slot = 0; if (result == NO_ERROR) { @@ -1250,7 +1250,7 @@ status_t BnGraphicBufferProducer::onTransact( return result; } for (sp<GraphicBuffer>& buffer : buffers) { - buffer = new GraphicBuffer(); + buffer = sp<GraphicBuffer>::make(); result = data.read(*buffer.get()); if (result != NO_ERROR) { return result; @@ -1306,7 +1306,7 @@ status_t BnGraphicBufferProducer::onTransact( case CANCEL_BUFFER: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); int buf = data.readInt32(); - sp<Fence> fence = new Fence(); + sp<Fence> fence = sp<Fence>::make(); status_t result = data.read(*fence.get()); if (result == NO_ERROR) { result = cancelBuffer(buf, fence); diff --git a/libs/gui/IGraphicBufferProducerFlattenables.cpp b/libs/gui/IGraphicBufferProducerFlattenables.cpp index 8b2e2ddc59..393e1c3068 100644 --- a/libs/gui/IGraphicBufferProducerFlattenables.cpp +++ b/libs/gui/IGraphicBufferProducerFlattenables.cpp @@ -105,7 +105,7 @@ status_t IGraphicBufferProducer::QueueBufferInput::unflatten( : std::nullopt; #endif // COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES - fence = new Fence(); + fence = sp<Fence>::make(); status_t result = fence->unflatten(buffer, size, fds, count); if (result != NO_ERROR) { return result; @@ -228,7 +228,7 @@ status_t IGraphicBufferProducer::RequestBufferOutput::unflatten( FlattenableUtils::read(fBuffer, size, result); int32_t isBufferNull = 0; FlattenableUtils::read(fBuffer, size, isBufferNull); - buffer = new GraphicBuffer(); + buffer = sp<GraphicBuffer>::make(); if (!isBufferNull) { status_t status = buffer->unflatten(fBuffer, size, fds, count); if (status != NO_ERROR) { @@ -323,7 +323,7 @@ status_t IGraphicBufferProducer::DequeueBufferOutput::unflatten( FlattenableUtils::read(buffer, size, slot); FlattenableUtils::read(buffer, size, bufferAge); - fence = new Fence(); + fence = sp<Fence>::make(); status_t status = fence->unflatten(buffer, size, fds, count); if (status != NO_ERROR) { return status; @@ -395,7 +395,7 @@ status_t IGraphicBufferProducer::CancelBufferInput::unflatten( FlattenableUtils::read(buffer, size, slot); - fence = new Fence(); + fence = sp<Fence>::make(); return fence->unflatten(buffer, size, fds, count); } diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp index 269936858a..ae4b74e03b 100644 --- a/libs/gui/ISurfaceComposer.cpp +++ b/libs/gui/ISurfaceComposer.cpp @@ -26,6 +26,7 @@ #include <gui/ISurfaceComposer.h> #include <gui/LayerState.h> #include <gui/SchedulingPolicy.h> +#include <gui/TransactionState.h> #include <private/gui/ParcelUtils.h> #include <stdint.h> #include <sys/types.h> @@ -60,54 +61,12 @@ public: virtual ~BpSurfaceComposer(); - status_t setTransactionState( - const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& state, - Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken, - InputWindowCommands commands, int64_t desiredPresentTime, bool isAutoTimestamp, - const std::vector<client_cache_t>& uncacheBuffers, bool hasListenerCallbacks, - const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId, - const std::vector<uint64_t>& mergedTransactionIds) override { + status_t setTransactionState(TransactionState&& state) override { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + SAFE_PARCEL(state.writeToParcel, &data); - frameTimelineInfo.writeToParcel(&data); - - SAFE_PARCEL(data.writeUint32, static_cast<uint32_t>(state.size())); - for (const auto& s : state) { - SAFE_PARCEL(s.write, data); - } - - SAFE_PARCEL(data.writeUint32, static_cast<uint32_t>(displays.size())); - for (const auto& d : displays) { - SAFE_PARCEL(d.write, data); - } - - SAFE_PARCEL(data.writeUint32, flags); - SAFE_PARCEL(data.writeStrongBinder, applyToken); - SAFE_PARCEL(commands.write, data); - SAFE_PARCEL(data.writeInt64, desiredPresentTime); - SAFE_PARCEL(data.writeBool, isAutoTimestamp); - SAFE_PARCEL(data.writeUint32, static_cast<uint32_t>(uncacheBuffers.size())); - for (const client_cache_t& uncacheBuffer : uncacheBuffers) { - SAFE_PARCEL(data.writeStrongBinder, uncacheBuffer.token.promote()); - SAFE_PARCEL(data.writeUint64, uncacheBuffer.id); - } - SAFE_PARCEL(data.writeBool, hasListenerCallbacks); - - SAFE_PARCEL(data.writeVectorSize, listenerCallbacks); - for (const auto& [listener, callbackIds] : listenerCallbacks) { - SAFE_PARCEL(data.writeStrongBinder, listener); - SAFE_PARCEL(data.writeParcelableVector, callbackIds); - } - - SAFE_PARCEL(data.writeUint64, transactionId); - - SAFE_PARCEL(data.writeUint32, static_cast<uint32_t>(mergedTransactionIds.size())); - for (auto mergedTransactionId : mergedTransactionIds) { - SAFE_PARCEL(data.writeUint64, mergedTransactionId); - } - - if (flags & ISurfaceComposer::eOneWay) { + if (state.mFlags & ISurfaceComposer::eOneWay) { return remote()->transact(BnSurfaceComposer::SET_TRANSACTION_STATE, data, &reply, IBinder::FLAG_ONEWAY); } else { @@ -132,75 +91,9 @@ status_t BnSurfaceComposer::onTransact( case SET_TRANSACTION_STATE: { CHECK_INTERFACE(ISurfaceComposer, data, reply); - FrameTimelineInfo frameTimelineInfo; - frameTimelineInfo.readFromParcel(&data); - - uint32_t count = 0; - SAFE_PARCEL_READ_SIZE(data.readUint32, &count, data.dataSize()); - Vector<ComposerState> state; - state.setCapacity(count); - for (size_t i = 0; i < count; i++) { - ComposerState s; - SAFE_PARCEL(s.read, data); - state.add(s); - } - - SAFE_PARCEL_READ_SIZE(data.readUint32, &count, data.dataSize()); - DisplayState d; - Vector<DisplayState> displays; - displays.setCapacity(count); - for (size_t i = 0; i < count; i++) { - SAFE_PARCEL(d.read, data); - displays.add(d); - } - - uint32_t stateFlags = 0; - SAFE_PARCEL(data.readUint32, &stateFlags); - sp<IBinder> applyToken; - SAFE_PARCEL(data.readStrongBinder, &applyToken); - InputWindowCommands inputWindowCommands; - SAFE_PARCEL(inputWindowCommands.read, data); - - int64_t desiredPresentTime = 0; - bool isAutoTimestamp = true; - SAFE_PARCEL(data.readInt64, &desiredPresentTime); - SAFE_PARCEL(data.readBool, &isAutoTimestamp); - - SAFE_PARCEL_READ_SIZE(data.readUint32, &count, data.dataSize()); - std::vector<client_cache_t> uncacheBuffers(count); - sp<IBinder> tmpBinder; - for (size_t i = 0; i < count; i++) { - SAFE_PARCEL(data.readNullableStrongBinder, &tmpBinder); - uncacheBuffers[i].token = tmpBinder; - SAFE_PARCEL(data.readUint64, &uncacheBuffers[i].id); - } - - bool hasListenerCallbacks = false; - SAFE_PARCEL(data.readBool, &hasListenerCallbacks); - - std::vector<ListenerCallbacks> listenerCallbacks; - int32_t listenersSize = 0; - SAFE_PARCEL_READ_SIZE(data.readInt32, &listenersSize, data.dataSize()); - for (int32_t i = 0; i < listenersSize; i++) { - SAFE_PARCEL(data.readStrongBinder, &tmpBinder); - std::vector<CallbackId> callbackIds; - SAFE_PARCEL(data.readParcelableVector, &callbackIds); - listenerCallbacks.emplace_back(tmpBinder, callbackIds); - } - - uint64_t transactionId = -1; - SAFE_PARCEL(data.readUint64, &transactionId); - - SAFE_PARCEL_READ_SIZE(data.readUint32, &count, data.dataSize()); - std::vector<uint64_t> mergedTransactions(count); - for (size_t i = 0; i < count; i++) { - SAFE_PARCEL(data.readUint64, &mergedTransactions[i]); - } - - return setTransactionState(frameTimelineInfo, state, displays, stateFlags, applyToken, - std::move(inputWindowCommands), desiredPresentTime, - isAutoTimestamp, uncacheBuffers, hasListenerCallbacks, - listenerCallbacks, transactionId, mergedTransactions); + TransactionState state; + SAFE_PARCEL(state.readFromParcel, &data); + return setTransactionState(std::move(state)); } case GET_SCHEDULING_POLICY: { gui::SchedulingPolicy policy; diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp index 83fc827c5f..ed28e7960b 100644 --- a/libs/gui/ITransactionCompletedListener.cpp +++ b/libs/gui/ITransactionCompletedListener.cpp @@ -92,7 +92,7 @@ status_t FrameEventHistoryStats::readFromParcel(const Parcel* input) { if (err != NO_ERROR) return err; if (hasFence) { - gpuCompositionDoneFence = new Fence(); + gpuCompositionDoneFence = sp<Fence>::make(); err = input->read(*gpuCompositionDoneFence); if (err != NO_ERROR) return err; } @@ -157,7 +157,7 @@ status_t SurfaceStats::readFromParcel(const Parcel* input) { SAFE_PARCEL(input->readBool, &hasFence); if (hasFence) { - previousReleaseFence = new Fence(); + previousReleaseFence = sp<Fence>::make(); SAFE_PARCEL(input->read, *previousReleaseFence); } bool hasTransformHint = false; @@ -216,7 +216,7 @@ status_t TransactionStats::readFromParcel(const Parcel* input) { return err; } if (hasFence) { - presentFence = new Fence(); + presentFence = sp<Fence>::make(); err = input->read(*presentFence); if (err != NO_ERROR) { return err; diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index c1a03fcfea..ad95d1a85a 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -55,6 +55,28 @@ namespace android { using gui::FocusRequest; using gui::WindowInfoHandle; +namespace { +bool isSameWindowHandle(const sp<WindowInfoHandle>& lhs, const sp<WindowInfoHandle>& rhs) { + if (lhs == rhs) { + return true; + } + + if (!lhs || !rhs) { + return false; + } + + return *lhs->getInfo() == *rhs->getInfo(); +}; + +bool isSameSurfaceControl(const sp<SurfaceControl>& lhs, const sp<SurfaceControl>& rhs) { + if (lhs == rhs) { + return true; + } + + return SurfaceControl::isSameSurface(lhs, rhs); +}; +} // namespace + layer_state_t::layer_state_t() : surface(nullptr), layerId(-1), @@ -66,13 +88,13 @@ layer_state_t::layer_state_t() mask(0), reserved(0), cornerRadius(0.0f), + clientDrawnCornerRadius(0.0f), backgroundBlurRadius(0), color(0), bufferTransform(0), transformToDisplayInverse(false), crop({0, 0, -1, -1}), dataspace(ui::Dataspace::UNKNOWN), - surfaceDamageRegion(), api(-1), colorTransform(mat4()), bgColor(0), @@ -116,19 +138,21 @@ status_t layer_state_t::write(Parcel& output) const SAFE_PARCEL(output.writeFloat, crop.left); SAFE_PARCEL(output.writeFloat, crop.bottom); SAFE_PARCEL(output.writeFloat, crop.right); - SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, relativeLayerSurfaceControl); - SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, parentSurfaceControlForChild); + SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, + mNotDefCmpState.relativeLayerSurfaceControl); + SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, + mNotDefCmpState.parentSurfaceControlForChild); SAFE_PARCEL(output.writeFloat, color.r); SAFE_PARCEL(output.writeFloat, color.g); SAFE_PARCEL(output.writeFloat, color.b); SAFE_PARCEL(output.writeFloat, color.a); - SAFE_PARCEL(windowInfoHandle->writeToParcel, &output); - SAFE_PARCEL(output.write, transparentRegion); + SAFE_PARCEL(mNotDefCmpState.windowInfoHandle->writeToParcel, &output); + SAFE_PARCEL(output.write, mNotDefCmpState.transparentRegion); SAFE_PARCEL(output.writeUint32, bufferTransform); SAFE_PARCEL(output.writeBool, transformToDisplayInverse); SAFE_PARCEL(output.writeUint32, static_cast<uint32_t>(dataspace)); SAFE_PARCEL(output.write, hdrMetadata); - SAFE_PARCEL(output.write, surfaceDamageRegion); + SAFE_PARCEL(output.write, mNotDefCmpState.surfaceDamageRegion); SAFE_PARCEL(output.writeInt32, api); if (sidebandStream) { @@ -140,6 +164,7 @@ status_t layer_state_t::write(Parcel& output) const SAFE_PARCEL(output.write, colorTransform.asArray(), 16 * sizeof(float)); SAFE_PARCEL(output.writeFloat, cornerRadius); + SAFE_PARCEL(output.writeFloat, clientDrawnCornerRadius); SAFE_PARCEL(output.writeUint32, backgroundBlurRadius); SAFE_PARCEL(output.writeParcelable, metadata); SAFE_PARCEL(output.writeFloat, bgColor.r); @@ -239,8 +264,10 @@ status_t layer_state_t::read(const Parcel& input) SAFE_PARCEL(input.readFloat, &crop.bottom); SAFE_PARCEL(input.readFloat, &crop.right); - SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &relativeLayerSurfaceControl); - SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &parentSurfaceControlForChild); + SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, + &mNotDefCmpState.relativeLayerSurfaceControl); + SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, + &mNotDefCmpState.parentSurfaceControlForChild); float tmpFloat = 0; SAFE_PARCEL(input.readFloat, &tmpFloat); @@ -252,9 +279,9 @@ status_t layer_state_t::read(const Parcel& input) SAFE_PARCEL(input.readFloat, &tmpFloat); color.a = tmpFloat; - SAFE_PARCEL(windowInfoHandle->readFromParcel, &input); + SAFE_PARCEL(mNotDefCmpState.windowInfoHandle->readFromParcel, &input); - SAFE_PARCEL(input.read, transparentRegion); + SAFE_PARCEL(input.read, mNotDefCmpState.transparentRegion); SAFE_PARCEL(input.readUint32, &bufferTransform); SAFE_PARCEL(input.readBool, &transformToDisplayInverse); @@ -263,7 +290,7 @@ status_t layer_state_t::read(const Parcel& input) dataspace = static_cast<ui::Dataspace>(tmpUint32); SAFE_PARCEL(input.read, hdrMetadata); - SAFE_PARCEL(input.read, surfaceDamageRegion); + SAFE_PARCEL(input.read, mNotDefCmpState.surfaceDamageRegion); SAFE_PARCEL(input.readInt32, &api); bool tmpBool = false; @@ -274,6 +301,7 @@ status_t layer_state_t::read(const Parcel& input) SAFE_PARCEL(input.read, &colorTransform, 16 * sizeof(float)); SAFE_PARCEL(input.readFloat, &cornerRadius); + SAFE_PARCEL(input.readFloat, &clientDrawnCornerRadius); SAFE_PARCEL(input.readUint32, &backgroundBlurRadius); SAFE_PARCEL(input.readParcelable, &metadata); @@ -580,7 +608,7 @@ void layer_state_t::merge(const layer_state_t& other) { } if (other.what & eTransparentRegionChanged) { what |= eTransparentRegionChanged; - transparentRegion = other.transparentRegion; + mNotDefCmpState.transparentRegion = other.mNotDefCmpState.transparentRegion; } if (other.what & eFlagsChanged) { what |= eFlagsChanged; @@ -596,6 +624,10 @@ void layer_state_t::merge(const layer_state_t& other) { what |= eCornerRadiusChanged; cornerRadius = other.cornerRadius; } + if (other.what & eClientDrawnCornerRadiusChanged) { + what |= eClientDrawnCornerRadiusChanged; + clientDrawnCornerRadius = other.clientDrawnCornerRadius; + } if (other.what & eBackgroundBlurRadiusChanged) { what |= eBackgroundBlurRadiusChanged; backgroundBlurRadius = other.backgroundBlurRadius; @@ -608,11 +640,13 @@ void layer_state_t::merge(const layer_state_t& other) { what |= eRelativeLayerChanged; what &= ~eLayerChanged; z = other.z; - relativeLayerSurfaceControl = other.relativeLayerSurfaceControl; + mNotDefCmpState.relativeLayerSurfaceControl = + other.mNotDefCmpState.relativeLayerSurfaceControl; } if (other.what & eReparent) { what |= eReparent; - parentSurfaceControlForChild = other.parentSurfaceControlForChild; + mNotDefCmpState.parentSurfaceControlForChild = + other.mNotDefCmpState.parentSurfaceControlForChild; } if (other.what & eBufferTransformChanged) { what |= eBufferTransformChanged; @@ -658,7 +692,7 @@ void layer_state_t::merge(const layer_state_t& other) { } if (other.what & eSurfaceDamageRegionChanged) { what |= eSurfaceDamageRegionChanged; - surfaceDamageRegion = other.surfaceDamageRegion; + mNotDefCmpState.surfaceDamageRegion = other.mNotDefCmpState.surfaceDamageRegion; } if (other.what & eApiChanged) { what |= eApiChanged; @@ -677,7 +711,8 @@ void layer_state_t::merge(const layer_state_t& other) { } if (other.what & eInputInfoChanged) { what |= eInputInfoChanged; - windowInfoHandle = new WindowInfoHandle(*other.windowInfoHandle); + mNotDefCmpState.windowInfoHandle = + sp<WindowInfoHandle>::make(*other.mNotDefCmpState.windowInfoHandle); } if (other.what & eBackgroundColorChanged) { what |= eBackgroundColorChanged; @@ -800,7 +835,8 @@ uint64_t layer_state_t::diff(const layer_state_t& other) const { CHECK_DIFF(diff, eAlphaChanged, other, color.a); CHECK_DIFF(diff, eMatrixChanged, other, matrix); if (other.what & eTransparentRegionChanged && - (!transparentRegion.hasSameRects(other.transparentRegion))) { + (!mNotDefCmpState.transparentRegion.hasSameRects( + other.mNotDefCmpState.transparentRegion))) { diff |= eTransparentRegionChanged; } if (other.what & eFlagsChanged) { @@ -809,6 +845,7 @@ uint64_t layer_state_t::diff(const layer_state_t& other) const { } CHECK_DIFF(diff, eLayerStackChanged, other, layerStack); CHECK_DIFF(diff, eCornerRadiusChanged, other, cornerRadius); + CHECK_DIFF(diff, eClientDrawnCornerRadiusChanged, other, clientDrawnCornerRadius); CHECK_DIFF(diff, eBackgroundBlurRadiusChanged, other, backgroundBlurRadius); if (other.what & eBlurRegionsChanged) diff |= eBlurRegionsChanged; if (other.what & eRelativeLayerChanged) { @@ -816,8 +853,8 @@ uint64_t layer_state_t::diff(const layer_state_t& other) const { diff &= ~eLayerChanged; } if (other.what & eReparent && - !SurfaceControl::isSameSurface(parentSurfaceControlForChild, - other.parentSurfaceControlForChild)) { + !SurfaceControl::isSameSurface(mNotDefCmpState.parentSurfaceControlForChild, + other.mNotDefCmpState.parentSurfaceControlForChild)) { diff |= eReparent; } CHECK_DIFF(diff, eBufferTransformChanged, other, bufferTransform); @@ -831,7 +868,8 @@ uint64_t layer_state_t::diff(const layer_state_t& other) const { CHECK_DIFF(diff, eCachingHintChanged, other, cachingHint); CHECK_DIFF(diff, eHdrMetadataChanged, other, hdrMetadata); if (other.what & eSurfaceDamageRegionChanged && - (!surfaceDamageRegion.hasSameRects(other.surfaceDamageRegion))) { + (!mNotDefCmpState.surfaceDamageRegion.hasSameRects( + other.mNotDefCmpState.surfaceDamageRegion))) { diff |= eSurfaceDamageRegionChanged; } CHECK_DIFF(diff, eApiChanged, other, api); @@ -893,6 +931,38 @@ status_t layer_state_t::matrix22_t::read(const Parcel& input) { SAFE_PARCEL(input.readFloat, &dsdy); return NO_ERROR; } +void layer_state_t::updateTransparentRegion(const Region& transparentRegion) { + what |= eTransparentRegionChanged; + mNotDefCmpState.transparentRegion = transparentRegion; +} +void layer_state_t::updateSurfaceDamageRegion(const Region& surfaceDamageRegion) { + what |= eSurfaceDamageRegionChanged; + mNotDefCmpState.surfaceDamageRegion = surfaceDamageRegion; +} +void layer_state_t::updateRelativeLayer(const sp<SurfaceControl>& relativeTo, int32_t z) { + what |= layer_state_t::eRelativeLayerChanged; + what &= ~layer_state_t::eLayerChanged; + mNotDefCmpState.relativeLayerSurfaceControl = relativeTo; + this->z = z; +} +void layer_state_t::updateParentLayer(const sp<SurfaceControl>& newParent) { + what |= layer_state_t::eReparent; + mNotDefCmpState.parentSurfaceControlForChild = + newParent ? newParent->getParentingLayer() : nullptr; +} +void layer_state_t::updateInputWindowInfo(sp<gui::WindowInfoHandle>&& info) { + what |= eInputInfoChanged; + mNotDefCmpState.windowInfoHandle = std::move(info); +} + +bool layer_state_t::NotDefaultComparableState::operator==( + const NotDefaultComparableState& rhs) const { + return transparentRegion.hasSameRects(rhs.transparentRegion) && + surfaceDamageRegion.hasSameRects(rhs.surfaceDamageRegion) && + isSameWindowHandle(windowInfoHandle, rhs.windowInfoHandle) && + isSameSurfaceControl(relativeLayerSurfaceControl, rhs.relativeLayerSurfaceControl) && + isSameSurfaceControl(parentSurfaceControlForChild, rhs.parentSurfaceControlForChild); +} // ------------------------------- InputWindowCommands ---------------------------------------- @@ -993,13 +1063,13 @@ status_t BufferData::readFromParcel(const Parcel* input) { bool tmpBool = false; SAFE_PARCEL(input->readBool, &tmpBool); if (tmpBool) { - buffer = new GraphicBuffer(); + buffer = sp<GraphicBuffer>::make(); SAFE_PARCEL(input->read, *buffer); } SAFE_PARCEL(input->readBool, &tmpBool); if (tmpBool) { - acquireFence = new Fence(); + acquireFence = sp<Fence>::make(); SAFE_PARCEL(input->read, *acquireFence); } @@ -1026,8 +1096,8 @@ status_t BufferData::readFromParcel(const Parcel* input) { } status_t TrustedPresentationListener::writeToParcel(Parcel* parcel) const { - SAFE_PARCEL(parcel->writeStrongBinder, callbackInterface); - SAFE_PARCEL(parcel->writeInt32, callbackId); + SAFE_PARCEL(parcel->writeStrongBinder, mState.callbackInterface); + SAFE_PARCEL(parcel->writeInt32, mState.callbackId); return NO_ERROR; } @@ -1035,9 +1105,9 @@ status_t TrustedPresentationListener::readFromParcel(const Parcel* parcel) { sp<IBinder> tmpBinder = nullptr; SAFE_PARCEL(parcel->readNullableStrongBinder, &tmpBinder); if (tmpBinder) { - callbackInterface = checked_interface_cast<ITransactionCompletedListener>(tmpBinder); + mState.callbackInterface = checked_interface_cast<ITransactionCompletedListener>(tmpBinder); } - SAFE_PARCEL(parcel->readInt32, &callbackId); + SAFE_PARCEL(parcel->readInt32, &mState.callbackId); return NO_ERROR; } diff --git a/libs/gui/ScreenCaptureResults.cpp b/libs/gui/ScreenCaptureResults.cpp index 2de023e5b2..30b5785b6a 100644 --- a/libs/gui/ScreenCaptureResults.cpp +++ b/libs/gui/ScreenCaptureResults.cpp @@ -18,6 +18,7 @@ #include <private/gui/ParcelUtils.h> #include <ui/FenceResult.h> +#include <ui/GraphicBuffer.h> namespace android::gui { @@ -54,7 +55,7 @@ status_t ScreenCaptureResults::readFromParcel(const android::Parcel* parcel) { bool hasGraphicBuffer; SAFE_PARCEL(parcel->readBool, &hasGraphicBuffer); if (hasGraphicBuffer) { - buffer = new GraphicBuffer(); + buffer = sp<GraphicBuffer>::make(); SAFE_PARCEL(parcel->read, *buffer); } @@ -79,7 +80,7 @@ status_t ScreenCaptureResults::readFromParcel(const android::Parcel* parcel) { bool hasGainmap; SAFE_PARCEL(parcel->readBool, &hasGainmap); if (hasGainmap) { - optionalGainMap = new GraphicBuffer(); + optionalGainMap = sp<GraphicBuffer>::make(); SAFE_PARCEL(parcel->read, *optionalGainMap); } SAFE_PARCEL(parcel->readFloat, &hdrSdrRatio); diff --git a/libs/gui/StreamSplitter.cpp b/libs/gui/StreamSplitter.cpp index 653b91bcf6..848ae7a154 100644 --- a/libs/gui/StreamSplitter.cpp +++ b/libs/gui/StreamSplitter.cpp @@ -47,7 +47,7 @@ status_t StreamSplitter::createSplitter( return BAD_VALUE; } - sp<StreamSplitter> splitter(new StreamSplitter(inputQueue)); + sp<StreamSplitter> splitter = sp<StreamSplitter>::make(inputQueue); status_t status = splitter->mInput->consumerConnect(splitter, false); if (status == NO_ERROR) { splitter->mInput->setConsumerName(String8("StreamSplitter")); @@ -82,7 +82,7 @@ status_t StreamSplitter::addOutput( Mutex::Autolock lock(mMutex); IGraphicBufferProducer::QueueBufferOutput queueBufferOutput; - sp<OutputListener> listener(new OutputListener(this, outputQueue)); + sp<OutputListener> listener = sp<OutputListener>::make(this, outputQueue); IInterface::asBinder(outputQueue)->linkToDeath(listener); status_t status = outputQueue->connect(listener, NATIVE_WINDOW_API_CPU, /* producerControlledByApp */ false, &queueBufferOutput); @@ -140,7 +140,7 @@ void StreamSplitter::onFrameAvailable(const BufferItem& /* item */) { // Initialize our reference count for this buffer mBuffers.add(bufferItem.mGraphicBuffer->getId(), - new BufferTracker(bufferItem.mGraphicBuffer)); + sp<BufferTracker>::make(bufferItem.mGraphicBuffer)); IGraphicBufferProducer::QueueBufferInput queueInput( bufferItem.mTimestamp, bufferItem.mIsAutoTimestamp, diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index ec23365e1f..63dcfbcb9b 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -509,7 +509,7 @@ int Surface::hook_dequeueBuffer_DEPRECATED(ANativeWindow* window, if (result != OK) { return result; } - sp<Fence> fence(new Fence(fenceFd)); + sp<Fence> fence = sp<Fence>::make(fenceFd); int waitResult = fence->waitForever("dequeueBuffer_DEPRECATED"); if (waitResult != OK) { ALOGE("dequeueBuffer_DEPRECATED: Fence::wait returned an error: %d", @@ -979,7 +979,7 @@ int Surface::cancelBuffer(android_native_buffer_t* buffer, } return OK; } - sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE); + sp<Fence> fence(fenceFd >= 0 ? sp<Fence>::make(fenceFd) : Fence::NO_FENCE); mGraphicBufferProducer->cancelBuffer(i, fence); if (mSharedBufferMode && mAutoRefresh && mSharedBufferSlot == i) { @@ -1017,7 +1017,7 @@ int Surface::cancelBuffers(const std::vector<BatchBuffer>& buffers) { ALOGE("%s: cannot find slot number for cancelled buffer", __FUNCTION__); badSlotResult = slot; } else { - sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE); + sp<Fence> fence(fenceFd >= 0 ? sp<Fence>::make(fenceFd) : Fence::NO_FENCE); cancelBufferInputs[numBuffersCancelled].slot = slot; cancelBufferInputs[numBuffersCancelled++].fence = fence; } @@ -1078,7 +1078,7 @@ void Surface::getQueueBufferInputLocked(android_native_buffer_t* buffer, int fen Rect crop(Rect::EMPTY_RECT); mCrop.intersect(Rect(buffer->width, buffer->height), &crop); - sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE); + sp<Fence> fence(fenceFd >= 0 ? sp<Fence>::make(fenceFd) : Fence::NO_FENCE); IGraphicBufferProducer::QueueBufferInput input(timestamp, isAutoTimestamp, static_cast<android_dataspace>(mDataSpace), crop, mScalingMode, mTransform ^ mStickyTransform, fence, mStickyTransform, @@ -2092,7 +2092,7 @@ bool Surface::transformToDisplayInverse() const { } int Surface::connect(int api) { - static sp<SurfaceListener> listener = new StubSurfaceListener(); + static sp<SurfaceListener> listener = sp<StubSurfaceListener>::make(); return connect(api, listener); } @@ -2104,7 +2104,7 @@ int Surface::connect(int api, const sp<SurfaceListener>& listener, bool reportBu mReportRemovedBuffers = reportBufferRemoval; if (listener != nullptr) { - mListenerProxy = new ProducerListenerProxy(this, listener); + mListenerProxy = sp<ProducerListenerProxy>::make(this, listener); } int err = diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index cabde22c6d..b0650d56ff 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -19,6 +19,7 @@ #include <semaphore.h> #include <stdint.h> #include <sys/types.h> +#include <algorithm> #include <android/gui/BnWindowInfosReportedListener.h> #include <android/gui/DisplayState.h> @@ -122,7 +123,7 @@ bool ComposerService::connectLocked() { explicit DeathObserver(ComposerService& mgr) : mComposerService(mgr) { } }; - mDeathObserver = new DeathObserver(*const_cast<ComposerService*>(this)); + mDeathObserver = sp<DeathObserver>::make(*const_cast<ComposerService*>(this)); IInterface::asBinder(mComposerService)->linkToDeath(mDeathObserver); return true; } @@ -169,7 +170,7 @@ bool ComposerServiceAIDL::connectLocked() { explicit DeathObserver(ComposerServiceAIDL& mgr) : mComposerService(mgr) {} }; - mDeathObserver = new DeathObserver(*const_cast<ComposerServiceAIDL*>(this)); + mDeathObserver = sp<DeathObserver>::make(*const_cast<ComposerServiceAIDL*>(this)); IInterface::asBinder(mComposerService)->linkToDeath(mDeathObserver); return true; } @@ -201,7 +202,7 @@ public: DefaultComposerClient& dc = DefaultComposerClient::getInstance(); Mutex::Autolock _l(dc.mLock); if (dc.mClient == nullptr) { - dc.mClient = new SurfaceComposerClient; + dc.mClient = sp<SurfaceComposerClient>::make(); } return dc.mClient; } @@ -398,7 +399,7 @@ void TransactionCompletedListener::setInstance(const sp<TransactionCompletedList sp<TransactionCompletedListener> TransactionCompletedListener::getInstance() { std::lock_guard<std::mutex> lock(sListenerInstanceMutex); if (sInstance == nullptr) { - sInstance = new TransactionCompletedListener; + sInstance = sp<TransactionCompletedListener>::make(); } return sInstance; } @@ -690,7 +691,7 @@ TransactionCompletedListener::addTrustedPresentationCallback(TrustedPresentation std::scoped_lock<std::mutex> lock(mMutex); mTrustedPresentationCallbacks[id] = std::tuple<TrustedPresentationCallback, void*>(tpc, context); - return new SurfaceComposerClient::PresentationCallbackRAII(this, id); + return sp<SurfaceComposerClient::PresentationCallbackRAII>::make(this, id); } void TransactionCompletedListener::clearTrustedPresentationCallback(int id) { @@ -742,7 +743,7 @@ void removeDeadBufferCallback(void* /*context*/, uint64_t graphicBufferId); */ class BufferCache : public Singleton<BufferCache> { public: - BufferCache() : token(new BBinder()) {} + BufferCache() : token(sp<BBinder>::make()) {} sp<IBinder> getToken() { return IInterface::asBinder(TransactionCompletedListener::getIInstance()); @@ -823,36 +824,25 @@ void removeDeadBufferCallback(void* /*context*/, uint64_t graphicBufferId) { // --------------------------------------------------------------------------- SurfaceComposerClient::Transaction::Transaction() { - mId = generateId(); + mState.mId = generateId(); mTransactionCompletedListener = TransactionCompletedListener::getInstance(); } -SurfaceComposerClient::Transaction::Transaction(const Transaction& other) - : mId(other.mId), - mAnimation(other.mAnimation), - mEarlyWakeupStart(other.mEarlyWakeupStart), - mEarlyWakeupEnd(other.mEarlyWakeupEnd), - mMayContainBuffer(other.mMayContainBuffer), - mDesiredPresentTime(other.mDesiredPresentTime), - mIsAutoTimestamp(other.mIsAutoTimestamp), - mFrameTimelineInfo(other.mFrameTimelineInfo), - mApplyToken(other.mApplyToken) { - mDisplayStates = other.mDisplayStates; - mComposerStates = other.mComposerStates; - mInputWindowCommands = other.mInputWindowCommands; +SurfaceComposerClient::Transaction::Transaction(const Transaction& other) { + mState = other.mState; mListenerCallbacks = other.mListenerCallbacks; mTransactionCompletedListener = TransactionCompletedListener::getInstance(); } void SurfaceComposerClient::Transaction::sanitize(int pid, int uid) { uint32_t permissions = LayerStatePermissions::getTransactionPermissions(pid, uid); - for (auto & [handle, composerState] : mComposerStates) { + for (auto& composerState : mState.mComposerStates) { composerState.state.sanitize(permissions); } - if (!mInputWindowCommands.empty() && + if (!mState.mInputWindowCommands.empty() && (permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER) == 0) { ALOGE("Only privileged callers are allowed to send input commands."); - mInputWindowCommands.clear(); + mState.mInputWindowCommands.clear(); } } @@ -867,33 +857,10 @@ SurfaceComposerClient::Transaction::createFromParcel(const Parcel* parcel) { status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel) { - const uint64_t transactionId = parcel->readUint64(); - const bool animation = parcel->readBool(); - const bool earlyWakeupStart = parcel->readBool(); - 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); - - sp<IBinder> applyToken; - parcel->readNullableStrongBinder(&applyToken); - size_t count = static_cast<size_t>(parcel->readUint32()); - if (count > parcel->dataSize()) { - return BAD_VALUE; - } - SortedVector<DisplayState> displayStates; - displayStates.setCapacity(count); - for (size_t i = 0; i < count; i++) { - DisplayState displayState; - if (displayState.read(*parcel) == BAD_VALUE) { - return BAD_VALUE; - } - displayStates.add(displayState); - } + TransactionState tmpState; + SAFE_PARCEL(tmpState.readFromParcel, parcel); - count = static_cast<size_t>(parcel->readUint32()); + size_t count = static_cast<size_t>(parcel->readUint32()); if (count > parcel->dataSize()) { return BAD_VALUE; } @@ -922,62 +889,8 @@ status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel } } - count = static_cast<size_t>(parcel->readUint32()); - if (count > parcel->dataSize()) { - return BAD_VALUE; - } - std::unordered_map<sp<IBinder>, ComposerState, IBinderHash> composerStates; - composerStates.reserve(count); - for (size_t i = 0; i < count; i++) { - sp<IBinder> surfaceControlHandle; - SAFE_PARCEL(parcel->readStrongBinder, &surfaceControlHandle); - - ComposerState composerState; - if (composerState.read(*parcel) == BAD_VALUE) { - return BAD_VALUE; - } - composerStates[surfaceControlHandle] = composerState; - } - - InputWindowCommands inputWindowCommands; - inputWindowCommands.read(*parcel); - - count = static_cast<size_t>(parcel->readUint32()); - if (count > parcel->dataSize()) { - return BAD_VALUE; - } - std::vector<client_cache_t> uncacheBuffers(count); - for (size_t i = 0; i < count; i++) { - sp<IBinder> tmpBinder; - SAFE_PARCEL(parcel->readStrongBinder, &tmpBinder); - uncacheBuffers[i].token = tmpBinder; - SAFE_PARCEL(parcel->readUint64, &uncacheBuffers[i].id); - } - - count = static_cast<size_t>(parcel->readUint32()); - if (count > parcel->dataSize()) { - return BAD_VALUE; - } - std::vector<uint64_t> mergedTransactionIds(count); - for (size_t i = 0; i < count; i++) { - SAFE_PARCEL(parcel->readUint64, &mergedTransactionIds[i]); - } - - // Parsing was successful. Update the object. - mId = transactionId; - mAnimation = animation; - mEarlyWakeupStart = earlyWakeupStart; - mEarlyWakeupEnd = earlyWakeupEnd; - mDesiredPresentTime = desiredPresentTime; - mIsAutoTimestamp = isAutoTimestamp; - mFrameTimelineInfo = frameTimelineInfo; - mDisplayStates = displayStates; - mListenerCallbacks = listenerCallbacks; - mComposerStates = composerStates; - mInputWindowCommands = inputWindowCommands; - mApplyToken = applyToken; - mUncacheBuffers = std::move(uncacheBuffers); - mMergedTransactionIds = std::move(mergedTransactionIds); + mState = std::move(tmpState); + mListenerCallbacks = std::move(listenerCallbacks); return NO_ERROR; } @@ -995,19 +908,7 @@ status_t SurfaceComposerClient::Transaction::writeToParcel(Parcel* parcel) const const_cast<SurfaceComposerClient::Transaction*>(this)->cacheBuffers(); - parcel->writeUint64(mId); - parcel->writeBool(mAnimation); - parcel->writeBool(mEarlyWakeupStart); - 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())); - for (auto const& displayState : mDisplayStates) { - displayState.write(*parcel); - } + SAFE_PARCEL(mState.writeToParcel, parcel); parcel->writeUint32(static_cast<uint32_t>(mListenerCallbacks.size())); for (auto const& [listener, callbackInfo] : mListenerCallbacks) { @@ -1022,25 +923,6 @@ status_t SurfaceComposerClient::Transaction::writeToParcel(Parcel* parcel) const } } - parcel->writeUint32(static_cast<uint32_t>(mComposerStates.size())); - for (auto const& [handle, composerState] : mComposerStates) { - SAFE_PARCEL(parcel->writeStrongBinder, handle); - composerState.write(*parcel); - } - - mInputWindowCommands.write(*parcel); - - SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(mUncacheBuffers.size())); - for (const client_cache_t& uncacheBuffer : mUncacheBuffers) { - SAFE_PARCEL(parcel->writeStrongBinder, uncacheBuffer.token.promote()); - SAFE_PARCEL(parcel->writeUint64, uncacheBuffer.id); - } - - SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(mMergedTransactionIds.size())); - for (auto mergedTransactionId : mMergedTransactionIds) { - SAFE_PARCEL(parcel->writeUint64, mergedTransactionId); - } - return NO_ERROR; } @@ -1065,42 +947,8 @@ void SurfaceComposerClient::Transaction::releaseBufferIfOverwriting(const layer_ } SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::merge(Transaction&& other) { - while (mMergedTransactionIds.size() + other.mMergedTransactionIds.size() > - MAX_MERGE_HISTORY_LENGTH - 1 && - mMergedTransactionIds.size() > 0) { - mMergedTransactionIds.pop_back(); - } - if (other.mMergedTransactionIds.size() == MAX_MERGE_HISTORY_LENGTH) { - mMergedTransactionIds.insert(mMergedTransactionIds.begin(), - other.mMergedTransactionIds.begin(), - other.mMergedTransactionIds.end() - 1); - } else if (other.mMergedTransactionIds.size() > 0u) { - mMergedTransactionIds.insert(mMergedTransactionIds.begin(), - other.mMergedTransactionIds.begin(), - other.mMergedTransactionIds.end()); - } - mMergedTransactionIds.insert(mMergedTransactionIds.begin(), other.mId); - - for (auto const& [handle, composerState] : other.mComposerStates) { - if (mComposerStates.count(handle) == 0) { - mComposerStates[handle] = composerState; - } else { - if (composerState.state.what & layer_state_t::eBufferChanged) { - releaseBufferIfOverwriting(mComposerStates[handle].state); - } - mComposerStates[handle].state.merge(composerState.state); - } - } - - for (auto const& state : other.mDisplayStates) { - ssize_t index = mDisplayStates.indexOf(state); - if (index < 0) { - mDisplayStates.add(state); - } else { - mDisplayStates.editItemAt(static_cast<size_t>(index)).merge(state); - } - } - + mState.merge(std::move(other.mState), + std::bind(&Transaction::releaseBufferIfOverwriting, this, std::placeholders::_1)); for (const auto& [listener, callbackInfo] : other.mListenerCallbacks) { auto& [callbackIds, surfaceControls] = callbackInfo; mListenerCallbacks[listener].callbackIds.insert(std::make_move_iterator( @@ -1124,53 +972,21 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::merge(Tr } } - for (const auto& cacheId : other.mUncacheBuffers) { - mUncacheBuffers.push_back(cacheId); - } - - mInputWindowCommands.merge(other.mInputWindowCommands); - - mMayContainBuffer |= other.mMayContainBuffer; - mEarlyWakeupStart = mEarlyWakeupStart || other.mEarlyWakeupStart; - mEarlyWakeupEnd = mEarlyWakeupEnd || other.mEarlyWakeupEnd; - mApplyToken = other.mApplyToken; - - 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; } void SurfaceComposerClient::Transaction::clear() { - mComposerStates.clear(); - mDisplayStates.clear(); + mState.clear(); mListenerCallbacks.clear(); - mInputWindowCommands.clear(); - mUncacheBuffers.clear(); - mMayContainBuffer = false; - mAnimation = false; - mEarlyWakeupStart = false; - mEarlyWakeupEnd = false; - mDesiredPresentTime = 0; - mIsAutoTimestamp = true; - mFrameTimelineInfo = {}; - mApplyToken = nullptr; - mMergedTransactionIds.clear(); - mLogCallPoints = false; } -uint64_t SurfaceComposerClient::Transaction::getId() { - return mId; +uint64_t SurfaceComposerClient::Transaction::getId() const { + return mState.mId; } std::vector<uint64_t> SurfaceComposerClient::Transaction::getMergedTransactionIds() { - return mMergedTransactionIds; + return mState.mMergedTransactionIds; } void SurfaceComposerClient::doUncacheBufferTransaction(uint64_t cacheId) { @@ -1179,12 +995,13 @@ void SurfaceComposerClient::doUncacheBufferTransaction(uint64_t cacheId) { client_cache_t uncacheBuffer; uncacheBuffer.token = BufferCache::getInstance().getToken(); uncacheBuffer.id = cacheId; - Vector<ComposerState> composerStates; - Vector<DisplayState> displayStates; - status_t status = sf->setTransactionState(FrameTimelineInfo{}, composerStates, displayStates, - ISurfaceComposer::eOneWay, - Transaction::getDefaultApplyToken(), {}, systemTime(), - true, {uncacheBuffer}, false, {}, generateId(), {}); + TransactionState state; + state.mId = generateId(); + state.mApplyToken = Transaction::getDefaultApplyToken(); + state.mUncacheBuffers.emplace_back(std::move(uncacheBuffer)); + state.mFlags = ISurfaceComposer::eOneWay; + state.mDesiredPresentTime = systemTime(); + status_t status = sf->setTransactionState(std::move(state)); if (status != NO_ERROR) { ALOGE_AND_TRACE("SurfaceComposerClient::doUncacheBufferTransaction - %s", strerror(-status)); @@ -1192,13 +1009,13 @@ void SurfaceComposerClient::doUncacheBufferTransaction(uint64_t cacheId) { } void SurfaceComposerClient::Transaction::cacheBuffers() { - if (!mMayContainBuffer) { + if (!mState.mMayContainBuffer) { return; } size_t count = 0; - for (auto& [handle, cs] : mComposerStates) { - layer_state_t* s = &(mComposerStates[handle].state); + for (auto& cs : mState.mComposerStates) { + layer_state_t* s = &cs.state; if (!(s->what & layer_state_t::eBufferChanged)) { continue; } else if (s->bufferData && @@ -1225,7 +1042,7 @@ void SurfaceComposerClient::Transaction::cacheBuffers() { std::optional<client_cache_t> uncacheBuffer; cacheId = BufferCache::getInstance().cache(s->bufferData->buffer, uncacheBuffer); if (uncacheBuffer) { - mUncacheBuffers.push_back(*uncacheBuffer); + mState.mUncacheBuffers.emplace_back(*uncacheBuffer); } } s->bufferData->flags |= BufferData::BufferDataChange::cachedBufferChanged; @@ -1294,8 +1111,7 @@ status_t SurfaceComposerClient::Transaction::apply(bool synchronous, bool oneWay /*callbackContext=*/nullptr); } - bool hasListenerCallbacks = !mListenerCallbacks.empty(); - std::vector<ListenerCallbacks> listenerCallbacks; + mState.mHasListenerCallbacks = !mListenerCallbacks.empty(); // For every listener with registered callbacks for (const auto& [listener, callbackInfo] : mListenerCallbacks) { auto& [callbackIds, surfaceControls] = callbackInfo; @@ -1304,7 +1120,8 @@ status_t SurfaceComposerClient::Transaction::apply(bool synchronous, bool oneWay } if (surfaceControls.empty()) { - listenerCallbacks.emplace_back(IInterface::asBinder(listener), std::move(callbackIds)); + mState.mListenerCallbacks.emplace_back(IInterface::asBinder(listener), + std::move(callbackIds)); } else { // If the listener has any SurfaceControls set on this Transaction update the surface // state @@ -1316,53 +1133,33 @@ status_t SurfaceComposerClient::Transaction::apply(bool synchronous, bool oneWay } std::vector<CallbackId> callbacks(callbackIds.begin(), callbackIds.end()); s->what |= layer_state_t::eHasListenerCallbacksChanged; - s->listeners.emplace_back(IInterface::asBinder(listener), callbacks); + s->listeners.emplace_back(IInterface::asBinder(listener), std::move(callbacks)); } } } cacheBuffers(); - Vector<ComposerState> composerStates; - Vector<DisplayState> displayStates; - uint32_t flags = 0; - - for (auto const& kv : mComposerStates) { - composerStates.add(kv.second); - } - - displayStates = std::move(mDisplayStates); - - if (mAnimation) { - flags |= ISurfaceComposer::eAnimation; - } if (oneWay) { if (synchronous) { ALOGE("Transaction attempted to set synchronous and one way at the same time" " this is an invalid request. Synchronous will win for safety"); } else { - flags |= ISurfaceComposer::eOneWay; + mState.mFlags |= ISurfaceComposer::eOneWay; } } - // If both mEarlyWakeupStart and mEarlyWakeupEnd are set + // If both ISurfaceComposer::eEarlyWakeupStart and ISurfaceComposer::eEarlyWakeupEnd are set // it is equivalent for none - if (mEarlyWakeupStart && !mEarlyWakeupEnd) { - flags |= ISurfaceComposer::eEarlyWakeupStart; - } - if (mEarlyWakeupEnd && !mEarlyWakeupStart) { - flags |= ISurfaceComposer::eEarlyWakeupEnd; + uint32_t wakeupFlags = ISurfaceComposer::eEarlyWakeupStart | ISurfaceComposer::eEarlyWakeupEnd; + if ((mState.mFlags & wakeupFlags) == wakeupFlags) { + mState.mFlags &= ~(wakeupFlags); } - - sp<IBinder> applyToken = mApplyToken ? mApplyToken : getDefaultApplyToken(); + if (!mState.mApplyToken) mState.mApplyToken = getDefaultApplyToken(); sp<ISurfaceComposer> sf(ComposerService::getComposerService()); - status_t binderStatus = - sf->setTransactionState(mFrameTimelineInfo, composerStates, displayStates, flags, - applyToken, mInputWindowCommands, mDesiredPresentTime, - mIsAutoTimestamp, mUncacheBuffers, hasListenerCallbacks, - listenerCallbacks, mId, mMergedTransactionIds); - mId = generateId(); + status_t binderStatus = sf->setTransactionState(std::move(mState)); + mState.mId = generateId(); // Clear the current states and flags clear(); @@ -1371,15 +1168,15 @@ status_t SurfaceComposerClient::Transaction::apply(bool synchronous, bool oneWay syncCallback->wait(); } - if (mLogCallPoints) { - ALOG(LOG_DEBUG, LOG_SURFACE_CONTROL_REGISTRY, "Transaction %" PRIu64 " applied", mId); + if (mState.mLogCallPoints) { + ALOG(LOG_DEBUG, LOG_SURFACE_CONTROL_REGISTRY, "Transaction %" PRIu64 " applied", getId()); } mStatus = NO_ERROR; return binderStatus; } -sp<IBinder> SurfaceComposerClient::Transaction::sApplyToken = new BBinder(); +sp<IBinder> SurfaceComposerClient::Transaction::sApplyToken = sp<BBinder>::make(); std::mutex SurfaceComposerClient::Transaction::sApplyTokenMutex; @@ -1407,17 +1204,22 @@ status_t SurfaceComposerClient::Transaction::sendSurfaceFlushJankDataTransaction } void SurfaceComposerClient::Transaction::enableDebugLogCallPoints() { - mLogCallPoints = true; + mState.mLogCallPoints = true; } // --------------------------------------------------------------------------- sp<IBinder> SurfaceComposerClient::createVirtualDisplay(const std::string& displayName, - bool isSecure, const std::string& uniqueId, + bool isSecure, bool optimizeForPower, + const std::string& uniqueId, float requestedRefreshRate) { + const gui::ISurfaceComposer::OptimizationPolicy optimizationPolicy = optimizeForPower + ? gui::ISurfaceComposer::OptimizationPolicy::optimizeForPower + : gui::ISurfaceComposer::OptimizationPolicy::optimizeForPerformance; sp<IBinder> display = nullptr; binder::Status status = ComposerServiceAIDL::getComposerService()->createVirtualDisplay(displayName, isSecure, + optimizationPolicy, uniqueId, requestedRefreshRate, &display); @@ -1437,9 +1239,8 @@ std::vector<PhysicalDisplayId> SurfaceComposerClient::getPhysicalDisplayIds() { ComposerServiceAIDL::getComposerService()->getPhysicalDisplayIds(&displayIds); if (status.isOk()) { physicalDisplayIds.reserve(displayIds.size()); - for (auto item : displayIds) { - auto id = DisplayId::fromValue<PhysicalDisplayId>(static_cast<uint64_t>(item)); - physicalDisplayIds.push_back(*id); + for (auto id : displayIds) { + physicalDisplayIds.push_back(PhysicalDisplayId::fromValue(static_cast<uint64_t>(id))); } } return physicalDisplayIds; @@ -1461,31 +1262,19 @@ std::optional<gui::StalledTransactionInfo> SurfaceComposerClient::getStalledTran } void SurfaceComposerClient::Transaction::setAnimationTransaction() { - mAnimation = true; + mState.mFlags |= ISurfaceComposer::eAnimation; } void SurfaceComposerClient::Transaction::setEarlyWakeupStart() { - mEarlyWakeupStart = true; + mState.mFlags |= ISurfaceComposer::eEarlyWakeupStart; } void SurfaceComposerClient::Transaction::setEarlyWakeupEnd() { - mEarlyWakeupEnd = true; + mState.mFlags |= ISurfaceComposer::eEarlyWakeupEnd; } layer_state_t* SurfaceComposerClient::Transaction::getLayerState(const sp<SurfaceControl>& sc) { - auto handle = sc->getLayerStateHandle(); - - if (mComposerStates.count(handle) == 0) { - // we don't have it, add an initialized layer_state to our list - ComposerState s; - - s.state.surface = handle; - s.state.layerId = sc->getLayerId(); - - mComposerStates[handle] = s; - } - - return &(mComposerStates[handle].state); + return mState.getLayerState(sc); } void SurfaceComposerClient::Transaction::registerSurfaceControlForCallback( @@ -1543,11 +1332,7 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setRelat mStatus = BAD_INDEX; return *this; } - s->what |= layer_state_t::eRelativeLayerChanged; - s->what &= ~layer_state_t::eLayerChanged; - s->relativeLayerSurfaceControl = relativeTo; - s->z = z; - + s->updateRelativeLayer(relativeTo, z); registerSurfaceControlForCallback(sc); return *this; } @@ -1577,9 +1362,7 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setTrans mStatus = BAD_INDEX; return *this; } - s->what |= layer_state_t::eTransparentRegionChanged; - s->transparentRegion = transparentRegion; - + s->updateTransparentRegion(transparentRegion); registerSurfaceControlForCallback(sc); return *this; } @@ -1695,6 +1478,18 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCorne return *this; } +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setClientDrawnCornerRadius( + const sp<SurfaceControl>& sc, float clientDrawnCornerRadius) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + s->what |= layer_state_t::eClientDrawnCornerRadiusChanged; + s->clientDrawnCornerRadius = clientDrawnCornerRadius; + return *this; +} + SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBackgroundBlurRadius( const sp<SurfaceControl>& sc, int backgroundBlurRadius) { layer_state_t* s = getLayerState(sc); @@ -1729,9 +1524,7 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::reparent if (SurfaceControl::isSameSurface(sc, newParent)) { return *this; } - s->what |= layer_state_t::eReparent; - s->parentSurfaceControlForChild = newParent ? newParent->getParentingLayer() : nullptr; - + s->updateParentLayer(newParent); registerSurfaceControlForCallback(sc); return *this; } @@ -1857,8 +1650,8 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBuffe setReleaseBufferCallback(bufferData.get(), callback); } - if (mIsAutoTimestamp) { - mDesiredPresentTime = systemTime(); + if (mState.mIsAutoTimestamp) { + mState.mDesiredPresentTime = systemTime(); } s->what |= layer_state_t::eBufferChanged; s->bufferData = std::move(bufferData); @@ -1876,7 +1669,7 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBuffe const std::vector<SurfaceControlStats>&) {}, nullptr); - mMayContainBuffer = true; + mState.mMayContainBuffer = true; return *this; } @@ -1961,9 +1754,9 @@ 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, 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; @@ -1972,8 +1765,8 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setLuts( 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); + s->luts = std::make_shared<gui::DisplayLuts>(std::move(lutFd), offsets, dimensions, sizes, + samplingKeys); } else { s->luts = nullptr; } @@ -2017,9 +1810,7 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setSurfa mStatus = BAD_INDEX; return *this; } - s->what |= layer_state_t::eSurfaceDamageRegionChanged; - s->surfaceDamageRegion = surfaceDamageRegion; - + s->updateSurfaceDamageRegion(surfaceDamageRegion); registerSurfaceControlForCallback(sc); return *this; } @@ -2054,8 +1845,8 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setSideb SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setDesiredPresentTime( nsecs_t desiredPresentTime) { - mDesiredPresentTime = desiredPresentTime; - mIsAutoTimestamp = false; + mState.mDesiredPresentTime = desiredPresentTime; + mState.mIsAutoTimestamp = false; return *this; } @@ -2138,21 +1929,20 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setInput mStatus = BAD_INDEX; return *this; } - s->windowInfoHandle = std::move(info); - s->what |= layer_state_t::eInputInfoChanged; + s->updateInputWindowInfo(std::move(info)); return *this; } SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFocusedWindow( const FocusRequest& request) { - mInputWindowCommands.focusRequests.push_back(request); + mState.mInputWindowCommands.addFocusRequest(request); return *this; } SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::addWindowInfosReportedListener( sp<gui::IWindowInfosReportedListener> windowInfosReportedListener) { - mInputWindowCommands.windowInfosReportedListeners.insert(windowInfosReportedListener); + mState.mInputWindowCommands.addWindowInfosReportedListener(windowInfosReportedListener); return *this; } @@ -2316,7 +2106,7 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFixed SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrameTimelineInfo( const FrameTimelineInfo& frameTimelineInfo) { - mergeFrameTimelineInfo(mFrameTimelineInfo, frameTimelineInfo); + mState.mergeFrameTimelineInfo(frameTimelineInfo); return *this; } @@ -2355,7 +2145,7 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setTrust SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setApplyToken( const sp<IBinder>& applyToken) { - mApplyToken = applyToken; + mState.mApplyToken = applyToken; return *this; } @@ -2372,10 +2162,6 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setStret return *this; } -bool SurfaceComposerClient::flagEdgeExtensionEffectUseShader() { - return com::android::graphics::libgui::flags::edge_extension_shader(); -} - SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setEdgeExtensionEffect( const sp<SurfaceControl>& sc, const gui::EdgeExtensionParameters& effect) { layer_state_t* s = getLayerState(sc); @@ -2487,15 +2273,7 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setConte // --------------------------------------------------------------------------- DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp<IBinder>& token) { - DisplayState s; - s.token = token; - ssize_t index = mDisplayStates.indexOf(s); - if (index < 0) { - // we don't have it, add an initialized layer_state to our list - s.what = 0; - index = mDisplayStates.add(s); - } - return mDisplayStates.editItemAt(static_cast<size_t>(index)); + return mState.getDisplayState(token); } status_t SurfaceComposerClient::Transaction::setDisplaySurface(const sp<IBinder>& token, @@ -2548,20 +2326,6 @@ void SurfaceComposerClient::Transaction::setDisplaySize(const sp<IBinder>& token s.what |= DisplayState::eDisplaySizeChanged; } -// copied from FrameTimelineInfo::merge() -void SurfaceComposerClient::Transaction::mergeFrameTimelineInfo(FrameTimelineInfo& t, - const FrameTimelineInfo& other) { - // When merging vsync Ids we take the oldest valid one - if (t.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID && - other.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID) { - if (other.vsyncId > t.vsyncId) { - t = other; - } - } else if (t.vsyncId == FrameTimelineInfo::INVALID_VSYNC_ID) { - t = other; - } -} - SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setTrustedPresentationCallback( const sp<SurfaceControl>& sc, TrustedPresentationCallback cb, @@ -2578,8 +2342,9 @@ SurfaceComposerClient::Transaction::setTrustedPresentationCallback( } s->what |= layer_state_t::eTrustedPresentationInfoChanged; s->trustedPresentationThresholds = thresholds; - s->trustedPresentationListener.callbackInterface = TransactionCompletedListener::getIInstance(); - s->trustedPresentationListener.callbackId = sc->getLayerId(); + s->trustedPresentationListener.configure( + {.callbackInterface = TransactionCompletedListener::getIInstance(), + .callbackId = sc->getLayerId()}); return *this; } @@ -2595,8 +2360,7 @@ SurfaceComposerClient::Transaction::clearTrustedPresentationCallback(const sp<Su } s->what |= layer_state_t::eTrustedPresentationInfoChanged; s->trustedPresentationThresholds = TrustedPresentationThresholds(); - s->trustedPresentationListener.callbackInterface = nullptr; - s->trustedPresentationListener.callbackId = -1; + s->trustedPresentationListener.clear(); return *this; } @@ -2689,9 +2453,9 @@ status_t SurfaceComposerClient::createSurfaceChecked(const String8& name, uint32 } ALOGE_IF(err, "SurfaceComposerClient::createSurface error %s", strerror(-err)); if (err == NO_ERROR) { - *outSurface = new SurfaceControl(this, result.handle, result.layerId, - toString(result.layerName), w, h, format, - result.transformHint, flags); + *outSurface = sp<SurfaceControl>::make(this, result.handle, result.layerId, + toString(result.layerName), w, h, format, + result.transformHint, flags); } } return err; @@ -2707,7 +2471,8 @@ sp<SurfaceControl> SurfaceComposerClient::mirrorSurface(SurfaceControl* mirrorFr const binder::Status status = mClient->mirrorSurface(mirrorFromHandle, &result); const status_t err = statusTFromBinderStatus(status); if (err == NO_ERROR) { - return new SurfaceControl(this, result.handle, result.layerId, toString(result.layerName)); + return sp<SurfaceControl>::make(this, result.handle, result.layerId, + toString(result.layerName)); } return nullptr; } @@ -2717,7 +2482,8 @@ sp<SurfaceControl> SurfaceComposerClient::mirrorDisplay(DisplayId displayId) { const binder::Status status = mClient->mirrorDisplay(displayId.value, &result); const status_t err = statusTFromBinderStatus(status); if (err == NO_ERROR) { - return new SurfaceControl(this, result.handle, result.layerId, toString(result.layerName)); + return sp<SurfaceControl>::make(this, result.handle, result.layerId, + toString(result.layerName)); } return nullptr; } @@ -2780,6 +2546,7 @@ status_t SurfaceComposerClient::getStaticDisplayInfo(int64_t displayId, if (status.isOk()) { // convert gui::StaticDisplayInfo to ui::StaticDisplayInfo outInfo->connectionType = static_cast<ui::DisplayConnectionType>(ginfo.connectionType); + outInfo->port = ginfo.port; outInfo->density = ginfo.density; outInfo->secure = ginfo.secure; outInfo->installOrientation = static_cast<ui::Rotation>(ginfo.installOrientation); diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp index f126c0be2f..1eb9b87c3c 100644 --- a/libs/gui/SurfaceControl.cpp +++ b/libs/gui/SurfaceControl.cpp @@ -141,7 +141,8 @@ sp<Surface> SurfaceControl::generateSurfaceLocked() ISurfaceComposerClient::eOpaque); mBbqChild = mClient->createSurface(String8::format("[BBQ] %s", mName.c_str()), 0, 0, mFormat, flags, mHandle, {}, &ignore); - mBbq = sp<BLASTBufferQueue>::make("[BBQ]" + mName, mBbqChild, mWidth, mHeight, mFormat); + mBbq = sp<BLASTBufferQueue>::make("[BBQ] " + mName, /* updateDestinationFrame */ true); + mBbq->update(mBbqChild, mWidth, mHeight, mFormat); // This surface is always consumed by SurfaceFlinger, so the // producerControlledByApp value doesn't matter; using false. @@ -268,10 +269,11 @@ status_t SurfaceControl::readFromParcel(const Parcel& parcel, SAFE_PARCEL(parcel.readUint32, &format); // We aren't the original owner of the surface. - *outSurfaceControl = new SurfaceControl(new SurfaceComposerClient( - interface_cast<ISurfaceComposerClient>(client)), - handle.get(), layerId, layerName, width, height, format, - transformHint); + *outSurfaceControl = + sp<SurfaceControl>::make(sp<SurfaceComposerClient>::make( + interface_cast<ISurfaceComposerClient>(client)), + handle, layerId, layerName, width, height, format, + transformHint); return NO_ERROR; } diff --git a/libs/gui/TEST_MAPPING b/libs/gui/TEST_MAPPING index a590c86ceb..14d6df62cb 100644 --- a/libs/gui/TEST_MAPPING +++ b/libs/gui/TEST_MAPPING @@ -60,5 +60,34 @@ } ] } + ], + "postsubmit": [ + { + "name": "libgui_test", + "keywords": [ "primary-device" ], + "options": [ + // TODO(b/397776630): Failing on real devices. + { + "exclude-filter": "InputSurfacesTest#input_respects_scaled_touchable_region_overflow" + }, + // TODO(b/233363648): Failing on real devices. + { + "exclude-filter": "SurfaceTextureGLTest#TexturingFromCpuFilledYV12BufferNpot" + }, + { + "exclude-filter": "SurfaceTextureGLTest#TexturingFromCpuFilledYV12BufferPow2" + }, + { + "exclude-filter": "SurfaceTextureGLTest#TexturingFromCpuFilledYV12BufferWithCrop" + }, + // TODO(b/233363648): Flaky on real devices. + { + "exclude-filter": "SurfaceTextureGLToGLTest#EglMakeCurrentBeforeConsumerDeathUnrefsBuffers" + }, + { + "exclude-filter": "SurfaceTextureGLToGLTest#EglMakeCurrentAfterConsumerDeathUnrefsBuffers" + } + ] + } ] } diff --git a/libs/gui/TransactionState.cpp b/libs/gui/TransactionState.cpp new file mode 100644 index 0000000000..9e09bc2644 --- /dev/null +++ b/libs/gui/TransactionState.cpp @@ -0,0 +1,263 @@ +/* + * Copyright (C) 2025 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 "TransactionState" +#include <gui/LayerState.h> +#include <gui/SurfaceComposerClient.h> +#include <gui/TransactionState.h> +#include <private/gui/ParcelUtils.h> +#include <algorithm> + +namespace android { + +status_t TransactionState::writeToParcel(Parcel* parcel) const { + SAFE_PARCEL(parcel->writeUint64, mId); + SAFE_PARCEL(parcel->writeUint32, mFlags); + SAFE_PARCEL(parcel->writeInt64, mDesiredPresentTime); + SAFE_PARCEL(parcel->writeBool, mIsAutoTimestamp); + SAFE_PARCEL(parcel->writeParcelable, mFrameTimelineInfo); + SAFE_PARCEL(parcel->writeStrongBinder, mApplyToken); + SAFE_PARCEL(parcel->writeBool, mMayContainBuffer); + SAFE_PARCEL(parcel->writeBool, mLogCallPoints); + + SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(mDisplayStates.size())); + for (auto const& displayState : mDisplayStates) { + displayState.write(*parcel); + } + SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(mComposerStates.size())); + for (auto const& composerState : mComposerStates) { + composerState.write(*parcel); + } + + mInputWindowCommands.write(*parcel); + SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(mUncacheBuffers.size())); + for (const client_cache_t& uncacheBuffer : mUncacheBuffers) { + SAFE_PARCEL(parcel->writeStrongBinder, uncacheBuffer.token.promote()); + SAFE_PARCEL(parcel->writeUint64, uncacheBuffer.id); + } + + SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(mMergedTransactionIds.size())); + for (auto mergedTransactionId : mMergedTransactionIds) { + SAFE_PARCEL(parcel->writeUint64, mergedTransactionId); + } + + SAFE_PARCEL(parcel->writeBool, mHasListenerCallbacks); + SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(mListenerCallbacks.size())); + for (const auto& [listener, callbackIds] : mListenerCallbacks) { + SAFE_PARCEL(parcel->writeStrongBinder, listener); + SAFE_PARCEL(parcel->writeParcelableVector, callbackIds); + } + + return NO_ERROR; +} + +status_t TransactionState::readFromParcel(const Parcel* parcel) { + SAFE_PARCEL(parcel->readUint64, &mId); + SAFE_PARCEL(parcel->readUint32, &mFlags); + SAFE_PARCEL(parcel->readInt64, &mDesiredPresentTime); + SAFE_PARCEL(parcel->readBool, &mIsAutoTimestamp); + SAFE_PARCEL(parcel->readParcelable, &mFrameTimelineInfo); + SAFE_PARCEL(parcel->readNullableStrongBinder, &mApplyToken); + SAFE_PARCEL(parcel->readBool, &mMayContainBuffer); + SAFE_PARCEL(parcel->readBool, &mLogCallPoints); + + uint32_t count; + SAFE_PARCEL_READ_SIZE(parcel->readUint32, &count, parcel->dataSize()) + mDisplayStates.clear(); + mDisplayStates.reserve(count); + for (size_t i = 0; i < count; i++) { + DisplayState displayState; + if (displayState.read(*parcel) == BAD_VALUE) { + return BAD_VALUE; + } + mDisplayStates.emplace_back(std::move(displayState)); + } + + SAFE_PARCEL_READ_SIZE(parcel->readUint32, &count, parcel->dataSize()) + mComposerStates.clear(); + mComposerStates.reserve(count); + for (size_t i = 0; i < count; i++) { + ComposerState composerState; + if (composerState.read(*parcel) == BAD_VALUE) { + return BAD_VALUE; + } + mComposerStates.emplace_back(std::move(composerState)); + } + + if (status_t status = mInputWindowCommands.read(*parcel) != NO_ERROR) { + return status; + } + + SAFE_PARCEL_READ_SIZE(parcel->readUint32, &count, parcel->dataSize()) + mUncacheBuffers.clear(); + mUncacheBuffers.reserve(count); + for (size_t i = 0; i < count; i++) { + client_cache_t client_cache; + sp<IBinder> tmpBinder; + SAFE_PARCEL(parcel->readStrongBinder, &tmpBinder); + client_cache.token = tmpBinder; + SAFE_PARCEL(parcel->readUint64, &client_cache.id); + mUncacheBuffers.emplace_back(std::move(client_cache)); + } + + SAFE_PARCEL_READ_SIZE(parcel->readUint32, &count, parcel->dataSize()) + mMergedTransactionIds.clear(); + mMergedTransactionIds.resize(count); + for (size_t i = 0; i < count; i++) { + SAFE_PARCEL(parcel->readUint64, &mMergedTransactionIds[i]); + } + + SAFE_PARCEL(parcel->readBool, &mHasListenerCallbacks); + SAFE_PARCEL_READ_SIZE(parcel->readUint32, &count, parcel->dataSize()); + mListenerCallbacks.clear(); + mListenerCallbacks.reserve(count); + for (uint32_t i = 0; i < count; i++) { + sp<IBinder> tmpBinder; + SAFE_PARCEL(parcel->readStrongBinder, &tmpBinder); + std::vector<CallbackId> callbackIds; + SAFE_PARCEL(parcel->readParcelableVector, &callbackIds); + mListenerCallbacks.emplace_back(tmpBinder, callbackIds); + } + + return NO_ERROR; +} + +void TransactionState::merge(TransactionState&& other, + const std::function<void(layer_state_t&)>& onBufferOverwrite) { + while (mMergedTransactionIds.size() + other.mMergedTransactionIds.size() > + MAX_MERGE_HISTORY_LENGTH - 1 && + mMergedTransactionIds.size() > 0) { + mMergedTransactionIds.pop_back(); + } + if (other.mMergedTransactionIds.size() == MAX_MERGE_HISTORY_LENGTH) { + mMergedTransactionIds.insert(mMergedTransactionIds.begin(), + other.mMergedTransactionIds.begin(), + other.mMergedTransactionIds.end() - 1); + } else if (other.mMergedTransactionIds.size() > 0u) { + mMergedTransactionIds.insert(mMergedTransactionIds.begin(), + other.mMergedTransactionIds.begin(), + other.mMergedTransactionIds.end()); + } + mMergedTransactionIds.insert(mMergedTransactionIds.begin(), other.mId); + + for (auto const& otherState : other.mComposerStates) { + if (auto it = std::find_if(mComposerStates.begin(), mComposerStates.end(), + [&otherState](const auto& composerState) { + return composerState.state.surface == + otherState.state.surface; + }); + it != mComposerStates.end()) { + if (otherState.state.what & layer_state_t::eBufferChanged) { + onBufferOverwrite(it->state); + } + it->state.merge(otherState.state); + } else { + mComposerStates.push_back(otherState); + } + } + + for (auto const& state : other.mDisplayStates) { + if (auto it = std::find_if(mDisplayStates.begin(), mDisplayStates.end(), + [&state](const auto& displayState) { + return displayState.token == state.token; + }); + it != mDisplayStates.end()) { + it->merge(state); + } else { + mDisplayStates.push_back(state); + } + } + + for (const auto& cacheId : other.mUncacheBuffers) { + mUncacheBuffers.push_back(cacheId); + } + + mInputWindowCommands.merge(other.mInputWindowCommands); + // TODO(b/385156191) Consider merging desired present time. + mFlags |= other.mFlags; + mMayContainBuffer |= other.mMayContainBuffer; + mLogCallPoints |= other.mLogCallPoints; + + // mApplyToken is explicitly not merged. Token should be set before applying the transactions to + // make synchronization decisions a bit simpler. + mergeFrameTimelineInfo(other.mFrameTimelineInfo); + other.clear(); +} + +// copied from FrameTimelineInfo::merge() +void TransactionState::mergeFrameTimelineInfo(const FrameTimelineInfo& other) { + // When merging vsync Ids we take the oldest valid one + if (mFrameTimelineInfo.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID && + other.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID) { + if (other.vsyncId > mFrameTimelineInfo.vsyncId) { + mFrameTimelineInfo = other; + } + } else if (mFrameTimelineInfo.vsyncId == FrameTimelineInfo::INVALID_VSYNC_ID) { + mFrameTimelineInfo = other; + } +} + +void TransactionState::clear() { + mComposerStates.clear(); + mDisplayStates.clear(); + mListenerCallbacks.clear(); + mHasListenerCallbacks = false; + mInputWindowCommands.clear(); + mUncacheBuffers.clear(); + mDesiredPresentTime = 0; + mIsAutoTimestamp = true; + mApplyToken = nullptr; + mFrameTimelineInfo = {}; + mMergedTransactionIds.clear(); + mFlags = 0; + mMayContainBuffer = false; + mLogCallPoints = false; +} + +layer_state_t* TransactionState::getLayerState(const sp<SurfaceControl>& sc) { + auto handle = sc->getLayerStateHandle(); + if (auto it = std::find_if(mComposerStates.begin(), mComposerStates.end(), + [&handle](const auto& composerState) { + return composerState.state.surface == handle; + }); + it != mComposerStates.end()) { + return &it->state; + } + + // we don't have it, add an initialized layer_state to our list + ComposerState s; + s.state.surface = handle; + s.state.layerId = sc->getLayerId(); + mComposerStates.push_back(s); + + return &mComposerStates.back().state; +} + +DisplayState& TransactionState::getDisplayState(const sp<IBinder>& token) { + if (auto it = std::find_if(mDisplayStates.begin(), mDisplayStates.end(), + [token](const auto& display) { return display.token == token; }); + it != mDisplayStates.end()) { + return *it; + } + + // If display state doesn't exist, add a new one. + DisplayState s; + s.token = token; + mDisplayStates.push_back(s); + return mDisplayStates.back(); +} + +}; // namespace android diff --git a/libs/gui/WindowInfo.cpp b/libs/gui/WindowInfo.cpp index 82d2554340..3fb66d1f33 100644 --- a/libs/gui/WindowInfo.cpp +++ b/libs/gui/WindowInfo.cpp @@ -59,6 +59,32 @@ std::ostream& operator<<(std::ostream& out, const Region& region) { return out; } +status_t writeTransform(android::Parcel* parcel, const ui::Transform& transform) { + return parcel->writeFloat(transform.dsdx()) ?: + parcel->writeFloat(transform.dtdx()) ?: + parcel->writeFloat(transform.tx()) ?: + parcel->writeFloat(transform.dtdy()) ?: + parcel->writeFloat(transform.dsdy()) ?: + parcel->writeFloat(transform.ty()); +} + +status_t readTransform(const android::Parcel* parcel, ui::Transform& transform) { + float dsdx, dtdx, tx, dtdy, dsdy, ty; + + const status_t status = parcel->readFloat(&dsdx) ?: + parcel->readFloat(&dtdx) ?: + parcel->readFloat(&tx) ?: + parcel->readFloat(&dtdy) ?: + parcel->readFloat(&dsdy) ?: + parcel->readFloat(&ty); + if (status != OK) { + return status; + } + + transform.set({dsdx, dtdx, tx, dtdy, dsdy, ty, 0, 0, 1}); + return OK; +} + } // namespace void WindowInfo::setInputConfig(ftl::Flags<InputConfig> config, bool value) { @@ -73,10 +99,6 @@ void WindowInfo::addTouchableRegion(const Rect& region) { touchableRegion.orSelf(region); } -bool WindowInfo::supportsSplitTouch() const { - return !inputConfig.test(InputConfig::PREVENT_SPLITTING); -} - bool WindowInfo::isSpy() const { return inputConfig.test(InputConfig::SPY); } @@ -135,12 +157,7 @@ status_t WindowInfo::writeToParcel(android::Parcel* parcel) const { parcel->writeInt32(surfaceInset) ?: parcel->writeFloat(globalScaleFactor) ?: parcel->writeFloat(alpha) ?: - parcel->writeFloat(transform.dsdx()) ?: - parcel->writeFloat(transform.dtdx()) ?: - parcel->writeFloat(transform.tx()) ?: - parcel->writeFloat(transform.dtdy()) ?: - parcel->writeFloat(transform.dsdy()) ?: - parcel->writeFloat(transform.ty()) ?: + writeTransform(parcel, transform) ?: parcel->writeInt32(static_cast<int32_t>(touchOcclusionMode)) ?: parcel->writeInt32(ownerPid.val()) ?: parcel->writeInt32(ownerUid.val()) ?: @@ -153,8 +170,12 @@ status_t WindowInfo::writeToParcel(android::Parcel* parcel) const { parcel->writeStrongBinder(touchableRegionCropHandle.promote()) ?: parcel->writeStrongBinder(windowToken) ?: parcel->writeStrongBinder(focusTransferTarget) ?: - parcel->writeBool(canOccludePresentation); + parcel->writeBool(canOccludePresentation) ?: + parcel->writeBool(cloneLayerStackTransform.has_value()); // clang-format on + if (cloneLayerStackTransform) { + status = status ?: writeTransform(parcel, *cloneLayerStackTransform); + } return status; } @@ -174,10 +195,10 @@ status_t WindowInfo::readFromParcel(const android::Parcel* parcel) { return status; } - float dsdx, dtdx, tx, dtdy, dsdy, ty; int32_t lpFlags, lpType, touchOcclusionModeInt, inputConfigInt, ownerPidInt, ownerUidInt, displayIdInt; sp<IBinder> touchableRegionCropHandleSp; + bool hasCloneLayerStackTransform = false; // clang-format off status = parcel->readInt32(&lpFlags) ?: @@ -188,12 +209,7 @@ status_t WindowInfo::readFromParcel(const android::Parcel* parcel) { parcel->readInt32(&surfaceInset) ?: parcel->readFloat(&globalScaleFactor) ?: parcel->readFloat(&alpha) ?: - parcel->readFloat(&dsdx) ?: - parcel->readFloat(&dtdx) ?: - parcel->readFloat(&tx) ?: - parcel->readFloat(&dtdy) ?: - parcel->readFloat(&dsdy) ?: - parcel->readFloat(&ty) ?: + readTransform(parcel, /*byRef*/ transform) ?: parcel->readInt32(&touchOcclusionModeInt) ?: parcel->readInt32(&ownerPidInt) ?: parcel->readInt32(&ownerUidInt) ?: @@ -206,8 +222,8 @@ status_t WindowInfo::readFromParcel(const android::Parcel* parcel) { parcel->readNullableStrongBinder(&touchableRegionCropHandleSp) ?: parcel->readNullableStrongBinder(&windowToken) ?: parcel->readNullableStrongBinder(&focusTransferTarget) ?: - parcel->readBool(&canOccludePresentation); - + parcel->readBool(&canOccludePresentation)?: + parcel->readBool(&hasCloneLayerStackTransform); // clang-format on if (status != OK) { @@ -216,7 +232,6 @@ status_t WindowInfo::readFromParcel(const android::Parcel* parcel) { layoutParamsFlags = ftl::Flags<Flag>(lpFlags); layoutParamsType = static_cast<Type>(lpType); - transform.set({dsdx, dtdx, tx, dtdy, dsdy, ty, 0, 0, 1}); touchOcclusionMode = static_cast<TouchOcclusionMode>(touchOcclusionModeInt); inputConfig = ftl::Flags<InputConfig>(inputConfigInt); ownerPid = Pid{ownerPidInt}; @@ -224,6 +239,15 @@ status_t WindowInfo::readFromParcel(const android::Parcel* parcel) { touchableRegionCropHandle = touchableRegionCropHandleSp; displayId = ui::LogicalDisplayId{displayIdInt}; + cloneLayerStackTransform = + hasCloneLayerStackTransform ? std::make_optional<ui::Transform>() : std::nullopt; + if (cloneLayerStackTransform) { + status = readTransform(parcel, /*byRef*/ *cloneLayerStackTransform); + if (status != OK) { + return status; + } + } + return OK; } diff --git a/libs/gui/WindowInfosListenerReporter.cpp b/libs/gui/WindowInfosListenerReporter.cpp index 91c9a85149..d633f9f15e 100644 --- a/libs/gui/WindowInfosListenerReporter.cpp +++ b/libs/gui/WindowInfosListenerReporter.cpp @@ -15,6 +15,7 @@ */ #include <android/gui/ISurfaceComposer.h> +#include <android/gui/IWindowInfosListener.h> #include <gui/AidlUtil.h> #include <gui/WindowInfosListenerReporter.h> #include "gui/WindowInfosUpdate.h" @@ -27,7 +28,7 @@ using gui::WindowInfosListener; using gui::aidl_utils::statusTFromBinderStatus; sp<WindowInfosListenerReporter> WindowInfosListenerReporter::getInstance() { - static sp<WindowInfosListenerReporter> sInstance = new WindowInfosListenerReporter; + static sp<WindowInfosListenerReporter> sInstance = sp<WindowInfosListenerReporter>::make(); return sInstance; } @@ -116,7 +117,8 @@ void WindowInfosListenerReporter::reconnect(const sp<gui::ISurfaceComposer>& com std::scoped_lock lock(mListenersMutex); if (!mWindowInfosListeners.empty()) { gui::WindowInfosListenerInfo listenerInfo; - composerService->addWindowInfosListener(this, &listenerInfo); + composerService->addWindowInfosListener(sp<gui::IWindowInfosListener>::fromExisting(this), + &listenerInfo); mWindowInfosPublisher = std::move(listenerInfo.windowInfosPublisher); mListenerId = listenerInfo.listenerId; } diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl index da47ee27ba..9b2f089665 100644 --- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl +++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl @@ -67,6 +67,11 @@ interface ISurfaceComposer { frameRateOverride = 1 << 1, } + enum OptimizationPolicy { + optimizeForPower = 0, + optimizeForPerformance = 1, + } + /** * Signal that we're done booting. * Requires ACCESS_SURFACE_FLINGER permission @@ -97,6 +102,10 @@ interface ISurfaceComposer { * The name of the virtual display. * isSecure * Whether this virtual display is secure. + * optimizationPolicy + * Whether to optimize for power or performance. Displays that are optimizing for power may + * be dependent on a different display that optimizes for performance when they are on, + * which will guarantee performance for all of the other displays. * uniqueId * The unique ID for the display. * requestedRefreshRate @@ -108,7 +117,7 @@ interface ISurfaceComposer { * requires ACCESS_SURFACE_FLINGER permission. */ @nullable IBinder createVirtualDisplay(@utf8InCpp String displayName, boolean isSecure, - @utf8InCpp String uniqueId, float requestedRefreshRate); + OptimizationPolicy optimizationPolicy, @utf8InCpp String uniqueId, float requestedRefreshRate); /** * Destroy a virtual display. diff --git a/libs/gui/aidl/android/gui/StaticDisplayInfo.aidl b/libs/gui/aidl/android/gui/StaticDisplayInfo.aidl index 0ccda56ef5..7ff332c29e 100644 --- a/libs/gui/aidl/android/gui/StaticDisplayInfo.aidl +++ b/libs/gui/aidl/android/gui/StaticDisplayInfo.aidl @@ -23,6 +23,7 @@ import android.gui.Rotation; /** @hide */ parcelable StaticDisplayInfo { DisplayConnectionType connectionType = DisplayConnectionType.Internal; + int port = -1; float density; boolean secure; @nullable DeviceProductInfo deviceProductInfo; diff --git a/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp b/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp index fd8ffe1f01..b1a23b309e 100644 --- a/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp +++ b/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp @@ -971,7 +971,7 @@ inline HGraphicBufferProducer::DisconnectMode toHDisconnectMode( // H2BGraphicBufferProducer status_t H2BGraphicBufferProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) { - *buf = new GraphicBuffer(); + *buf = sp<GraphicBuffer>::make(); status_t fnStatus; status_t transStatus = toStatusT(mBase->requestBuffer( static_cast<int32_t>(slot), @@ -999,7 +999,7 @@ status_t H2BGraphicBufferProducer::dequeueBuffer(int* slot, sp<Fence>* fence, ui uint32_t h, ::android::PixelFormat format, uint64_t usage, uint64_t* outBufferAge, FrameEventHistoryDelta* outTimestamps) { - *fence = new Fence(); + *fence = sp<Fence>::make(); status_t fnStatus; status_t transStatus = toStatusT(mBase->dequeueBuffer( w, h, static_cast<PixelFormat>(format), uint32_t(usage), @@ -1035,8 +1035,8 @@ status_t H2BGraphicBufferProducer::detachBuffer(int slot) { status_t H2BGraphicBufferProducer::detachNextBuffer( sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) { - *outBuffer = new GraphicBuffer(); - *outFence = new Fence(); + *outBuffer = sp<GraphicBuffer>::make(); + *outFence = sp<Fence>::make(); status_t fnStatus; status_t transStatus = toStatusT(mBase->detachNextBuffer( [&fnStatus, outBuffer, outFence] ( @@ -1127,8 +1127,8 @@ int H2BGraphicBufferProducer::query(int what, int* value) { status_t H2BGraphicBufferProducer::connect( const sp<IProducerListener>& listener, int api, bool producerControlledByApp, QueueBufferOutput* output) { - sp<HProducerListener> tListener = listener == nullptr ? - nullptr : new B2HProducerListener(listener); + sp<HProducerListener> tListener = + listener == nullptr ? nullptr : sp<B2HProducerListener>::make(listener); status_t fnStatus; status_t transStatus = toStatusT(mBase->connect( tListener, static_cast<int32_t>(api), producerControlledByApp, @@ -1205,13 +1205,13 @@ status_t H2BGraphicBufferProducer::getLastQueuedBuffer( hidl_handle const& fence, hidl_array<float, 16> const& transformMatrix) { fnStatus = toStatusT(status); - *outBuffer = new GraphicBuffer(); + *outBuffer = sp<GraphicBuffer>::make(); if (!convertTo(outBuffer->get(), buffer)) { ALOGE("H2BGraphicBufferProducer::getLastQueuedBuffer - " "Invalid output buffer"); fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus; } - *outFence = new Fence(); + *outFence = sp<Fence>::make(); if (!convertTo(outFence->get(), fence)) { ALOGE("H2BGraphicBufferProducer::getLastQueuedBuffer - " "Invalid output fence"); diff --git a/libs/gui/bufferqueue/2.0/B2HGraphicBufferProducer.cpp b/libs/gui/bufferqueue/2.0/B2HGraphicBufferProducer.cpp index c76d771262..4384bd5faa 100644 --- a/libs/gui/bufferqueue/2.0/B2HGraphicBufferProducer.cpp +++ b/libs/gui/bufferqueue/2.0/B2HGraphicBufferProducer.cpp @@ -272,7 +272,7 @@ Return<void> B2HGraphicBufferProducer::connect( HConnectionType hConnectionType, bool producerControlledByApp, connect_cb _hidl_cb) { - sp<BProducerListener> bListener = new H2BProducerListener(hListener); + sp<BProducerListener> bListener = sp<H2BProducerListener>::make(hListener); int bConnectionType{}; if (!bListener || !h2b(hConnectionType, &bConnectionType)) { _hidl_cb(HStatus::UNKNOWN_ERROR, QueueBufferOutput{}); diff --git a/libs/gui/bufferqueue/2.0/H2BGraphicBufferProducer.cpp b/libs/gui/bufferqueue/2.0/H2BGraphicBufferProducer.cpp index ae00a2642e..7121bb7aef 100644 --- a/libs/gui/bufferqueue/2.0/H2BGraphicBufferProducer.cpp +++ b/libs/gui/bufferqueue/2.0/H2BGraphicBufferProducer.cpp @@ -325,7 +325,7 @@ status_t H2BGraphicBufferProducer::connect( } sp<HProducerListener> hListener = nullptr; if (listener && listener->needsReleaseNotify()) { - hListener = new B2HProducerListener(listener); + hListener = sp<B2HProducerListener>::make(listener); if (!hListener) { LOG(ERROR) << "connect: failed to wrap listener."; return UNKNOWN_ERROR; diff --git a/libs/gui/bufferqueue/2.0/types.cpp b/libs/gui/bufferqueue/2.0/types.cpp index cbd6cad847..c245766b42 100644 --- a/libs/gui/bufferqueue/2.0/types.cpp +++ b/libs/gui/bufferqueue/2.0/types.cpp @@ -147,13 +147,13 @@ bool b2h(sp<BFence> const& from, HFenceWrapper* to) { bool h2b(native_handle_t const* from, sp<BFence>* to) { if (!from || from->numFds == 0) { - *to = new ::android::Fence(); + *to = sp<::android::Fence>::make(); return true; } if (from->numFds != 1 || from->numInts != 0) { return false; } - *to = new BFence(dup(from->data[0])); + *to = sp<BFence>::make(dup(from->data[0])); return true; } diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h index 1bc1dd0685..db1b9fb8eb 100644 --- a/libs/gui/include/gui/BLASTBufferQueue.h +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -20,6 +20,7 @@ #include <optional> #include <queue> +#include <ftl/small_map.h> #include <gui/BufferItem.h> #include <gui/BufferItemConsumer.h> #include <gui/IGraphicBufferConsumer.h> @@ -36,26 +37,15 @@ namespace android { +// Sizes determined empirically to avoid allocations during common activity. +constexpr size_t kSubmittedBuffersMapSizeHint = 8; +constexpr size_t kDequeueTimestampsMapSizeHint = 32; + class BLASTBufferQueue; class BufferItemConsumer; class BLASTBufferItemConsumer : public BufferItemConsumer { public: -#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) - BLASTBufferItemConsumer(const sp<IGraphicBufferProducer>& producer, - const sp<IGraphicBufferConsumer>& consumer, uint64_t consumerUsage, - int bufferCount, bool controlledByApp, wp<BLASTBufferQueue> bbq) - : BufferItemConsumer(producer, consumer, consumerUsage, bufferCount, controlledByApp), -#else - BLASTBufferItemConsumer(const sp<IGraphicBufferConsumer>& consumer, uint64_t consumerUsage, - int bufferCount, bool controlledByApp, wp<BLASTBufferQueue> bbq) - : BufferItemConsumer(consumer, consumerUsage, bufferCount, controlledByApp), -#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) - mBLASTBufferQueue(std::move(bbq)), - mCurrentlyConnected(false), - mPreviouslyConnected(false) { - } - void onDisconnect() override EXCLUDES(mMutex); void addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps, FrameEventHistoryDelta* outDelta) override EXCLUDES(mMutex); @@ -76,6 +66,23 @@ protected: #endif private: +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) + BLASTBufferItemConsumer(const sp<IGraphicBufferProducer>& producer, + const sp<IGraphicBufferConsumer>& consumer, uint64_t consumerUsage, + int bufferCount, bool controlledByApp, wp<BLASTBufferQueue> bbq) + : BufferItemConsumer(producer, consumer, consumerUsage, bufferCount, controlledByApp), +#else + BLASTBufferItemConsumer(const sp<IGraphicBufferConsumer>& consumer, uint64_t consumerUsage, + int bufferCount, bool controlledByApp, wp<BLASTBufferQueue> bbq) + : BufferItemConsumer(consumer, consumerUsage, bufferCount, controlledByApp), +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) + mBLASTBufferQueue(std::move(bbq)), + mCurrentlyConnected(false), + mPreviouslyConnected(false) { + } + + friend class sp<BLASTBufferItemConsumer>; + const wp<BLASTBufferQueue> mBLASTBufferQueue; uint64_t mCurrentFrameNumber GUARDED_BY(mMutex) = 0; @@ -89,10 +96,6 @@ private: class BLASTBufferQueue : public ConsumerBase::FrameAvailableListener { public: - BLASTBufferQueue(const std::string& name, bool updateDestinationFrame = true); - BLASTBufferQueue(const std::string& name, const sp<SurfaceControl>& surface, int width, - int height, int32_t format); - sp<IGraphicBufferProducer> getIGraphicBufferProducer() const { return mProducer; } @@ -145,9 +148,9 @@ public: void setTransactionHangCallback(std::function<void(const std::string&)> callback); void setApplyToken(sp<IBinder>); - void setWaitForBufferReleaseCallback(std::function<void()> callback) + void setWaitForBufferReleaseCallback(std::function<void(const nsecs_t)> callback) EXCLUDES(mWaitForBufferReleaseMutex); - std::function<void()> getWaitForBufferReleaseCallback() const + std::function<void(const nsecs_t)> getWaitForBufferReleaseCallback() const EXCLUDES(mWaitForBufferReleaseMutex); virtual ~BLASTBufferQueue(); @@ -155,8 +158,13 @@ public: void onFirstRef() override; private: + // Not public to ensure construction via sp<>::make(). + BLASTBufferQueue(const std::string& name, bool updateDestinationFrame = true); + + friend class sp<BLASTBufferQueue>; friend class BLASTBufferQueueHelper; friend class BBQBufferQueueProducer; + friend class TestBLASTBufferQueue; #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) friend class BBQBufferQueueCore; #endif @@ -208,7 +216,7 @@ private: // Keep a reference to the submitted buffers so we can release when surfaceflinger drops the // buffer or the buffer has been presented and a new buffer is ready to be presented. - std::unordered_map<ReleaseCallbackId, BufferItem, ReleaseBufferCallbackIdHash> mSubmitted + ftl::SmallMap<ReleaseCallbackId, BufferItem, kSubmittedBuffersMapSizeHint> mSubmitted GUARDED_BY(mMutex); // Keep a queue of the released buffers instead of immediately releasing @@ -293,8 +301,8 @@ private: std::mutex mTimestampMutex; // Tracks buffer dequeue times by the client. This info is sent to SurfaceFlinger which uses // it for debugging purposes. - std::unordered_map<uint64_t /* bufferId */, nsecs_t> mDequeueTimestamps - GUARDED_BY(mTimestampMutex); + ftl::SmallMap<uint64_t /* bufferId */, nsecs_t, kDequeueTimestampsMapSizeHint> + mDequeueTimestamps GUARDED_BY(mTimestampMutex); // Keep track of SurfaceControls that have submitted a transaction and BBQ is waiting on a // callback for them. @@ -331,7 +339,8 @@ private: std::unordered_set<uint64_t> mSyncedFrameNumbers GUARDED_BY(mMutex); - std::function<void()> mWaitForBufferReleaseCallback GUARDED_BY(mWaitForBufferReleaseMutex); + std::function<void(const nsecs_t)> mWaitForBufferReleaseCallback + GUARDED_BY(mWaitForBufferReleaseMutex); #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) // BufferReleaseChannel is used to communicate buffer releases from SurfaceFlinger to the // client. diff --git a/libs/gui/include/gui/BufferItemConsumer.h b/libs/gui/include/gui/BufferItemConsumer.h index 6810edaf7c..fc31f4636c 100644 --- a/libs/gui/include/gui/BufferItemConsumer.h +++ b/libs/gui/include/gui/BufferItemConsumer.h @@ -47,6 +47,16 @@ class BufferItemConsumer: public ConsumerBase enum { INVALID_BUFFER_SLOT = BufferQueue::INVALID_BUFFER_SLOT }; enum { NO_BUFFER_AVAILABLE = BufferQueue::NO_BUFFER_AVAILABLE }; + static std::tuple<sp<BufferItemConsumer>, sp<Surface>> create( + uint64_t consumerUsage, int bufferCount = DEFAULT_MAX_BUFFERS, + bool controlledByApp = false, bool isConsumerSurfaceFlinger = false); + + static sp<BufferItemConsumer> create(const sp<IGraphicBufferConsumer>& consumer, + uint64_t consumerUsage, + int bufferCount = DEFAULT_MAX_BUFFERS, + bool controlledByApp = false) + __attribute((deprecated("Prefer ctors that create their own surface and consumer."))); + // Create a new buffer item consumer. The consumerUsage parameter determines // the consumer usage flags passed to the graphics allocator. The // bufferCount parameter specifies how many buffers can be locked for user @@ -86,6 +96,14 @@ class BufferItemConsumer: public ConsumerBase status_t acquireBuffer(BufferItem* item, nsecs_t presentWhen, bool waitForFence = true); + // Transfer ownership of a buffer to the BufferQueue. On NO_ERROR, the buffer + // is considered as if it were acquired. Buffer must not be null. + // + // Returns + // - BAD_VALUE if buffer is null + // - INVALID_OPERATION if too many buffers have already been acquired + status_t attachBuffer(const sp<GraphicBuffer>& buffer); + // Returns an acquired buffer to the queue, allowing it to be reused. Since // only a fixed number of buffers may be acquired at a time, old buffers // must be released by calling releaseBuffer to ensure new buffers can be @@ -95,10 +113,8 @@ class BufferItemConsumer: public ConsumerBase status_t releaseBuffer(const BufferItem &item, const sp<Fence>& releaseFence = Fence::NO_FENCE); -#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) status_t releaseBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence>& releaseFence = Fence::NO_FENCE); -#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) protected: #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) diff --git a/libs/gui/include/gui/BufferQueue.h b/libs/gui/include/gui/BufferQueue.h index f1c75d3a45..7b97e13649 100644 --- a/libs/gui/include/gui/BufferQueue.h +++ b/libs/gui/include/gui/BufferQueue.h @@ -57,7 +57,7 @@ public: // reference in the BufferQueue class is because we're planning to expose the // consumer side of a BufferQueue as a binder interface, which doesn't support // weak references. - class ProxyConsumerListener : public BnConsumerListener { + class ProxyConsumerListener : public IConsumerListener { public: explicit ProxyConsumerListener(const wp<ConsumerListener>& consumerListener); ~ProxyConsumerListener() override; diff --git a/libs/gui/include/gui/BufferQueueConsumer.h b/libs/gui/include/gui/BufferQueueConsumer.h index e00c44eb70..ba6a6a73f4 100644 --- a/libs/gui/include/gui/BufferQueueConsumer.h +++ b/libs/gui/include/gui/BufferQueueConsumer.h @@ -28,8 +28,7 @@ namespace android { class BufferQueueCore; -class BufferQueueConsumer : public BnGraphicBufferConsumer { - +class BufferQueueConsumer : public IGraphicBufferConsumer { public: explicit BufferQueueConsumer(const sp<BufferQueueCore>& core); ~BufferQueueConsumer() override; @@ -65,13 +64,14 @@ public: // any references to the just-released buffer that it might have, as if it // had received a onBuffersReleased() call with a mask set for the released // buffer. - // - // Note that the dependencies on EGL will be removed once we switch to using - // the Android HW Sync HAL. +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) + virtual status_t releaseBuffer(int slot, uint64_t frameNumber, + const sp<Fence>& releaseFence) override; +#else virtual status_t releaseBuffer(int slot, uint64_t frameNumber, const sp<Fence>& releaseFence, EGLDisplay display, EGLSyncKHR fence); - +#endif // connect connects a consumer to the BufferQueue. Only one // consumer may be connected, and when that consumer disconnects the // BufferQueue is placed into the "abandoned" state, causing most @@ -122,7 +122,10 @@ public: // setMaxAcquiredBufferCount sets the maximum number of buffers that can // be acquired by the consumer at one time (default 1). This call will // fail if a producer is connected to the BufferQueue. - virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers); + virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) override; + virtual status_t setMaxAcquiredBufferCount( + int maxAcquiredBuffers, + std::optional<OnBufferReleasedCallback> onBuffersReleasedCallback) override; // setConsumerName sets the name used in logging status_t setConsumerName(const String8& name) override; @@ -167,6 +170,7 @@ public: // dump our state in a String status_t dumpState(const String8& prefix, String8* outResult) const override; +#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) // Functions required for backwards compatibility. // These will be modified/renamed in IGraphicBufferConsumer and will be // removed from this class at that time. See b/13306289. @@ -176,6 +180,7 @@ public: const sp<Fence>& releaseFence) { return releaseBuffer(buf, frameNumber, releaseFence, display, fence); } +#endif virtual status_t consumerConnect(const sp<IConsumerListener>& consumer, bool controlledByApp) { diff --git a/libs/gui/include/gui/Choreographer.h b/libs/gui/include/gui/Choreographer.h index a93ba14c57..5862967d4a 100644 --- a/libs/gui/include/gui/Choreographer.h +++ b/libs/gui/include/gui/Choreographer.h @@ -103,7 +103,7 @@ public: virtual void handleMessage(const Message& message) override; static void initJVM(JNIEnv* env); - static Choreographer* getForThread(); + static sp<Choreographer> getForThread(); static void signalRefreshRateCallbacks(nsecs_t vsyncPeriod) EXCLUDES(gChoreographers.lock); static int64_t getStartTimeNanosForVsyncId(AVsyncId vsyncId) EXCLUDES(gChoreographers.lock); virtual ~Choreographer() override EXCLUDES(gChoreographers.lock); diff --git a/libs/gui/include/gui/ConsumerBase.h b/libs/gui/include/gui/ConsumerBase.h index 5cd19c1583..d2215ef7e6 100644 --- a/libs/gui/include/gui/ConsumerBase.h +++ b/libs/gui/include/gui/ConsumerBase.h @@ -98,6 +98,8 @@ public: status_t detachBuffer(const sp<GraphicBuffer>& buffer); #endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + status_t addReleaseFence(const sp<GraphicBuffer> buffer, const sp<Fence>& fence); + // See IGraphicBufferConsumer::setDefaultBufferSize status_t setDefaultBufferSize(uint32_t width, uint32_t height); @@ -121,9 +123,7 @@ public: // See IGraphicBufferConsumer::setMaxAcquiredBufferCount status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers); -#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) status_t setConsumerIsProtected(bool isProtected); -#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) // See IGraphicBufferConsumer::getSidebandStream sp<NativeHandle> getSidebandStream() const; @@ -139,7 +139,8 @@ private: ConsumerBase(const ConsumerBase&); void operator=(const ConsumerBase&); - void initialize(bool controlledByApp); + // Requires `this` to be sp/wp so must not be called from ctor. + void initialize(); protected: // ConsumerBase constructs a new ConsumerBase object to consume image @@ -190,6 +191,8 @@ protected: #endif virtual int getSlotForBufferLocked(const sp<GraphicBuffer>& buffer); + virtual void onBuffersReleasedLocked(); + virtual status_t detachBufferLocked(int slotIndex); // freeBufferLocked frees up the given buffer slot. If the slot has been @@ -245,9 +248,16 @@ protected: // must take place when a buffer is released back to the BufferQueue. If // it is overridden the derived class's implementation must call // ConsumerBase::releaseBufferLocked. +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) + virtual status_t releaseBufferLocked(int slot, const sp<GraphicBuffer> graphicBuffer); +#else virtual status_t releaseBufferLocked(int slot, const sp<GraphicBuffer> graphicBuffer, EGLDisplay display = EGL_NO_DISPLAY, EGLSyncKHR eglFence = EGL_NO_SYNC_KHR); +#endif + // Required to complete initialization, so `final` lest overrides forget to + // delegate. + void onFirstRef() override final; // returns true iff the slot still has the graphicBuffer in it. bool stillTracking(int slot, const sp<GraphicBuffer> graphicBuffer); @@ -324,6 +334,8 @@ protected: // releaseBufferLocked. sp<Fence> mPrevFinalReleaseFence; + const bool mIsControlledByApp; + // mMutex is the mutex used to prevent concurrent access to the member // variables of ConsumerBase objects. It must be locked whenever the // member variables are accessed or when any of the *Locked methods are diff --git a/libs/gui/include/gui/CpuConsumer.h b/libs/gui/include/gui/CpuConsumer.h index 2bba61bbe8..995cdfb53d 100644 --- a/libs/gui/include/gui/CpuConsumer.h +++ b/libs/gui/include/gui/CpuConsumer.h @@ -31,6 +31,7 @@ namespace android { class BufferQueue; class GraphicBuffer; class String8; +class Surface; /** * CpuConsumer is a BufferQueue consumer endpoint that allows direct CPU @@ -92,6 +93,13 @@ class CpuConsumer : public ConsumerBase // Create a new CPU consumer. The maxLockedBuffers parameter specifies // how many buffers can be locked for user access at the same time. + static std::tuple<sp<CpuConsumer>, sp<Surface>> create(size_t maxLockedBuffers, + bool controlledByApp = false, + bool isConsumerSurfaceFlinger = false); + static sp<CpuConsumer> create(const sp<IGraphicBufferConsumer>& bq, size_t maxLockedBuffers, + bool controlledByApp = false) + __attribute((deprecated("Prefer ctors that create their own surface and consumer."))); + #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) CpuConsumer(size_t maxLockedBuffers, bool controlledByApp = false, bool isConsumerSurfaceFlinger = false); @@ -100,8 +108,8 @@ class CpuConsumer : public ConsumerBase bool controlledByApp = false) __attribute((deprecated("Prefer ctors that create their own surface and consumer."))); #else - CpuConsumer(const sp<IGraphicBufferConsumer>& bq, - size_t maxLockedBuffers, bool controlledByApp = false); + CpuConsumer(const sp<IGraphicBufferConsumer>& bq, size_t maxLockedBuffers, + bool controlledByApp = false); #endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) // Gets the next graphics buffer from the producer and locks it for CPU use, diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h index ab6a6b78f7..f51390a0d8 100644 --- a/libs/gui/include/gui/DisplayEventReceiver.h +++ b/libs/gui/include/gui/DisplayEventReceiver.h @@ -55,20 +55,20 @@ static inline constexpr uint32_t fourcc(char c1, char c2, char c3, char c4) { static_cast<uint32_t>(c4); } +enum class DisplayEventType : uint32_t { + DISPLAY_EVENT_VSYNC = fourcc('v', 's', 'y', 'n'), + DISPLAY_EVENT_HOTPLUG = fourcc('p', 'l', 'u', 'g'), + DISPLAY_EVENT_MODE_CHANGE = fourcc('m', 'o', 'd', 'e'), + DISPLAY_EVENT_MODE_REJECTION = fourcc('r', 'e', 'j', 'e'), + DISPLAY_EVENT_NULL = fourcc('n', 'u', 'l', 'l'), + DISPLAY_EVENT_FRAME_RATE_OVERRIDE = fourcc('r', 'a', 't', 'e'), + DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH = fourcc('f', 'l', 's', 'h'), + DISPLAY_EVENT_HDCP_LEVELS_CHANGE = fourcc('h', 'd', 'c', 'p'), +}; + // ---------------------------------------------------------------------------- class DisplayEventReceiver { public: - enum { - DISPLAY_EVENT_VSYNC = fourcc('v', 's', 'y', 'n'), - DISPLAY_EVENT_HOTPLUG = fourcc('p', 'l', 'u', 'g'), - DISPLAY_EVENT_MODE_CHANGE = fourcc('m', 'o', 'd', 'e'), - DISPLAY_EVENT_MODE_REJECTION = fourcc('r', 'e', 'j', 'e'), - DISPLAY_EVENT_NULL = fourcc('n', 'u', 'l', 'l'), - DISPLAY_EVENT_FRAME_RATE_OVERRIDE = fourcc('r', 'a', 't', 'e'), - DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH = fourcc('f', 'l', 's', 'h'), - DISPLAY_EVENT_HDCP_LEVELS_CHANGE = fourcc('h', 'd', 'c', 'p'), - }; - struct Event { // We add __attribute__((aligned(8))) for nsecs_t fields because // we need to make sure all fields are aligned the same with x86 @@ -77,7 +77,7 @@ public: // https://en.wikipedia.org/wiki/Data_structure_alignment struct Header { - uint32_t type; + DisplayEventType type; PhysicalDisplayId displayId __attribute__((aligned(8))); nsecs_t timestamp __attribute__((aligned(8))); }; diff --git a/libs/gui/include/gui/DisplayLuts.h b/libs/gui/include/gui/DisplayLuts.h index ab86ac4af8..187381c4ae 100644 --- a/libs/gui/include/gui/DisplayLuts.h +++ b/libs/gui/include/gui/DisplayLuts.h @@ -18,6 +18,10 @@ #include <android-base/unique_fd.h> #include <binder/Parcel.h> #include <binder/Parcelable.h> +#include <cutils/ashmem.h> +#include <sys/mman.h> +#include <algorithm> +#include <ostream> #include <vector> namespace android::gui { @@ -62,4 +66,99 @@ private: base::unique_fd fd; }; // struct DisplayLuts +static inline void PrintTo(const std::vector<int32_t>& offsets, ::std::ostream* os) { + *os << "\n .offsets = {"; + for (size_t i = 0; i < offsets.size(); i++) { + *os << offsets[i]; + if (i != offsets.size() - 1) { + *os << ", "; + } + } + *os << "}"; +} + +static inline void PrintTo(const std::vector<DisplayLuts::Entry>& entries, ::std::ostream* os) { + *os << "\n .lutProperties = {\n"; + for (auto& [dimension, size, samplingKey] : entries) { + *os << " Entry{" + << "dimension: " << dimension << ", size: " << size << ", samplingKey: " << samplingKey + << "}\n"; + } + *os << " }"; +} + +static constexpr size_t kMaxPrintCount = 100; + +static inline void PrintTo(const std::vector<float>& buffer, size_t offset, int32_t dimension, + size_t size, ::std::ostream* os) { + size_t range = std::min(size, kMaxPrintCount); + *os << "{"; + if (dimension == 1) { + for (size_t i = 0; i < range; i++) { + *os << buffer[offset + i]; + if (i != range - 1) { + *os << ", "; + } + } + } else { + *os << "\n {R channel:"; + for (size_t i = 0; i < range; i++) { + *os << buffer[offset + i]; + if (i != range - 1) { + *os << ", "; + } + } + *os << "}\n {G channel:"; + for (size_t i = 0; i < range; i++) { + *os << buffer[offset + size + i]; + if (i != range - 1) { + *os << ", "; + } + } + *os << "}\n {B channel:"; + for (size_t i = 0; i < range; i++) { + *os << buffer[offset + 2 * size + i]; + if (i != range - 1) { + *os << ", "; + } + } + } + *os << "}"; +} + +static inline void PrintTo(const std::shared_ptr<DisplayLuts> luts, ::std::ostream* os) { + *os << "gui::DisplayLuts {"; + auto& fd = luts->getLutFileDescriptor(); + *os << "\n .pfd = " << fd.get(); + if (fd.ok()) { + PrintTo(luts->offsets, os); + PrintTo(luts->lutProperties, os); + // decode luts + int32_t fullLength = luts->offsets[luts->offsets.size() - 1]; + if (luts->lutProperties[luts->offsets.size() - 1].dimension == 1) { + fullLength += luts->lutProperties[luts->offsets.size() - 1].size; + } else { + fullLength += (luts->lutProperties[luts->offsets.size() - 1].size * + luts->lutProperties[luts->offsets.size() - 1].size * + luts->lutProperties[luts->offsets.size() - 1].size * 3); + } + size_t bufferSize = static_cast<size_t>(fullLength) * sizeof(float); + float* ptr = (float*)mmap(NULL, bufferSize, PROT_READ, MAP_SHARED, fd.get(), 0); + if (ptr == MAP_FAILED) { + *os << "\n .bufferdata cannot mmap!"; + return; + } + std::vector<float> buffers(ptr, ptr + fullLength); + munmap(ptr, bufferSize); + + *os << "\n .bufferdata = "; + for (size_t i = 0; i < luts->offsets.size(); i++) { + PrintTo(buffers, static_cast<size_t>(luts->offsets[i]), + luts->lutProperties[i].dimension, + static_cast<size_t>(luts->lutProperties[i].size), os); + } + } + *os << "\n }"; +} + } // namespace android::gui
\ No newline at end of file diff --git a/libs/gui/include/gui/GLConsumer.h b/libs/gui/include/gui/GLConsumer.h index 30cbfa2f9f..254d8ac79c 100644 --- a/libs/gui/include/gui/GLConsumer.h +++ b/libs/gui/include/gui/GLConsumer.h @@ -83,6 +83,20 @@ public: // If the constructor without the tex parameter is used, the GLConsumer is // created in a detached state, and attachToContext must be called before // calls to updateTexImage. + static std::tuple<sp<GLConsumer>, sp<Surface>> create(uint32_t tex, uint32_t textureTarget, + bool useFenceSync, + bool isControlledByApp); + static std::tuple<sp<GLConsumer>, sp<Surface>> create(uint32_t textureTarget, bool useFenceSync, + bool isControlledByApp); + static sp<GLConsumer> create(const sp<IGraphicBufferConsumer>& bq, uint32_t tex, + uint32_t textureTarget, bool useFenceSync, bool isControlledByApp) + __attribute((deprecated( + "Prefer create functions that create their own surface and consumer."))); + static sp<GLConsumer> create(const sp<IGraphicBufferConsumer>& bq, uint32_t textureTarget, + bool useFenceSync, bool isControlledByApp) + __attribute((deprecated( + "Prefer create functions that create their own surface and consumer."))); + #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) GLConsumer(uint32_t tex, uint32_t textureTarget, bool useFenceSync, bool isControlledByApp); @@ -271,6 +285,7 @@ protected: #endif // releaseBufferLocked overrides the ConsumerBase method to update the // mEglSlots array in addition to the ConsumerBase. +#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) virtual status_t releaseBufferLocked(int slot, const sp<GraphicBuffer> graphicBuffer, EGLDisplay display = EGL_NO_DISPLAY, EGLSyncKHR eglFence = EGL_NO_SYNC_KHR) override; @@ -279,16 +294,14 @@ protected: const sp<GraphicBuffer> graphicBuffer, EGLSyncKHR eglFence) { return releaseBufferLocked(slot, graphicBuffer, mEglDisplay, eglFence); } +#endif struct PendingRelease { - PendingRelease() : isPending(false), currentTexture(-1), - graphicBuffer(), display(nullptr), fence(nullptr) {} + PendingRelease() : isPending(false), currentTexture(-1), graphicBuffer() {} bool isPending; int currentTexture; sp<GraphicBuffer> graphicBuffer; - EGLDisplay display; - EGLSyncKHR fence; }; // This releases the buffer in the slot referenced by mCurrentTexture, @@ -468,16 +481,18 @@ private: // EGLSlot contains the information and object references that // GLConsumer maintains about a BufferQueue buffer slot. struct EglSlot { +#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) EglSlot() : mEglFence(EGL_NO_SYNC_KHR) {} - +#endif // mEglImage is the EGLImage created from mGraphicBuffer. sp<EglImage> mEglImage; - +#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) // mFence is the EGL sync object that must signal before the buffer // associated with this buffer slot may be dequeued. It is initialized // to EGL_NO_SYNC_KHR when the buffer is created and (optionally, based // on a compile-time option) set to a new sync object in updateTexImage. EGLSyncKHR mEglFence; +#endif }; // mEglDisplay is the EGLDisplay with which this GLConsumer is currently diff --git a/libs/gui/include/gui/IConsumerListener.h b/libs/gui/include/gui/IConsumerListener.h index 1695aae876..95e66c778e 100644 --- a/libs/gui/include/gui/IConsumerListener.h +++ b/libs/gui/include/gui/IConsumerListener.h @@ -16,9 +16,6 @@ #pragma once -#include <binder/IInterface.h> -#include <binder/SafeInterface.h> - #include <utils/Errors.h> #include <utils/RefBase.h> @@ -110,25 +107,6 @@ public: #endif }; -#ifndef NO_BINDER -class IConsumerListener : public ConsumerListener, public IInterface { -public: - DECLARE_META_INTERFACE(ConsumerListener) -}; - -class BnConsumerListener : public SafeBnInterface<IConsumerListener> { -public: - BnConsumerListener() : SafeBnInterface<IConsumerListener>("BnConsumerListener") {} - - status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, - uint32_t flags = 0) override; -}; - -#else -class IConsumerListener : public ConsumerListener { -}; -class BnConsumerListener : public IConsumerListener { -}; -#endif +class IConsumerListener : public ConsumerListener {}; } // namespace android diff --git a/libs/gui/include/gui/IGraphicBufferConsumer.h b/libs/gui/include/gui/IGraphicBufferConsumer.h index 56eb291335..8066b070fb 100644 --- a/libs/gui/include/gui/IGraphicBufferConsumer.h +++ b/libs/gui/include/gui/IGraphicBufferConsumer.h @@ -40,15 +40,8 @@ class NativeHandle; /* * See IGraphicBufferProducer for details on SLOT_COUNT. */ -#ifndef NO_BINDER -class IGraphicBufferConsumer : public IInterface { -public: - DECLARE_META_INTERFACE(GraphicBufferConsumer) -#else class IGraphicBufferConsumer : public RefBase { public: -#endif - enum { // Returned by releaseBuffer, after which the consumer must free any references to the // just-released buffer that it might have. @@ -139,12 +132,17 @@ public: // * the buffer slot was invalid // * the fence was NULL // * the buffer slot specified is not in the acquired state +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) + virtual status_t releaseBuffer(int buf, uint64_t frameNumber, + const sp<Fence>& releaseFence) = 0; +#else virtual status_t releaseBuffer(int buf, uint64_t frameNumber, EGLDisplay display, EGLSyncKHR fence, const sp<Fence>& releaseFence) = 0; status_t releaseBuffer(int buf, uint64_t frameNumber, const sp<Fence>& releaseFence) { return releaseBuffer(buf, frameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, releaseFence); } +#endif // consumerConnect connects a consumer to the BufferQueue. Only one consumer may be connected, // and when that consumer disconnects the BufferQueue is placed into the "abandoned" state, @@ -245,6 +243,9 @@ public: // maxAcquiredBuffers must be (inclusive) between 1 and MAX_MAX_ACQUIRED_BUFFERS. It also cannot // cause the maxBufferCount value to be exceeded. // + // If called with onBuffersReleasedCallback, that call back will be called in lieu of + // IConsumerListener::onBuffersReleased. + // // Return of a value other than NO_ERROR means an error has occurred: // * NO_INIT - the BufferQueue has been abandoned // * BAD_VALUE - one of the below conditions occurred: @@ -255,6 +256,11 @@ public: // * INVALID_OPERATION - attempting to call this after a producer connected. virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) = 0; + using OnBufferReleasedCallback = std::function<void(void)>; + virtual status_t setMaxAcquiredBufferCount( + int maxAcquiredBuffers, + std::optional<OnBufferReleasedCallback> onBuffersReleasedCallback) = 0; + // setConsumerName sets the name used in logging virtual status_t setConsumerName(const String8& name) = 0; @@ -317,18 +323,4 @@ public: } }; -#ifndef NO_BINDER -class BnGraphicBufferConsumer : public SafeBnInterface<IGraphicBufferConsumer> { -public: - BnGraphicBufferConsumer() - : SafeBnInterface<IGraphicBufferConsumer>("BnGraphicBufferConsumer") {} - - status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, - uint32_t flags = 0) override; -}; -#else -class BnGraphicBufferConsumer : public IGraphicBufferConsumer { -}; -#endif - } // namespace android diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h index 9a422fd808..de553aef72 100644 --- a/libs/gui/include/gui/ISurfaceComposer.h +++ b/libs/gui/include/gui/ISurfaceComposer.h @@ -65,6 +65,7 @@ struct DisplayState; struct InputWindowCommands; class HdrCapabilities; class Rect; +class TransactionState; using gui::FrameTimelineInfo; using gui::IDisplayEventConnection; @@ -105,13 +106,7 @@ public: }; /* open/close transactions. requires ACCESS_SURFACE_FLINGER permission */ - virtual status_t setTransactionState( - const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& state, - Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken, - InputWindowCommands inputWindowCommands, int64_t desiredPresentTime, - bool isAutoTimestamp, const std::vector<client_cache_t>& uncacheBuffer, - bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks, - uint64_t transactionId, const std::vector<uint64_t>& mergedTransactionIds) = 0; + virtual status_t setTransactionState(TransactionState&& state) = 0; }; // ---------------------------------------------------------------------------- diff --git a/libs/gui/include/gui/InputTransferToken.h b/libs/gui/include/gui/InputTransferToken.h index 6530b5069a..b83f24562b 100644 --- a/libs/gui/include/gui/InputTransferToken.h +++ b/libs/gui/include/gui/InputTransferToken.h @@ -25,7 +25,7 @@ namespace android { struct InputTransferToken : public RefBase, Parcelable { public: - InputTransferToken() { mToken = new BBinder(); } + InputTransferToken() { mToken = sp<BBinder>::make(); } InputTransferToken(const sp<IBinder>& token) { mToken = token; } @@ -39,15 +39,9 @@ public: return NO_ERROR; }; + bool operator==(const InputTransferToken& other) const { return mToken == other.mToken; } + sp<IBinder> mToken; }; -static inline bool operator==(const sp<InputTransferToken>& token1, - const sp<InputTransferToken>& token2) { - if (token1.get() == token2.get()) { - return true; - } - return token1->mToken == token2->mToken; -} - -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/libs/gui/include/gui/LayerMetadata.h b/libs/gui/include/gui/LayerMetadata.h index 7ee291df4c..6381db228b 100644 --- a/libs/gui/include/gui/LayerMetadata.h +++ b/libs/gui/include/gui/LayerMetadata.h @@ -44,6 +44,10 @@ struct LayerMetadata : public Parcelable { LayerMetadata& operator=(const LayerMetadata& other); LayerMetadata& operator=(LayerMetadata&& other); + // Note: `default` is not feasible because Parcelable does not provide ==. + bool operator==(const LayerMetadata& rhs) const { return mMap == rhs.mMap; } + bool operator!=(const LayerMetadata&) const = default; + // Merges other into this LayerMetadata. If eraseEmpty is true, any entries in // in this whose keys are paired with empty values in other will be erased. bool merge(const LayerMetadata& other, bool eraseEmpty = false); diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index 64f191b867..369d3d136a 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -17,9 +17,9 @@ #ifndef ANDROID_SF_LAYER_STATE_H #define ANDROID_SF_LAYER_STATE_H - #include <stdint.h> #include <sys/types.h> +#include <span> #include <android/gui/DisplayCaptureArgs.h> #include <android/gui/IWindowInfosReportedListener.h> @@ -69,21 +69,39 @@ struct client_cache_t { uint64_t id; bool operator==(const client_cache_t& other) const { return id == other.id; } + bool operator!=(const client_cache_t&) const = default; bool isValid() const { return token != nullptr; } }; class TrustedPresentationListener : public Parcelable { public: - sp<ITransactionCompletedListener> callbackInterface; - int callbackId = -1; + struct State { + sp<ITransactionCompletedListener> callbackInterface; + int callbackId = -1; + bool operator==(const State&) const = default; + bool operator!=(const State&) const = default; + }; void invoke(bool presentedWithinThresholds) { - callbackInterface->onTrustedPresentationChanged(callbackId, presentedWithinThresholds); + mState.callbackInterface->onTrustedPresentationChanged(mState.callbackId, + presentedWithinThresholds); + } + void configure(State&& state) { mState = std::move(state); } + const sp<ITransactionCompletedListener>& getCallback() { return mState.callbackInterface; } + void clear() { + mState.callbackInterface = nullptr; + mState.callbackId = -1; } status_t writeToParcel(Parcel* parcel) const; status_t readFromParcel(const Parcel* parcel); + + bool operator==(const TrustedPresentationListener& rhs) const { return mState == rhs.mState; } + bool operator!=(const TrustedPresentationListener&) const = default; + +private: + State mState; }; class BufferData : public Parcelable { @@ -231,6 +249,7 @@ struct layer_state_t { eBufferReleaseChannelChanged = 0x40000'00000000, ePictureProfileHandleChanged = 0x80000'00000000, eAppContentPriorityChanged = 0x100000'00000000, + eClientDrawnCornerRadiusChanged = 0x200000'00000000, }; layer_state_t(); @@ -251,9 +270,9 @@ struct layer_state_t { // Geometry updates. static constexpr uint64_t GEOMETRY_CHANGES = layer_state_t::eBufferCropChanged | layer_state_t::eBufferTransformChanged | layer_state_t::eCornerRadiusChanged | - layer_state_t::eCropChanged | layer_state_t::eDestinationFrameChanged | - layer_state_t::eMatrixChanged | layer_state_t::ePositionChanged | - layer_state_t::eTransformToDisplayInverseChanged | + layer_state_t::eClientDrawnCornerRadiusChanged | layer_state_t::eCropChanged | + layer_state_t::eDestinationFrameChanged | layer_state_t::eMatrixChanged | + layer_state_t::ePositionChanged | layer_state_t::eTransformToDisplayInverseChanged | layer_state_t::eTransparentRegionChanged | layer_state_t::eEdgeExtensionChanged; // Buffer and related updates. @@ -274,8 +293,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::ePictureProfileHandleChanged | - layer_state_t::eAppContentPriorityChanged; + 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 | @@ -300,9 +319,40 @@ struct layer_state_t { static constexpr uint64_t VISIBLE_REGION_CHANGES = layer_state_t::GEOMETRY_CHANGES | layer_state_t::HIERARCHY_CHANGES | layer_state_t::eAlphaChanged; + // Changes that force GPU composition. + static constexpr uint64_t COMPOSITION_EFFECTS = layer_state_t::eBackgroundBlurRadiusChanged | + layer_state_t::eBlurRegionsChanged | layer_state_t::eCornerRadiusChanged | + layer_state_t::eShadowRadiusChanged | layer_state_t::eStretchChanged; + bool hasValidBuffer() const; void sanitize(int32_t permissions); + void updateTransparentRegion(const Region& transparentRegion); + const Region& getTransparentRegion() const { return mNotDefCmpState.transparentRegion; } + void updateSurfaceDamageRegion(const Region& surfaceDamageRegion); + const Region& getSurfaceDamageRegion() const { return mNotDefCmpState.surfaceDamageRegion; } + // Do not update state flags. Used to set up test state. + void setSurfaceDamageRegion(Region&& surfaceDamageRegion) { + mNotDefCmpState.surfaceDamageRegion = std::move(surfaceDamageRegion); + } + void updateRelativeLayer(const sp<SurfaceControl>& relativeTo, int32_t z); + void updateParentLayer(const sp<SurfaceControl>& newParent); + void updateInputWindowInfo(sp<gui::WindowInfoHandle>&& info); + const gui::WindowInfo& getWindowInfo() const { + return *mNotDefCmpState.windowInfoHandle->getInfo(); + } + gui::WindowInfo* editWindowInfo() { return mNotDefCmpState.windowInfoHandle->editInfo(); } + + const sp<SurfaceControl>& getParentSurfaceControlForChild() const { + return mNotDefCmpState.parentSurfaceControlForChild; + } + const sp<SurfaceControl>& getRelativeLayerSurfaceControl() const { + return mNotDefCmpState.relativeLayerSurfaceControl; + } + + bool operator==(const layer_state_t&) const = default; + bool operator!=(const layer_state_t&) const = default; + struct matrix22_t { float dsdx{0}; float dtdx{0}; @@ -328,30 +378,23 @@ struct layer_state_t { uint8_t reserved; matrix22_t matrix; float cornerRadius; + float clientDrawnCornerRadius; uint32_t backgroundBlurRadius; - sp<SurfaceControl> relativeLayerSurfaceControl; - - sp<SurfaceControl> parentSurfaceControlForChild; - half4 color; // non POD must be last. see write/read - Region transparentRegion; uint32_t bufferTransform; bool transformToDisplayInverse; FloatRect crop; std::shared_ptr<BufferData> bufferData = nullptr; ui::Dataspace dataspace; HdrMetadata hdrMetadata; - Region surfaceDamageRegion; int32_t api; sp<NativeHandle> sidebandStream; mat4 colorTransform; std::vector<BlurRegion> blurRegions; - sp<gui::WindowInfoHandle> windowInfoHandle = sp<gui::WindowInfoHandle>::make(); - LayerMetadata metadata; // The following refer to the alpha, and dataspace, respectively of @@ -437,6 +480,18 @@ struct layer_state_t { std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint> bufferReleaseChannel; std::shared_ptr<gui::DisplayLuts> luts; + +protected: + struct NotDefaultComparableState { + Region transparentRegion; + Region surfaceDamageRegion; + sp<gui::WindowInfoHandle> windowInfoHandle = sp<gui::WindowInfoHandle>::make(); + sp<SurfaceControl> relativeLayerSurfaceControl; + sp<SurfaceControl> parentSurfaceControlForChild; + + bool operator==(const NotDefaultComparableState& rhs) const; + bool operator!=(const NotDefaultComparableState& rhs) const = default; + } mNotDefCmpState; }; class ComposerState { @@ -444,6 +499,9 @@ public: layer_state_t state; status_t write(Parcel& output) const; status_t read(const Parcel& input); + + bool operator==(const ComposerState&) const = default; + bool operator!=(const ComposerState&) const = default; }; struct DisplayState { @@ -495,28 +553,49 @@ struct DisplayState { Rect layerStackSpaceRect = Rect::EMPTY_RECT; Rect orientedDisplaySpaceRect = Rect::EMPTY_RECT; - // Exclusive to virtual displays: The sink surface into which the virtual display is rendered, - // and an optional resolution that overrides its default dimensions. - sp<IGraphicBufferProducer> surface; + // For physical displays, this is the resolution, which must match the active display mode. To + // change the resolution, the client must first call SurfaceControl.setDesiredDisplayModeSpecs + // with the new DesiredDisplayModeSpecs#defaultMode, then commit the matching width and height. + // + // For virtual displays, this is an optional resolution that overrides its default dimensions. + // uint32_t width = 0; uint32_t height = 0; + // For virtual displays, this is the sink surface into which the virtual display is rendered. + sp<IGraphicBufferProducer> surface; + status_t write(Parcel& output) const; status_t read(const Parcel& input); + + bool operator==(const DisplayState&) const = default; + bool operator!=(const DisplayState&) const = default; }; struct InputWindowCommands { - std::vector<gui::FocusRequest> focusRequests; - std::unordered_set<sp<gui::IWindowInfosReportedListener>, - SpHash<gui::IWindowInfosReportedListener>> - windowInfosReportedListeners; - + using Listener = gui::IWindowInfosReportedListener; + using ListenerSet = std::unordered_set<sp<Listener>, SpHash<Listener>>; // Merges the passed in commands and returns true if there were any changes. bool merge(const InputWindowCommands& other); bool empty() const; void clear(); + void addFocusRequest(const gui::FocusRequest& request) { focusRequests.push_back(request); } + void addWindowInfosReportedListener(const sp<Listener>& listener) { + windowInfosReportedListeners.insert(listener); + } + ListenerSet&& releaseListeners() { return std::move(windowInfosReportedListeners); } + status_t write(Parcel& output) const; status_t read(const Parcel& input); + + std::span<const gui::FocusRequest> getFocusRequests() const { return focusRequests; } + const ListenerSet& getListeners() const { return windowInfosReportedListeners; } + bool operator==(const InputWindowCommands&) const = default; + bool operator!=(const InputWindowCommands&) const = default; + +private: + std::vector<gui::FocusRequest> focusRequests; + ListenerSet windowInfosReportedListeners; }; static inline int compare_type(const ComposerState& lhs, const ComposerState& rhs) { diff --git a/libs/gui/include/gui/StreamSplitter.h b/libs/gui/include/gui/StreamSplitter.h index b4eef292c1..8176f753c3 100644 --- a/libs/gui/include/gui/StreamSplitter.h +++ b/libs/gui/include/gui/StreamSplitter.h @@ -37,7 +37,7 @@ class IGraphicBufferProducer; // BufferQueue, where each buffer queued to the input is available to be // acquired by each of the outputs, and is able to be dequeued by the input // again only once all of the outputs have released it. -class StreamSplitter : public BnConsumerListener { +class StreamSplitter : public IConsumerListener { public: // createSplitter creates a new splitter, outSplitter, using inputQueue as // the input BufferQueue. Output BufferQueues must be added using addOutput @@ -153,6 +153,8 @@ private: size_t mReleaseCount; }; + friend class sp<StreamSplitter>; + // Only called from createSplitter explicit StreamSplitter(const sp<IGraphicBufferConsumer>& inputQueue); diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index 0f66c8b492..15e3341ca2 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -52,6 +52,7 @@ #include <gui/ITransactionCompletedListener.h> #include <gui/LayerState.h> #include <gui/SurfaceControl.h> +#include <gui/TransactionState.h> #include <gui/WindowInfosListenerReporter.h> #include <math/vec3.h> @@ -345,8 +346,6 @@ public: static std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport> getDisplayDecorationSupport(const sp<IBinder>& displayToken); - static bool flagEdgeExtensionEffectUseShader(); - /** * Returns how many picture profiles are supported by the display. * @@ -396,6 +395,7 @@ public: static const std::string kEmpty; static sp<IBinder> createVirtualDisplay(const std::string& displayName, bool isSecure, + bool optimizeForPower = true, const std::string& uniqueId = kEmpty, float requestedRefreshRate = 0); @@ -448,59 +448,11 @@ public: static sp<IBinder> sApplyToken; static std::mutex sApplyTokenMutex; void releaseBufferIfOverwriting(const layer_state_t& state); - 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; - SortedVector<DisplayState> mDisplayStates; - std::unordered_map<sp<ITransactionCompletedListener>, CallbackInfo, TCLHash> - mListenerCallbacks; - std::vector<client_cache_t> mUncacheBuffers; - - // We keep track of the last MAX_MERGE_HISTORY_LENGTH merged transaction ids. - // Ordered most recently merged to least recently merged. - static const size_t MAX_MERGE_HISTORY_LENGTH = 10u; - std::vector<uint64_t> mMergedTransactionIds; - - uint64_t mId; - - bool mAnimation = false; - bool mEarlyWakeupStart = false; - bool mEarlyWakeupEnd = false; - - // Indicates that the Transaction may contain buffers that should be cached. The reason this - // is only a guess is that buffers can be removed before cache is called. This is only a - // hint that at some point a buffer was added to this transaction before apply was called. - bool mMayContainBuffer = false; - // mDesiredPresentTime is the time in nanoseconds that the client would like the transaction - // to be presented. When it is not possible to present at exactly that time, it will be - // presented after the time has passed. - // - // If the client didn't pass a desired presentation time, mDesiredPresentTime will be - // populated to the time setBuffer was called, and mIsAutoTimestamp will be set to true. - // - // Desired present times that are more than 1 second in the future may be ignored. - // When a desired present time has already passed, the transaction will be presented as soon - // as possible. - // - // Transactions from the same process are presented in the same order that they are applied. - // The desired present time does not affect this ordering. - int64_t mDesiredPresentTime = 0; - bool mIsAutoTimestamp = true; - - // The vsync id provided by Choreographer.getVsyncId and the input event id - FrameTimelineInfo mFrameTimelineInfo; - - // If not null, transactions will be queued up using this token otherwise a common token - // per process will be used. - sp<IBinder> mApplyToken = nullptr; + TransactionState mState; - InputWindowCommands mInputWindowCommands; int mStatus = NO_ERROR; layer_state_t* getLayerState(const sp<SurfaceControl>& sc); @@ -510,6 +462,11 @@ public: void registerSurfaceControlForCallback(const sp<SurfaceControl>& sc); void setReleaseBufferCallback(BufferData*, ReleaseBufferCallback); + protected: + // Accessed in tests. + std::unordered_map<sp<ITransactionCompletedListener>, CallbackInfo, TCLHash> + mListenerCallbacks; + public: Transaction(); virtual ~Transaction() = default; @@ -526,7 +483,7 @@ public: // Returns the current id of the transaction. // The id is updated every time the transaction is applied. - uint64_t getId(); + uint64_t getId() const; std::vector<uint64_t> getMergedTransactionIds(); @@ -567,6 +524,11 @@ public: Transaction& setCrop(const sp<SurfaceControl>& sc, const Rect& crop); Transaction& setCrop(const sp<SurfaceControl>& sc, const FloatRect& crop); Transaction& setCornerRadius(const sp<SurfaceControl>& sc, float cornerRadius); + // Sets the client drawn corner radius for the layer. If both a corner radius and a client + // radius are sent to SF, the client radius will be used. This indicates that the corner + // radius is drawn by the client and not SurfaceFlinger. + Transaction& setClientDrawnCornerRadius(const sp<SurfaceControl>& sc, + float clientDrawnCornerRadius); Transaction& setBackgroundBlurRadius(const sp<SurfaceControl>& sc, int backgroundBlurRadius); Transaction& setBlurRegions(const sp<SurfaceControl>& sc, @@ -618,7 +580,7 @@ public: Transaction& setExtendedRangeBrightness(const sp<SurfaceControl>& sc, float currentBufferRatio, float desiredRatio); Transaction& setDesiredHdrHeadroom(const sp<SurfaceControl>& sc, float desiredRatio); - Transaction& setLuts(const sp<SurfaceControl>& sc, const base::unique_fd& lutFd, + Transaction& setLuts(const sp<SurfaceControl>& sc, base::unique_fd&& lutFd, const std::vector<int32_t>& offsets, const std::vector<int32_t>& dimensions, const std::vector<int32_t>& sizes, diff --git a/libs/gui/include/gui/TransactionState.h b/libs/gui/include/gui/TransactionState.h new file mode 100644 index 0000000000..4358227dae --- /dev/null +++ b/libs/gui/include/gui/TransactionState.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2025 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/gui/FrameTimelineInfo.h> +#include <binder/Parcelable.h> +#include <gui/LayerState.h> + +namespace android { + +// Class to store all the transaction data and the parcelling logic +class TransactionState { +public: + explicit TransactionState() = default; + TransactionState(TransactionState const& other) = default; + status_t writeToParcel(Parcel* parcel) const; + status_t readFromParcel(const Parcel* parcel); + layer_state_t* getLayerState(const sp<SurfaceControl>& sc); + DisplayState& getDisplayState(const sp<IBinder>& token); + + // Returns the current id of the transaction. + // The id is updated every time the transaction is applied. + uint64_t getId() const { return mId; } + std::vector<uint64_t> getMergedTransactionIds() const { return mMergedTransactionIds; } + void enableDebugLogCallPoints() { mLogCallPoints = true; } + void merge(TransactionState&& other, + const std::function<void(layer_state_t&)>& onBufferOverwrite); + + // copied from FrameTimelineInfo::merge() + void mergeFrameTimelineInfo(const FrameTimelineInfo& other); + void clear(); + bool operator==(const TransactionState& rhs) const = default; + bool operator!=(const TransactionState& rhs) const = default; + + uint64_t mId = 0; + std::vector<uint64_t> mMergedTransactionIds; + uint32_t mFlags = 0; + // The vsync id provided by Choreographer.getVsyncId and the input event id + gui::FrameTimelineInfo mFrameTimelineInfo; + // mDesiredPresentTime is the time in nanoseconds that the client would like the transaction + // to be presented. When it is not possible to present at exactly that time, it will be + // presented after the time has passed. + // + // If the client didn't pass a desired presentation time, mDesiredPresentTime will be + // populated to the time setBuffer was called, and mIsAutoTimestamp will be set to true. + // + // Desired present times that are more than 1 second in the future may be ignored. + // When a desired present time has already passed, the transaction will be presented as soon + // as possible. + // + // Transactions from the same process are presented in the same order that they are applied. + // The desired present time does not affect this ordering. + int64_t mDesiredPresentTime = 0; + bool mIsAutoTimestamp = true; + // If not null, transactions will be queued up using this token otherwise a common token + // per process will be used. + sp<IBinder> mApplyToken; + // Indicates that the Transaction may contain buffers that should be cached. The reason this + // is only a guess is that buffers can be removed before cache is called. This is only a + // hint that at some point a buffer was added to this transaction before apply was called. + bool mMayContainBuffer = false; + // Prints debug logs when enabled. + bool mLogCallPoints = false; + + std::vector<DisplayState> mDisplayStates; + std::vector<ComposerState> mComposerStates; + InputWindowCommands mInputWindowCommands; + std::vector<client_cache_t> mUncacheBuffers; + // Note: mHasListenerCallbacks can be true even if mListenerCallbacks is + // empty. + bool mHasListenerCallbacks = false; + std::vector<ListenerCallbacks> mListenerCallbacks; + +private: + // We keep track of the last MAX_MERGE_HISTORY_LENGTH merged transaction ids. + // Ordered most recently merged to least recently merged. + static constexpr size_t MAX_MERGE_HISTORY_LENGTH = 10u; +}; + +}; // namespace android diff --git a/libs/gui/include/gui/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h index eb3be5588a..9ac49c0fb6 100644 --- a/libs/gui/include/gui/WindowInfo.h +++ b/libs/gui/include/gui/WindowInfo.h @@ -150,8 +150,6 @@ struct WindowInfo : public Parcelable { static_cast<uint32_t>(os::InputConfig::NOT_FOCUSABLE), NOT_TOUCHABLE = static_cast<uint32_t>(os::InputConfig::NOT_TOUCHABLE), - PREVENT_SPLITTING = - static_cast<uint32_t>(os::InputConfig::PREVENT_SPLITTING), DUPLICATE_TOUCH_TO_WALLPAPER = static_cast<uint32_t>(os::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER), IS_WALLPAPER = @@ -220,9 +218,14 @@ struct WindowInfo : public Parcelable { // An alpha of 1.0 means fully opaque and 0.0 means fully transparent. float alpha; - // Transform applied to individual windows. + // Transform applied to individual windows for input. + // Maps display coordinates to the window's input coordinate space. ui::Transform transform; + // Transform applied to get to the layer stack space of the cloned window for input. + // Maps display coordinates of the clone window to the layer stack space of the cloned window. + std::optional<ui::Transform> cloneLayerStackTransform; + /* * This is filled in by the WM relative to the frame and then translated * to absolute coordinates by SurfaceFlinger once the frame is computed. @@ -265,6 +268,7 @@ struct WindowInfo : public Parcelable { bool overlaps(const WindowInfo* other) const; bool operator==(const WindowInfo& inputChannel) const; + bool operator!=(const WindowInfo&) const = default; status_t writeToParcel(android::Parcel* parcel) const override; @@ -316,6 +320,9 @@ public: status_t readFromParcel(const android::Parcel* parcel); status_t writeToParcel(android::Parcel* parcel) const; + bool operator==(const WindowInfoHandle& rhs) const { return mInfo == rhs.mInfo; } + bool operator!=(const WindowInfoHandle&) const = default; + protected: virtual ~WindowInfoHandle(); diff --git a/libs/gui/include/gui/mock/GraphicBufferConsumer.h b/libs/gui/include/gui/mock/GraphicBufferConsumer.h index 98f24c2d44..18a7e12fa7 100644 --- a/libs/gui/include/gui/mock/GraphicBufferConsumer.h +++ b/libs/gui/include/gui/mock/GraphicBufferConsumer.h @@ -18,6 +18,7 @@ #include <gmock/gmock.h> +#include <com_android_graphics_libgui_flags.h> #include <gui/IGraphicBufferConsumer.h> #include <utils/RefBase.h> @@ -25,21 +26,28 @@ namespace android { namespace mock { -class GraphicBufferConsumer : public BnGraphicBufferConsumer, public virtual android::RefBase { +class GraphicBufferConsumer : public IGraphicBufferConsumer { public: GraphicBufferConsumer(); - ~GraphicBufferConsumer() override; + ~GraphicBufferConsumer(); MOCK_METHOD3(acquireBuffer, status_t(BufferItem*, nsecs_t, uint64_t)); MOCK_METHOD1(detachBuffer, status_t(int)); MOCK_METHOD2(attachBuffer, status_t(int*, const sp<GraphicBuffer>&)); +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) + MOCK_METHOD3(releaseBuffer, status_t(int, uint64_t, const sp<Fence>&)); +#else MOCK_METHOD5(releaseBuffer, status_t(int, uint64_t, EGLDisplay, EGLSyncKHR, const sp<Fence>&)); +#endif MOCK_METHOD2(consumerConnect, status_t(const sp<IConsumerListener>&, bool)); MOCK_METHOD0(consumerDisconnect, status_t()); + MOCK_METHOD1(allowUnlimitedSlots, status_t(bool)); MOCK_METHOD1(getReleasedBuffers, status_t(uint64_t*)); + MOCK_METHOD1(getReleasedBuffersExtended, status_t(std::vector<bool>*)); MOCK_METHOD2(setDefaultBufferSize, status_t(uint32_t, uint32_t)); MOCK_METHOD1(setMaxBufferCount, status_t(int)); MOCK_METHOD1(setMaxAcquiredBufferCount, status_t(int)); + MOCK_METHOD2(setMaxAcquiredBufferCount, status_t(int, std::optional<OnBufferReleasedCallback>)); MOCK_METHOD1(setConsumerName, status_t(const String8&)); MOCK_METHOD1(setDefaultBufferFormat, status_t(PixelFormat)); MOCK_METHOD1(setDefaultBufferDataSpace, status_t(android_dataspace)); diff --git a/libs/gui/libgui_flags.aconfig b/libs/gui/libgui_flags.aconfig index 6bf38c05f1..ce1bc9512c 100644 --- a/libs/gui/libgui_flags.aconfig +++ b/libs/gui/libgui_flags.aconfig @@ -77,14 +77,6 @@ flag { } # wb_stream_splitter flag { - name: "edge_extension_shader" - namespace: "windowing_frontend" - description: "Enable edge extension via shader" - bug: "322036393" - is_fixed_read_only: true -} # edge_extension_shader - -flag { name: "buffer_release_channel" namespace: "window_surfaces" description: "Enable BufferReleaseChannel to optimize buffer releases" @@ -139,3 +131,33 @@ flag { bug: "339705065" is_fixed_read_only: true } # bq_gl_fence_cleanup + +flag { + name: "wb_media_migration" + namespace: "core_graphics" + description: "Main flag for the warren buffers media migration." + bug: "340934031" + is_fixed_read_only: true +} # wb_media_migration + +flag { + name: "allocate_buffer_priority" + namespace: "wear_system_health" + description: "Boost priority for buffer allocation" + bug: "399701430" + metadata { + purpose: PURPOSE_BUGFIX + } + is_fixed_read_only: true +} # allocate_buffer_priority + +flag { + name: "bq_always_use_max_dequeued_buffer_count" + namespace: "core_graphics" + description: "BufferQueueProducer::dequeue's respects setMaxDequeuedBufferCount even before a buffer is dequeued." + bug: "399328309" + metadata { + purpose: PURPOSE_BUGFIX + } + is_fixed_read_only: true +} # bq_always_use_max_dequeued_buffer_count diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp index 87051a7aac..e20345dd1a 100644 --- a/libs/gui/tests/Android.bp +++ b/libs/gui/tests/Android.bp @@ -90,6 +90,7 @@ cc_test { "testserver/TestServerClient.cpp", "testserver/TestServerHost.cpp", "TextureRenderer.cpp", + "TransactionState_test.cpp", "VsyncEventData_test.cpp", "WindowInfo_test.cpp", ], diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp index 53f4a36c42..b861c6d4b7 100644 --- a/libs/gui/tests/BLASTBufferQueue_test.cpp +++ b/libs/gui/tests/BLASTBufferQueue_test.cpp @@ -81,7 +81,9 @@ class TestBLASTBufferQueue : public BLASTBufferQueue { public: TestBLASTBufferQueue(const std::string& name, const sp<SurfaceControl>& surface, int width, int height, int32_t format) - : BLASTBufferQueue(name, surface, width, height, format) {} + : BLASTBufferQueue(name) { + update(surface, width, height, format); + } void transactionCallback(nsecs_t latchTime, const sp<Fence>& presentFence, const std::vector<SurfaceControlStats>& stats) override { @@ -112,8 +114,8 @@ private: class BLASTBufferQueueHelper { public: BLASTBufferQueueHelper(const sp<SurfaceControl>& sc, int width, int height) { - mBlastBufferQueueAdapter = new TestBLASTBufferQueue("TestBLASTBufferQueue", sc, width, - height, PIXEL_FORMAT_RGBA_8888); + mBlastBufferQueueAdapter = sp<TestBLASTBufferQueue>::make("TestBLASTBufferQueue", sc, width, + height, PIXEL_FORMAT_RGBA_8888); } void update(const sp<SurfaceControl>& sc, int width, int height) { diff --git a/libs/gui/tests/BufferItemConsumer_test.cpp b/libs/gui/tests/BufferItemConsumer_test.cpp index 6453885804..80eea267bf 100644 --- a/libs/gui/tests/BufferItemConsumer_test.cpp +++ b/libs/gui/tests/BufferItemConsumer_test.cpp @@ -24,6 +24,7 @@ #include <gui/Surface.h> #include <ui/BufferQueueDefs.h> #include <ui/GraphicBuffer.h> +#include <utils/Errors.h> #include <unordered_set> @@ -62,14 +63,15 @@ class BufferItemConsumerTest : public ::testing::Test { void SetUp() override { mBuffers.resize(BufferQueueDefs::NUM_BUFFER_SLOTS); - mBIC = new BufferItemConsumer(kUsage, kMaxLockedBuffers, true); + sp<Surface> surface; + std::tie(mBIC, surface) = BufferItemConsumer::create(kUsage, kMaxLockedBuffers, true); String8 name("BufferItemConsumer_Under_Test"); mBIC->setName(name); mBFL = new BufferFreedListener(this); mBIC->setBufferFreedListener(mBFL); sp<IProducerListener> producerListener = new TrackingProducerListener(this); - mProducer = mBIC->getSurface()->getIGraphicBufferProducer(); + mProducer = surface->getIGraphicBufferProducer(); IGraphicBufferProducer::QueueBufferOutput bufferOutput; ASSERT_EQ(NO_ERROR, mProducer->connect(producerListener, NATIVE_WINDOW_API_CPU, @@ -234,6 +236,38 @@ TEST_F(BufferItemConsumerTest, TriggerBufferFreed_DeleteBufferItemConsumer) { ASSERT_EQ(1, GetFreedBufferCount()); } +TEST_F(BufferItemConsumerTest, ResizeAcquireCount) { + EXPECT_EQ(OK, mBIC->setMaxAcquiredBufferCount(kMaxLockedBuffers + 1)); + EXPECT_EQ(OK, mBIC->setMaxAcquiredBufferCount(kMaxLockedBuffers + 2)); + EXPECT_EQ(OK, mBIC->setMaxAcquiredBufferCount(kMaxLockedBuffers - 1)); + EXPECT_EQ(OK, mBIC->setMaxAcquiredBufferCount(kMaxLockedBuffers - 2)); + EXPECT_EQ(OK, mBIC->setMaxAcquiredBufferCount(kMaxLockedBuffers + 1)); + EXPECT_EQ(OK, mBIC->setMaxAcquiredBufferCount(kMaxLockedBuffers - 1)); +} + +TEST_F(BufferItemConsumerTest, AttachBuffer) { + ASSERT_EQ(OK, mBIC->setMaxAcquiredBufferCount(1)); + + int slot; + DequeueBuffer(&slot); + QueueBuffer(slot); + AcquireBuffer(&slot); + + sp<GraphicBuffer> newBuffer1 = sp<GraphicBuffer>::make(kWidth, kHeight, kFormat, kUsage); + sp<GraphicBuffer> newBuffer2 = sp<GraphicBuffer>::make(kWidth, kHeight, kFormat, kUsage); + + // For some reason, you can attach an extra buffer? + // b/400973991 to investigate + EXPECT_EQ(OK, mBIC->attachBuffer(newBuffer1)); + EXPECT_EQ(INVALID_OPERATION, mBIC->attachBuffer(newBuffer2)); + + ReleaseBuffer(slot); + + EXPECT_EQ(OK, mBIC->attachBuffer(newBuffer2)); + EXPECT_EQ(OK, mBIC->releaseBuffer(newBuffer1, Fence::NO_FENCE)); + EXPECT_EQ(OK, mBIC->releaseBuffer(newBuffer2, Fence::NO_FENCE)); +} + #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) // Test that delete BufferItemConsumer triggers onBufferFreed. TEST_F(BufferItemConsumerTest, DetachBufferWithBuffer) { diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp index 77b4ae8623..cfbb2e7386 100644 --- a/libs/gui/tests/BufferQueue_test.cpp +++ b/libs/gui/tests/BufferQueue_test.cpp @@ -32,6 +32,7 @@ #include <ui/PictureProfileHandle.h> #include <android-base/properties.h> +#include <android-base/unique_fd.h> #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> @@ -45,6 +46,7 @@ #include <gmock/gmock.h> #include <gtest/gtest.h> +#include <csignal> #include <future> #include <optional> #include <thread> @@ -65,6 +67,15 @@ class BufferQueueTest : public ::testing::Test { public: protected: + void TearDown() override { + std::vector<std::function<void()>> teardownFns; + teardownFns.swap(mTeardownFns); + + for (auto& fn : teardownFns) { + fn(); + } + } + void GetMinUndequeuedBufferCount(int* bufferCount) { ASSERT_TRUE(bufferCount != nullptr); ASSERT_EQ(OK, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, @@ -99,6 +110,7 @@ protected: sp<IGraphicBufferProducer> mProducer; sp<IGraphicBufferConsumer> mConsumer; + std::vector<std::function<void()>> mTeardownFns; }; static const uint32_t TEST_DATA = 0x12345678u; @@ -106,12 +118,14 @@ static const uint32_t TEST_DATA = 0x12345678u; // XXX: Tests that fork a process to hold the BufferQueue must run before tests // that use a local BufferQueue, or else Binder will get unhappy // -// In one instance this was a crash in the createBufferQueue where the -// binder call to create a buffer allocator apparently got garbage back. -// See b/36592665. +// TODO(b/392945118): In one instance this was a crash in the createBufferQueue +// where the binder call to create a buffer allocator apparently got garbage +// back. See b/36592665. TEST_F(BufferQueueTest, DISABLED_BufferQueueInAnotherProcess) { const String16 PRODUCER_NAME = String16("BQTestProducer"); - const String16 CONSUMER_NAME = String16("BQTestConsumer"); + + base::unique_fd readfd, writefd; + ASSERT_TRUE(base::Pipe(&readfd, &writefd)); pid_t forkPid = fork(); ASSERT_NE(forkPid, -1); @@ -123,23 +137,51 @@ TEST_F(BufferQueueTest, DISABLED_BufferQueueInAnotherProcess) { BufferQueue::createBufferQueue(&producer, &consumer); sp<IServiceManager> serviceManager = defaultServiceManager(); serviceManager->addService(PRODUCER_NAME, IInterface::asBinder(producer)); - serviceManager->addService(CONSUMER_NAME, IInterface::asBinder(consumer)); + + class ChildConsumerListener : public IConsumerListener { + public: + ChildConsumerListener(const sp<IGraphicBufferConsumer>& consumer, + base::unique_fd&& writeFd) + : mConsumer(consumer), mWriteFd(std::move(writeFd)) {} + + virtual void onFrameAvailable(const BufferItem&) override { + BufferItem item; + ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); + + uint32_t* dataOut; + ASSERT_EQ(OK, + item.mGraphicBuffer->lock(GraphicBuffer::USAGE_SW_READ_OFTEN, + reinterpret_cast<void**>(&dataOut))); + ASSERT_EQ(*dataOut, TEST_DATA); + ASSERT_EQ(OK, item.mGraphicBuffer->unlock()); + + bool isOk = true; + write(mWriteFd, &isOk, sizeof(bool)); + } + virtual void onBuffersReleased() override {} + virtual void onSidebandStreamChanged() override {} + + private: + sp<IGraphicBufferConsumer> mConsumer; + base::unique_fd mWriteFd; + }; + + sp<ChildConsumerListener> mc = + sp<ChildConsumerListener>::make(consumer, std::move(writefd)); + ASSERT_EQ(OK, consumer->consumerConnect(mc, false)); + ProcessState::self()->startThreadPool(); IPCThreadState::self()->joinThreadPool(); LOG_ALWAYS_FATAL("Shouldn't be here"); + } else { + mTeardownFns.emplace_back([forkPid]() { kill(forkPid, SIGTERM); }); } sp<IServiceManager> serviceManager = defaultServiceManager(); sp<IBinder> binderProducer = serviceManager->waitForService(PRODUCER_NAME); mProducer = interface_cast<IGraphicBufferProducer>(binderProducer); EXPECT_TRUE(mProducer != nullptr); - sp<IBinder> binderConsumer = - serviceManager->getService(CONSUMER_NAME); - mConsumer = interface_cast<IGraphicBufferConsumer>(binderConsumer); - EXPECT_TRUE(mConsumer != nullptr); - sp<MockConsumer> mc(new MockConsumer); - ASSERT_EQ(OK, mConsumer->consumerConnect(mc, false)); IGraphicBufferProducer::QueueBufferOutput output; ASSERT_EQ(OK, mProducer->connect(nullptr, NATIVE_WINDOW_API_CPU, false, &output)); @@ -163,14 +205,9 @@ TEST_F(BufferQueueTest, DISABLED_BufferQueueInAnotherProcess) { NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE); ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); - BufferItem item; - ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); - - uint32_t* dataOut; - ASSERT_EQ(OK, item.mGraphicBuffer->lock(GraphicBuffer::USAGE_SW_READ_OFTEN, - reinterpret_cast<void**>(&dataOut))); - ASSERT_EQ(*dataOut, TEST_DATA); - ASSERT_EQ(OK, item.mGraphicBuffer->unlock()); + bool isOk; + read(readfd, &isOk, sizeof(bool)); + ASSERT_TRUE(isOk); } TEST_F(BufferQueueTest, GetMaxBufferCountInQueueBufferOutput_Succeeds) { diff --git a/libs/gui/tests/Choreographer_test.cpp b/libs/gui/tests/Choreographer_test.cpp index 8db48d2eb0..314dea62d4 100644 --- a/libs/gui/tests/Choreographer_test.cpp +++ b/libs/gui/tests/Choreographer_test.cpp @@ -50,7 +50,7 @@ static void vsyncCallback(const AChoreographerFrameCallbackData* callbackData, v TEST_F(ChoreographerTest, InputCallbackBeforeAnimation) { sp<Looper> looper = Looper::prepare(0); - Choreographer* choreographer = Choreographer::getForThread(); + sp<Choreographer> choreographer = Choreographer::getForThread(); VsyncCallback animationCb; choreographer->postFrameCallbackDelayed(nullptr, nullptr, vsyncCallback, &animationCb, 0, CALLBACK_ANIMATION); @@ -83,4 +83,4 @@ TEST_F(ChoreographerTest, InputCallbackBeforeAnimation) { animationCb.frameTime.count()); } -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/libs/gui/tests/CpuConsumer_test.cpp b/libs/gui/tests/CpuConsumer_test.cpp index 9476930de3..482cfde6e9 100644 --- a/libs/gui/tests/CpuConsumer_test.cpp +++ b/libs/gui/tests/CpuConsumer_test.cpp @@ -66,10 +66,9 @@ protected: test_info->name(), params.width, params.height, params.maxLockedBuffers, params.format); - mCC = new CpuConsumer(params.maxLockedBuffers); + std::tie(mCC, mSTC) = CpuConsumer::create(params.maxLockedBuffers); String8 name("CpuConsumer_Under_Test"); mCC->setName(name); - mSTC = mCC->getSurface(); mANW = mSTC; } diff --git a/libs/gui/tests/DisconnectWaiter.h b/libs/gui/tests/DisconnectWaiter.h index 6e6915b299..3d5f63375d 100644 --- a/libs/gui/tests/DisconnectWaiter.h +++ b/libs/gui/tests/DisconnectWaiter.h @@ -29,7 +29,7 @@ namespace android { // no way to forward the events. This DisconnectWaiter will not let the // disconnect finish until finishDisconnect() is called. It will // also block until a disconnect is called -class DisconnectWaiter : public BnConsumerListener { +class DisconnectWaiter : public IConsumerListener { public: DisconnectWaiter () : mWaitForDisconnect(false), diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp index 06f00a4d74..cf05fd4ba5 100644 --- a/libs/gui/tests/EndToEndNativeInputTest.cpp +++ b/libs/gui/tests/EndToEndNativeInputTest.cpp @@ -85,6 +85,7 @@ sp<IInputFlinger> getInputFlinger() { // We use the top 10 layers as a way to haphazardly place ourselves above anything else. static const int LAYER_BASE = INT32_MAX - 10; static constexpr std::chrono::nanoseconds DISPATCHING_TIMEOUT = 5s; +static constexpr float EPSILON = MotionEvent::ROUNDING_PRECISION; class SynchronousWindowInfosReportedListener : public gui::BnWindowInfosReportedListener { public: @@ -203,8 +204,8 @@ public: ASSERT_EQ(InputEventType::MOTION, ev->getType()); MotionEvent* mev = static_cast<MotionEvent*>(ev); EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, mev->getAction()); - EXPECT_EQ(x, mev->getX(0)); - EXPECT_EQ(y, mev->getY(0)); + EXPECT_NEAR(x, mev->getX(0), EPSILON); + EXPECT_NEAR(y, mev->getY(0), EPSILON); EXPECT_EQ(0, mev->getFlags() & VERIFIED_MOTION_EVENT_FLAGS); ev = consumeEvent(); @@ -1230,7 +1231,7 @@ public: consumer->setConsumerName(String8("Virtual disp consumer (MultiDisplayTests)")); consumer->setDefaultBufferSize(width, height); - class StubConsumerListener : public BnConsumerListener { + class StubConsumerListener : public IConsumerListener { virtual void onFrameAvailable(const BufferItem&) override {} virtual void onBuffersReleased() override {} virtual void onSidebandStreamChanged() override {} diff --git a/libs/gui/tests/Malicious.cpp b/libs/gui/tests/Malicious.cpp index 376420c42b..cc16383f39 100644 --- a/libs/gui/tests/Malicious.cpp +++ b/libs/gui/tests/Malicious.cpp @@ -129,7 +129,7 @@ private: int32_t mExpectedSlot = 0; }; -class FakeListener : public BnConsumerListener { +class FakeListener : public IConsumerListener { public: void onFrameAvailable(const BufferItem&) override {} void onBuffersReleased() override {} diff --git a/libs/gui/tests/MockConsumer.h b/libs/gui/tests/MockConsumer.h index 4a6c51c17d..2e8bc63fb7 100644 --- a/libs/gui/tests/MockConsumer.h +++ b/libs/gui/tests/MockConsumer.h @@ -18,7 +18,7 @@ namespace android { -struct MockConsumer : public BnConsumerListener { +struct MockConsumer : public IConsumerListener { void onFrameAvailable(const BufferItem& /* item */) override {} void onBuffersReleased() override {} void onSidebandStreamChanged() override {} diff --git a/libs/gui/tests/MultiTextureConsumer_test.cpp b/libs/gui/tests/MultiTextureConsumer_test.cpp index 2428bb3110..7ae6f40f8c 100644 --- a/libs/gui/tests/MultiTextureConsumer_test.cpp +++ b/libs/gui/tests/MultiTextureConsumer_test.cpp @@ -34,8 +34,8 @@ protected: virtual void SetUp() { GLTest::SetUp(); - mGlConsumer = new GLConsumer(TEX_ID, GLConsumer::TEXTURE_EXTERNAL, true, false); - mSurface = mGlConsumer->getSurface(); + std::tie(mGlConsumer, mSurface) = + GLConsumer::create(TEX_ID, GLConsumer::TEXTURE_EXTERNAL, true, false); mANW = mSurface.get(); } diff --git a/libs/gui/tests/RegionSampling_test.cpp b/libs/gui/tests/RegionSampling_test.cpp index a0d8c53385..c35efe28a3 100644 --- a/libs/gui/tests/RegionSampling_test.cpp +++ b/libs/gui/tests/RegionSampling_test.cpp @@ -40,7 +40,7 @@ struct ChoreographerSync { std::unique_lock<decltype(mutex_)> lk(mutex_); auto check_event = [](auto const& ev) -> bool { - return ev.header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC; + return ev.header.type == DisplayEventType::DISPLAY_EVENT_VSYNC; }; DisplayEventReceiver::Event ev_; int evs = receiver_.getEvents(&ev_, 1); diff --git a/libs/gui/tests/StreamSplitter_test.cpp b/libs/gui/tests/StreamSplitter_test.cpp index 1c439cdb45..9570a369bd 100644 --- a/libs/gui/tests/StreamSplitter_test.cpp +++ b/libs/gui/tests/StreamSplitter_test.cpp @@ -32,7 +32,7 @@ namespace android { class StreamSplitterTest : public ::testing::Test {}; -struct FakeListener : public BnConsumerListener { +struct FakeListener : public IConsumerListener { virtual void onFrameAvailable(const BufferItem& /* item */) {} virtual void onBuffersReleased() {} virtual void onSidebandStreamChanged() {} diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp index 59d05b673c..d3434ea288 100644 --- a/libs/gui/tests/SurfaceTextureClient_test.cpp +++ b/libs/gui/tests/SurfaceTextureClient_test.cpp @@ -40,8 +40,7 @@ protected: } virtual void SetUp() { - mST = new GLConsumer(123, GLConsumer::TEXTURE_EXTERNAL, true, false); - mSTC = mST->getSurface(); + std::tie(mST, mSTC) = GLConsumer::create(123, GLConsumer::TEXTURE_EXTERNAL, true, false); mANW = mSTC; // We need a valid GL context so we can test updateTexImage() @@ -727,8 +726,7 @@ protected: ASSERT_NE(EGL_NO_CONTEXT, mEglContext); for (int i = 0; i < NUM_SURFACE_TEXTURES; i++) { - sp<GLConsumer> st(new GLConsumer(i, GLConsumer::TEXTURE_EXTERNAL, true, false)); - sp<Surface> stc = st->getSurface(); + auto [st, stc] = GLConsumer::create(i, GLConsumer::TEXTURE_EXTERNAL, true, false); mEglSurfaces[i] = eglCreateWindowSurface(mEglDisplay, myConfig, static_cast<ANativeWindow*>(stc.get()), nullptr); ASSERT_EQ(EGL_SUCCESS, eglGetError()); diff --git a/libs/gui/tests/SurfaceTextureGL.h b/libs/gui/tests/SurfaceTextureGL.h index 1309635afd..eb31cd1be7 100644 --- a/libs/gui/tests/SurfaceTextureGL.h +++ b/libs/gui/tests/SurfaceTextureGL.h @@ -38,8 +38,7 @@ protected: void SetUp() { GLTest::SetUp(); - mST = new GLConsumer(TEX_ID, GLConsumer::TEXTURE_EXTERNAL, true, false); - mSTC = mST->getSurface(); + std::tie(mST, mSTC) = GLConsumer::create(TEX_ID, GLConsumer::TEXTURE_EXTERNAL, true, false); mANW = mSTC; ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), TEST_PRODUCER_USAGE_BITS)); mTextureRenderer = new TextureRenderer(TEX_ID, mST); diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index 646e30e5a8..c4dcba856a 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -294,9 +294,7 @@ TEST_F(SurfaceTest, LayerCountIsOne) { TEST_F(SurfaceTest, QueryConsumerUsage) { const int TEST_USAGE_FLAGS = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER; - sp<BufferItemConsumer> c = new BufferItemConsumer(TEST_USAGE_FLAGS); - - sp<Surface> s = c->getSurface(); + auto [c, s] = BufferItemConsumer::create(TEST_USAGE_FLAGS); sp<ANativeWindow> anw(s); int flags = -1; @@ -309,10 +307,8 @@ TEST_F(SurfaceTest, QueryConsumerUsage) { TEST_F(SurfaceTest, QueryDefaultBuffersDataSpace) { const android_dataspace TEST_DATASPACE = HAL_DATASPACE_V0_SRGB; - sp<CpuConsumer> cpuConsumer = new CpuConsumer(1); + auto [cpuConsumer, s] = CpuConsumer::create(1); cpuConsumer->setDefaultBufferDataSpace(TEST_DATASPACE); - - sp<Surface> s = cpuConsumer->getSurface(); sp<ANativeWindow> anw(s); android_dataspace dataSpace; @@ -325,8 +321,7 @@ TEST_F(SurfaceTest, QueryDefaultBuffersDataSpace) { } TEST_F(SurfaceTest, SettingGenerationNumber) { - sp<CpuConsumer> cpuConsumer = new CpuConsumer(1); - sp<Surface> surface = cpuConsumer->getSurface(); + auto [cpuConsumer, surface] = CpuConsumer::create(1); sp<ANativeWindow> window(surface); // Allocate a buffer with a generation number of 0 @@ -603,7 +598,7 @@ TEST_F(SurfaceTest, TestGetLastDequeueStartTime) { ASSERT_GE(after, lastDequeueTime); } -class FakeConsumer : public BnConsumerListener { +class FakeConsumer : public IConsumerListener { public: void onFrameAvailable(const BufferItem& /*item*/) override {} void onBuffersReleased() override {} @@ -653,16 +648,7 @@ public: mSupportsPresent = supportsPresent; } - status_t setTransactionState( - const FrameTimelineInfo& /*frameTimelineInfo*/, Vector<ComposerState>& /*state*/, - Vector<DisplayState>& /*displays*/, uint32_t /*flags*/, - const sp<IBinder>& /*applyToken*/, InputWindowCommands /*inputWindowCommands*/, - int64_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/, - const std::vector<client_cache_t>& /*cachedBuffer*/, bool /*hasListenerCallbacks*/, - const std::vector<ListenerCallbacks>& /*listenerCallbacks*/, uint64_t /*transactionId*/, - const std::vector<uint64_t>& /*mergedTransactionIds*/) override { - return NO_ERROR; - } + status_t setTransactionState(TransactionState&&) override { return NO_ERROR; } protected: IBinder* onAsBinder() override { return nullptr; } @@ -692,10 +678,11 @@ public: return binder::Status::ok(); } - binder::Status createVirtualDisplay(const std::string& /*displayName*/, bool /*isSecure*/, - const std::string& /*uniqueId*/, - float /*requestedRefreshRate*/, - sp<IBinder>* /*outDisplay*/) override { + binder::Status createVirtualDisplay( + const std::string& /*displayName*/, bool /*isSecure*/, + gui::ISurfaceComposer::OptimizationPolicy /*optimizationPolicy*/, + const std::string& /*uniqueId*/, float /*requestedRefreshRate*/, + sp<IBinder>* /*outDisplay*/) override { return binder::Status::ok(); } @@ -2180,8 +2167,7 @@ TEST_F(SurfaceTest, BatchOperations) { const int BUFFER_COUNT = 16; const int BATCH_SIZE = 8; - sp<CpuConsumer> cpuConsumer = new CpuConsumer(1); - sp<Surface> surface = cpuConsumer->getSurface(); + auto [cpuConsumer, surface] = CpuConsumer::create(1); sp<ANativeWindow> window(surface); sp<StubSurfaceListener> listener = new StubSurfaceListener(); @@ -2229,8 +2215,7 @@ TEST_F(SurfaceTest, BatchIllegalOperations) { const int BUFFER_COUNT = 16; const int BATCH_SIZE = 8; - sp<CpuConsumer> cpuConsumer = new CpuConsumer(1); - sp<Surface> surface = cpuConsumer->getSurface(); + auto [cpuConsumer, surface] = CpuConsumer::create(1); sp<ANativeWindow> window(surface); sp<StubSurfaceListener> listener = new StubSurfaceListener(); @@ -2252,6 +2237,52 @@ TEST_F(SurfaceTest, BatchIllegalOperations) { ASSERT_EQ(NO_ERROR, surface->disconnect(NATIVE_WINDOW_API_CPU)); } +TEST_F(SurfaceTest, setMaxDequeuedBufferCount_setMaxAcquiredBufferCount_allocations) { + // + // Set up the consumer and producer--nothing fancy. + // + auto [consumer, surface] = + BufferItemConsumer::create(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER); + sp<SurfaceListener> surfaceListener = sp<StubSurfaceListener>::make(); + surface->connect(NATIVE_WINDOW_API_CPU, surfaceListener); + sp<GraphicBuffer> buffer; + sp<Fence> fence; + + // + // These values are independent. The consumer can dequeue 3 and the consumer can acquire 3 at + // the same time. + // + ASSERT_EQ(OK, consumer->setMaxAcquiredBufferCount(3)); + ASSERT_EQ(OK, surface->setMaxDequeuedBufferCount(3)); + + // + // Take all three buffers out of the queue--a fourth can't be retrieved. Then queue them. + // + std::vector<Surface::BatchBuffer> dequeuedBuffers(3); + EXPECT_EQ(OK, surface->dequeueBuffers(&dequeuedBuffers)); + if (::com::android::graphics::libgui::flags::bq_always_use_max_dequeued_buffer_count()) { + EXPECT_EQ(INVALID_OPERATION, surface->dequeueBuffer(&buffer, &fence)); + } + + for (auto& batchBuffer : dequeuedBuffers) { + EXPECT_EQ(OK, + surface->queueBuffer(GraphicBuffer::from(batchBuffer.buffer), + sp<Fence>::make(batchBuffer.fenceFd))); + } + dequeuedBuffers.assign(3, {}); + + // + // Acquire all three, then we should be able to dequeue 3 more. + // + std::vector<BufferItem> acquiredBuffers(3); + for (auto& bufferItem : acquiredBuffers) { + EXPECT_EQ(OK, consumer->acquireBuffer(&bufferItem, 0)); + } + + EXPECT_EQ(OK, surface->dequeueBuffers(&dequeuedBuffers)); + EXPECT_EQ(INVALID_OPERATION, surface->dequeueBuffer(&buffer, &fence)); +} + #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) TEST_F(SurfaceTest, PlatformBufferMethods) { @@ -2377,8 +2408,7 @@ TEST_F(SurfaceTest, QueueAcquireReleaseDequeue_CalledInStack_DoesNotDeadlock) { sp<IGraphicBufferConsumer> bqConsumer; BufferQueue::createBufferQueue(&bqProducer, &bqConsumer); - sp<BufferItemConsumer> consumer = sp<BufferItemConsumer>::make(bqConsumer, 3); - sp<Surface> surface = sp<Surface>::make(bqProducer); + auto [consumer, surface] = BufferItemConsumer::create(3); sp<ImmediateReleaseConsumerListener> consumerListener = sp<ImmediateReleaseConsumerListener>::make(consumer); consumer->setFrameAvailableListener(consumerListener); diff --git a/libs/gui/tests/TransactionState_test.cpp b/libs/gui/tests/TransactionState_test.cpp new file mode 100644 index 0000000000..179b264dbf --- /dev/null +++ b/libs/gui/tests/TransactionState_test.cpp @@ -0,0 +1,284 @@ +/* + * Copyright (C) 2025 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 <gmock/gmock.h> + +#include <gtest/gtest.h> +#include <unordered_map> +#include "android/gui/FocusRequest.h" +#include "binder/Binder.h" +#include "binder/Parcel.h" +#include "gtest/gtest.h" +#include "gui/LayerState.h" +#include "gui/WindowInfo.h" + +#include "gui/TransactionState.h" + +namespace android { + +void sprintf(std::string& out, const char* format, ...) { + va_list arg_list; + va_start(arg_list, format); + + int len = vsnprintf(nullptr, 0, format, arg_list); + if (len < 0) { + va_end(arg_list); + } + std::string line(len, '\0'); + int written = vsnprintf(line.data(), len + 1, format, arg_list); + if (written != len) { + va_end(arg_list); + } + line.pop_back(); + out += line; + va_end(arg_list); +} + +constexpr std::string dump_struct(auto& x) { + std::string s; +#if __has_builtin(__builtin_dump_struct) + __builtin_dump_struct(&x, sprintf, s); +#else + (void)x; +#endif + return s; +} + +void PrintTo(const TransactionState& state, ::std::ostream* os) { + *os << dump_struct(state); + *os << state.mFrameTimelineInfo.toString(); + for (auto mergedId : state.mMergedTransactionIds) { + *os << mergedId << ","; + } +} + +void PrintTo(const ComposerState& state, ::std::ostream* os) { + *os << dump_struct(state.state); + *os << state.state.getWindowInfo(); +} + +// In case EXPECT_EQ fails, this function is useful to pinpoint exactly which +// field did not compare ==. +void Compare(const TransactionState& s1, const TransactionState& s2) { + EXPECT_EQ(s1.mId, s2.mId); + EXPECT_EQ(s1.mMergedTransactionIds, s2.mMergedTransactionIds); + EXPECT_EQ(s1.mFlags, s2.mFlags); + EXPECT_EQ(s1.mFrameTimelineInfo, s2.mFrameTimelineInfo); + EXPECT_EQ(s1.mDesiredPresentTime, s2.mDesiredPresentTime); + EXPECT_EQ(s1.mIsAutoTimestamp, s2.mIsAutoTimestamp); + EXPECT_EQ(s1.mApplyToken, s2.mApplyToken); + EXPECT_EQ(s1.mMayContainBuffer, s2.mMayContainBuffer); + EXPECT_EQ(s1.mLogCallPoints, s2.mLogCallPoints); + EXPECT_EQ(s1.mDisplayStates.size(), s2.mDisplayStates.size()); + EXPECT_THAT(s1.mDisplayStates, ::testing::ContainerEq(s2.mDisplayStates)); + EXPECT_EQ(s1.mComposerStates.size(), s2.mComposerStates.size()); + EXPECT_EQ(s1.mComposerStates, s2.mComposerStates); + EXPECT_EQ(s1.mInputWindowCommands, s2.mInputWindowCommands); + EXPECT_EQ(s1.mUncacheBuffers, s2.mUncacheBuffers); + EXPECT_EQ(s1.mHasListenerCallbacks, s2.mHasListenerCallbacks); + EXPECT_EQ(s1.mListenerCallbacks.size(), s2.mListenerCallbacks.size()); + EXPECT_EQ(s1.mListenerCallbacks, s2.mListenerCallbacks); +} + +std::unique_ptr<std::unordered_map<int, sp<BBinder>>> createTokenMap(size_t maxSize) { + auto result = std::make_unique<std::unordered_map<int, sp<BBinder>>>(); + for (size_t i = 0; i < maxSize; ++i) { + result->emplace(i, sp<BBinder>::make()); + } + return result; +} + +constexpr size_t kMaxComposerStates = 2; +ComposerState createComposerStateForTest(size_t i) { + static const auto* const sLayerHandle = createTokenMap(kMaxComposerStates).release(); + + ComposerState state; + state.state.what = layer_state_t::eFlagsChanged; + state.state.surface = sLayerHandle->at(i); + state.state.layerId = i; + state.state.flags = 20 * i; + return state; +} + +constexpr size_t kMaxDisplayStates = 5; +DisplayState createDisplayStateForTest(size_t i) { + static const auto* const sDisplayTokens = createTokenMap(kMaxDisplayStates).release(); + + DisplayState displayState; + displayState.what = DisplayState::eFlagsChanged; + displayState.token = sDisplayTokens->at(i); + displayState.flags = 20 * i; + return displayState; +} + +TransactionState createTransactionStateForTest() { + static sp<BBinder> sApplyToken = sp<BBinder>::make(); + + TransactionState state; + state.mId = 123; + state.mMergedTransactionIds.push_back(15); + state.mMergedTransactionIds.push_back(0); + state.mFrameTimelineInfo.vsyncId = 14; + state.mDesiredPresentTime = 11; + state.mIsAutoTimestamp = true; + state.mApplyToken = sApplyToken; + for (size_t i = 0; i < kMaxDisplayStates; i++) { + state.mDisplayStates.push_back(createDisplayStateForTest(i)); + } + for (size_t i = 0; i < kMaxComposerStates; i++) { + state.mComposerStates.push_back(createComposerStateForTest(i)); + } + static const auto* const sFocusRequestTokens = createTokenMap(5).release(); + for (int i = 0; i < 5; i++) { + gui::FocusRequest request; + request.token = sFocusRequestTokens->at(i); + request.timestamp = i; + state.mInputWindowCommands.addFocusRequest(request); + } + static const auto* const sCacheToken = createTokenMap(5).release(); + for (int i = 0; i < 5; i++) { + client_cache_t cache; + cache.token = sCacheToken->at(i); + cache.id = i; + state.mUncacheBuffers.emplace_back(std::move(cache)); + } + static const auto* const sListenerCallbacks = []() { + auto* callbacks = new std::vector<ListenerCallbacks>(); + for (int i = 0; i < 5; i++) { + callbacks->emplace_back(sp<BBinder>::make(), + std::unordered_set<CallbackId, CallbackIdHash>{}); + } + return callbacks; + }(); + state.mHasListenerCallbacks = true; + state.mListenerCallbacks = *sListenerCallbacks; + return state; +} + +TransactionState createEmptyTransaction(uint64_t id) { + TransactionState state; + state.mId = id; + return state; +} + +TEST(TransactionStateTest, parcel) { + TransactionState state = createTransactionStateForTest(); + Parcel p; + state.writeToParcel(&p); + p.setDataPosition(0); + TransactionState parcelledState; + parcelledState.readFromParcel(&p); + EXPECT_EQ(state, parcelledState); +}; + +TEST(TransactionStateTest, parcelDisplayState) { + DisplayState state = createDisplayStateForTest(0); + Parcel p; + state.write(p); + p.setDataPosition(0); + DisplayState parcelledState; + parcelledState.read(p); + EXPECT_EQ(state, parcelledState); +}; + +TEST(TransactionStateTest, parcelLayerState) { + ComposerState state = createComposerStateForTest(0); + Parcel p; + state.write(p); + p.setDataPosition(0); + ComposerState parcelledState; + parcelledState.read(p); + EXPECT_EQ(state, parcelledState); +}; + +TEST(TransactionStateTest, parcelEmptyState) { + TransactionState state; + Parcel p; + state.writeToParcel(&p); + p.setDataPosition(0); + TransactionState parcelledState; + state.readFromParcel(&p); + EXPECT_EQ(state, parcelledState); +}; + +TEST(TransactionStateTest, mergeLayerState) { + ComposerState composerState = createComposerStateForTest(0); + ComposerState update; + update.state.surface = composerState.state.surface; + update.state.layerId = 0; + update.state.what = layer_state_t::eAlphaChanged; + update.state.color.a = .42; + composerState.state.merge(update.state); + + ComposerState expectedMergedState = createComposerStateForTest(0); + expectedMergedState.state.what |= layer_state_t::eAlphaChanged; + expectedMergedState.state.color.a = .42; + EXPECT_EQ(composerState, expectedMergedState); +}; + +TEST(TransactionStateTest, merge) { + // Setup. + static constexpr uint64_t kUpdateTransactionId = 200; + + TransactionState state = createTransactionStateForTest(); + + TransactionState update; + update.mId = kUpdateTransactionId; + { + ComposerState composerState; + composerState.state.surface = state.mComposerStates[0].state.surface; + composerState.state.what = layer_state_t::eAlphaChanged; + composerState.state.color.a = .42; + update.mComposerStates.push_back(composerState); + } + { + ComposerState composerState; + composerState.state.surface = state.mComposerStates[1].state.surface; + composerState.state.what = layer_state_t::eBufferChanged; + update.mComposerStates.push_back(composerState); + } + int32_t overrwiteLayerId = -1; + // Mutation. + state.merge(std::move(update), + [&overrwiteLayerId](layer_state_t ls) { overrwiteLayerId = ls.layerId; }); + // Assertions. + EXPECT_EQ(1, overrwiteLayerId); + EXPECT_EQ(update, createEmptyTransaction(update.getId())); + + TransactionState expectedMergedState = createTransactionStateForTest(); + expectedMergedState.mMergedTransactionIds + .insert(expectedMergedState.mMergedTransactionIds.begin(), kUpdateTransactionId); + expectedMergedState.mComposerStates.at(0).state.what |= layer_state_t::eAlphaChanged; + expectedMergedState.mComposerStates.at(0).state.color.a = .42; + expectedMergedState.mComposerStates.at(1).state.what |= layer_state_t::eBufferChanged; + auto inputCommands = expectedMergedState.mInputWindowCommands; + + // desired present time is not merged. + expectedMergedState.mDesiredPresentTime = state.mDesiredPresentTime; + + EXPECT_EQ(state.mComposerStates[0], expectedMergedState.mComposerStates[0]); + EXPECT_EQ(state.mInputWindowCommands, expectedMergedState.mInputWindowCommands); + EXPECT_EQ(state, expectedMergedState); +}; + +TEST(TransactionStateTest, clear) { + TransactionState state = createTransactionStateForTest(); + state.clear(); + TransactionState emptyState = createEmptyTransaction(state.getId()); + EXPECT_EQ(state, emptyState); +}; + +} // namespace android diff --git a/libs/gui/tests/WindowInfo_test.cpp b/libs/gui/tests/WindowInfo_test.cpp index ce22082a9f..e3f9a07b34 100644 --- a/libs/gui/tests/WindowInfo_test.cpp +++ b/libs/gui/tests/WindowInfo_test.cpp @@ -40,7 +40,18 @@ TEST(WindowInfo, ParcellingWithoutToken) { ASSERT_EQ(OK, i.writeToParcel(&p)); p.setDataPosition(0); i2.readFromParcel(&p); - ASSERT_TRUE(i2.token == nullptr); + ASSERT_EQ(i2.token, nullptr); +} + +TEST(WindowInfo, ParcellingWithoutCloneTransform) { + WindowInfo i, i2; + i.cloneLayerStackTransform.reset(); + + Parcel p; + ASSERT_EQ(OK, i.writeToParcel(&p)); + p.setDataPosition(0); + i2.readFromParcel(&p); + ASSERT_EQ(i2.cloneLayerStackTransform, std::nullopt); } TEST(WindowInfo, Parcelling) { @@ -71,6 +82,8 @@ TEST(WindowInfo, Parcelling) { i.applicationInfo.token = new BBinder(); i.applicationInfo.dispatchingTimeoutMillis = 0x12345678ABCD; i.focusTransferTarget = new BBinder(); + i.cloneLayerStackTransform = ui::Transform(); + i.cloneLayerStackTransform->set({5, -1, 100, 4, 0, 40, 0, 0, 1}); Parcel p; i.writeToParcel(&p); @@ -100,6 +113,7 @@ TEST(WindowInfo, Parcelling) { ASSERT_EQ(i.touchableRegionCropHandle, i2.touchableRegionCropHandle); ASSERT_EQ(i.applicationInfo, i2.applicationInfo); ASSERT_EQ(i.focusTransferTarget, i2.focusTransferTarget); + ASSERT_EQ(i.cloneLayerStackTransform, i2.cloneLayerStackTransform); } TEST(InputApplicationInfo, Parcelling) { diff --git a/libs/gui/tests/testserver/TestServer.cpp b/libs/gui/tests/testserver/TestServer.cpp index cd8824e355..17d1b4abe9 100644 --- a/libs/gui/tests/testserver/TestServer.cpp +++ b/libs/gui/tests/testserver/TestServer.cpp @@ -45,7 +45,7 @@ namespace android { namespace { -class TestConsumerListener : public BnConsumerListener { +class TestConsumerListener : public IConsumerListener { virtual void onFrameAvailable(const BufferItem&) override {} virtual void onBuffersReleased() override {} virtual void onSidebandStreamChanged() override {} diff --git a/libs/input/AccelerationCurve.cpp b/libs/input/AccelerationCurve.cpp index 0b47f3e6e7..1ed9794105 100644 --- a/libs/input/AccelerationCurve.cpp +++ b/libs/input/AccelerationCurve.cpp @@ -47,9 +47,9 @@ constexpr std::array<double, 15> kSensitivityFactors = {1, 2, 4, 6, 7, 8, // in faster pointer movements. // // The base gain is calculated using a linear mapping function that maps the -// sensitivity range [-7, 7] to a base gain range [0.5, 2.0]. +// sensitivity range [-7, 7] to a base gain range [1.0, 3.5]. double calculateBaseGain(int32_t sensitivity) { - return 0.5 + (sensitivity + 7) * (2.0 - 0.5) / (7 + 7); + return 1.0 + (sensitivity + 7) * (3.5 - 1.0) / (7 + 7); } } // namespace diff --git a/libs/input/Android.bp b/libs/input/Android.bp index 389fb7f6ab..52e0276cad 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -27,9 +27,9 @@ filegroup { name: "inputconstants_aidl", srcs: [ "android/os/IInputConstants.aidl", + "android/os/InputConfig.aidl", "android/os/InputEventInjectionResult.aidl", "android/os/InputEventInjectionSync.aidl", - "android/os/InputConfig.aidl", "android/os/MotionEventFlag.aidl", "android/os/PointerIconType.aidl", ], @@ -85,56 +85,63 @@ rust_bindgen { source_stem: "bindings", bindgen_flags: [ - "--verbose", - "--allowlist-var=AMOTION_EVENT_ACTION_CANCEL", - "--allowlist-var=AMOTION_EVENT_ACTION_UP", - "--allowlist-var=AMOTION_EVENT_ACTION_POINTER_DOWN", - "--allowlist-var=AMOTION_EVENT_ACTION_DOWN", - "--allowlist-var=AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT", - "--allowlist-var=MAX_POINTER_ID", - "--allowlist-var=AINPUT_SOURCE_CLASS_NONE", + "--allowlist-var=AINPUT_KEYBOARD_TYPE_ALPHABETIC", + "--allowlist-var=AINPUT_KEYBOARD_TYPE_NONE", + "--allowlist-var=AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC", + "--allowlist-var=AINPUT_SOURCE_BLUETOOTH_STYLUS", "--allowlist-var=AINPUT_SOURCE_CLASS_BUTTON", - "--allowlist-var=AINPUT_SOURCE_CLASS_POINTER", + "--allowlist-var=AINPUT_SOURCE_CLASS_JOYSTICK", "--allowlist-var=AINPUT_SOURCE_CLASS_NAVIGATION", + "--allowlist-var=AINPUT_SOURCE_CLASS_NONE", + "--allowlist-var=AINPUT_SOURCE_CLASS_POINTER", "--allowlist-var=AINPUT_SOURCE_CLASS_POSITION", - "--allowlist-var=AINPUT_SOURCE_CLASS_JOYSTICK", - "--allowlist-var=AINPUT_SOURCE_UNKNOWN", - "--allowlist-var=AINPUT_SOURCE_KEYBOARD", "--allowlist-var=AINPUT_SOURCE_DPAD", "--allowlist-var=AINPUT_SOURCE_GAMEPAD", - "--allowlist-var=AINPUT_SOURCE_TOUCHSCREEN", + "--allowlist-var=AINPUT_SOURCE_HDMI", + "--allowlist-var=AINPUT_SOURCE_JOYSTICK", + "--allowlist-var=AINPUT_SOURCE_KEYBOARD", "--allowlist-var=AINPUT_SOURCE_MOUSE", - "--allowlist-var=AINPUT_SOURCE_STYLUS", - "--allowlist-var=AINPUT_SOURCE_BLUETOOTH_STYLUS", - "--allowlist-var=AINPUT_SOURCE_TRACKBALL", "--allowlist-var=AINPUT_SOURCE_MOUSE_RELATIVE", + "--allowlist-var=AINPUT_SOURCE_ROTARY_ENCODER", + "--allowlist-var=AINPUT_SOURCE_SENSOR", + "--allowlist-var=AINPUT_SOURCE_STYLUS", "--allowlist-var=AINPUT_SOURCE_TOUCHPAD", + "--allowlist-var=AINPUT_SOURCE_TOUCHSCREEN", "--allowlist-var=AINPUT_SOURCE_TOUCH_NAVIGATION", - "--allowlist-var=AINPUT_SOURCE_JOYSTICK", - "--allowlist-var=AINPUT_SOURCE_HDMI", - "--allowlist-var=AINPUT_SOURCE_SENSOR", - "--allowlist-var=AINPUT_SOURCE_ROTARY_ENCODER", - "--allowlist-var=AINPUT_KEYBOARD_TYPE_NONE", - "--allowlist-var=AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC", - "--allowlist-var=AINPUT_KEYBOARD_TYPE_ALPHABETIC", - "--allowlist-var=AMETA_NONE", - "--allowlist-var=AMETA_ALT_ON", + "--allowlist-var=AINPUT_SOURCE_TRACKBALL", + "--allowlist-var=AINPUT_SOURCE_UNKNOWN", "--allowlist-var=AMETA_ALT_LEFT_ON", + "--allowlist-var=AMETA_ALT_ON", "--allowlist-var=AMETA_ALT_RIGHT_ON", - "--allowlist-var=AMETA_SHIFT_ON", - "--allowlist-var=AMETA_SHIFT_LEFT_ON", - "--allowlist-var=AMETA_SHIFT_RIGHT_ON", - "--allowlist-var=AMETA_SYM_ON", - "--allowlist-var=AMETA_FUNCTION_ON", - "--allowlist-var=AMETA_CTRL_ON", + "--allowlist-var=AMETA_CAPS_LOCK_ON", "--allowlist-var=AMETA_CTRL_LEFT_ON", + "--allowlist-var=AMETA_CTRL_ON", "--allowlist-var=AMETA_CTRL_RIGHT_ON", - "--allowlist-var=AMETA_META_ON", + "--allowlist-var=AMETA_FUNCTION_ON", "--allowlist-var=AMETA_META_LEFT_ON", + "--allowlist-var=AMETA_META_ON", "--allowlist-var=AMETA_META_RIGHT_ON", - "--allowlist-var=AMETA_CAPS_LOCK_ON", + "--allowlist-var=AMETA_NONE", "--allowlist-var=AMETA_NUM_LOCK_ON", "--allowlist-var=AMETA_SCROLL_LOCK_ON", + "--allowlist-var=AMETA_SHIFT_LEFT_ON", + "--allowlist-var=AMETA_SHIFT_ON", + "--allowlist-var=AMETA_SHIFT_RIGHT_ON", + "--allowlist-var=AMETA_SYM_ON", + "--allowlist-var=AMOTION_EVENT_ACTION_CANCEL", + "--allowlist-var=AMOTION_EVENT_ACTION_DOWN", + "--allowlist-var=AMOTION_EVENT_ACTION_POINTER_DOWN", + "--allowlist-var=AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT", + "--allowlist-var=AMOTION_EVENT_ACTION_UP", + "--allowlist-var=AMOTION_EVENT_BUTTON_BACK", + "--allowlist-var=AMOTION_EVENT_BUTTON_FORWARD", + "--allowlist-var=AMOTION_EVENT_BUTTON_PRIMARY", + "--allowlist-var=AMOTION_EVENT_BUTTON_SECONDARY", + "--allowlist-var=AMOTION_EVENT_BUTTON_STYLUS_PRIMARY", + "--allowlist-var=AMOTION_EVENT_BUTTON_STYLUS_SECONDARY", + "--allowlist-var=AMOTION_EVENT_BUTTON_TERTIARY", + "--allowlist-var=MAX_POINTER_ID", + "--verbose", ], static_libs: [ @@ -143,9 +150,9 @@ rust_bindgen { ], shared_libs: ["libc++"], header_libs: [ - "native_headers", - "jni_headers", "flatbuffer_headers", + "jni_headers", + "native_headers", ], } @@ -179,8 +186,8 @@ cc_library_static { host_supported: true, cflags: [ "-Wall", - "-Wextra", "-Werror", + "-Wextra", ], srcs: [ "FromRustToCpp.cpp", @@ -205,30 +212,32 @@ cc_library { cpp_std: "c++20", host_supported: true, cflags: [ + "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION", "-Wall", - "-Wextra", "-Werror", + "-Wextra", "-Wno-unused-parameter", - "-Wthread-safety", "-Wshadow", "-Wshadow-field-in-constructor-modified", "-Wshadow-uncaptured-local", - "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION", + "-Wthread-safety", ], srcs: [ "AccelerationCurve.cpp", "CoordinateFilter.cpp", + "DisplayTopologyGraph.cpp", "Input.cpp", "InputConsumer.cpp", "InputConsumerNoResampling.cpp", "InputDevice.cpp", "InputEventLabels.cpp", + "InputFlags.cpp", "InputTransport.cpp", "InputVerifier.cpp", - "Keyboard.cpp", "KeyCharacterMap.cpp", - "KeyboardClassifier.cpp", "KeyLayoutMap.cpp", + "Keyboard.cpp", + "KeyboardClassifier.cpp", "MotionPredictor.cpp", "MotionPredictorMetricsManager.cpp", "OneEuroFilter.cpp", @@ -262,13 +271,14 @@ cc_library { shared_libs: [ "android.companion.virtualdevice.flags-aconfig-cc", + "com.android.window.flags.window-aconfig_flags_c_lib", + "libPlatformProperties", "libaconfig_storage_read_api_cc", "libbase", "libbinder", "libbinder_ndk", "libcutils", "liblog", - "libPlatformProperties", "libtinyxml2", "libutils", "libz", // needed by libkernelconfigs @@ -287,15 +297,15 @@ cc_library { static_libs: [ "inputconstants-cpp", - "libui-types", - "libtflite_static", "libkernelconfigs", + "libtflite_static", + "libui-types", ], whole_static_libs: [ "com.android.input.flags-aconfig-cc", - "libinput_rust_ffi", "iinputflinger_aidl_lib_static", + "libinput_rust_ffi", ], export_static_lib_headers: [ @@ -310,8 +320,8 @@ cc_library { target: { android: { required: [ - "motion_predictor_model_prebuilt", "motion_predictor_model_config", + "motion_predictor_model_prebuilt", ], static_libs: [ "libstatslog_libinput", @@ -372,9 +382,9 @@ cc_defaults { cpp_std: "c++20", host_supported: true, shared_libs: [ - "libutils", "libbase", "liblog", + "libutils", ], } diff --git a/libs/input/DisplayTopologyGraph.cpp b/libs/input/DisplayTopologyGraph.cpp new file mode 100644 index 0000000000..934f2e8135 --- /dev/null +++ b/libs/input/DisplayTopologyGraph.cpp @@ -0,0 +1,144 @@ +/* + * Copyright 2025 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 "DisplayTopologyValidator" + +#include <android-base/logging.h> +#include <android-base/stringprintf.h> +#include <ftl/enum.h> +#include <input/DisplayTopologyGraph.h> +#include <input/PrintTools.h> +#include <ui/LogicalDisplayId.h> + +#include <algorithm> + +#define INDENT " " + +namespace android { + +namespace { + +DisplayTopologyPosition getOppositePosition(DisplayTopologyPosition position) { + switch (position) { + case DisplayTopologyPosition::LEFT: + return DisplayTopologyPosition::RIGHT; + case DisplayTopologyPosition::TOP: + return DisplayTopologyPosition::BOTTOM; + case DisplayTopologyPosition::RIGHT: + return DisplayTopologyPosition::LEFT; + case DisplayTopologyPosition::BOTTOM: + return DisplayTopologyPosition::TOP; + } +} + +bool validatePrimaryDisplay(const android::DisplayTopologyGraph& displayTopologyGraph) { + return displayTopologyGraph.primaryDisplayId != ui::LogicalDisplayId::INVALID && + displayTopologyGraph.graph.contains(displayTopologyGraph.primaryDisplayId); +} + +bool validateTopologyGraph(const android::DisplayTopologyGraph& displayTopologyGraph) { + for (const auto& [sourceDisplay, adjacentDisplays] : displayTopologyGraph.graph) { + for (const DisplayTopologyAdjacentDisplay& adjacentDisplay : adjacentDisplays) { + const auto adjacentGraphIt = displayTopologyGraph.graph.find(adjacentDisplay.displayId); + if (adjacentGraphIt == displayTopologyGraph.graph.end()) { + LOG(ERROR) << "Missing adjacent display in topology graph: " + << adjacentDisplay.displayId << " for source " << sourceDisplay; + return false; + } + const auto reverseEdgeIt = + std::find_if(adjacentGraphIt->second.begin(), adjacentGraphIt->second.end(), + [sourceDisplay](const DisplayTopologyAdjacentDisplay& + reverseAdjacentDisplay) { + return sourceDisplay == reverseAdjacentDisplay.displayId; + }); + if (reverseEdgeIt == adjacentGraphIt->second.end()) { + LOG(ERROR) << "Missing reverse edge in topology graph for: " << sourceDisplay + << " -> " << adjacentDisplay.displayId; + return false; + } + DisplayTopologyPosition expectedPosition = + getOppositePosition(adjacentDisplay.position); + if (reverseEdgeIt->position != expectedPosition) { + LOG(ERROR) << "Unexpected reverse edge for: " << sourceDisplay << " -> " + << adjacentDisplay.displayId + << " expected position: " << ftl::enum_string(expectedPosition) + << " actual " << ftl::enum_string(reverseEdgeIt->position); + return false; + } + if (reverseEdgeIt->offsetDp != -adjacentDisplay.offsetDp) { + LOG(ERROR) << "Unexpected reverse edge offset: " << sourceDisplay << " -> " + << adjacentDisplay.displayId + << " expected offset: " << -adjacentDisplay.offsetDp << " actual " + << reverseEdgeIt->offsetDp; + return false; + } + } + } + return true; +} + +bool validateDensities(const android::DisplayTopologyGraph& displayTopologyGraph) { + for (const auto& [sourceDisplay, adjacentDisplays] : displayTopologyGraph.graph) { + if (!displayTopologyGraph.displaysDensity.contains(sourceDisplay)) { + LOG(ERROR) << "Missing density value in topology graph for display: " << sourceDisplay; + return false; + } + } + return true; +} + +std::string logicalDisplayIdToString(const ui::LogicalDisplayId& displayId) { + return base::StringPrintf("displayId(%d)", displayId.val()); +} + +std::string adjacentDisplayToString(const DisplayTopologyAdjacentDisplay& adjacentDisplay) { + return adjacentDisplay.dump(); +} + +std::string adjacentDisplayVectorToString( + const std::vector<DisplayTopologyAdjacentDisplay>& adjacentDisplays) { + return dumpVector(adjacentDisplays, adjacentDisplayToString); +} + +} // namespace + +std::string DisplayTopologyAdjacentDisplay::dump() const { + std::string dump; + dump += base::StringPrintf("DisplayTopologyAdjacentDisplay: {displayId: %d, position: %s, " + "offsetDp: %f}", + displayId.val(), ftl::enum_string(position).c_str(), offsetDp); + return dump; +} + +bool DisplayTopologyGraph::isValid() const { + return validatePrimaryDisplay(*this) && validateTopologyGraph(*this) && + validateDensities(*this); +} + +std::string DisplayTopologyGraph::dump() const { + std::string dump; + dump += base::StringPrintf("PrimaryDisplayId: %d\n", primaryDisplayId.val()); + dump += base::StringPrintf("TopologyGraph:\n"); + dump += addLinePrefix(dumpMap(graph, logicalDisplayIdToString, adjacentDisplayVectorToString), + INDENT); + dump += "\n"; + dump += base::StringPrintf("DisplaysDensity:\n"); + dump += addLinePrefix(dumpMap(displaysDensity, logicalDisplayIdToString), INDENT); + dump += "\n"; + return dump; +} + +} // namespace android diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index 65a088eb6d..155ea000e3 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -284,6 +284,36 @@ bool isStylusEvent(uint32_t source, const std::vector<PointerProperties>& proper return false; } +bool isStylusHoverEvent(uint32_t source, const std::vector<PointerProperties>& properties, + int32_t action) { + return isStylusEvent(source, properties) && isHoverAction(action); +} + +bool isFromMouse(uint32_t source, ToolType toolType) { + return isFromSource(source, AINPUT_SOURCE_MOUSE) && toolType == ToolType::MOUSE; +} + +bool isFromTouchpad(uint32_t source, ToolType toolType) { + return isFromSource(source, AINPUT_SOURCE_MOUSE) && toolType == ToolType::FINGER; +} + +bool isFromDrawingTablet(uint32_t source, ToolType toolType) { + return isFromSource(source, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_STYLUS) && + isStylusToolType(toolType); +} + +bool isHoverAction(int32_t action) { + return action == AMOTION_EVENT_ACTION_HOVER_ENTER || + action == AMOTION_EVENT_ACTION_HOVER_MOVE || action == AMOTION_EVENT_ACTION_HOVER_EXIT; +} + +bool isMouseOrTouchpad(uint32_t sources) { + // Check if this is a mouse or touchpad, but not a drawing tablet. + return isFromSource(sources, AINPUT_SOURCE_MOUSE_RELATIVE) || + (isFromSource(sources, AINPUT_SOURCE_MOUSE) && + !isFromSource(sources, AINPUT_SOURCE_STYLUS)); +} + VerifiedKeyEvent verifiedKeyEventFromKeyEvent(const KeyEvent& event) { return {{VerifiedInputEvent::Type::KEY, event.getDeviceId(), event.getEventTime(), event.getSource(), event.getDisplayId()}, diff --git a/libs/input/InputConsumerNoResampling.cpp b/libs/input/InputConsumerNoResampling.cpp index cd8582182a..6087461f6c 100644 --- a/libs/input/InputConsumerNoResampling.cpp +++ b/libs/input/InputConsumerNoResampling.cpp @@ -18,6 +18,7 @@ #define ATRACE_TAG ATRACE_TAG_INPUT #include <inttypes.h> +#include <set> #include <android-base/logging.h> #include <android-base/properties.h> diff --git a/libs/input/InputFlags.cpp b/libs/input/InputFlags.cpp new file mode 100644 index 0000000000..6aa9ae6b16 --- /dev/null +++ b/libs/input/InputFlags.cpp @@ -0,0 +1,51 @@ +/* + * Copyright 2025 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/InputFlags.h> + +#include <android-base/logging.h> +#include <com_android_input_flags.h> +#include <com_android_window_flags.h> +#include <cutils/properties.h> + +#include <string> + +namespace android { + +bool InputFlags::connectedDisplaysCursorEnabled() { + if (!com::android::window::flags::enable_desktop_mode_through_dev_option()) { + return com::android::input::flags::connected_displays_cursor(); + } + static std::optional<bool> cachedDevOption; + if (!cachedDevOption.has_value()) { + char value[PROPERTY_VALUE_MAX]; + constexpr static auto sysprop_name = "persist.wm.debug.desktop_experience_devopts"; + const int devOptionEnabled = + property_get(sysprop_name, value, nullptr) > 0 ? std::atoi(value) : 0; + cachedDevOption = devOptionEnabled == 1; + } + if (cachedDevOption.value_or(false)) { + return true; + } + return com::android::input::flags::connected_displays_cursor(); +} + +bool InputFlags::connectedDisplaysCursorAndAssociatedDisplayCursorBugfixEnabled() { + return connectedDisplaysCursorEnabled() && + com::android::input::flags::connected_displays_associated_display_cursor_bugfix(); +} + +} // namespace android
\ No newline at end of file diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index 56ccaab9ad..d388d48e8d 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -651,9 +651,9 @@ status_t InputPublisher::publishMotionEvent( const status_t status = mChannel->sendMessage(&msg); if (status == OK && verifyEvents()) { - Result<void> result = - mInputVerifier.processMovement(deviceId, source, action, pointerCount, - pointerProperties, pointerCoords, flags); + Result<void> result = mInputVerifier.processMovement(deviceId, source, action, actionButton, + pointerCount, pointerProperties, + pointerCoords, flags, buttonState); if (!result.ok()) { LOG(ERROR) << "Bad stream: " << result.error(); return BAD_VALUE; diff --git a/libs/input/InputVerifier.cpp b/libs/input/InputVerifier.cpp index cec244539e..7811acefd0 100644 --- a/libs/input/InputVerifier.cpp +++ b/libs/input/InputVerifier.cpp @@ -17,6 +17,7 @@ #define LOG_TAG "InputVerifier" #include <android-base/logging.h> +#include <com_android_input_flags.h> #include <input/InputVerifier.h> #include "input_cxx_bridge.rs.h" @@ -26,17 +27,23 @@ using android::input::RustPointerProperties; using DeviceId = int32_t; +namespace input_flags = com::android::input::flags; + namespace android { // --- InputVerifier --- InputVerifier::InputVerifier(const std::string& name) - : mVerifier(android::input::verifier::create(rust::String::lossy(name))){}; + : mVerifier( + android::input::verifier::create(rust::String::lossy(name), + input_flags::enable_button_state_verification())) { +} Result<void> InputVerifier::processMovement(DeviceId deviceId, int32_t source, int32_t action, - uint32_t pointerCount, + int32_t actionButton, uint32_t pointerCount, const PointerProperties* pointerProperties, - const PointerCoords* pointerCoords, int32_t flags) { + const PointerCoords* pointerCoords, int32_t flags, + int32_t buttonState) { std::vector<RustPointerProperties> rpp; for (size_t i = 0; i < pointerCount; i++) { rpp.emplace_back(RustPointerProperties{.id = pointerProperties[i].id}); @@ -44,7 +51,9 @@ Result<void> InputVerifier::processMovement(DeviceId deviceId, int32_t source, i rust::Slice<const RustPointerProperties> properties{rpp.data(), rpp.size()}; rust::String errorMessage = android::input::verifier::process_movement(*mVerifier, deviceId, source, action, - properties, static_cast<uint32_t>(flags)); + actionButton, properties, + static_cast<uint32_t>(flags), + static_cast<uint32_t>(buttonState)); if (errorMessage.empty()) { return {}; } else { diff --git a/libs/input/android/os/IInputConstants.aidl b/libs/input/android/os/IInputConstants.aidl index 6ce3fba477..4b87dab01b 100644 --- a/libs/input/android/os/IInputConstants.aidl +++ b/libs/input/android/os/IInputConstants.aidl @@ -43,8 +43,9 @@ interface IInputConstants const int INVALID_INPUT_DEVICE_ID = -2; /** - * The input event was injected from accessibility. Used in policyFlags for input event - * injection. + * The input event was injected from some AccessibilityService, which may be either an + * Accessibility Tool OR a service using that API for purposes other than assisting users with + * disabilities. Used in policyFlags for input event injection. */ const int POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY = 0x20000; @@ -55,18 +56,33 @@ interface IInputConstants const int POLICY_FLAG_KEY_GESTURE_TRIGGERED = 0x40000; /** + * The input event was injected from an AccessibilityService with the + * AccessibilityServiceInfo#isAccessibilityTool property set to true. These services (known as + * "Accessibility Tools") are used to assist users with disabilities, so events from these + * services should be able to reach all Views including Views which set + * View#isAccessibilityDataSensitive to true. Used in policyFlags for input event injection. + */ + const int POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY_TOOL = 0x80000; + + /** * Common input event flag used for both motion and key events for a gesture or pointer being * canceled. */ const int INPUT_EVENT_FLAG_CANCELED = 0x20; /** - * Common input event flag used for both motion and key events, indicating that the event - * was generated or modified by accessibility service. + * Input event flag used for both motion and key events. + * See POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY for more information. */ const int INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT = 0x800; /** + * Input event flag used for motion events. + * See POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY_TOOL for more information. + */ + const int INPUT_EVENT_FLAG_INJECTED_FROM_ACCESSIBILITY_TOOL = 0x1000; + + /** * Common input event flag used for both motion and key events, indicating that the system has * detected this event may be inconsistent with the current event sequence or gesture, such as * when a pointer move event is sent but the pointer is not down. diff --git a/libs/input/android/os/InputConfig.aidl b/libs/input/android/os/InputConfig.aidl index da62e03821..e5f7b56561 100644 --- a/libs/input/android/os/InputConfig.aidl +++ b/libs/input/android/os/InputConfig.aidl @@ -57,16 +57,9 @@ enum InputConfig { NOT_TOUCHABLE = 1 << 3, /** - * Indicates that this window will not accept a touch event that is split between - * more than one window. When set: - * - If this window receives a DOWN event with the first pointer, all successive - * pointers that go down, regardless of their location on the screen, will be - * directed to this window; - * - If the DOWN event lands outside the touchable bounds of this window, no - * successive pointers that go down, regardless of their location on the screen, - * will be directed to this window. - */ - PREVENT_SPLITTING = 1 << 4, + * This flag is now deprecated and should not be used. + */ + DEPRECATED_PREVENT_SPLITTING = 1 << 4, /** * Indicates that this window shows the wallpaper behind it, so all touch events diff --git a/libs/input/android/os/MotionEventFlag.aidl b/libs/input/android/os/MotionEventFlag.aidl index 2093b0636a..6c9ccfbd04 100644 --- a/libs/input/android/os/MotionEventFlag.aidl +++ b/libs/input/android/os/MotionEventFlag.aidl @@ -118,13 +118,24 @@ enum MotionEventFlag { PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION = 0x100, /** - * The input event was generated or modified by accessibility service. - * Shared by both KeyEvent and MotionEvent flags, so this value should not overlap with either - * set of flags, including in input/Input.h and in android/input.h. + * The input event was injected from some AccessibilityService, which may be either an + * Accessibility Tool OR a service using that API for purposes other than assisting users with + * disabilities. Shared by both KeyEvent and MotionEvent flags, so this value should not + * overlap with either set of flags, including in input/Input.h and in android/input.h. */ IS_ACCESSIBILITY_EVENT = IInputConstants.INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT, /** + * The input event was injected from an AccessibilityService with the + * AccessibilityServiceInfo#isAccessibilityTool property set to true. These services (known as + * "Accessibility Tools") are used to assist users with disabilities, so events from these + * services should be able to reach all Views including Views which set + * View#isAccessibilityDataSensitive to true. Only used by MotionEvent flags. + */ + INJECTED_FROM_ACCESSIBILITY_TOOL = + IInputConstants.INPUT_EVENT_FLAG_INJECTED_FROM_ACCESSIBILITY_TOOL, + + /** * Private flag that indicates when the system has detected that this motion event * may be inconsistent with respect to the sequence of previously delivered motion events, * such as when a pointer move event is sent but the pointer is not down. diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig index 6cdd249b9e..983bbdee6e 100644 --- a/libs/input/input_flags.aconfig +++ b/libs/input/input_flags.aconfig @@ -16,10 +16,13 @@ flag { } flag { - name: "remove_input_channel_from_windowstate" + name: "enable_button_state_verification" namespace: "input" - description: "Do not store a copy of input channel inside WindowState." - bug: "323450804" + description: "Set to true to enable crashing whenever bad inbound events are going into InputDispatcher" + bug: "392870542" + metadata { + purpose: PURPOSE_BUGFIX + } } flag { @@ -37,9 +40,9 @@ flag { } flag { - name: "split_all_touches" + name: "deprecate_split_touch_apis" namespace: "input" - description: "Set FLAG_SPLIT_TOUCHES to true for all windows, regardless of what they specify. This is essentially deprecating this flag by forcefully enabling the split functionality" + description: "Deprecate all public APIs related to split touch because now all windows behave as if split touch is permanently enabled and there's no way for a window to disable split touch." bug: "239934827" } @@ -51,20 +54,6 @@ flag { } flag { - name: "report_palms_to_gestures_library" - namespace: "input" - description: "Report touches marked as palm by firmware to gestures library" - bug: "302505955" -} - -flag { - name: "enable_touchpad_typing_palm_rejection" - namespace: "input" - description: "Enabling additional touchpad palm rejection will disable the tap to click while the user is typing on a physical keyboard" - bug: "301055381" -} - -flag { name: "enable_v2_touchpad_typing_palm_rejection" namespace: "input" description: "In addition to touchpad palm rejection v1, v2 will also cancel ongoing move gestures while typing and add delay in re-enabling the tap to click." @@ -79,13 +68,6 @@ flag { } flag { - name: "enable_input_filter_rust_impl" - namespace: "input" - description: "Enable input filter rust implementation" - bug: "294546335" -} - -flag { name: "override_key_behavior_permission_apis" is_exported: true namespace: "input" @@ -131,13 +113,6 @@ flag { } flag { - name: "hide_pointer_indicators_for_secure_windows" - namespace: "input" - description: "Hide touch and pointer indicators if a secure window is present on display" - bug: "325252005" -} - -flag { name: "enable_keyboard_classifier" namespace: "input" description: "Keyboard classifier that classifies all keyboards into alphabetic or non-alphabetic" @@ -152,13 +127,6 @@ flag { } flag { - name: "include_relative_axis_values_for_captured_touchpads" - namespace: "input" - description: "Include AXIS_RELATIVE_X and AXIS_RELATIVE_Y values when reporting touches from captured touchpads." - bug: "330522990" -} - -flag { name: "enable_per_device_input_latency_metrics" namespace: "input" description: "Capture input latency metrics on a per device granular level using histograms." @@ -188,6 +156,16 @@ flag { } flag { + name: "disable_touch_input_mapper_pointer_usage" + namespace: "input" + description: "Disable the PointerUsage concept in TouchInputMapper since the old touchpad stack is no longer used." + bug: "281840344" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "keyboard_repeat_keys" namespace: "input" description: "Allow user to enable key repeats or configure timeout before key repeat and key repeat delay rates." @@ -224,3 +202,50 @@ flag { description: "Allow cursor to transition across multiple connected displays" bug: "362719483" } + +flag { + name: "connected_displays_associated_display_cursor_bugfix" + namespace: "lse_desktop_experience" + description: "Apply some rules to define associated display cursor behavior in connected displays" + bug: "396568321" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { + name: "use_cloned_screen_coordinates_as_raw" + namespace: "input" + description: "Use the cloned window's layer stack (screen) space as the raw coordinate space for input going to clones" + bug: "377846505" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { + name: "deprecate_uiautomation_input_injection" + namespace: "input" + description: "Deprecate UiAutomation#injectInputEvent and UiAutomation#injectInputEventToInputFilter test/hidden APIs" + bug: "277261245" +} + +flag { + name: "prevent_merging_input_pointer_devices" + namespace: "desktop_input" + description: "Prevent merging input sub-devices that provide pointer input streams" + bug: "389689566" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { + name: "enable_display_topology_validation" + namespace: "input" + description: "Set to true to enable display topology validation" + bug: "401219231" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/libs/input/rust/Android.bp b/libs/input/rust/Android.bp index 63853f77fa..fae9074c4d 100644 --- a/libs/input/rust/Android.bp +++ b/libs/input/rust/Android.bp @@ -18,12 +18,12 @@ rust_defaults { srcs: ["lib.rs"], host_supported: true, rustlibs: [ + "inputconstants-rust", "libbitflags", "libcxx", "libinput_bindgen", - "liblogger", "liblog_rust", - "inputconstants-rust", + "liblogger", "libserde", "libserde_json", ], diff --git a/libs/input/rust/input.rs b/libs/input/rust/input.rs index 90f509d97f..35ba04f5b6 100644 --- a/libs/input/rust/input.rs +++ b/libs/input/rust/input.rs @@ -50,7 +50,7 @@ pub enum SourceClass { bitflags! { /// Source of the input device or input events. - #[derive(Debug, PartialEq)] + #[derive(Clone, Copy, Debug, PartialEq)] pub struct Source: u32 { // Constants from SourceClass, added here for compatibility reasons /// SourceClass::Button @@ -101,6 +101,7 @@ bitflags! { /// A rust enum representation of a MotionEvent action. #[repr(u32)] +#[derive(Clone, Copy, Eq, PartialEq)] pub enum MotionAction { /// ACTION_DOWN Down = input_bindgen::AMOTION_EVENT_ACTION_DOWN, @@ -131,9 +132,15 @@ pub enum MotionAction { /// ACTION_SCROLL Scroll = input_bindgen::AMOTION_EVENT_ACTION_SCROLL, /// ACTION_BUTTON_PRESS - ButtonPress = input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS, + ButtonPress { + /// The button being pressed. + action_button: MotionButton, + } = input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS, /// ACTION_BUTTON_RELEASE - ButtonRelease = input_bindgen::AMOTION_EVENT_ACTION_BUTTON_RELEASE, + ButtonRelease { + /// The button being released. + action_button: MotionButton, + } = input_bindgen::AMOTION_EVENT_ACTION_BUTTON_RELEASE, } impl fmt::Display for MotionAction { @@ -152,14 +159,20 @@ impl fmt::Display for MotionAction { MotionAction::Scroll => write!(f, "SCROLL"), MotionAction::HoverEnter => write!(f, "HOVER_ENTER"), MotionAction::HoverExit => write!(f, "HOVER_EXIT"), - MotionAction::ButtonPress => write!(f, "BUTTON_PRESS"), - MotionAction::ButtonRelease => write!(f, "BUTTON_RELEASE"), + MotionAction::ButtonPress { action_button } => { + write!(f, "BUTTON_PRESS({action_button:?})") + } + MotionAction::ButtonRelease { action_button } => { + write!(f, "BUTTON_RELEASE({action_button:?})") + } } } } -impl From<u32> for MotionAction { - fn from(action: u32) -> Self { +impl MotionAction { + /// Creates a [`MotionAction`] from an `AMOTION_EVENT_ACTION_…` constant and an action button + /// (which should be empty for all actions except `BUTTON_PRESS` and `…_RELEASE`). + pub fn from_code(action: u32, action_button: MotionButton) -> Self { let (action_masked, action_index) = MotionAction::breakdown_action(action); match action_masked { input_bindgen::AMOTION_EVENT_ACTION_DOWN => MotionAction::Down, @@ -177,14 +190,16 @@ impl From<u32> for MotionAction { input_bindgen::AMOTION_EVENT_ACTION_HOVER_MOVE => MotionAction::HoverMove, input_bindgen::AMOTION_EVENT_ACTION_HOVER_EXIT => MotionAction::HoverExit, input_bindgen::AMOTION_EVENT_ACTION_SCROLL => MotionAction::Scroll, - input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS => MotionAction::ButtonPress, - input_bindgen::AMOTION_EVENT_ACTION_BUTTON_RELEASE => MotionAction::ButtonRelease, + input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS => { + MotionAction::ButtonPress { action_button } + } + input_bindgen::AMOTION_EVENT_ACTION_BUTTON_RELEASE => { + MotionAction::ButtonRelease { action_button } + } _ => panic!("Unknown action: {}", action), } } -} -impl MotionAction { fn breakdown_action(action: u32) -> (u32, usize) { let action_masked = action & input_bindgen::AMOTION_EVENT_ACTION_MASK; let index = (action & input_bindgen::AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) @@ -194,10 +209,31 @@ impl MotionAction { } bitflags! { + /// MotionEvent buttons. + #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] + pub struct MotionButton: u32 { + /// Primary button (e.g. the left mouse button) + const Primary = input_bindgen::AMOTION_EVENT_BUTTON_PRIMARY; + /// Secondary button (e.g. the right mouse button) + const Secondary = input_bindgen::AMOTION_EVENT_BUTTON_SECONDARY; + /// Tertiary button (e.g. the middle mouse button) + const Tertiary = input_bindgen::AMOTION_EVENT_BUTTON_TERTIARY; + /// Back button + const Back = input_bindgen::AMOTION_EVENT_BUTTON_BACK; + /// Forward button + const Forward = input_bindgen::AMOTION_EVENT_BUTTON_FORWARD; + /// Primary stylus button + const StylusPrimary = input_bindgen::AMOTION_EVENT_BUTTON_STYLUS_PRIMARY; + /// Secondary stylus button + const StylusSecondary = input_bindgen::AMOTION_EVENT_BUTTON_STYLUS_SECONDARY; + } +} + +bitflags! { /// MotionEvent flags. /// The source of truth for the flag definitions are the MotionEventFlag AIDL enum. /// The flag values are redefined here as a bitflags API. - #[derive(Debug)] + #[derive(Clone, Copy, Debug)] pub struct MotionFlags: u32 { /// FLAG_WINDOW_IS_OBSCURED const WINDOW_IS_OBSCURED = MotionEventFlag::WINDOW_IS_OBSCURED.0 as u32; @@ -219,6 +255,9 @@ bitflags! { MotionEventFlag::PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION.0 as u32; /// FLAG_IS_ACCESSIBILITY_EVENT const IS_ACCESSIBILITY_EVENT = MotionEventFlag::IS_ACCESSIBILITY_EVENT.0 as u32; + /// FLAG_INJECTED_FROM_ACCESSIBILITY_TOOL + const INJECTED_FROM_ACCESSIBILITY_TOOL = + MotionEventFlag::INJECTED_FROM_ACCESSIBILITY_TOOL.0 as u32; /// FLAG_TAINTED const TAINTED = MotionEventFlag::TAINTED.0 as u32; /// FLAG_TARGET_ACCESSIBILITY_FOCUS diff --git a/libs/input/rust/input_verifier.rs b/libs/input/rust/input_verifier.rs index b1d7760682..f87dd413c6 100644 --- a/libs/input/rust/input_verifier.rs +++ b/libs/input/rust/input_verifier.rs @@ -17,21 +17,55 @@ //! Contains the InputVerifier, used to validate a stream of input events. use crate::ffi::RustPointerProperties; -use crate::input::{DeviceId, MotionAction, MotionFlags, Source, SourceClass}; +use crate::input::{DeviceId, MotionAction, MotionButton, MotionFlags, Source, SourceClass}; use log::info; use std::collections::HashMap; use std::collections::HashSet; -fn verify_event( - action: MotionAction, - pointer_properties: &[RustPointerProperties], - flags: &MotionFlags, -) -> Result<(), String> { - let pointer_count = pointer_properties.len(); +/// Represents a movement or state change event from a pointer device. The Rust equivalent of the +/// C++ NotifyMotionArgs struct. +#[derive(Clone, Copy)] +pub struct NotifyMotionArgs<'a> { + /// The ID of the device that emitted the event. + pub device_id: DeviceId, + + /// The type of device that emitted the event. + pub source: Source, + + /// The type of event that took place. + pub action: MotionAction, + + /// The properties of each of the pointers involved in the event. + pub pointer_properties: &'a [RustPointerProperties], + + /// Flags applied to the event. + pub flags: MotionFlags, + + /// The set of buttons that were pressed at the time of the event. + /// + /// We allow DOWN events to include buttons in their state for which BUTTON_PRESS events haven't + /// been sent yet. In that case, the DOWN should be immediately followed by BUTTON_PRESS events + /// for those buttons, building up to a button state matching that of the DOWN. For example, if + /// the user presses the primary and secondary buttons at exactly the same time, we'd expect + /// this sequence: + /// + /// | Action | Action button | Button state | + /// |----------------|---------------|------------------------| + /// | `HOVER_EXIT` | - | - | + /// | `DOWN` | - | `PRIMARY`, `SECONDARY` | + /// | `BUTTON_PRESS` | `PRIMARY` | `PRIMARY` | + /// | `BUTTON_PRESS` | `SECONDARY` | `PRIMARY`, `SECONDARY` | + /// | `MOVE` | - | `PRIMARY`, `SECONDARY` | + pub button_state: MotionButton, +} + +/// Verifies the properties of an event that should always be true, regardless of the current state. +fn verify_event(event: NotifyMotionArgs<'_>, verify_buttons: bool) -> Result<(), String> { + let pointer_count = event.pointer_properties.len(); if pointer_count < 1 { - return Err(format!("Invalid {} event: no pointers", action)); + return Err(format!("Invalid {} event: no pointers", event.action)); } - match action { + match event.action { MotionAction::Down | MotionAction::HoverEnter | MotionAction::HoverExit @@ -40,23 +74,40 @@ fn verify_event( if pointer_count != 1 { return Err(format!( "Invalid {} event: there are {} pointers in the event", - action, pointer_count + event.action, pointer_count )); } } MotionAction::Cancel => { - if !flags.contains(MotionFlags::CANCELED) { + if !event.flags.contains(MotionFlags::CANCELED) { return Err(format!( "For ACTION_CANCEL, must set FLAG_CANCELED. Received flags: {:#?}", - flags + event.flags )); } } MotionAction::PointerDown { action_index } | MotionAction::PointerUp { action_index } => { if action_index >= pointer_count { - return Err(format!("Got {}, but event has {} pointer(s)", action, pointer_count)); + return Err(format!( + "Got {}, but event has {} pointer(s)", + event.action, pointer_count + )); + } + } + + MotionAction::ButtonPress { action_button } + | MotionAction::ButtonRelease { action_button } => { + if verify_buttons { + let button_count = action_button.iter().count(); + if button_count != 1 { + return Err(format!( + "Invalid {} event: must specify a single action button, not {} action \ + buttons", + event.action, button_count + )); + } } } @@ -65,17 +116,94 @@ fn verify_event( Ok(()) } +/// Keeps track of the button state for a single device and verifies events against it. +#[derive(Default)] +struct ButtonVerifier { + /// The current button state of the device. + button_state: MotionButton, + + /// The set of "pending buttons", which were seen in the last DOWN event's button state but + /// for which we haven't seen BUTTON_PRESS events yet (see [`NotifyMotionArgs::button_state`]). + pending_buttons: MotionButton, +} + +impl ButtonVerifier { + pub fn process_event(&mut self, event: NotifyMotionArgs<'_>) -> Result<(), String> { + if !self.pending_buttons.is_empty() { + // We just saw a DOWN with some additional buttons in its state, so it should be + // immediately followed by ButtonPress events for those buttons. + match event.action { + MotionAction::ButtonPress { action_button } + if self.pending_buttons.contains(action_button) => + { + self.pending_buttons -= action_button; + } + _ => { + return Err(format!( + "After DOWN event, expected BUTTON_PRESS event(s) for {:?}, but got {}", + self.pending_buttons, event.action + )); + } + } + } + let expected_state = match event.action { + MotionAction::Down => { + if self.button_state - event.button_state != MotionButton::empty() { + return Err(format!( + "DOWN event button state is missing {:?}", + self.button_state - event.button_state + )); + } + self.pending_buttons = event.button_state - self.button_state; + // We've already checked that the state isn't missing any already-down buttons, and + // extra buttons are valid on DOWN actions, so bypass the expected state check. + event.button_state + } + MotionAction::ButtonPress { action_button } => { + if self.button_state.contains(action_button) { + return Err(format!( + "Duplicate BUTTON_PRESS; button state already contains {action_button:?}" + )); + } + self.button_state | action_button + } + MotionAction::ButtonRelease { action_button } => { + if !self.button_state.contains(action_button) { + return Err(format!( + "Invalid BUTTON_RELEASE; button state doesn't contain {action_button:?}", + )); + } + self.button_state - action_button + } + _ => self.button_state, + }; + if event.button_state != expected_state { + return Err(format!( + "Expected {} button state to be {:?}, but was {:?}", + event.action, expected_state, event.button_state + )); + } + // DOWN events can have pending buttons, so don't update the state for them. + if event.action != MotionAction::Down { + self.button_state = event.button_state; + } + Ok(()) + } +} + /// The InputVerifier is used to validate a stream of input events. pub struct InputVerifier { name: String, should_log: bool, + verify_buttons: bool, touching_pointer_ids_by_device: HashMap<DeviceId, HashSet<i32>>, hovering_pointer_ids_by_device: HashMap<DeviceId, HashSet<i32>>, + button_verifier_by_device: HashMap<DeviceId, ButtonVerifier>, } impl InputVerifier { /// Create a new InputVerifier. - pub fn new(name: &str, should_log: bool) -> Self { + pub fn new(name: &str, should_log: bool, verify_buttons: bool) -> Self { logger::init( logger::Config::default() .with_tag_on_device("InputVerifier") @@ -84,68 +212,70 @@ impl InputVerifier { Self { name: name.to_owned(), should_log, + verify_buttons, touching_pointer_ids_by_device: HashMap::new(), hovering_pointer_ids_by_device: HashMap::new(), + button_verifier_by_device: HashMap::new(), } } /// Process a pointer movement event from an InputDevice. /// If the event is not valid, we return an error string that describes the issue. - pub fn process_movement( - &mut self, - device_id: DeviceId, - source: Source, - action: u32, - pointer_properties: &[RustPointerProperties], - flags: MotionFlags, - ) -> Result<(), String> { - if !source.is_from_class(SourceClass::Pointer) { + pub fn process_movement(&mut self, event: NotifyMotionArgs<'_>) -> Result<(), String> { + if !event.source.is_from_class(SourceClass::Pointer) { // Skip non-pointer sources like MOUSE_RELATIVE for now return Ok(()); } if self.should_log { info!( "Processing {} for device {:?} ({} pointer{}) on {}", - MotionAction::from(action).to_string(), - device_id, - pointer_properties.len(), - if pointer_properties.len() == 1 { "" } else { "s" }, + event.action, + event.device_id, + event.pointer_properties.len(), + if event.pointer_properties.len() == 1 { "" } else { "s" }, self.name ); } - verify_event(action.into(), pointer_properties, &flags)?; + verify_event(event, self.verify_buttons)?; + + if self.verify_buttons { + self.button_verifier_by_device + .entry(event.device_id) + .or_default() + .process_event(event)?; + } - match action.into() { + match event.action { MotionAction::Down => { - if self.touching_pointer_ids_by_device.contains_key(&device_id) { + if self.touching_pointer_ids_by_device.contains_key(&event.device_id) { return Err(format!( "{}: Invalid DOWN event - pointers already down for device {:?}: {:?}", - self.name, device_id, self.touching_pointer_ids_by_device + self.name, event.device_id, self.touching_pointer_ids_by_device )); } - let it = self.touching_pointer_ids_by_device.entry(device_id).or_default(); - it.insert(pointer_properties[0].id); + let it = self.touching_pointer_ids_by_device.entry(event.device_id).or_default(); + it.insert(event.pointer_properties[0].id); } MotionAction::PointerDown { action_index } => { - if !self.touching_pointer_ids_by_device.contains_key(&device_id) { + if !self.touching_pointer_ids_by_device.contains_key(&event.device_id) { return Err(format!( "{}: Received POINTER_DOWN but no pointers are currently down \ for device {:?}", - self.name, device_id + self.name, event.device_id )); } - let it = self.touching_pointer_ids_by_device.get_mut(&device_id).unwrap(); - if it.len() != pointer_properties.len() - 1 { + let it = self.touching_pointer_ids_by_device.get_mut(&event.device_id).unwrap(); + if it.len() != event.pointer_properties.len() - 1 { return Err(format!( "{}: There are currently {} touching pointers, but the incoming \ POINTER_DOWN event has {}", self.name, it.len(), - pointer_properties.len() + event.pointer_properties.len() )); } - let pointer_id = pointer_properties[action_index].id; + let pointer_id = event.pointer_properties[action_index].id; if it.contains(&pointer_id) { return Err(format!( "{}: Pointer with id={} already present found in the properties", @@ -155,7 +285,7 @@ impl InputVerifier { it.insert(pointer_id); } MotionAction::Move => { - if !self.ensure_touching_pointers_match(device_id, pointer_properties) { + if !self.ensure_touching_pointers_match(event.device_id, event.pointer_properties) { return Err(format!( "{}: ACTION_MOVE touching pointers don't match", self.name @@ -163,49 +293,49 @@ impl InputVerifier { } } MotionAction::PointerUp { action_index } => { - if !self.ensure_touching_pointers_match(device_id, pointer_properties) { + if !self.ensure_touching_pointers_match(event.device_id, event.pointer_properties) { return Err(format!( "{}: ACTION_POINTER_UP touching pointers don't match", self.name )); } - let it = self.touching_pointer_ids_by_device.get_mut(&device_id).unwrap(); - let pointer_id = pointer_properties[action_index].id; + let it = self.touching_pointer_ids_by_device.get_mut(&event.device_id).unwrap(); + let pointer_id = event.pointer_properties[action_index].id; it.remove(&pointer_id); } MotionAction::Up => { - if !self.touching_pointer_ids_by_device.contains_key(&device_id) { + if !self.touching_pointer_ids_by_device.contains_key(&event.device_id) { return Err(format!( "{} Received ACTION_UP but no pointers are currently down for device {:?}", - self.name, device_id + self.name, event.device_id )); } - let it = self.touching_pointer_ids_by_device.get_mut(&device_id).unwrap(); + let it = self.touching_pointer_ids_by_device.get_mut(&event.device_id).unwrap(); if it.len() != 1 { return Err(format!( "{}: Got ACTION_UP, but we have pointers: {:?} for device {:?}", - self.name, it, device_id + self.name, it, event.device_id )); } - let pointer_id = pointer_properties[0].id; + let pointer_id = event.pointer_properties[0].id; if !it.contains(&pointer_id) { return Err(format!( "{}: Got ACTION_UP, but pointerId {} is not touching. Touching pointers:\ {:?} for device {:?}", - self.name, pointer_id, it, device_id + self.name, pointer_id, it, event.device_id )); } - self.touching_pointer_ids_by_device.remove(&device_id); + self.touching_pointer_ids_by_device.remove(&event.device_id); } MotionAction::Cancel => { - if !self.ensure_touching_pointers_match(device_id, pointer_properties) { + if !self.ensure_touching_pointers_match(event.device_id, event.pointer_properties) { return Err(format!( "{}: Got ACTION_CANCEL, but the pointers don't match. \ Existing pointers: {:?}", self.name, self.touching_pointer_ids_by_device )); } - self.touching_pointer_ids_by_device.remove(&device_id); + self.touching_pointer_ids_by_device.remove(&event.device_id); } /* * The hovering protocol currently supports a single pointer only, because we do not @@ -214,41 +344,41 @@ impl InputVerifier { * eventually supported. */ MotionAction::HoverEnter => { - if self.hovering_pointer_ids_by_device.contains_key(&device_id) { + if self.hovering_pointer_ids_by_device.contains_key(&event.device_id) { return Err(format!( "{}: Invalid HOVER_ENTER event - pointers already hovering for device {:?}:\ {:?}", - self.name, device_id, self.hovering_pointer_ids_by_device + self.name, event.device_id, self.hovering_pointer_ids_by_device )); } - let it = self.hovering_pointer_ids_by_device.entry(device_id).or_default(); - it.insert(pointer_properties[0].id); + let it = self.hovering_pointer_ids_by_device.entry(event.device_id).or_default(); + it.insert(event.pointer_properties[0].id); } MotionAction::HoverMove => { // For compatibility reasons, we allow HOVER_MOVE without a prior HOVER_ENTER. // If there was no prior HOVER_ENTER, just start a new hovering pointer. - let it = self.hovering_pointer_ids_by_device.entry(device_id).or_default(); - it.insert(pointer_properties[0].id); + let it = self.hovering_pointer_ids_by_device.entry(event.device_id).or_default(); + it.insert(event.pointer_properties[0].id); } MotionAction::HoverExit => { - if !self.hovering_pointer_ids_by_device.contains_key(&device_id) { + if !self.hovering_pointer_ids_by_device.contains_key(&event.device_id) { return Err(format!( "{}: Invalid HOVER_EXIT event - no pointers are hovering for device {:?}", - self.name, device_id + self.name, event.device_id )); } - let pointer_id = pointer_properties[0].id; - let it = self.hovering_pointer_ids_by_device.get_mut(&device_id).unwrap(); + let pointer_id = event.pointer_properties[0].id; + let it = self.hovering_pointer_ids_by_device.get_mut(&event.device_id).unwrap(); it.remove(&pointer_id); if !it.is_empty() { return Err(format!( "{}: Removed hovering pointer {}, but pointers are still\ hovering for device {:?}: {:?}", - self.name, pointer_id, device_id, it + self.name, pointer_id, event.device_id, it )); } - self.hovering_pointer_ids_by_device.remove(&device_id); + self.hovering_pointer_ids_by_device.remove(&event.device_id); } _ => return Ok(()), } @@ -288,295 +418,227 @@ impl InputVerifier { #[cfg(test)] mod tests { + use crate::input::MotionButton; use crate::input_verifier::InputVerifier; + use crate::input_verifier::NotifyMotionArgs; use crate::DeviceId; + use crate::MotionAction; use crate::MotionFlags; use crate::RustPointerProperties; use crate::Source; + const BASE_POINTER_PROPERTIES: [RustPointerProperties; 1] = [RustPointerProperties { id: 0 }]; + const BASE_EVENT: NotifyMotionArgs = NotifyMotionArgs { + device_id: DeviceId(1), + source: Source::Touchscreen, + action: MotionAction::Down, + pointer_properties: &BASE_POINTER_PROPERTIES, + flags: MotionFlags::empty(), + button_state: MotionButton::empty(), + }; + const BASE_MOUSE_EVENT: NotifyMotionArgs = + NotifyMotionArgs { source: Source::Mouse, ..BASE_EVENT }; + #[test] /** * Send a DOWN event with 2 pointers and ensure that it's marked as invalid. */ fn bad_down_event() { - let mut verifier = InputVerifier::new("Test", /*should_log*/ true); + let mut verifier = + InputVerifier::new("Test", /*should_log*/ true, /*verify_buttons*/ true); let pointer_properties = Vec::from([RustPointerProperties { id: 0 }, RustPointerProperties { id: 1 }]); assert!(verifier - .process_movement( - DeviceId(1), - Source::Touchscreen, - input_bindgen::AMOTION_EVENT_ACTION_DOWN, - &pointer_properties, - MotionFlags::empty(), - ) + .process_movement(NotifyMotionArgs { + action: MotionAction::Down, + pointer_properties: &pointer_properties, + ..BASE_EVENT + }) .is_err()); } #[test] fn single_pointer_stream() { - let mut verifier = InputVerifier::new("Test", /*should_log*/ false); + let mut verifier = + InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true); let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]); assert!(verifier - .process_movement( - DeviceId(1), - Source::Touchscreen, - input_bindgen::AMOTION_EVENT_ACTION_DOWN, - &pointer_properties, - MotionFlags::empty(), - ) + .process_movement(NotifyMotionArgs { + action: MotionAction::Down, + pointer_properties: &pointer_properties, + ..BASE_EVENT + }) .is_ok()); assert!(verifier - .process_movement( - DeviceId(1), - Source::Touchscreen, - input_bindgen::AMOTION_EVENT_ACTION_MOVE, - &pointer_properties, - MotionFlags::empty(), - ) + .process_movement(NotifyMotionArgs { + action: MotionAction::Move, + pointer_properties: &pointer_properties, + ..BASE_EVENT + }) .is_ok()); assert!(verifier - .process_movement( - DeviceId(1), - Source::Touchscreen, - input_bindgen::AMOTION_EVENT_ACTION_UP, - &pointer_properties, - MotionFlags::empty(), - ) + .process_movement(NotifyMotionArgs { + action: MotionAction::Up, + pointer_properties: &pointer_properties, + ..BASE_EVENT + }) .is_ok()); } #[test] fn two_pointer_stream() { - let mut verifier = InputVerifier::new("Test", /*should_log*/ false); + let mut verifier = + InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true); let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]); assert!(verifier - .process_movement( - DeviceId(1), - Source::Touchscreen, - input_bindgen::AMOTION_EVENT_ACTION_DOWN, - &pointer_properties, - MotionFlags::empty(), - ) + .process_movement(NotifyMotionArgs { + action: MotionAction::Down, + pointer_properties: &pointer_properties, + ..BASE_EVENT + }) .is_ok()); // POINTER 1 DOWN let two_pointer_properties = Vec::from([RustPointerProperties { id: 0 }, RustPointerProperties { id: 1 }]); assert!(verifier - .process_movement( - DeviceId(1), - Source::Touchscreen, - input_bindgen::AMOTION_EVENT_ACTION_POINTER_DOWN - | (1 << input_bindgen::AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - &two_pointer_properties, - MotionFlags::empty(), - ) + .process_movement(NotifyMotionArgs { + action: MotionAction::PointerDown { action_index: 1 }, + pointer_properties: &two_pointer_properties, + ..BASE_EVENT + }) .is_ok()); // POINTER 0 UP assert!(verifier - .process_movement( - DeviceId(1), - Source::Touchscreen, - input_bindgen::AMOTION_EVENT_ACTION_POINTER_UP - | (0 << input_bindgen::AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - &two_pointer_properties, - MotionFlags::empty(), - ) + .process_movement(NotifyMotionArgs { + action: MotionAction::PointerUp { action_index: 0 }, + pointer_properties: &two_pointer_properties, + ..BASE_EVENT + }) .is_ok()); // ACTION_UP for pointer id=1 let pointer_1_properties = Vec::from([RustPointerProperties { id: 1 }]); assert!(verifier - .process_movement( - DeviceId(1), - Source::Touchscreen, - input_bindgen::AMOTION_EVENT_ACTION_UP, - &pointer_1_properties, - MotionFlags::empty(), - ) + .process_movement(NotifyMotionArgs { + action: MotionAction::Up, + pointer_properties: &pointer_1_properties, + ..BASE_EVENT + }) .is_ok()); } #[test] fn multi_device_stream() { - let mut verifier = InputVerifier::new("Test", /*should_log*/ false); - let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]); + let mut verifier = + InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true); assert!(verifier - .process_movement( - DeviceId(1), - Source::Touchscreen, - input_bindgen::AMOTION_EVENT_ACTION_DOWN, - &pointer_properties, - MotionFlags::empty(), - ) + .process_movement(NotifyMotionArgs { + device_id: DeviceId(1), + action: MotionAction::Down, + ..BASE_EVENT + }) .is_ok()); assert!(verifier - .process_movement( - DeviceId(1), - Source::Touchscreen, - input_bindgen::AMOTION_EVENT_ACTION_MOVE, - &pointer_properties, - MotionFlags::empty(), - ) + .process_movement(NotifyMotionArgs { + device_id: DeviceId(1), + action: MotionAction::Move, + ..BASE_EVENT + }) .is_ok()); assert!(verifier - .process_movement( - DeviceId(2), - Source::Touchscreen, - input_bindgen::AMOTION_EVENT_ACTION_DOWN, - &pointer_properties, - MotionFlags::empty(), - ) + .process_movement(NotifyMotionArgs { + device_id: DeviceId(2), + action: MotionAction::Down, + ..BASE_EVENT + }) .is_ok()); assert!(verifier - .process_movement( - DeviceId(2), - Source::Touchscreen, - input_bindgen::AMOTION_EVENT_ACTION_MOVE, - &pointer_properties, - MotionFlags::empty(), - ) + .process_movement(NotifyMotionArgs { + device_id: DeviceId(2), + action: MotionAction::Move, + ..BASE_EVENT + }) .is_ok()); assert!(verifier - .process_movement( - DeviceId(1), - Source::Touchscreen, - input_bindgen::AMOTION_EVENT_ACTION_UP, - &pointer_properties, - MotionFlags::empty(), - ) + .process_movement(NotifyMotionArgs { + device_id: DeviceId(1), + action: MotionAction::Up, + ..BASE_EVENT + }) .is_ok()); } #[test] fn action_cancel() { - let mut verifier = InputVerifier::new("Test", /*should_log*/ false); - let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]); + let mut verifier = + InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true); assert!(verifier - .process_movement( - DeviceId(1), - Source::Touchscreen, - input_bindgen::AMOTION_EVENT_ACTION_DOWN, - &pointer_properties, - MotionFlags::empty(), - ) + .process_movement(NotifyMotionArgs { + action: MotionAction::Down, + flags: MotionFlags::empty(), + ..BASE_EVENT + }) .is_ok()); assert!(verifier - .process_movement( - DeviceId(1), - Source::Touchscreen, - input_bindgen::AMOTION_EVENT_ACTION_CANCEL, - &pointer_properties, - MotionFlags::CANCELED, - ) + .process_movement(NotifyMotionArgs { + action: MotionAction::Cancel, + flags: MotionFlags::CANCELED, + ..BASE_EVENT + }) .is_ok()); } #[test] fn invalid_action_cancel() { - let mut verifier = InputVerifier::new("Test", /*should_log*/ false); - let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]); + let mut verifier = + InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true); assert!(verifier - .process_movement( - DeviceId(1), - Source::Touchscreen, - input_bindgen::AMOTION_EVENT_ACTION_DOWN, - &pointer_properties, - MotionFlags::empty(), - ) - .is_ok()); - assert!(verifier - .process_movement( - DeviceId(1), - Source::Touchscreen, - input_bindgen::AMOTION_EVENT_ACTION_CANCEL, - &pointer_properties, - MotionFlags::empty(), // forgot to set FLAG_CANCELED - ) + .process_movement(NotifyMotionArgs { action: MotionAction::Down, ..BASE_EVENT }) + .is_ok()); + assert!(verifier + .process_movement(NotifyMotionArgs { action: MotionAction::Cancel, ..BASE_EVENT }) .is_err()); } #[test] fn invalid_up() { - let mut verifier = InputVerifier::new("Test", /*should_log*/ false); - let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]); + let mut verifier = + InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true); assert!(verifier - .process_movement( - DeviceId(1), - Source::Touchscreen, - input_bindgen::AMOTION_EVENT_ACTION_UP, - &pointer_properties, - MotionFlags::empty(), - ) + .process_movement(NotifyMotionArgs { action: MotionAction::Up, ..BASE_EVENT }) .is_err()); } #[test] fn correct_hover_sequence() { - let mut verifier = InputVerifier::new("Test", /*should_log*/ false); - let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]); + let mut verifier = + InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true); assert!(verifier - .process_movement( - DeviceId(1), - Source::Touchscreen, - input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER, - &pointer_properties, - MotionFlags::empty(), - ) + .process_movement(NotifyMotionArgs { action: MotionAction::HoverEnter, ..BASE_EVENT }) .is_ok()); assert!(verifier - .process_movement( - DeviceId(1), - Source::Touchscreen, - input_bindgen::AMOTION_EVENT_ACTION_HOVER_MOVE, - &pointer_properties, - MotionFlags::empty(), - ) + .process_movement(NotifyMotionArgs { action: MotionAction::HoverMove, ..BASE_EVENT }) .is_ok()); assert!(verifier - .process_movement( - DeviceId(1), - Source::Touchscreen, - input_bindgen::AMOTION_EVENT_ACTION_HOVER_EXIT, - &pointer_properties, - MotionFlags::empty(), - ) + .process_movement(NotifyMotionArgs { action: MotionAction::HoverExit, ..BASE_EVENT }) .is_ok()); assert!(verifier - .process_movement( - DeviceId(1), - Source::Touchscreen, - input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER, - &pointer_properties, - MotionFlags::empty(), - ) + .process_movement(NotifyMotionArgs { action: MotionAction::HoverEnter, ..BASE_EVENT }) .is_ok()); } #[test] fn double_hover_enter() { - let mut verifier = InputVerifier::new("Test", /*should_log*/ false); - let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]); + let mut verifier = + InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true); assert!(verifier - .process_movement( - DeviceId(1), - Source::Touchscreen, - input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER, - &pointer_properties, - MotionFlags::empty(), - ) + .process_movement(NotifyMotionArgs { action: MotionAction::HoverEnter, ..BASE_EVENT }) .is_ok()); assert!(verifier - .process_movement( - DeviceId(1), - Source::Touchscreen, - input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER, - &pointer_properties, - MotionFlags::empty(), - ) + .process_movement(NotifyMotionArgs { action: MotionAction::HoverEnter, ..BASE_EVENT }) .is_err()); } @@ -584,55 +646,356 @@ mod tests { // MOUSE_RELATIVE, which is used during pointer capture. The verifier should allow such event. #[test] fn relative_mouse_move() { - let mut verifier = InputVerifier::new("Test", /*should_log*/ false); - let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]); + let mut verifier = + InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true); assert!(verifier - .process_movement( - DeviceId(2), - Source::MouseRelative, - input_bindgen::AMOTION_EVENT_ACTION_MOVE, - &pointer_properties, - MotionFlags::empty(), - ) + .process_movement(NotifyMotionArgs { + device_id: DeviceId(2), + source: Source::MouseRelative, + action: MotionAction::Move, + ..BASE_EVENT + }) .is_ok()); } // Send a MOVE event with incorrect number of pointers (one of the pointers is missing). #[test] fn move_with_wrong_number_of_pointers() { - let mut verifier = InputVerifier::new("Test", /*should_log*/ false); + let mut verifier = + InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true); let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]); assert!(verifier - .process_movement( - DeviceId(1), - Source::Touchscreen, - input_bindgen::AMOTION_EVENT_ACTION_DOWN, - &pointer_properties, - MotionFlags::empty(), - ) + .process_movement(NotifyMotionArgs { + action: MotionAction::Down, + pointer_properties: &pointer_properties, + ..BASE_EVENT + }) .is_ok()); // POINTER 1 DOWN let two_pointer_properties = Vec::from([RustPointerProperties { id: 0 }, RustPointerProperties { id: 1 }]); assert!(verifier - .process_movement( - DeviceId(1), - Source::Touchscreen, - input_bindgen::AMOTION_EVENT_ACTION_POINTER_DOWN - | (1 << input_bindgen::AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - &two_pointer_properties, - MotionFlags::empty(), - ) + .process_movement(NotifyMotionArgs { + action: MotionAction::PointerDown { action_index: 1 }, + pointer_properties: &two_pointer_properties, + ..BASE_EVENT + }) .is_ok()); // MOVE event with 1 pointer missing (the pointer with id = 1). It should be rejected assert!(verifier - .process_movement( - DeviceId(1), - Source::Touchscreen, - input_bindgen::AMOTION_EVENT_ACTION_MOVE, - &pointer_properties, - MotionFlags::empty(), - ) + .process_movement(NotifyMotionArgs { + action: MotionAction::Move, + pointer_properties: &pointer_properties, + ..BASE_EVENT + }) + .is_err()); + } + + #[test] + fn correct_button_press() { + let mut verifier = + InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true); + assert!(verifier + .process_movement(NotifyMotionArgs { + action: MotionAction::ButtonPress { action_button: MotionButton::Primary }, + button_state: MotionButton::Primary, + ..BASE_MOUSE_EVENT + }) + .is_ok()); + } + + #[test] + fn button_press_without_action_button() { + let mut verifier = + InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true); + assert!(verifier + .process_movement(NotifyMotionArgs { + action: MotionAction::ButtonPress { action_button: MotionButton::empty() }, + button_state: MotionButton::empty(), + ..BASE_MOUSE_EVENT + }) + .is_err()); + } + + #[test] + fn button_press_with_multiple_action_buttons() { + let mut verifier = + InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true); + assert!(verifier + .process_movement(NotifyMotionArgs { + action: MotionAction::ButtonPress { + action_button: MotionButton::Back | MotionButton::Forward + }, + button_state: MotionButton::Back | MotionButton::Forward, + ..BASE_MOUSE_EVENT + }) + .is_err()); + } + + #[test] + fn button_press_without_action_button_in_state() { + let mut verifier = + InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true); + assert!(verifier + .process_movement(NotifyMotionArgs { + action: MotionAction::ButtonPress { action_button: MotionButton::Primary }, + button_state: MotionButton::empty(), + ..BASE_MOUSE_EVENT + }) + .is_err()); + } + + #[test] + fn button_release_with_action_button_in_state() { + let mut verifier = + InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true); + assert!(verifier + .process_movement(NotifyMotionArgs { + action: MotionAction::ButtonPress { action_button: MotionButton::Primary }, + button_state: MotionButton::Primary, + ..BASE_MOUSE_EVENT + }) + .is_ok()); + assert!(verifier + .process_movement(NotifyMotionArgs { + action: MotionAction::ButtonRelease { action_button: MotionButton::Primary }, + button_state: MotionButton::Primary, + ..BASE_MOUSE_EVENT + }) + .is_err()); + } + + #[test] + fn nonbutton_action_with_button_state_change() { + let mut verifier = + InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true); + assert!(verifier + .process_movement(NotifyMotionArgs { + action: MotionAction::HoverEnter, + button_state: MotionButton::empty(), + ..BASE_MOUSE_EVENT + }) + .is_ok()); + assert!(verifier + .process_movement(NotifyMotionArgs { + action: MotionAction::HoverMove, + button_state: MotionButton::Back, + ..BASE_MOUSE_EVENT + }) + .is_err()); + } + + #[test] + fn nonbutton_action_missing_button_state() { + let mut verifier = + InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true); + assert!(verifier + .process_movement(NotifyMotionArgs { + action: MotionAction::HoverEnter, + button_state: MotionButton::empty(), + ..BASE_MOUSE_EVENT + }) + .is_ok()); + assert!(verifier + .process_movement(NotifyMotionArgs { + action: MotionAction::ButtonPress { action_button: MotionButton::Back }, + button_state: MotionButton::Back, + ..BASE_MOUSE_EVENT + }) + .is_ok()); + assert!(verifier + .process_movement(NotifyMotionArgs { + action: MotionAction::HoverMove, + button_state: MotionButton::empty(), + ..BASE_MOUSE_EVENT + }) + .is_err()); + } + + #[test] + fn up_without_button_release() { + let mut verifier = + InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true); + assert!(verifier + .process_movement(NotifyMotionArgs { + action: MotionAction::Down, + button_state: MotionButton::Primary, + ..BASE_MOUSE_EVENT + }) + .is_ok()); + assert!(verifier + .process_movement(NotifyMotionArgs { + action: MotionAction::ButtonPress { action_button: MotionButton::Primary }, + button_state: MotionButton::Primary, + ..BASE_MOUSE_EVENT + }) + .is_ok()); + // This UP event shouldn't change the button state; a BUTTON_RELEASE before it should. + assert!(verifier + .process_movement(NotifyMotionArgs { + action: MotionAction::Up, + button_state: MotionButton::empty(), + ..BASE_MOUSE_EVENT + }) + .is_err()); + } + + #[test] + fn button_press_for_already_pressed_button() { + let mut verifier = + InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true); + assert!(verifier + .process_movement(NotifyMotionArgs { + action: MotionAction::ButtonPress { action_button: MotionButton::Back }, + button_state: MotionButton::Back, + ..BASE_MOUSE_EVENT + }) + .is_ok()); + assert!(verifier + .process_movement(NotifyMotionArgs { + action: MotionAction::ButtonPress { action_button: MotionButton::Back }, + button_state: MotionButton::Back, + ..BASE_MOUSE_EVENT + }) + .is_err()); + } + + #[test] + fn button_release_for_unpressed_button() { + let mut verifier = + InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true); + assert!(verifier + .process_movement(NotifyMotionArgs { + action: MotionAction::ButtonRelease { action_button: MotionButton::Back }, + button_state: MotionButton::empty(), + ..BASE_MOUSE_EVENT + }) + .is_err()); + } + + #[test] + fn correct_multiple_button_presses_without_down() { + let mut verifier = + InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true); + assert!(verifier + .process_movement(NotifyMotionArgs { + action: MotionAction::ButtonPress { action_button: MotionButton::Back }, + button_state: MotionButton::Back, + ..BASE_MOUSE_EVENT + }) + .is_ok()); + assert!(verifier + .process_movement(NotifyMotionArgs { + action: MotionAction::ButtonPress { action_button: MotionButton::Forward }, + button_state: MotionButton::Back | MotionButton::Forward, + ..BASE_MOUSE_EVENT + }) + .is_ok()); + } + + #[test] + fn correct_down_with_button_press() { + let mut verifier = + InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true); + assert!(verifier + .process_movement(NotifyMotionArgs { + action: MotionAction::Down, + button_state: MotionButton::Primary | MotionButton::Secondary, + ..BASE_MOUSE_EVENT + }) + .is_ok()); + assert!(verifier + .process_movement(NotifyMotionArgs { + action: MotionAction::ButtonPress { action_button: MotionButton::Primary }, + button_state: MotionButton::Primary, + ..BASE_MOUSE_EVENT + }) + .is_ok()); + assert!(verifier + .process_movement(NotifyMotionArgs { + action: MotionAction::ButtonPress { action_button: MotionButton::Secondary }, + button_state: MotionButton::Primary | MotionButton::Secondary, + ..BASE_MOUSE_EVENT + }) + .is_ok()); + // Also check that the MOVE afterwards is OK, as that's where errors would be raised if not + // enough BUTTON_PRESSes were sent. + assert!(verifier + .process_movement(NotifyMotionArgs { + action: MotionAction::Move, + button_state: MotionButton::Primary | MotionButton::Secondary, + ..BASE_MOUSE_EVENT + }) + .is_ok()); + } + + #[test] + fn down_with_button_state_change_not_followed_by_button_press() { + let mut verifier = + InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true); + assert!(verifier + .process_movement(NotifyMotionArgs { + action: MotionAction::Down, + button_state: MotionButton::Primary, + ..BASE_MOUSE_EVENT + }) + .is_ok()); + // The DOWN event itself is OK, but it needs to be immediately followed by a BUTTON_PRESS. + assert!(verifier + .process_movement(NotifyMotionArgs { + action: MotionAction::Move, + button_state: MotionButton::Primary, + ..BASE_MOUSE_EVENT + }) + .is_err()); + } + + #[test] + fn down_with_button_state_change_not_followed_by_enough_button_presses() { + let mut verifier = + InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true); + assert!(verifier + .process_movement(NotifyMotionArgs { + action: MotionAction::Down, + button_state: MotionButton::Primary | MotionButton::Secondary, + ..BASE_MOUSE_EVENT + }) + .is_ok()); + // The DOWN event itself is OK, but it needs to be immediately followed by two + // BUTTON_PRESSes, one for each button. + assert!(verifier + .process_movement(NotifyMotionArgs { + action: MotionAction::ButtonPress { action_button: MotionButton::Primary }, + button_state: MotionButton::Primary, + ..BASE_MOUSE_EVENT + }) + .is_ok()); + assert!(verifier + .process_movement(NotifyMotionArgs { + action: MotionAction::Move, + button_state: MotionButton::Primary, + ..BASE_MOUSE_EVENT + }) + .is_err()); + } + + #[test] + fn down_missing_already_pressed_button() { + let mut verifier = + InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true); + assert!(verifier + .process_movement(NotifyMotionArgs { + action: MotionAction::ButtonPress { action_button: MotionButton::Back }, + button_state: MotionButton::Back, + ..BASE_MOUSE_EVENT + }) + .is_ok()); + assert!(verifier + .process_movement(NotifyMotionArgs { + action: MotionAction::Down, + button_state: MotionButton::empty(), + ..BASE_MOUSE_EVENT + }) .is_err()); } } diff --git a/libs/input/rust/keyboard_classification_config.rs b/libs/input/rust/keyboard_classification_config.rs index ab74efb674..26a8d8f4b8 100644 --- a/libs/input/rust/keyboard_classification_config.rs +++ b/libs/input/rust/keyboard_classification_config.rs @@ -20,6 +20,15 @@ use crate::input::KeyboardType; // key events at all. (Requires setup allowing InputDevice to dynamically add/remove // KeyboardInputMapper based on blocklist and KeyEvents in case a KeyboardType::None device ends // up producing a key event) + +/// This list pre-classifies a device into Alphabetic/Non-Alphabetic keyboard and tells us whether +/// further re-classification should be allowed or not (using is_finalized value). +/// This list DOES NOT change the source of the device or change the input mappers associated with +/// the device. It only changes the "KeyboardType" classification. This list should be primarily +/// used to pre-classify devices that are NOT keyboards(like mice, game pads, etc.) but generate +/// evdev nodes that say that they are alphabetic keyboards. +/// +/// NOTE: Pls keep the list sorted by vendor id and product id for easy searching. pub static CLASSIFIED_DEVICES: &[( /* vendorId */ u16, /* productId */ u16, @@ -96,6 +105,8 @@ pub static CLASSIFIED_DEVICES: &[( (0x056e, 0x0159, KeyboardType::NonAlphabetic, true), // Zebra LS2208 barcode scanner (0x05e0, 0x1200, KeyboardType::NonAlphabetic, true), + // Glorious O2 Wireless + (0x093a, 0x822d, KeyboardType::NonAlphabetic, true), // RDing FootSwitch1F1 (0x0c45, 0x7403, KeyboardType::NonAlphabetic, true), // SteelSeries Sensei RAW Frost Blue @@ -108,6 +119,8 @@ pub static CLASSIFIED_DEVICES: &[( (0x1050, 0x0010, KeyboardType::NonAlphabetic, true), // Yubico.com Yubikey 4 OTP+U2F+CCID (0x1050, 0x0407, KeyboardType::NonAlphabetic, true), + // Razer DeathAdder Essential + (0x1532, 0x0098, KeyboardType::NonAlphabetic, true), // Lenovo USB-C Wired Compact Mouse (0x17ef, 0x6123, KeyboardType::NonAlphabetic, true), // Corsair Katar Pro Wireless (USB dongle) diff --git a/libs/input/rust/keyboard_classifier.rs b/libs/input/rust/keyboard_classifier.rs index 3c789b41e2..1b89a5cf2b 100644 --- a/libs/input/rust/keyboard_classifier.rs +++ b/libs/input/rust/keyboard_classifier.rs @@ -66,11 +66,11 @@ impl KeyboardClassifier { /// Get keyboard type for a tracked keyboard in KeyboardClassifier pub fn get_keyboard_type(&self, device_id: DeviceId) -> KeyboardType { - return if let Some(keyboard) = self.device_map.get(&device_id) { + if let Some(keyboard) = self.device_map.get(&device_id) { keyboard.keyboard_type } else { KeyboardType::None - }; + } } /// Tells if keyboard type classification is finalized. Once finalized the classification can't @@ -79,11 +79,11 @@ impl KeyboardClassifier { /// Finalized devices are either "alphabetic" keyboards or keyboards in blocklist or /// allowlist that are explicitly categorized and won't change with future key events pub fn is_finalized(&self, device_id: DeviceId) -> bool { - return if let Some(keyboard) = self.device_map.get(&device_id) { + if let Some(keyboard) = self.device_map.get(&device_id) { keyboard.is_finalized } else { false - }; + } } /// Process a key event and change keyboard type if required. diff --git a/libs/input/rust/lib.rs b/libs/input/rust/lib.rs index 4f4ea8568b..ee999f7666 100644 --- a/libs/input/rust/lib.rs +++ b/libs/input/rust/lib.rs @@ -24,10 +24,10 @@ mod keyboard_classifier; pub use data_store::{DataStore, DefaultFileReaderWriter}; pub use input::{ - DeviceClass, DeviceId, InputDevice, KeyboardType, ModifierState, MotionAction, MotionFlags, - Source, + DeviceClass, DeviceId, InputDevice, KeyboardType, ModifierState, MotionAction, MotionButton, + MotionFlags, Source, }; -pub use input_verifier::InputVerifier; +pub use input_verifier::{InputVerifier, NotifyMotionArgs}; pub use keyboard_classifier::KeyboardClassifier; #[cxx::bridge(namespace = "android::input")] @@ -57,14 +57,17 @@ mod ffi { /// ``` type InputVerifier; #[cxx_name = create] - fn create_input_verifier(name: String) -> Box<InputVerifier>; + fn create_input_verifier(name: String, verify_buttons: bool) -> Box<InputVerifier>; + #[allow(clippy::too_many_arguments)] fn process_movement( verifier: &mut InputVerifier, device_id: i32, source: u32, action: u32, + action_button: u32, pointer_properties: &[RustPointerProperties], flags: u32, + button_state: u32, ) -> String; fn reset_device(verifier: &mut InputVerifier, device_id: i32); } @@ -115,33 +118,67 @@ mod ffi { use crate::ffi::{RustInputDeviceIdentifier, RustPointerProperties}; -fn create_input_verifier(name: String) -> Box<InputVerifier> { - Box::new(InputVerifier::new(&name, ffi::shouldLog("InputVerifierLogEvents"))) +fn create_input_verifier(name: String, verify_buttons: bool) -> Box<InputVerifier> { + Box::new(InputVerifier::new(&name, ffi::shouldLog("InputVerifierLogEvents"), verify_buttons)) } +#[allow(clippy::too_many_arguments)] fn process_movement( verifier: &mut InputVerifier, device_id: i32, source: u32, action: u32, + action_button: u32, pointer_properties: &[RustPointerProperties], flags: u32, + button_state: u32, ) -> String { - let motion_flags = MotionFlags::from_bits(flags); - if motion_flags.is_none() { + let Some(converted_source) = Source::from_bits(source) else { + panic!( + "The conversion of source 0x{source:08x} failed, please check if some sources have not \ + been added to Source." + ); + }; + let Some(motion_flags) = MotionFlags::from_bits(flags) else { panic!( "The conversion of flags 0x{:08x} failed, please check if some flags have not been \ added to MotionFlags.", flags ); + }; + let Some(motion_action_button) = MotionButton::from_bits(action_button) else { + panic!( + "The conversion of action button 0x{action_button:08x} failed, please check if some \ + buttons need to be added to MotionButton." + ); + }; + let Some(motion_button_state) = MotionButton::from_bits(button_state) else { + panic!( + "The conversion of button state 0x{button_state:08x} failed, please check if some \ + buttons need to be added to MotionButton." + ); + }; + let motion_action = MotionAction::from_code(action, motion_action_button); + if motion_action_button != MotionButton::empty() { + match motion_action { + MotionAction::ButtonPress { action_button: _ } + | MotionAction::ButtonRelease { action_button: _ } => {} + _ => { + return format!( + "Invalid {motion_action} event: has action button {motion_action_button:?} but \ + is not a button action" + ); + } + } } - let result = verifier.process_movement( - DeviceId(device_id), - Source::from_bits(source).unwrap(), - action, + let result = verifier.process_movement(NotifyMotionArgs { + device_id: DeviceId(device_id), + source: converted_source, + action: motion_action, pointer_properties, - motion_flags.unwrap(), - ); + flags: motion_flags, + button_state: motion_button_state, + }); match result { Ok(()) => "".to_string(), Err(e) => e, @@ -208,3 +245,44 @@ fn process_key( } classifier.process_key(DeviceId(device_id), evdev_code, modifier_state.unwrap()); } + +#[cfg(test)] +mod tests { + use crate::create_input_verifier; + use crate::process_movement; + use crate::RustPointerProperties; + + const BASE_POINTER_PROPERTIES: [RustPointerProperties; 1] = [RustPointerProperties { id: 0 }]; + + #[test] + fn verify_nonbutton_action_with_action_button() { + let mut verifier = create_input_verifier("Test".to_string(), /*verify_buttons*/ true); + assert!(process_movement( + &mut verifier, + 1, + input_bindgen::AINPUT_SOURCE_MOUSE, + input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER, + input_bindgen::AMOTION_EVENT_BUTTON_PRIMARY, + &BASE_POINTER_PROPERTIES, + 0, + 0, + ) + .contains("button action")); + } + + #[test] + fn verify_nonbutton_action_with_action_button_and_button_state() { + let mut verifier = create_input_verifier("Test".to_string(), /*verify_buttons*/ true); + assert!(process_movement( + &mut verifier, + 1, + input_bindgen::AINPUT_SOURCE_MOUSE, + input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER, + input_bindgen::AMOTION_EVENT_BUTTON_PRIMARY, + &BASE_POINTER_PROPERTIES, + 0, + input_bindgen::AMOTION_EVENT_BUTTON_PRIMARY, + ) + .contains("button action")); + } +} diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index 85a37fe04a..968fa5fc1a 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -16,16 +16,16 @@ cc_test { "BlockingQueue_test.cpp", "IdGenerator_test.cpp", "InputChannel_test.cpp", - "InputConsumer_test.cpp", "InputConsumerFilteredResampling_test.cpp", "InputConsumerResampling_test.cpp", + "InputConsumer_test.cpp", "InputDevice_test.cpp", "InputEvent_test.cpp", - "InputPublisherAndConsumer_test.cpp", "InputPublisherAndConsumerNoResampling_test.cpp", + "InputPublisherAndConsumer_test.cpp", "InputVerifier_test.cpp", - "MotionPredictor_test.cpp", "MotionPredictorMetricsManager_test.cpp", + "MotionPredictor_test.cpp", "OneEuroFilter_test.cpp", "Resampler_test.cpp", "RingBuffer_test.cpp", @@ -53,8 +53,8 @@ cc_test { ], cflags: [ "-Wall", - "-Wextra", "-Werror", + "-Wextra", "-Wno-unused-parameter", ], sanitize: { @@ -68,26 +68,26 @@ cc_test { memtag_heap: true, undefined: true, misc_undefined: [ - "bounds", "all", + "bounds", ], }, }, shared_libs: [ + "libPlatformProperties", "libaconfig_storage_read_api_cc", "libbase", "libbinder", "libcutils", "liblog", - "libPlatformProperties", "libstatslog", "libtinyxml2", "libutils", "server_configurable_flags", ], data: [ - "data/*", ":motion_predictor_model", + "data/*", ], test_options: { unit_test: true, @@ -117,10 +117,10 @@ cc_library_static { "-Wextra", ], shared_libs: [ - "libinput", + "libbase", + "libbinder", "libcutils", + "libinput", "libutils", - "libbinder", - "libbase", ], } diff --git a/libs/input/tests/InputVerifier_test.cpp b/libs/input/tests/InputVerifier_test.cpp index e2eb08096b..8e0d9068c1 100644 --- a/libs/input/tests/InputVerifier_test.cpp +++ b/libs/input/tests/InputVerifier_test.cpp @@ -14,9 +14,13 @@ * limitations under the License. */ +#include <android/input.h> +#include <android-base/result.h> #include <gtest/gtest.h> +#include <input/Input.h> #include <input/InputVerifier.h> #include <string> +#include <vector> namespace android { @@ -45,10 +49,10 @@ TEST(InputVerifierTest, ProcessSourceClassPointer) { const Result<void> result = verifier.processMovement(/*deviceId=*/0, AINPUT_SOURCE_CLASS_POINTER, - AMOTION_EVENT_ACTION_DOWN, + AMOTION_EVENT_ACTION_DOWN, /*actionButton=*/0, /*pointerCount=*/properties.size(), properties.data(), - coords.data(), /*flags=*/0); - ASSERT_TRUE(result.ok()); + coords.data(), /*flags=*/0, /*buttonState=*/0); + ASSERT_RESULT_OK(result); } } // namespace android diff --git a/libs/input/tests/TestEventMatchers.h b/libs/input/tests/TestEventMatchers.h index 56eaefd074..8dbdcb310a 100644 --- a/libs/input/tests/TestEventMatchers.h +++ b/libs/input/tests/TestEventMatchers.h @@ -27,12 +27,8 @@ namespace android { -namespace { - using ::testing::Matcher; -} // namespace - /** * This file contains a copy of Matchers from .../inputflinger/tests/TestEventMatchers.h. Ideally, * implementations must not be duplicated. diff --git a/libs/math/OWNERS b/libs/math/OWNERS index 82ae422893..08f0c5f0dc 100644 --- a/libs/math/OWNERS +++ b/libs/math/OWNERS @@ -1,5 +1,4 @@ mathias@google.com -randolphs@google.com romainguy@google.com sumir@google.com jreck@google.com diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp index bed31e27a8..89a97deffd 100644 --- a/libs/nativedisplay/AChoreographer.cpp +++ b/libs/nativedisplay/AChoreographer.cpp @@ -142,7 +142,7 @@ static inline AChoreographer* Choreographer_to_AChoreographer(Choreographer* cho } AChoreographer* AChoreographer_getInstance() { - return Choreographer_to_AChoreographer(Choreographer::getForThread()); + return Choreographer_to_AChoreographer(Choreographer::getForThread().get()); } void AChoreographer_postFrameCallback(AChoreographer* choreographer, @@ -238,13 +238,17 @@ int64_t AChoreographerFrameCallbackData_getFrameTimelineDeadlineNanos( } AChoreographer* AChoreographer_create() { - Choreographer* choreographer = new Choreographer(nullptr); + // Increments default strongRef count on construction, will be decremented on + // function exit. + auto choreographer = sp<Choreographer>::make(nullptr); status_t result = choreographer->initialize(); if (result != OK) { ALOGW("Failed to initialize"); return nullptr; } - return Choreographer_to_AChoreographer(choreographer); + // Will be decremented and destroyed by AChoreographer_destroy + choreographer->incStrong((void*)AChoreographer_create); + return Choreographer_to_AChoreographer(choreographer.get()); } void AChoreographer_destroy(AChoreographer* choreographer) { @@ -252,7 +256,7 @@ void AChoreographer_destroy(AChoreographer* choreographer) { return; } - delete AChoreographer_to_Choreographer(choreographer); + AChoreographer_to_Choreographer(choreographer)->decStrong((void*)AChoreographer_create); } int AChoreographer_getFd(const AChoreographer* choreographer) { diff --git a/libs/nativedisplay/include/surfacetexture/EGLConsumer.h b/libs/nativedisplay/include/surfacetexture/EGLConsumer.h index 444722bf83..226a8a60af 100644 --- a/libs/nativedisplay/include/surfacetexture/EGLConsumer.h +++ b/libs/nativedisplay/include/surfacetexture/EGLConsumer.h @@ -113,18 +113,11 @@ public: protected: struct PendingRelease { - PendingRelease() - : isPending(false), - currentTexture(-1), - graphicBuffer(), - display(nullptr), - fence(nullptr) {} + PendingRelease() : isPending(false), currentTexture(-1), graphicBuffer() {} bool isPending; int currentTexture; sp<GraphicBuffer> graphicBuffer; - EGLDisplay display; - EGLSyncKHR fence; }; /** @@ -250,13 +243,16 @@ protected: * EGLConsumer maintains about a BufferQueue buffer slot. */ struct EglSlot { - EglSlot() : mEglFence(EGL_NO_SYNC_KHR) {} +#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) + EglSlot() : mEglFence(EGL_NO_SYNC_KHR) {} +#endif /** * mEglImage is the EGLImage created from mGraphicBuffer. */ sp<EglImage> mEglImage; +#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) /** * mFence is the EGL sync object that must signal before the buffer * associated with this buffer slot may be dequeued. It is initialized @@ -264,6 +260,7 @@ protected: * on a compile-time option) set to a new sync object in updateTexImage. */ EGLSyncKHR mEglFence; +#endif }; /** diff --git a/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h b/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h index 006a785cb7..253aa18a24 100644 --- a/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h +++ b/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h @@ -343,9 +343,13 @@ protected: * releaseBufferLocked overrides the ConsumerBase method to update the * mEglSlots array in addition to the ConsumerBase. */ +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) + virtual status_t releaseBufferLocked(int slot, const sp<GraphicBuffer> graphicBuffer) override; +#else virtual status_t releaseBufferLocked(int slot, const sp<GraphicBuffer> graphicBuffer, EGLDisplay display = EGL_NO_DISPLAY, EGLSyncKHR eglFence = EGL_NO_SYNC_KHR) override; +#endif /** * freeBufferLocked frees up the given buffer slot. If the slot has been diff --git a/libs/nativedisplay/surfacetexture/EGLConsumer.cpp b/libs/nativedisplay/surfacetexture/EGLConsumer.cpp index 3959fce008..fad0f6cd0b 100644 --- a/libs/nativedisplay/surfacetexture/EGLConsumer.cpp +++ b/libs/nativedisplay/surfacetexture/EGLConsumer.cpp @@ -221,7 +221,11 @@ void EGLConsumer::onAcquireBufferLocked(BufferItem* item, SurfaceTexture& st) { } void EGLConsumer::onReleaseBufferLocked(int buf) { +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) + (void)buf; +#else mEglSlots[buf].mEglFence = EGL_NO_SYNC_KHR; +#endif } status_t EGLConsumer::updateAndReleaseLocked(const BufferItem& item, PendingRelease* pendingRelease, @@ -283,10 +287,15 @@ status_t EGLConsumer::updateAndReleaseLocked(const BufferItem& item, PendingRele // release old buffer if (st.mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { if (pendingRelease == nullptr) { +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) + status_t status = st.releaseBufferLocked(st.mCurrentTexture, + mCurrentTextureImage->graphicBuffer()); +#else status_t status = st.releaseBufferLocked(st.mCurrentTexture, mCurrentTextureImage->graphicBuffer(), mEglDisplay, mEglSlots[st.mCurrentTexture].mEglFence); +#endif if (status < NO_ERROR) { EGC_LOGE("updateAndRelease: failed to release buffer: %s (%d)", strerror(-status), status); @@ -296,8 +305,6 @@ status_t EGLConsumer::updateAndReleaseLocked(const BufferItem& item, PendingRele } else { pendingRelease->currentTexture = st.mCurrentTexture; pendingRelease->graphicBuffer = mCurrentTextureImage->graphicBuffer(); - pendingRelease->display = mEglDisplay; - pendingRelease->fence = mEglSlots[st.mCurrentTexture].mEglFence; pendingRelease->isPending = true; } } @@ -502,6 +509,11 @@ status_t EGLConsumer::syncForReleaseLocked(EGLDisplay dpy, SurfaceTexture& st) { return err; } } else if (st.mUseFenceSync && SyncFeatures::getInstance().useFenceSync()) { +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) + // Basically all clients are using native fence syncs. If they aren't, we lose nothing + // by waiting here, because the alternative can cause deadlocks (b/339705065). + glFinish(); +#else EGLSyncKHR fence = mEglSlots[st.mCurrentTexture].mEglFence; if (fence != EGL_NO_SYNC_KHR) { // There is already a fence for the current slot. We need to @@ -531,6 +543,7 @@ status_t EGLConsumer::syncForReleaseLocked(EGLDisplay dpy, SurfaceTexture& st) { } glFlush(); mEglSlots[st.mCurrentTexture].mEglFence = fence; +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) } } diff --git a/libs/nativedisplay/surfacetexture/ImageConsumer.cpp b/libs/nativedisplay/surfacetexture/ImageConsumer.cpp index 60e87b54d5..1ffd382b80 100644 --- a/libs/nativedisplay/surfacetexture/ImageConsumer.cpp +++ b/libs/nativedisplay/surfacetexture/ImageConsumer.cpp @@ -14,6 +14,10 @@ * limitations under the License. */ +#define EGL_EGLEXT_PROTOTYPES +#include <EGL/egl.h> +#include <EGL/eglext.h> + #include <gui/BufferQueue.h> #include <surfacetexture/ImageConsumer.h> #include <surfacetexture/SurfaceTexture.h> @@ -95,10 +99,34 @@ sp<GraphicBuffer> ImageConsumer::dequeueBuffer(int* outSlotid, android_dataspace } // Finally release the old buffer. +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) + EGLSyncKHR previousFence = mImageSlots[st.mCurrentTexture].eglFence(); + if (previousFence != EGL_NO_SYNC_KHR) { + // Most platforms will be using native fences, so it's unlikely that we'll ever have to + // process an eglFence. Ideally we can remove this code eventually. In the mean time, do + // our best to wait for it so the buffer stays valid, otherwise return an error to the + // caller. + // + // EGL_SYNC_FLUSH_COMMANDS_BIT_KHR so that we don't wait forever on a fence that hasn't + // shown up on the GPU yet. + EGLint result = eglClientWaitSyncKHR(display, previousFence, + EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, 1000000000); + if (result == EGL_FALSE) { + IMG_LOGE("dequeueBuffer: error %#x waiting for fence", eglGetError()); + } else if (result == EGL_TIMEOUT_EXPIRED_KHR) { + IMG_LOGE("dequeueBuffer: timeout waiting for fence"); + } + eglDestroySyncKHR(display, previousFence); + } + + status_t status = st.releaseBufferLocked(st.mCurrentTexture, + st.mSlots[st.mCurrentTexture].mGraphicBuffer); +#else status_t status = st.releaseBufferLocked(st.mCurrentTexture, st.mSlots[st.mCurrentTexture].mGraphicBuffer, display, mImageSlots[st.mCurrentTexture].eglFence()); +#endif if (status < NO_ERROR) { IMG_LOGE("dequeueImage: failed to release buffer: %s (%d)", strerror(-status), status); err = status; diff --git a/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp b/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp index ce232cc4c7..c0a1cc5c36 100644 --- a/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp +++ b/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp @@ -178,13 +178,21 @@ status_t SurfaceTexture::acquireBufferLocked(BufferItem* item, nsecs_t presentWh return NO_ERROR; } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) +status_t SurfaceTexture::releaseBufferLocked(int buf, sp<GraphicBuffer> graphicBuffer) { +#else status_t SurfaceTexture::releaseBufferLocked(int buf, sp<GraphicBuffer> graphicBuffer, EGLDisplay display, EGLSyncKHR eglFence) { +#endif // release the buffer if it hasn't already been discarded by the // BufferQueue. This can happen, for example, when the producer of this // buffer has reallocated the original buffer slot after this buffer // was acquired. +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) + status_t err = ConsumerBase::releaseBufferLocked(buf, graphicBuffer); +#else status_t err = ConsumerBase::releaseBufferLocked(buf, graphicBuffer, display, eglFence); +#endif // We could be releasing an EGL/Vulkan buffer, even if not currently // attached to a GL context. mImageConsumer.onReleaseBufferLocked(buf); diff --git a/libs/nativewindow/rust/src/lib.rs b/libs/nativewindow/rust/src/lib.rs index d760285918..aeb26031a5 100644 --- a/libs/nativewindow/rust/src/lib.rs +++ b/libs/nativewindow/rust/src/lib.rs @@ -541,7 +541,7 @@ pub struct HardwareBufferGuard<'a> { pub address: NonNull<c_void>, } -impl<'a> Drop for HardwareBufferGuard<'a> { +impl Drop for HardwareBufferGuard<'_> { fn drop(&mut self) { self.buffer .unlock() diff --git a/libs/nativewindow/tests/ANativeWindowTest.cpp b/libs/nativewindow/tests/ANativeWindowTest.cpp index 937ff02241..51d0c8195a 100644 --- a/libs/nativewindow/tests/ANativeWindowTest.cpp +++ b/libs/nativewindow/tests/ANativeWindowTest.cpp @@ -50,14 +50,9 @@ protected: const ::testing::TestInfo* const test_info = ::testing::UnitTest::GetInstance()->current_test_info(); ALOGV("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); -#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) - mItemConsumer = new BufferItemConsumer(GRALLOC_USAGE_SW_READ_OFTEN); - mWindow = new TestableSurface(mItemConsumer->getSurface()->getIGraphicBufferProducer()); -#else - BufferQueue::createBufferQueue(&mProducer, &mConsumer); - mItemConsumer = new BufferItemConsumer(mConsumer, GRALLOC_USAGE_SW_READ_OFTEN); - mWindow = new TestableSurface(mProducer); -#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) + sp<Surface> surface; + std::tie(mItemConsumer, surface) = BufferItemConsumer::create(GRALLOC_USAGE_SW_READ_OFTEN); + mWindow = new TestableSurface(surface->getIGraphicBufferProducer()); const int success = native_window_api_connect(mWindow.get(), NATIVE_WINDOW_API_CPU); EXPECT_EQ(0, success); } diff --git a/libs/permission/Android.bp b/libs/permission/Android.bp index 0eeca5469e..929f067bcd 100644 --- a/libs/permission/Android.bp +++ b/libs/permission/Android.bp @@ -16,6 +16,7 @@ aidl_interface { double_loadable: true, srcs: [ "aidl/android/content/AttributionSourceState.aidl", + "aidl/com/android/internal/app/IAppOpsCallback.aidl", "aidl/android/permission/IPermissionChecker.aidl", ], } @@ -36,7 +37,6 @@ cc_library { ], srcs: [ "AppOpsManager.cpp", - "IAppOpsCallback.cpp", "IAppOpsService.cpp", "android/permission/PermissionChecker.cpp", ], diff --git a/libs/permission/IAppOpsCallback.cpp b/libs/permission/IAppOpsCallback.cpp deleted file mode 100644 index 2b3f462ab8..0000000000 --- a/libs/permission/IAppOpsCallback.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2013 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 "AppOpsCallback" - -#include <binder/IAppOpsCallback.h> - -#include <utils/Log.h> -#include <binder/Parcel.h> -#include <utils/String8.h> - -namespace android { - -// ---------------------------------------------------------------------- - -class BpAppOpsCallback : public BpInterface<IAppOpsCallback> -{ -public: - explicit BpAppOpsCallback(const sp<IBinder>& impl) - : BpInterface<IAppOpsCallback>(impl) - { - } - - virtual void opChanged(int32_t op, const String16& packageName) { - Parcel data, reply; - data.writeInterfaceToken(IAppOpsCallback::getInterfaceDescriptor()); - data.writeInt32(op); - data.writeString16(packageName); - remote()->transact(OP_CHANGED_TRANSACTION, data, &reply, IBinder::FLAG_ONEWAY); - } -}; - -IMPLEMENT_META_INTERFACE(AppOpsCallback, "com.android.internal.app.IAppOpsCallback") - -// ---------------------------------------------------------------------- - -// NOLINTNEXTLINE(google-default-arguments) -status_t BnAppOpsCallback::onTransact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) -{ - switch(code) { - case OP_CHANGED_TRANSACTION: { - CHECK_INTERFACE(IAppOpsCallback, data, reply); - int32_t op = data.readInt32(); - String16 packageName; - (void)data.readString16(&packageName); - opChanged(op, packageName); - return NO_ERROR; - } break; - default: - return BBinder::onTransact(code, data, reply, flags); - } -} - -} // namespace android diff --git a/libs/permission/aidl/com/android/internal/app/IAppOpsCallback.aidl b/libs/permission/aidl/com/android/internal/app/IAppOpsCallback.aidl new file mode 100644 index 0000000000..36b19dfb25 --- /dev/null +++ b/libs/permission/aidl/com/android/internal/app/IAppOpsCallback.aidl @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.app; + +oneway interface IAppOpsCallback { + void opChanged(int op, int uid, String packageName, String persistentDeviceId); +} diff --git a/libs/permission/include/binder/AppOpsManager.h b/libs/permission/include/binder/AppOpsManager.h index 7e179d6f7c..a22c975588 100644 --- a/libs/permission/include/binder/AppOpsManager.h +++ b/libs/permission/include/binder/AppOpsManager.h @@ -180,10 +180,10 @@ public: void finishOp(int32_t op, int32_t uid, const String16& callingPackage, const std::optional<String16>& attributionTag); void startWatchingMode(int32_t op, const String16& packageName, - const sp<IAppOpsCallback>& callback); + const sp<com::android::internal::app::IAppOpsCallback>& callback); void startWatchingMode(int32_t op, const String16& packageName, int32_t flags, - const sp<IAppOpsCallback>& callback); - void stopWatchingMode(const sp<IAppOpsCallback>& callback); + const sp<com::android::internal::app::IAppOpsCallback>& callback); + void stopWatchingMode(const sp<com::android::internal::app::IAppOpsCallback>& callback); int32_t permissionToOpCode(const String16& permission); void setCameraAudioRestriction(int32_t mode); diff --git a/libs/permission/include/binder/IAppOpsCallback.h b/libs/permission/include/binder/IAppOpsCallback.h deleted file mode 100644 index eb76f57bf8..0000000000 --- a/libs/permission/include/binder/IAppOpsCallback.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2013 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 - -#ifndef __ANDROID_VNDK__ - -#include <binder/IInterface.h> - -namespace android { - -// ---------------------------------------------------------------------- - -class IAppOpsCallback : public IInterface -{ -public: - DECLARE_META_INTERFACE(AppOpsCallback) - - virtual void opChanged(int32_t op, const String16& packageName) = 0; - - enum { - OP_CHANGED_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION - }; -}; - -// ---------------------------------------------------------------------- - -class BnAppOpsCallback : public BnInterface<IAppOpsCallback> -{ -public: - // NOLINTNEXTLINE(google-default-arguments) - virtual status_t onTransact( uint32_t code, - const Parcel& data, - Parcel* reply, - uint32_t flags = 0); -}; - -// ---------------------------------------------------------------------- - -} // namespace android - -#else // __ANDROID_VNDK__ -#error "This header is not visible to vendors" -#endif // __ANDROID_VNDK__ diff --git a/libs/permission/include/binder/IAppOpsService.h b/libs/permission/include/binder/IAppOpsService.h index 918fcdbce1..1468fd9415 100644 --- a/libs/permission/include/binder/IAppOpsService.h +++ b/libs/permission/include/binder/IAppOpsService.h @@ -16,7 +16,8 @@ #pragma once -#include <binder/IAppOpsCallback.h> +#include <com/android/internal/app/IAppOpsCallback.h> +#include <com/android/internal/app/BnAppOpsCallback.h> #include <binder/IInterface.h> #include <optional> @@ -27,6 +28,8 @@ namespace android { +using IAppOpsCallback = ::com::android::internal::app::IAppOpsCallback; + // ---------------------------------------------------------------------- class IAppOpsService : public IInterface diff --git a/libs/renderengine/OWNERS b/libs/renderengine/OWNERS index 17ab29fd29..e296283f26 100644 --- a/libs/renderengine/OWNERS +++ b/libs/renderengine/OWNERS @@ -7,4 +7,3 @@ jreck@google.com lpy@google.com nscobie@google.com sallyqi@google.com -scroggo@google.com diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h index ac43da8dcf..ecb16b2e17 100644 --- a/libs/renderengine/include/renderengine/LayerSettings.h +++ b/libs/renderengine/include/renderengine/LayerSettings.h @@ -301,6 +301,10 @@ static inline void PrintTo(const LayerSettings& settings, ::std::ostream* os) { *os << "\n .edgeExtensionEffect = " << settings.edgeExtensionEffect; } *os << "\n .whitePointNits = " << settings.whitePointNits; + if (settings.luts) { + *os << "\n .luts = "; + PrintTo(settings.luts, os); + } *os << "\n}"; } diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp index 57041ee6a1..f43694e228 100644 --- a/libs/renderengine/skia/Cache.cpp +++ b/libs/renderengine/skia/Cache.cpp @@ -14,6 +14,9 @@ * limitations under the License. */ #include "Cache.h" + +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + #include "AutoBackendTexture.h" #include "SkiaRenderEngine.h" #include "android-base/unique_fd.h" @@ -28,6 +31,7 @@ #include "utils/Timers.h" #include <com_android_graphics_libgui_flags.h> +#include <common/trace.h> namespace android::renderengine::skia { @@ -337,17 +341,17 @@ static void drawImageDimmedLayers(SkiaRenderEngine* renderengine, const DisplayS LayerSettings layer{ .geometry = Geometry{ + .boundaries = rect, // The position transform doesn't matter when the reduced shader mode // in in effect. A matrix transform stage is always included. .positionTransform = mat4(), - .boundaries = rect, - .roundedCornersCrop = rect, .roundedCornersRadius = {0.f, 0.f}, + .roundedCornersCrop = rect, }, .source = PixelSource{.buffer = Buffer{.buffer = srcTexture, - .maxLuminanceNits = 1000.f, .usePremultipliedAlpha = true, - .isOpaque = true}}, + .isOpaque = true, + .maxLuminanceNits = 1000.f}}, .alpha = 1.f, .sourceDataspace = kDestDataSpace, }; @@ -370,16 +374,16 @@ static void drawTransparentImageDimmedLayers(SkiaRenderEngine* renderengine, LayerSettings layer{ .geometry = Geometry{ - .positionTransform = mat4(), .boundaries = rect, + .positionTransform = mat4(), .roundedCornersCrop = rect, }, .source = PixelSource{.buffer = Buffer{ .buffer = srcTexture, - .maxLuminanceNits = 1000.f, .usePremultipliedAlpha = true, .isOpaque = false, + .maxLuminanceNits = 1000.f, }}, .sourceDataspace = kDestDataSpace, }; @@ -421,17 +425,17 @@ static void drawClippedDimmedImageLayers(SkiaRenderEngine* renderengine, LayerSettings layer{ .geometry = Geometry{ - .positionTransform = mat4(), .boundaries = boundary, - .roundedCornersCrop = rect, + .positionTransform = mat4(), .roundedCornersRadius = {27.f, 27.f}, + .roundedCornersCrop = rect, }, .source = PixelSource{.buffer = Buffer{ .buffer = srcTexture, - .maxLuminanceNits = 1000.f, .usePremultipliedAlpha = true, .isOpaque = false, + .maxLuminanceNits = 1000.f, }}, .alpha = 1.f, .sourceDataspace = kDestDataSpace, @@ -489,17 +493,17 @@ static void drawBT2020ImageLayers(SkiaRenderEngine* renderengine, const DisplayS LayerSettings layer{ .geometry = Geometry{ + .boundaries = rect, // The position transform doesn't matter when the reduced shader mode // in in effect. A matrix transform stage is always included. .positionTransform = mat4(), - .boundaries = rect, - .roundedCornersCrop = rect, .roundedCornersRadius = {0.f, 0.f}, + .roundedCornersCrop = rect, }, .source = PixelSource{.buffer = Buffer{.buffer = srcTexture, - .maxLuminanceNits = 1000.f, .usePremultipliedAlpha = true, - .isOpaque = true}}, + .isOpaque = true, + .maxLuminanceNits = 1000.f}}, .alpha = 1.f, .sourceDataspace = kBT2020DataSpace, }; @@ -527,17 +531,17 @@ static void drawBT2020ClippedImageLayers(SkiaRenderEngine* renderengine, LayerSettings layer{ .geometry = Geometry{ - .positionTransform = kScaleAsymmetric, .boundaries = boundary, - .roundedCornersCrop = rect, + .positionTransform = kScaleAsymmetric, .roundedCornersRadius = {64.1f, 64.1f}, + .roundedCornersCrop = rect, }, .source = PixelSource{.buffer = Buffer{ .buffer = srcTexture, - .maxLuminanceNits = 1000.f, .usePremultipliedAlpha = true, .isOpaque = true, + .maxLuminanceNits = 1000.f, }}, .alpha = 0.5f, .sourceDataspace = kBT2020DataSpace, @@ -556,17 +560,17 @@ static void drawExtendedHDRImageLayers(SkiaRenderEngine* renderengine, LayerSettings layer{ .geometry = Geometry{ + .boundaries = rect, // The position transform doesn't matter when the reduced shader mode // in in effect. A matrix transform stage is always included. .positionTransform = mat4(), - .boundaries = rect, - .roundedCornersCrop = rect, .roundedCornersRadius = {50.f, 50.f}, + .roundedCornersCrop = rect, }, .source = PixelSource{.buffer = Buffer{.buffer = srcTexture, - .maxLuminanceNits = 1000.f, .usePremultipliedAlpha = true, - .isOpaque = true}}, + .isOpaque = true, + .maxLuminanceNits = 1000.f}}, .alpha = 0.5f, .sourceDataspace = kExtendedHdrDataSpce, }; @@ -594,17 +598,17 @@ static void drawP3ImageLayers(SkiaRenderEngine* renderengine, const DisplaySetti LayerSettings layer{ .geometry = Geometry{ + .boundaries = rect, // The position transform doesn't matter when the reduced shader mode // in in effect. A matrix transform stage is always included. .positionTransform = mat4(), - .boundaries = rect, - .roundedCornersCrop = rect, .roundedCornersRadius = {50.f, 50.f}, + .roundedCornersCrop = rect, }, .source = PixelSource{.buffer = Buffer{.buffer = srcTexture, - .maxLuminanceNits = 1000.f, .usePremultipliedAlpha = true, - .isOpaque = false}}, + .isOpaque = false, + .maxLuminanceNits = 1000.f}}, .alpha = 0.5f, .sourceDataspace = kOtherDataSpace, }; @@ -659,6 +663,7 @@ static void drawEdgeExtensionLayers(SkiaRenderEngine* renderengine, const Displa // in external/skia/src/gpu/gl/builders/GrGLShaderStringBuilder.cpp // gPrintSKSL = true void Cache::primeShaderCache(SkiaRenderEngine* renderengine, PrimeCacheConfig config) { + SFTRACE_CALL(); const int previousCount = renderengine->reportShadersCompiled(); if (previousCount) { ALOGD("%d Shaders already compiled before Cache::primeShaderCache ran\n", previousCount); @@ -724,24 +729,29 @@ void Cache::primeShaderCache(SkiaRenderEngine* renderengine, PrimeCacheConfig co impl::ExternalTexture::Usage::WRITEABLE); if (config.cacheHolePunchLayer) { + SFTRACE_NAME("cacheHolePunchLayer"); drawHolePunchLayer(renderengine, display, dstTexture); } if (config.cacheSolidLayers) { + SFTRACE_NAME("cacheSolidLayers"); drawSolidLayers(renderengine, display, dstTexture); drawSolidLayers(renderengine, p3Display, dstTexture); } if (config.cacheSolidDimmedLayers) { + SFTRACE_NAME("cacheSolidDimmedLayers"); drawSolidDimmedLayers(renderengine, display, dstTexture); } if (config.cacheShadowLayers) { + SFTRACE_NAME("cacheShadowLayers"); drawShadowLayers(renderengine, display, srcTexture); drawShadowLayers(renderengine, p3Display, srcTexture); } if (renderengine->supportsBackgroundBlur()) { + SFTRACE_NAME("supportsBackgroundBlur"); drawBlurLayers(renderengine, display, dstTexture); } @@ -776,32 +786,37 @@ void Cache::primeShaderCache(SkiaRenderEngine* renderengine, PrimeCacheConfig co for (auto texture : textures) { if (config.cacheImageLayers) { + SFTRACE_NAME("cacheImageLayers"); drawImageLayers(renderengine, display, dstTexture, texture); } if (config.cacheImageDimmedLayers) { + SFTRACE_NAME("cacheImageDimmedLayers"); drawImageDimmedLayers(renderengine, display, dstTexture, texture); drawImageDimmedLayers(renderengine, p3Display, dstTexture, texture); drawImageDimmedLayers(renderengine, bt2020Display, dstTexture, texture); } if (config.cacheClippedLayers) { + SFTRACE_NAME("cacheClippedLayers"); // Draw layers for b/185569240. drawClippedLayers(renderengine, display, dstTexture, texture); } - if (com::android::graphics::libgui::flags::edge_extension_shader() && - config.cacheEdgeExtension) { + if (config.cacheEdgeExtension) { + SFTRACE_NAME("cacheEdgeExtension"); drawEdgeExtensionLayers(renderengine, display, dstTexture, texture); drawEdgeExtensionLayers(renderengine, p3Display, dstTexture, texture); } } if (config.cachePIPImageLayers) { + SFTRACE_NAME("cachePIPImageLayers"); drawPIPImageLayer(renderengine, display, dstTexture, externalTexture); } if (config.cacheTransparentImageDimmedLayers) { + SFTRACE_NAME("cacheTransparentImageDimmedLayers"); drawTransparentImageDimmedLayers(renderengine, bt2020Display, dstTexture, externalTexture); drawTransparentImageDimmedLayers(renderengine, display, dstTexture, externalTexture); @@ -811,10 +826,12 @@ void Cache::primeShaderCache(SkiaRenderEngine* renderengine, PrimeCacheConfig co } if (config.cacheClippedDimmedImageLayers) { + SFTRACE_NAME("cacheClippedDimmedImageLayers"); drawClippedDimmedImageLayers(renderengine, bt2020Display, dstTexture, externalTexture); } if (config.cacheUltraHDR) { + SFTRACE_NAME("cacheUltraHDR"); drawBT2020ClippedImageLayers(renderengine, bt2020Display, dstTexture, externalTexture); drawBT2020ImageLayers(renderengine, bt2020Display, dstTexture, externalTexture); @@ -833,7 +850,10 @@ void Cache::primeShaderCache(SkiaRenderEngine* renderengine, PrimeCacheConfig co }; auto layers = std::vector<LayerSettings>{layer}; // call get() to make it synchronous - renderengine->drawLayers(display, layers, dstTexture, base::unique_fd()).get(); + { + SFTRACE_NAME("finalLayer"); + renderengine->drawLayers(display, layers, dstTexture, base::unique_fd()).get(); + } const nsecs_t timeAfter = systemTime(); const float compileTimeMs = static_cast<float>(timeAfter - timeBefore) / 1.0E6; diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp index 14d08eea74..25afc7b465 100644 --- a/libs/renderengine/skia/SkiaRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaRenderEngine.cpp @@ -347,6 +347,7 @@ void SkiaRenderEngine::useProtectedContext(bool useProtectedContext) { if (useProtectedContextImpl( useProtectedContext ? GrProtected::kYes : GrProtected::kNo)) { mInProtectedContext = useProtectedContext; + SFTRACE_INT("RE inProtectedContext", mInProtectedContext); // given that we are sharing the same thread between two contexts we need to // make sure that the thread state is reset when switching between the two. if (getActiveContext()) { @@ -838,8 +839,7 @@ void SkiaRenderEngine::drawLayersInternal( LOG_ALWAYS_FATAL_IF(activeSurface == dstSurface); LOG_ALWAYS_FATAL_IF(canvas == dstCanvas); - // save a snapshot of the activeSurface to use as input to the blur shaders - blurInput = activeSurface->makeImageSnapshot(); + blurInput = activeSurface->makeTemporaryImage(); // blit the offscreen framebuffer into the destination AHB. This ensures that // even if the blurred image does not cover the screen (for example, during @@ -853,12 +853,9 @@ void SkiaRenderEngine::drawLayersInternal( dstCanvas->drawAnnotation(SkRect::Make(dstCanvas->imageInfo().dimensions()), String8::format("SurfaceID|%" PRId64, id).c_str(), nullptr); - dstCanvas->drawImage(blurInput, 0, 0, SkSamplingOptions(), &paint); - } else { - activeSurface->draw(dstCanvas, 0, 0, SkSamplingOptions(), &paint); } + dstCanvas->drawImage(blurInput, 0, 0, SkSamplingOptions(), &paint); } - // assign dstCanvas to canvas and ensure that the canvas state is up to date canvas = dstCanvas; surfaceAutoSaveRestore.replace(canvas); @@ -891,12 +888,6 @@ void SkiaRenderEngine::drawLayersInternal( if (mBlurFilter && layerHasBlur(layer, ctModifiesAlpha)) { std::unordered_map<uint32_t, sk_sp<SkImage>> cachedBlurs; - // if multiple layers have blur, then we need to take a snapshot now because - // only the lowest layer will have blurImage populated earlier - if (!blurInput) { - blurInput = activeSurface->makeImageSnapshot(); - } - // rect to be blurred in the coordinate space of blurInput SkRect blurRect = canvas->getTotalMatrix().mapRect(bounds.rect()); @@ -920,6 +911,29 @@ void SkiaRenderEngine::drawLayersInternal( // TODO(b/182216890): Filter out empty layers earlier if (blurRect.width() > 0 && blurRect.height() > 0) { + // if multiple layers have blur, then we need to take a snapshot now because + // only the lowest layer will have blurImage populated earlier + if (!blurInput) { + bool requiresCrossFadeWithBlurInput = false; + if (layer.backgroundBlurRadius > 0 && + layer.backgroundBlurRadius < mBlurFilter->getMaxCrossFadeRadius()) { + requiresCrossFadeWithBlurInput = true; + } + for (auto region : layer.blurRegions) { + if (region.blurRadius < mBlurFilter->getMaxCrossFadeRadius()) { + requiresCrossFadeWithBlurInput = true; + } + } + if (requiresCrossFadeWithBlurInput) { + // If we require cross fading with the blur input, we need to make sure we + // make a copy of the surface to the image since we will be writing to the + // surface while sampling the blurInput. + blurInput = activeSurface->makeImageSnapshot(); + } else { + blurInput = activeSurface->makeTemporaryImage(); + } + } + if (layer.backgroundBlurRadius > 0) { SFTRACE_NAME("BackgroundBlur"); auto blurredImage = mBlurFilter->generate(context, layer.backgroundBlurRadius, diff --git a/libs/renderengine/skia/compat/GraphiteGpuContext.cpp b/libs/renderengine/skia/compat/GraphiteGpuContext.cpp index 69f583226b..7a72d09804 100644 --- a/libs/renderengine/skia/compat/GraphiteGpuContext.cpp +++ b/libs/renderengine/skia/compat/GraphiteGpuContext.cpp @@ -110,8 +110,40 @@ bool GraphiteGpuContext::isAbandonedOrDeviceLost() { return mContext->isDeviceLost(); } +void GraphiteGpuContext::setResourceCacheLimit(size_t maxResourceBytes) { + // Graphite has a separate budget for its Context and its Recorder. For now the majority of + // memory that Graphite will allocate will be on the Recorder and minimal amount on the Context. + // The main allocations on the Context are MSAA buffers (not often, if ever used in + // RenderEngine) and stencil buffers. However, both of these should be "memoryless" in Vulkan on + // tiled GPUs, so they don't actually use GPU memory. However, in Vulkan there are scenarios + // where Vulkan could end up using real memory for them. Skia will regularly query the device to + // get the real memory usage and update the budgeted appropriately. Though for all real usage + // patterns we don't expect to ever trigger the device to allocate real memory. + // + // Therefore, we set the full maxResourceBytes budget on the Recorder. However, in the rare + // chance that the devcies does allocate real memory we don't want to immediately kill device + // performance by constantly trashing allocations on the Context. Thus we set the Context's + // budget to be 50% of the total budget to make sure we allow the MSAA or Stencil buffers to be + // allocated in Skia and not immediately discarded. But even with this extra 50% budget, as + // described above, this shouldn't result in actual GPU memory usage. + // + // TODO: We will need to revise this strategy for GLES which does not have the same memoryless + // textures. + // TODO: Work in Graphite has started to move a lot more of its scratch resources to be owned + // by the Context and not on Recorders. This will mean most memory is actually owned by the + // Context and thus the budgeting here will need to be updated. + mContext->setMaxBudgetedBytes(maxResourceBytes / 2); + mRecorder->setMaxBudgetedBytes(maxResourceBytes); +} + +void GraphiteGpuContext::purgeUnlockedScratchResources() { + mContext->freeGpuResources(); + mRecorder->freeGpuResources(); +} + void GraphiteGpuContext::dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const { mContext->dumpMemoryStatistics(traceMemoryDump); + mRecorder->dumpMemoryStatistics(traceMemoryDump); } } // namespace android::renderengine::skia diff --git a/libs/renderengine/skia/compat/GraphiteGpuContext.h b/libs/renderengine/skia/compat/GraphiteGpuContext.h index 413817ffff..57da796af5 100644 --- a/libs/renderengine/skia/compat/GraphiteGpuContext.h +++ b/libs/renderengine/skia/compat/GraphiteGpuContext.h @@ -39,16 +39,10 @@ public: size_t getMaxRenderTargetSize() const override; size_t getMaxTextureSize() const override; bool isAbandonedOrDeviceLost() override; - // No-op (large resources like textures, surfaces, images, etc. created by clients don't count - // towards Graphite's internal caching budgets, so adjusting its limits based on display change - // events should be unnecessary. Additionally, Graphite doesn't expose many cache tweaking - // functions yet, as its design may evolve.) - void setResourceCacheLimit(size_t maxResourceBytes) override{}; - // TODO: b/293371537 - Triple-check and validate that no cleanup is necessary when switching - // contexts. - // No-op (unnecessary during context switch for Graphite's client-budgeted memory model). - void purgeUnlockedScratchResources() override{}; + void setResourceCacheLimit(size_t maxResourceBytes) override; + void purgeUnlockedScratchResources() override; + // No-op (only applicable to GL). void resetContextIfApplicable() override{}; diff --git a/libs/renderengine/skia/debug/CaptureTimer.cpp b/libs/renderengine/skia/debug/CaptureTimer.cpp index 11bcdb8996..1c1ee0abb3 100644 --- a/libs/renderengine/skia/debug/CaptureTimer.cpp +++ b/libs/renderengine/skia/debug/CaptureTimer.cpp @@ -30,7 +30,7 @@ namespace skia { void CaptureTimer::setTimeout(TimeoutCallback function, std::chrono::milliseconds delay) { this->clear = false; - CommonPool::post([=]() { + CommonPool::post([=,this]() { if (this->clear) return; std::this_thread::sleep_for(delay); if (this->clear) return; diff --git a/libs/renderengine/skia/filters/BlurFilter.cpp b/libs/renderengine/skia/filters/BlurFilter.cpp index cd1bd71807..8edf98eb89 100644 --- a/libs/renderengine/skia/filters/BlurFilter.cpp +++ b/libs/renderengine/skia/filters/BlurFilter.cpp @@ -90,6 +90,8 @@ void BlurFilter::drawBlurRegion(SkCanvas* canvas, const SkRRect& effectRegion, linearSampling, &blurMatrix); if (blurRadius < mMaxCrossFadeRadius) { + LOG_ALWAYS_FATAL_IF(!input); + // For sampling Skia's API expects the inverse of what logically seems appropriate. In this // case you might expect the matrix to simply be the canvas matrix. SkMatrix inputMatrix; diff --git a/libs/renderengine/skia/filters/EdgeExtensionShaderFactory.cpp b/libs/renderengine/skia/filters/EdgeExtensionShaderFactory.cpp index 4164c4b4c9..f007427f1c 100644 --- a/libs/renderengine/skia/filters/EdgeExtensionShaderFactory.cpp +++ b/libs/renderengine/skia/filters/EdgeExtensionShaderFactory.cpp @@ -58,9 +58,6 @@ static const SkString edgeShader = SkString(R"( )"); EdgeExtensionShaderFactory::EdgeExtensionShaderFactory() { - if (!com::android::graphics::libgui::flags::edge_extension_shader()) { - return; - } mResult = std::make_unique<SkRuntimeEffect::Result>(SkRuntimeEffect::MakeForShader(edgeShader)); LOG_ALWAYS_FATAL_IF(!mResult->errorText.isEmpty(), "EdgeExtensionShaderFactory compilation " diff --git a/libs/renderengine/skia/filters/GaussianBlurFilter.cpp b/libs/renderengine/skia/filters/GaussianBlurFilter.cpp index 8c52c571a9..b03ebe3353 100644 --- a/libs/renderengine/skia/filters/GaussianBlurFilter.cpp +++ b/libs/renderengine/skia/filters/GaussianBlurFilter.cpp @@ -64,7 +64,7 @@ sk_sp<SkImage> GaussianBlurFilter::generate(SkiaGpuContext* context, const uint3 SkSamplingOptions{SkFilterMode::kLinear, SkMipmapMode::kNone}, &paint, SkCanvas::SrcRectConstraint::kFast_SrcRectConstraint); - return surface->makeImageSnapshot(); + return surface->makeTemporaryImage(); } } // namespace skia diff --git a/libs/renderengine/skia/filters/KawaseBlurDualFilter.cpp b/libs/renderengine/skia/filters/KawaseBlurDualFilter.cpp index 750e08fa55..ff96b08283 100644 --- a/libs/renderengine/skia/filters/KawaseBlurDualFilter.cpp +++ b/libs/renderengine/skia/filters/KawaseBlurDualFilter.cpp @@ -39,12 +39,36 @@ namespace renderengine { namespace skia { KawaseBlurDualFilter::KawaseBlurDualFilter() : BlurFilter() { - // A shader to sample each vertex of a unit regular heptagon - // plus the original fragment coordinate. - SkString blurString(R"( + // A shader to sample each vertex of a square, plus the original fragment coordinate, + // using a total of 5 samples. + SkString lowSampleBlurString(R"( uniform shader child; uniform float in_blurOffset; uniform float in_crossFade; + uniform float in_weightedCrossFade; + + const float2 STEP_0 = float2( 0.707106781, 0.707106781); + const float2 STEP_1 = float2( 0.707106781, -0.707106781); + const float2 STEP_2 = float2(-0.707106781, -0.707106781); + const float2 STEP_3 = float2(-0.707106781, 0.707106781); + + half4 main(float2 xy) { + half3 c = child.eval(xy).rgb; + + c += child.eval(xy + STEP_0 * in_blurOffset).rgb; + c += child.eval(xy + STEP_1 * in_blurOffset).rgb; + c += child.eval(xy + STEP_2 * in_blurOffset).rgb; + c += child.eval(xy + STEP_3 * in_blurOffset).rgb; + + return half4(c * in_weightedCrossFade, in_crossFade); + } + )"); + + // A shader to sample each vertex of a unit regular heptagon, plus the original fragment + // coordinate, using a total of 8 samples. + SkString highSampleBlurString(R"( + uniform shader child; + uniform float in_blurOffset; const float2 STEP_0 = float2( 1.0, 0.0); const float2 STEP_1 = float2( 0.623489802, 0.781831482); @@ -65,39 +89,46 @@ KawaseBlurDualFilter::KawaseBlurDualFilter() : BlurFilter() { c += child.eval(xy + STEP_5 * in_blurOffset).rgb; c += child.eval(xy + STEP_6 * in_blurOffset).rgb; - return half4(c * 0.125 * in_crossFade, in_crossFade); + return half4(c * 0.125, 1.0); } )"); - auto [blurEffect, error] = SkRuntimeEffect::MakeForShader(blurString); - LOG_ALWAYS_FATAL_IF(!blurEffect, "RuntimeShader error: %s", error.c_str()); - mBlurEffect = std::move(blurEffect); + auto [lowSampleBlurEffect, error] = SkRuntimeEffect::MakeForShader(lowSampleBlurString); + auto [highSampleBlurEffect, error2] = SkRuntimeEffect::MakeForShader(highSampleBlurString); + LOG_ALWAYS_FATAL_IF(!lowSampleBlurEffect, "RuntimeShader error: %s", error.c_str()); + LOG_ALWAYS_FATAL_IF(!highSampleBlurEffect, "RuntimeShader error: %s", error2.c_str()); + mLowSampleBlurEffect = std::move(lowSampleBlurEffect); + mHighSampleBlurEffect = std::move(highSampleBlurEffect); } void KawaseBlurDualFilter::blurInto(const sk_sp<SkSurface>& drawSurface, const sk_sp<SkImage>& readImage, const float radius, - const float alpha) const { + const float alpha, + const sk_sp<SkRuntimeEffect>& blurEffect) const { const float scale = static_cast<float>(drawSurface->width()) / readImage->width(); SkMatrix blurMatrix = SkMatrix::Scale(scale, scale); blurInto(drawSurface, readImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone), blurMatrix), - readImage->width() / static_cast<float>(drawSurface->width()), radius, alpha); + radius, alpha, blurEffect); } void KawaseBlurDualFilter::blurInto(const sk_sp<SkSurface>& drawSurface, sk_sp<SkShader> input, - const float inverseScale, const float radius, - const float alpha) const { + const float radius, const float alpha, + const sk_sp<SkRuntimeEffect>& blurEffect) const { SkPaint paint; if (radius == 0) { paint.setShader(std::move(input)); paint.setAlphaf(alpha); } else { - SkRuntimeShaderBuilder blurBuilder(mBlurEffect); + SkRuntimeShaderBuilder blurBuilder(blurEffect); blurBuilder.child("child") = std::move(input); + if (blurEffect == mLowSampleBlurEffect) { + blurBuilder.uniform("in_crossFade") = alpha; + blurBuilder.uniform("in_weightedCrossFade") = alpha * 0.2f; + } blurBuilder.uniform("in_blurOffset") = radius; - blurBuilder.uniform("in_crossFade") = alpha; paint.setShader(blurBuilder.makeShader(nullptr)); } paint.setBlendMode(alpha == 1.0f ? SkBlendMode::kSrc : SkBlendMode::kSrcOver); @@ -118,9 +149,12 @@ sk_sp<SkImage> KawaseBlurDualFilter::generate(SkiaGpuContext* context, const uin const int filterPasses = std::min(kMaxSurfaces - 1, static_cast<int>(ceil(filterDepth))); auto makeSurface = [&](float scale) -> sk_sp<SkSurface> { - const auto newW = static_cast<float>(blurRect.width() / scale); - const auto newH = static_cast<float>(blurRect.height() / scale); - return context->createRenderTarget(input->imageInfo().makeWH(newW, newH)); + const auto newW = ceil(static_cast<float>(blurRect.width() / scale)); + const auto newH = ceil(static_cast<float>(blurRect.height() / scale)); + sk_sp<SkSurface> surface = + context->createRenderTarget(input->imageInfo().makeWH(newW, newH)); + LOG_ALWAYS_FATAL_IF(!surface, "%s: Failed to create surface for blurring!", __func__); + return surface; }; // Render into surfaces downscaled by 1x, 2x, and 4x from the initial downscale. @@ -160,18 +194,21 @@ sk_sp<SkImage> KawaseBlurDualFilter::generate(SkiaGpuContext* context, const uin input->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone), blurMatrix); - blurInto(surfaces[0], std::move(sourceShader), kInputScale, kWeights[0] * step, 1.0f); + blurInto(surfaces[0], std::move(sourceShader), kWeights[0] * step, 1.0f, + mLowSampleBlurEffect); } // Next the remaining downscale blur passes. for (int i = 0; i < filterPasses; i++) { - blurInto(surfaces[i + 1], surfaces[i]->makeImageSnapshot(), kWeights[1 + i] * step, 1.0f); + // Blur with the higher sample effect into the smaller buffers, for better visual quality. + blurInto(surfaces[i + 1], surfaces[i]->makeTemporaryImage(), kWeights[1 + i] * step, 1.0f, + i == 0 ? mLowSampleBlurEffect : mHighSampleBlurEffect); } // Finally blur+upscale back to our original size. for (int i = filterPasses - 1; i >= 0; i--) { - blurInto(surfaces[i], surfaces[i + 1]->makeImageSnapshot(), kWeights[4 - i] * step, - std::min(1.0f, filterDepth - i)); + blurInto(surfaces[i], surfaces[i + 1]->makeTemporaryImage(), kWeights[4 - i] * step, + std::min(1.0f, filterDepth - i), mLowSampleBlurEffect); } - return surfaces[0]->makeImageSnapshot(); + return surfaces[0]->makeTemporaryImage(); } } // namespace skia diff --git a/libs/renderengine/skia/filters/KawaseBlurDualFilter.h b/libs/renderengine/skia/filters/KawaseBlurDualFilter.h index 6f4adbf34c..5efda35376 100644 --- a/libs/renderengine/skia/filters/KawaseBlurDualFilter.h +++ b/libs/renderengine/skia/filters/KawaseBlurDualFilter.h @@ -41,13 +41,14 @@ public: const sk_sp<SkImage> blurInput, const SkRect& blurRect) const override; private: - sk_sp<SkRuntimeEffect> mBlurEffect; + sk_sp<SkRuntimeEffect> mLowSampleBlurEffect; + sk_sp<SkRuntimeEffect> mHighSampleBlurEffect; void blurInto(const sk_sp<SkSurface>& drawSurface, const sk_sp<SkImage>& readImage, - const float radius, const float alpha) const; + const float radius, const float alpha, const sk_sp<SkRuntimeEffect>&) const; void blurInto(const sk_sp<SkSurface>& drawSurface, const sk_sp<SkShader> input, - const float inverseScale, const float radius, const float alpha) const; + const float radius, const float alpha, const sk_sp<SkRuntimeEffect>&) const; }; } // namespace skia diff --git a/libs/renderengine/skia/filters/KawaseBlurFilter.cpp b/libs/renderengine/skia/filters/KawaseBlurFilter.cpp index defaf6e476..f71a63d591 100644 --- a/libs/renderengine/skia/filters/KawaseBlurFilter.cpp +++ b/libs/renderengine/skia/filters/KawaseBlurFilter.cpp @@ -70,7 +70,7 @@ static sk_sp<SkImage> makeImage(SkSurface* surface, SkRuntimeShaderBuilder* buil paint.setShader(std::move(shader)); paint.setBlendMode(SkBlendMode::kSrc); surface->getCanvas()->drawPaint(paint); - return surface->makeImageSnapshot(); + return surface->makeTemporaryImage(); } sk_sp<SkImage> KawaseBlurFilter::generate(SkiaGpuContext* context, const uint32_t blurRadius, diff --git a/libs/renderengine/skia/filters/LutShader.cpp b/libs/renderengine/skia/filters/LutShader.cpp index 10218bb822..f262158f2c 100644 --- a/libs/renderengine/skia/filters/LutShader.cpp +++ b/libs/renderengine/skia/filters/LutShader.cpp @@ -182,8 +182,8 @@ sk_sp<SkShader> LutShader::generateLutShader(sk_sp<SkShader> input, * (R1, G1, B1, 0) * ... */ - SkImageInfo info = SkImageInfo::Make(length /* the number of rgba */ * 4, 1, - kRGBA_F16_SkColorType, kPremul_SkAlphaType); + SkImageInfo info = SkImageInfo::Make(length /* the number of rgba */, 1, kRGBA_F16_SkColorType, + kPremul_SkAlphaType); SkBitmap bitmap; bitmap.allocPixels(info); if (!bitmap.installPixels(info, buffer.data(), info.minRowBytes())) { diff --git a/libs/renderengine/skia/filters/MouriMap.cpp b/libs/renderengine/skia/filters/MouriMap.cpp index aa12cef615..5dc36e6358 100644 --- a/libs/renderengine/skia/filters/MouriMap.cpp +++ b/libs/renderengine/skia/filters/MouriMap.cpp @@ -104,7 +104,7 @@ sk_sp<SkImage> makeImage(SkSurface* surface, const SkRuntimeShaderBuilder& build paint.setShader(std::move(shader)); paint.setBlendMode(SkBlendMode::kSrc); surface->getCanvas()->drawPaint(paint); - return surface->makeImageSnapshot(); + return surface->makeTemporaryImage(); } } // namespace diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp index ea5605d4bd..67f4aa10f8 100644 --- a/libs/renderengine/threaded/RenderEngineThreaded.cpp +++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp @@ -23,6 +23,7 @@ #include <future> #include <android-base/stringprintf.h> +#include <common/FlagManager.h> #include <common/trace.h> #include <private/gui/SyncFeatures.h> #include <processgroup/processgroup.h> @@ -60,7 +61,7 @@ status_t RenderEngineThreaded::setSchedFifo(bool enabled) { struct sched_param param = {0}; int sched_policy; - if (enabled) { + if (enabled && !FlagManager::getInstance().disable_sched_fifo_re()) { sched_policy = SCHED_FIFO; param.sched_priority = kFifoPriority; } else { diff --git a/libs/sensor/OWNERS b/libs/sensor/OWNERS index 7347ac74e6..4929b3fc92 100644 --- a/libs/sensor/OWNERS +++ b/libs/sensor/OWNERS @@ -1 +1 @@ -bduddie@google.com +include platform/frameworks/native:/services/sensorservice/OWNERS
\ No newline at end of file diff --git a/libs/shaders/OWNERS b/libs/shaders/OWNERS index 6d91da3bd2..6977a49cc9 100644 --- a/libs/shaders/OWNERS +++ b/libs/shaders/OWNERS @@ -1,4 +1,3 @@ alecmouri@google.com jreck@google.com sallyqi@google.com -scroggo@google.com
\ No newline at end of file diff --git a/libs/tonemap/OWNERS b/libs/tonemap/OWNERS index 6d91da3bd2..6977a49cc9 100644 --- a/libs/tonemap/OWNERS +++ b/libs/tonemap/OWNERS @@ -1,4 +1,3 @@ alecmouri@google.com jreck@google.com sallyqi@google.com -scroggo@google.com
\ No newline at end of file diff --git a/libs/tracing_perfetto/include/tracing_sdk.h b/libs/tracing_perfetto/include/tracing_sdk.h index 4a6e849567..271d7c8563 100644 --- a/libs/tracing_perfetto/include/tracing_sdk.h +++ b/libs/tracing_perfetto/include/tracing_sdk.h @@ -27,6 +27,10 @@ #include "perfetto/public/te_macros.h" #include "perfetto/public/track_event.h" +#include "perfetto/public/abi/pb_decoder_abi.h" +#include "perfetto/public/pb_utils.h" +#include "perfetto/public/tracing_session.h" + /** * The objects declared here are intended to be managed by Java. * This means the Java Garbage Collector is responsible for freeing the @@ -288,13 +292,8 @@ class DebugArg { arg_ = std::move(arg); } - ~DebugArg() { - free_string_value(); - } - void set_value(T value) { if constexpr (std::is_same_v<T, const char*>) { - free_string_value(); arg_.value = value; } else if constexpr (std::is_same_v<T, int64_t>) { arg_.value = value; @@ -317,16 +316,6 @@ class DebugArg { DISALLOW_COPY_AND_ASSIGN(DebugArg); TypeMap::type arg_; const std::string name_; - - constexpr void free_string_value() { - if constexpr (std::is_same_v<typename TypeMap::type, - PerfettoTeHlExtraDebugArgString>) { - if (arg_.value) { - free((void*)arg_.value); - arg_.value = nullptr; - } - } - } }; template <typename T> @@ -371,10 +360,6 @@ class ProtoField { arg_ = std::move(arg); } - ~ProtoField() { - free_string_value(); - } - void set_value(uint32_t id, T value) { if constexpr (std::is_same_v<T, int64_t>) { arg_.header.id = id; @@ -383,7 +368,6 @@ class ProtoField { arg_.header.id = id; arg_.value = value; } else if constexpr (std::is_same_v<T, const char*>) { - free_string_value(); arg_.header.id = id; arg_.str = value; } @@ -400,16 +384,6 @@ class ProtoField { private: DISALLOW_COPY_AND_ASSIGN(ProtoField); TypeMap::type arg_; - - constexpr void free_string_value() { - if constexpr (std::is_same_v<typename TypeMap::type, - PerfettoTeHlProtoFieldCstr>) { - if (arg_.str) { - free((void*)arg_.str); - arg_.str = nullptr; - } - } - } }; class ProtoFieldNested { @@ -452,6 +426,22 @@ class Proto { std::vector<PerfettoTeHlProtoField*> fields_; }; +class Session { + public: + Session(bool is_backend_in_process, void* buf, size_t len); + ~Session(); + Session(const Session&) = delete; + Session& operator=(const Session&) = delete; + + bool FlushBlocking(uint32_t timeout_ms); + void StopBlocking(); + std::vector<uint8_t> ReadBlocking(); + + static void delete_session(Session* session); + + struct PerfettoTracingSessionImpl* session_ = nullptr; +}; + /** * @brief Activates a trigger. * @param name The name of the trigger. diff --git a/libs/tracing_perfetto/tests/Android.bp b/libs/tracing_perfetto/tests/Android.bp index 0dab517b7f..79fb704906 100644 --- a/libs/tracing_perfetto/tests/Android.bp +++ b/libs/tracing_perfetto/tests/Android.bp @@ -21,44 +21,12 @@ package { default_applicable_licenses: ["frameworks_native_license"], } -cc_library_static { - name: "libtracing_perfetto_test_utils", - export_include_dirs: [ - "include", - ], - static_libs: [ - "libflagtest", - "libgmock", - "perfetto_trace_protos", - ], - cflags: [ - "-Wall", - "-Werror", - "-Wno-enum-compare", - "-Wno-unused-function", - ], - - srcs: [ - "utils.cpp", - ], - - shared_libs: [ - "libperfetto_c", - "liblog", - "libprotobuf-cpp-lite", - ], - export_shared_lib_headers: [ - "libperfetto_c", - ], -} - cc_test { name: "libtracing_perfetto_tests", static_libs: [ "libflagtest", "libgmock", "perfetto_trace_protos", - "libtracing_perfetto_test_utils", ], cflags: [ "-Wall", @@ -68,12 +36,15 @@ cc_test { "android.os.flags-aconfig-cc-host", "libbase", "libperfetto_c", - "liblog", "libprotobuf-cpp-lite", "libtracing_perfetto", ], srcs: [ "tracing_perfetto_test.cpp", + "utils.cpp", + ], + local_include_dirs: [ + "include", ], test_suites: ["device-tests"], } diff --git a/libs/tracing_perfetto/tracing_perfetto_internal.cpp b/libs/tracing_perfetto/tracing_perfetto_internal.cpp index f92f6df990..447873290c 100644 --- a/libs/tracing_perfetto/tracing_perfetto_internal.cpp +++ b/libs/tracing_perfetto/tracing_perfetto_internal.cpp @@ -14,15 +14,16 @@ * limitations under the License. */ +// Should match the definitions in: frameworks/native/cmds/atrace/atrace.cpp #define FRAMEWORK_CATEGORIES(C) \ C(always, "always", "Always category") \ - C(graphics, "graphics", "Graphics category") \ + C(graphics, "gfx", "Graphics category") \ C(input, "input", "Input category") \ C(view, "view", "View category") \ C(webview, "webview", "WebView category") \ C(windowmanager, "wm", "WindowManager category") \ C(activitymanager, "am", "ActivityManager category") \ - C(syncmanager, "syncmanager", "SyncManager category") \ + C(syncmanager, "sm", "SyncManager category") \ C(audio, "audio", "Audio category") \ C(video, "video", "Video category") \ C(camera, "camera", "Camera category") \ @@ -33,7 +34,7 @@ C(rs, "rs", "RS category") \ C(bionic, "bionic", "Bionic category") \ C(power, "power", "Power category") \ - C(packagemanager, "packagemanager", "PackageManager category") \ + C(packagemanager, "pm", "PackageManager category") \ C(systemserver, "ss", "System Server category") \ C(database, "database", "Database category") \ C(network, "network", "Network category") \ diff --git a/libs/tracing_perfetto/tracing_perfetto_internal.h b/libs/tracing_perfetto/tracing_perfetto_internal.h index 3e1ac2a112..0e3fb07efd 100644 --- a/libs/tracing_perfetto/tracing_perfetto_internal.h +++ b/libs/tracing_perfetto/tracing_perfetto_internal.h @@ -25,8 +25,6 @@ namespace tracing_perfetto { namespace internal { -bool isPerfettoRegistered(); - struct PerfettoTeCategory* toPerfettoCategory(uint64_t category); void registerWithPerfetto(bool test = false); diff --git a/libs/tracing_perfetto/tracing_sdk.cpp b/libs/tracing_perfetto/tracing_sdk.cpp index 02e8d10aa4..70b8be981b 100644 --- a/libs/tracing_perfetto/tracing_sdk.cpp +++ b/libs/tracing_perfetto/tracing_sdk.cpp @@ -38,7 +38,7 @@ void trace_event(int type, const PerfettoTeCategory* perfettoTeCategory, PerfettoTeHlEmitImpl(perfettoTeCategory->impl, type, type == PERFETTO_TE_TYPE_COUNTER ? nullptr : name, extra->get()); - extra->pop_extra(); + extra->clear_extras(); } } @@ -254,6 +254,47 @@ const PerfettoTeHlProtoFieldNested* ProtoFieldNested::get() const { return &field_; } +Session::Session(bool is_backend_in_process, void* buf, size_t len) { + session_ = PerfettoTracingSessionCreate(is_backend_in_process + ? PERFETTO_BACKEND_IN_PROCESS + : PERFETTO_BACKEND_SYSTEM); + + PerfettoTracingSessionSetup(session_, buf, len); + + PerfettoTracingSessionStartBlocking(session_); +} + +Session::~Session() { + PerfettoTracingSessionStopBlocking(session_); + PerfettoTracingSessionDestroy(session_); +} + +bool Session::FlushBlocking(uint32_t timeout_ms) { + return PerfettoTracingSessionFlushBlocking(session_, timeout_ms); +} + +void Session::StopBlocking() { + PerfettoTracingSessionStopBlocking(session_); +} + +std::vector<uint8_t> Session::ReadBlocking() { + std::vector<uint8_t> data; + PerfettoTracingSessionReadTraceBlocking( + session_, + [](struct PerfettoTracingSessionImpl*, const void* trace_data, + size_t size, bool, void* user_arg) { + auto& dst = *static_cast<std::vector<uint8_t>*>(user_arg); + auto* src = static_cast<const uint8_t*>(trace_data); + dst.insert(dst.end(), src, src + size); + }, + &data); + return data; +} + +void Session::delete_session(Session* ptr) { + delete ptr; +} + void activate_trigger(const char* name, uint32_t ttl_ms) { const char* names[] = {name, nullptr}; PerfettoProducerActivateTriggers(names, ttl_ms); diff --git a/libs/ui/DisplayIdentification.cpp b/libs/ui/DisplayIdentification.cpp index c9f0761a01..78e84fc4dc 100644 --- a/libs/ui/DisplayIdentification.cpp +++ b/libs/ui/DisplayIdentification.cpp @@ -26,6 +26,7 @@ #include <string> #include <string_view> +#include <ftl/concat.h> #include <ftl/hash.h> #include <log/log.h> #include <ui/DisplayIdentification.h> @@ -392,10 +393,6 @@ std::optional<PnpId> getPnpId(uint16_t manufacturerId) { return a && b && c ? std::make_optional(PnpId{a, b, c}) : std::nullopt; } -std::optional<PnpId> getPnpId(PhysicalDisplayId displayId) { - return getPnpId(displayId.getManufacturerId()); -} - std::optional<DisplayIdentificationInfo> parseDisplayIdentificationData( uint8_t port, const DisplayIdentificationData& data) { if (data.empty()) { @@ -427,4 +424,27 @@ PhysicalDisplayId getVirtualDisplayId(uint32_t id) { return PhysicalDisplayId::fromEdid(0, kVirtualEdidManufacturerId, id); } +PhysicalDisplayId generateEdidDisplayId(const Edid& edid) { + const ftl::Concat displayDetailsString{edid.manufacturerId, + edid.productId, + ftl::truncated<13>(edid.displayName), + edid.manufactureWeek, + edid.manufactureOrModelYear, + edid.physicalSizeInCm.getWidth(), + edid.physicalSizeInCm.getHeight()}; + + // String has to be cropped to 64 characters (at most) for ftl::stable_hash. + // This is fine as the accuracy or completeness of the above fields is not + // critical for a ID fabrication. + const std::optional<uint64_t> hashedDisplayDetailsOpt = + ftl::stable_hash(std::string_view(displayDetailsString.c_str(), 64)); + + // Combine the hashes via bit-shifted XORs. + const uint64_t id = (hashedDisplayDetailsOpt.value_or(0) << 17) ^ + (edid.hashedBlockZeroSerialNumberOpt.value_or(0) >> 11) ^ + (edid.hashedDescriptorBlockSerialNumberOpt.value_or(0) << 23); + + return PhysicalDisplayId::fromValue(id); +} + } // namespace android diff --git a/libs/ui/OWNERS b/libs/ui/OWNERS index a0b5fe7119..2a85a4b493 100644 --- a/libs/ui/OWNERS +++ b/libs/ui/OWNERS @@ -2,6 +2,5 @@ adyabr@google.com alecmouri@google.com chrisforbes@google.com jreck@google.com -lpy@google.com mathias@google.com romainguy@google.com diff --git a/libs/ui/include/ui/DisplayId.h b/libs/ui/include/ui/DisplayId.h index 8a14db8ffa..1e1c77b9fc 100644 --- a/libs/ui/include/ui/DisplayId.h +++ b/libs/ui/include/ui/DisplayId.h @@ -20,7 +20,7 @@ #include <ostream> #include <string> -#include <ftl/hash.h> +#include <ftl/match.h> #include <ftl/optional.h> namespace android { @@ -31,31 +31,15 @@ struct DisplayId { // Flag indicating that the display is virtual. static constexpr uint64_t FLAG_VIRTUAL = 1ULL << 63; - // Flag indicating that the ID is stable across reboots. - static constexpr uint64_t FLAG_STABLE = 1ULL << 62; - - // TODO(b/162612135) Remove default constructor + // TODO: b/162612135 - Remove default constructor. DisplayId() = default; constexpr DisplayId(const DisplayId&) = default; DisplayId& operator=(const DisplayId&) = default; - constexpr bool isVirtual() const { return value & FLAG_VIRTUAL; } - constexpr bool isStable() const { return value & FLAG_STABLE; } + static constexpr DisplayId fromValue(uint64_t value) { return DisplayId(value); } uint64_t value; - // For deserialization. - static constexpr std::optional<DisplayId> fromValue(uint64_t); - - // As above, but also upcast to Id. - template <typename Id> - static constexpr std::optional<Id> fromValue(uint64_t value) { - if (const auto id = Id::tryCast(DisplayId(value))) { - return id; - } - return {}; - } - protected: explicit constexpr DisplayId(uint64_t id) : value(id) {} }; @@ -79,14 +63,10 @@ inline std::ostream& operator<<(std::ostream& stream, DisplayId displayId) { // DisplayId of a physical display, such as the internal display or externally connected display. struct PhysicalDisplayId : DisplayId { - static constexpr ftl::Optional<PhysicalDisplayId> tryCast(DisplayId id) { - if (id.isVirtual()) { - return std::nullopt; - } - return PhysicalDisplayId(id); - } + // TODO: b/162612135 - Remove default constructor. + PhysicalDisplayId() = default; - // Returns a stable ID based on EDID information. + // Returns a stable ID based on EDID and port information. static constexpr PhysicalDisplayId fromEdid(uint8_t port, uint16_t manufacturerId, uint32_t modelHash) { return PhysicalDisplayId(FLAG_STABLE, port, manufacturerId, modelHash); @@ -99,13 +79,16 @@ struct PhysicalDisplayId : DisplayId { return PhysicalDisplayId(0, port, kManufacturerId, kModelHash); } - // TODO(b/162612135) Remove default constructor - PhysicalDisplayId() = default; - - constexpr uint16_t getManufacturerId() const { return static_cast<uint16_t>(value >> 40); } - constexpr uint8_t getPort() const { return static_cast<uint8_t>(value); } + static constexpr PhysicalDisplayId fromValue(uint64_t value) { + return PhysicalDisplayId(value); + } private: + // Flag indicating that the ID is stable across reboots. + static constexpr uint64_t FLAG_STABLE = 1ULL << 62; + + using DisplayId::DisplayId; + constexpr PhysicalDisplayId(uint64_t flags, uint8_t port, uint16_t manufacturerId, uint32_t modelHash) : DisplayId(flags | (static_cast<uint64_t>(manufacturerId) << 40) | @@ -120,55 +103,38 @@ struct VirtualDisplayId : DisplayId { // Flag indicating that this virtual display is backed by the GPU. static constexpr uint64_t FLAG_GPU = 1ULL << 61; - static constexpr std::optional<VirtualDisplayId> tryCast(DisplayId id) { - if (id.isVirtual()) { - return VirtualDisplayId(id); - } - return std::nullopt; + static constexpr VirtualDisplayId fromValue(uint64_t value) { + return VirtualDisplayId(SkipVirtualFlag{}, value); } protected: + struct SkipVirtualFlag {}; + constexpr VirtualDisplayId(SkipVirtualFlag, uint64_t value) : DisplayId(value) {} explicit constexpr VirtualDisplayId(uint64_t value) : DisplayId(FLAG_VIRTUAL | value) {} + explicit constexpr VirtualDisplayId(DisplayId other) : DisplayId(other) {} }; struct HalVirtualDisplayId : VirtualDisplayId { explicit constexpr HalVirtualDisplayId(BaseId baseId) : VirtualDisplayId(baseId) {} - static constexpr std::optional<HalVirtualDisplayId> tryCast(DisplayId id) { - if (id.isVirtual() && !(id.value & FLAG_GPU)) { - return HalVirtualDisplayId(id); - } - return std::nullopt; + static constexpr HalVirtualDisplayId fromValue(uint64_t value) { + return HalVirtualDisplayId(SkipVirtualFlag{}, value); } private: - explicit constexpr HalVirtualDisplayId(DisplayId other) : VirtualDisplayId(other) {} + using VirtualDisplayId::VirtualDisplayId; }; struct GpuVirtualDisplayId : VirtualDisplayId { explicit constexpr GpuVirtualDisplayId(BaseId baseId) : VirtualDisplayId(FLAG_GPU | baseId) {} - static constexpr std::optional<GpuVirtualDisplayId> fromUniqueId(const std::string& uniqueId) { - if (const auto hashOpt = ftl::stable_hash(uniqueId)) { - return GpuVirtualDisplayId(HashTag{}, *hashOpt); - } - return {}; - } - - static constexpr std::optional<GpuVirtualDisplayId> tryCast(DisplayId id) { - if (id.isVirtual() && (id.value & FLAG_GPU)) { - return GpuVirtualDisplayId(id); - } - return std::nullopt; + static constexpr GpuVirtualDisplayId fromValue(uint64_t value) { + return GpuVirtualDisplayId(SkipVirtualFlag{}, value); } private: - struct HashTag {}; // Disambiguate with BaseId constructor. - constexpr GpuVirtualDisplayId(HashTag, uint64_t hash) - : VirtualDisplayId(FLAG_STABLE | FLAG_GPU | hash) {} - - explicit constexpr GpuVirtualDisplayId(DisplayId other) : VirtualDisplayId(other) {} + using VirtualDisplayId::VirtualDisplayId; }; // HalDisplayId is the ID of a display which is managed by HWC. @@ -176,26 +142,52 @@ private: struct HalDisplayId : DisplayId { constexpr HalDisplayId(HalVirtualDisplayId other) : DisplayId(other) {} constexpr HalDisplayId(PhysicalDisplayId other) : DisplayId(other) {} - - static constexpr std::optional<HalDisplayId> tryCast(DisplayId id) { - if (GpuVirtualDisplayId::tryCast(id)) { - return std::nullopt; - } - return HalDisplayId(id); - } + static constexpr HalDisplayId fromValue(uint64_t value) { return HalDisplayId(value); } private: + using DisplayId::DisplayId; explicit constexpr HalDisplayId(DisplayId other) : DisplayId(other) {} }; -constexpr std::optional<DisplayId> DisplayId::fromValue(uint64_t value) { - if (const auto id = fromValue<PhysicalDisplayId>(value)) { - return id; - } - if (const auto id = fromValue<VirtualDisplayId>(value)) { - return id; - } - return {}; +using DisplayIdVariant = std::variant<PhysicalDisplayId, GpuVirtualDisplayId, HalVirtualDisplayId>; +using VirtualDisplayIdVariant = std::variant<GpuVirtualDisplayId, HalVirtualDisplayId>; + +template <typename DisplayIdType> +inline auto asDisplayIdOfType(DisplayIdVariant variant) -> ftl::Optional<DisplayIdType> { + return ftl::match( + variant, + [](DisplayIdType id) -> ftl::Optional<DisplayIdType> { return ftl::Optional(id); }, + [](auto) -> ftl::Optional<DisplayIdType> { return std::nullopt; }); +} + +template <typename Variant> +inline auto asHalDisplayId(Variant variant) -> ftl::Optional<HalDisplayId> { + return ftl::match( + variant, + [](GpuVirtualDisplayId) -> ftl::Optional<HalDisplayId> { return std::nullopt; }, + [](auto id) -> ftl::Optional<HalDisplayId> { + return ftl::Optional(static_cast<HalDisplayId>(id)); + }); +} + +inline auto asPhysicalDisplayId(DisplayIdVariant variant) -> ftl::Optional<PhysicalDisplayId> { + return asDisplayIdOfType<PhysicalDisplayId>(variant); +} + +inline auto asVirtualDisplayId(DisplayIdVariant variant) -> ftl::Optional<VirtualDisplayId> { + return ftl::match( + variant, + [](GpuVirtualDisplayId id) -> ftl::Optional<VirtualDisplayId> { + return ftl::Optional(static_cast<VirtualDisplayId>(id)); + }, + [](HalVirtualDisplayId id) -> ftl::Optional<VirtualDisplayId> { + return ftl::Optional(static_cast<VirtualDisplayId>(id)); + }, + [](auto) -> ftl::Optional<VirtualDisplayId> { return std::nullopt; }); +} + +inline auto asDisplayId(DisplayIdVariant variant) -> DisplayId { + return ftl::match(variant, [](auto id) -> DisplayId { return static_cast<DisplayId>(id); }); } static_assert(sizeof(DisplayId) == sizeof(uint64_t)); diff --git a/libs/ui/include/ui/DisplayIdentification.h b/libs/ui/include/ui/DisplayIdentification.h index cdac2698dd..1e3449c004 100644 --- a/libs/ui/include/ui/DisplayIdentification.h +++ b/libs/ui/include/ui/DisplayIdentification.h @@ -74,6 +74,7 @@ struct Edid { std::optional<uint64_t> hashedDescriptorBlockSerialNumberOpt; PnpId pnpId; uint32_t modelHash; + // Up to 13 characters of ASCII text terminated by LF and padded with SP. std::string_view displayName; uint8_t manufactureOrModelYear; uint8_t manufactureWeek; @@ -85,11 +86,14 @@ struct Edid { bool isEdid(const DisplayIdentificationData&); std::optional<Edid> parseEdid(const DisplayIdentificationData&); std::optional<PnpId> getPnpId(uint16_t manufacturerId); -std::optional<PnpId> getPnpId(PhysicalDisplayId); std::optional<DisplayIdentificationInfo> parseDisplayIdentificationData( uint8_t port, const DisplayIdentificationData&); PhysicalDisplayId getVirtualDisplayId(uint32_t id); +// Generates a consistent, stable, and hashed display ID that is based on the +// display's parsed EDID fields. +PhysicalDisplayId generateEdidDisplayId(const Edid& edid); + } // namespace android diff --git a/libs/ui/include/ui/DisplayMap.h b/libs/ui/include/ui/DisplayMap.h index 65d2b8fa31..834a3042d1 100644 --- a/libs/ui/include/ui/DisplayMap.h +++ b/libs/ui/include/ui/DisplayMap.h @@ -18,6 +18,7 @@ #include <ftl/small_map.h> #include <ftl/small_vector.h> +#include <ftl/unit.h> namespace android::ui { @@ -30,6 +31,8 @@ using DisplayMap = ftl::SmallMap<Key, Value, kDisplayCapacity>; constexpr size_t kPhysicalDisplayCapacity = 3; template <typename Key, typename Value> using PhysicalDisplayMap = ftl::SmallMap<Key, Value, kPhysicalDisplayCapacity>; +template <typename Key> +using PhysicalDisplaySet = ftl::SmallMap<Key, ftl::Unit, kPhysicalDisplayCapacity>; template <typename T> using DisplayVector = ftl::SmallVector<T, kDisplayCapacity>; diff --git a/libs/ui/include/ui/RingBuffer.h b/libs/ui/include/ui/RingBuffer.h new file mode 100644 index 0000000000..31d5a950ef --- /dev/null +++ b/libs/ui/include/ui/RingBuffer.h @@ -0,0 +1,70 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <stddef.h> +#include <array> + +namespace android::ui { + +template <class T, size_t SIZE> +class RingBuffer { + RingBuffer(const RingBuffer&) = delete; + void operator=(const RingBuffer&) = delete; + +public: + RingBuffer() = default; + ~RingBuffer() = default; + + constexpr size_t capacity() const { return SIZE; } + size_t size() const { return mCount; } + bool isFull() const { return size() == capacity(); } + + T& next() { + mHead = static_cast<size_t>(mHead + 1) % SIZE; + if (mCount < SIZE) { + mCount++; + } + return mBuffer[static_cast<size_t>(mHead)]; + } + + T& front() { return (*this)[0]; } + const T& front() const { return (*this)[0]; } + + T& back() { return (*this)[size() - 1]; } + const T& back() const { return (*this)[size() - 1]; } + + T& operator[](size_t index) { + return mBuffer[(static_cast<size_t>(mHead + 1) + index) % mCount]; + } + + const T& operator[](size_t index) const { + return mBuffer[(static_cast<size_t>(mHead + 1) + index) % mCount]; + } + + void clear() { + mCount = 0; + mHead = -1; + } + +private: + std::array<T, SIZE> mBuffer; + int mHead = -1; + size_t mCount = 0; +}; + +} // namespace android::ui diff --git a/libs/ui/include/ui/StaticDisplayInfo.h b/libs/ui/include/ui/StaticDisplayInfo.h index 83da821f37..53164487f3 100644 --- a/libs/ui/include/ui/StaticDisplayInfo.h +++ b/libs/ui/include/ui/StaticDisplayInfo.h @@ -28,6 +28,7 @@ enum class DisplayConnectionType { Internal, External, ftl_last = External }; // Immutable information about physical display. struct StaticDisplayInfo { DisplayConnectionType connectionType = DisplayConnectionType::Internal; + uint8_t port; float density = 0.f; bool secure = false; std::optional<DeviceProductInfo> deviceProductInfo; diff --git a/libs/ui/include_types/ui/HdrRenderTypeUtils.h b/libs/ui/include_types/ui/HdrRenderTypeUtils.h index 70c50f07e5..98018d95d1 100644 --- a/libs/ui/include_types/ui/HdrRenderTypeUtils.h +++ b/libs/ui/include_types/ui/HdrRenderTypeUtils.h @@ -36,7 +36,7 @@ enum class HdrRenderType { */ inline HdrRenderType getHdrRenderType(ui::Dataspace dataspace, std::optional<ui::PixelFormat> pixelFormat, - float hdrSdrRatio = 1.f) { + float hdrSdrRatio = 1.f, bool hasHdrMetadata = false) { const auto transfer = dataspace & HAL_DATASPACE_TRANSFER_MASK; const auto range = dataspace & HAL_DATASPACE_RANGE_MASK; @@ -49,7 +49,8 @@ inline HdrRenderType getHdrRenderType(ui::Dataspace dataspace, HAL_DATASPACE_RANGE_EXTENDED); if ((dataspace == BT2020_LINEAR_EXT || dataspace == ui::Dataspace::V0_SCRGB) && - pixelFormat.has_value() && pixelFormat.value() == ui::PixelFormat::RGBA_FP16) { + pixelFormat.has_value() && pixelFormat.value() == ui::PixelFormat::RGBA_FP16 && + hasHdrMetadata) { return HdrRenderType::GENERIC_HDR; } diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp index 2d8a1e326c..d950f2a23f 100644 --- a/libs/ui/tests/Android.bp +++ b/libs/ui/tests/Android.bp @@ -45,16 +45,6 @@ cc_test { } cc_test { - name: "DisplayId_test", - shared_libs: ["libui"], - srcs: ["DisplayId_test.cpp"], - cflags: [ - "-Wall", - "-Werror", - ], -} - -cc_test { name: "DisplayIdentification_test", shared_libs: ["libui"], static_libs: ["libgmock"], @@ -144,6 +134,17 @@ cc_test { } cc_test { + name: "RingBuffer_test", + test_suites: ["device-tests"], + shared_libs: ["libui"], + srcs: ["RingBuffer_test.cpp"], + cflags: [ + "-Wall", + "-Werror", + ], +} + +cc_test { name: "Size_test", test_suites: ["device-tests"], shared_libs: ["libui"], diff --git a/libs/ui/tests/DisplayId_test.cpp b/libs/ui/tests/DisplayId_test.cpp deleted file mode 100644 index ef686dfc83..0000000000 --- a/libs/ui/tests/DisplayId_test.cpp +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <ui/DisplayId.h> - -#include <gtest/gtest.h> - -namespace android::ui { - -TEST(DisplayIdTest, createPhysicalIdFromEdid) { - constexpr uint8_t port = 1; - constexpr uint16_t manufacturerId = 13; - constexpr uint32_t modelHash = 42; - const PhysicalDisplayId id = PhysicalDisplayId::fromEdid(port, manufacturerId, modelHash); - EXPECT_EQ(port, id.getPort()); - EXPECT_EQ(manufacturerId, id.getManufacturerId()); - EXPECT_FALSE(VirtualDisplayId::tryCast(id)); - EXPECT_FALSE(HalVirtualDisplayId::tryCast(id)); - EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id)); - EXPECT_TRUE(PhysicalDisplayId::tryCast(id)); - EXPECT_TRUE(HalDisplayId::tryCast(id)); - - EXPECT_EQ(id, DisplayId::fromValue(id.value)); - EXPECT_EQ(id, DisplayId::fromValue<PhysicalDisplayId>(id.value)); -} - -TEST(DisplayIdTest, createPhysicalIdFromPort) { - constexpr uint8_t port = 3; - const PhysicalDisplayId id = PhysicalDisplayId::fromPort(port); - EXPECT_EQ(port, id.getPort()); - EXPECT_FALSE(VirtualDisplayId::tryCast(id)); - EXPECT_FALSE(HalVirtualDisplayId::tryCast(id)); - EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id)); - EXPECT_TRUE(PhysicalDisplayId::tryCast(id)); - EXPECT_TRUE(HalDisplayId::tryCast(id)); - - EXPECT_EQ(id, DisplayId::fromValue(id.value)); - EXPECT_EQ(id, DisplayId::fromValue<PhysicalDisplayId>(id.value)); -} - -TEST(DisplayIdTest, createGpuVirtualId) { - const GpuVirtualDisplayId id(42); - EXPECT_TRUE(VirtualDisplayId::tryCast(id)); - EXPECT_TRUE(GpuVirtualDisplayId::tryCast(id)); - EXPECT_FALSE(HalVirtualDisplayId::tryCast(id)); - EXPECT_FALSE(PhysicalDisplayId::tryCast(id)); - EXPECT_FALSE(HalDisplayId::tryCast(id)); - - EXPECT_EQ(id, DisplayId::fromValue(id.value)); - EXPECT_EQ(id, DisplayId::fromValue<GpuVirtualDisplayId>(id.value)); -} - -TEST(DisplayIdTest, createVirtualIdFromGpuVirtualId) { - const VirtualDisplayId id(GpuVirtualDisplayId(42)); - EXPECT_TRUE(VirtualDisplayId::tryCast(id)); - EXPECT_TRUE(GpuVirtualDisplayId::tryCast(id)); - EXPECT_FALSE(HalVirtualDisplayId::tryCast(id)); - EXPECT_FALSE(PhysicalDisplayId::tryCast(id)); - EXPECT_FALSE(HalDisplayId::tryCast(id)); - - const bool isGpuVirtualId = (id.value & VirtualDisplayId::FLAG_GPU); - EXPECT_EQ((id.isVirtual() && isGpuVirtualId), GpuVirtualDisplayId::tryCast(id).has_value()); -} - -TEST(DisplayIdTest, createGpuVirtualIdFromUniqueId) { - static const std::string kUniqueId("virtual:ui:DisplayId_test"); - const auto idOpt = GpuVirtualDisplayId::fromUniqueId(kUniqueId); - ASSERT_TRUE(idOpt.has_value()); - const GpuVirtualDisplayId id = idOpt.value(); - EXPECT_TRUE(VirtualDisplayId::tryCast(id)); - EXPECT_TRUE(GpuVirtualDisplayId::tryCast(id)); - EXPECT_FALSE(HalVirtualDisplayId::tryCast(id)); - EXPECT_FALSE(PhysicalDisplayId::tryCast(id)); - EXPECT_FALSE(HalDisplayId::tryCast(id)); - - EXPECT_EQ(id, DisplayId::fromValue(id.value)); - EXPECT_EQ(id, DisplayId::fromValue<GpuVirtualDisplayId>(id.value)); -} - -TEST(DisplayIdTest, createHalVirtualId) { - const HalVirtualDisplayId id(42); - EXPECT_TRUE(VirtualDisplayId::tryCast(id)); - EXPECT_TRUE(HalVirtualDisplayId::tryCast(id)); - EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id)); - EXPECT_FALSE(PhysicalDisplayId::tryCast(id)); - EXPECT_TRUE(HalDisplayId::tryCast(id)); - - EXPECT_EQ(id, DisplayId::fromValue(id.value)); - EXPECT_EQ(id, DisplayId::fromValue<HalVirtualDisplayId>(id.value)); -} - -TEST(DisplayIdTest, createVirtualIdFromHalVirtualId) { - const VirtualDisplayId id(HalVirtualDisplayId(42)); - EXPECT_TRUE(VirtualDisplayId::tryCast(id)); - EXPECT_TRUE(HalVirtualDisplayId::tryCast(id)); - EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id)); - EXPECT_FALSE(PhysicalDisplayId::tryCast(id)); - EXPECT_TRUE(HalDisplayId::tryCast(id)); - - const bool isGpuVirtualId = (id.value & VirtualDisplayId::FLAG_GPU); - EXPECT_EQ((id.isVirtual() && !isGpuVirtualId), HalVirtualDisplayId::tryCast(id).has_value()); -} - -} // namespace android::ui diff --git a/libs/ui/tests/DisplayIdentification_test.cpp b/libs/ui/tests/DisplayIdentification_test.cpp index d1699e79b6..75c71a598e 100644 --- a/libs/ui/tests/DisplayIdentification_test.cpp +++ b/libs/ui/tests/DisplayIdentification_test.cpp @@ -376,6 +376,22 @@ TEST(DisplayIdentificationTest, parseDisplayIdentificationData) { EXPECT_EQ(4633127902230889474, tertiaryInfo->id.value); } +TEST(DisplayIdentificationTest, generateEdidDisplayId) { + const auto firstExternalDisplayEdidOpt = parseEdid(getExternalEdid()); + ASSERT_TRUE(firstExternalDisplayEdidOpt); + const PhysicalDisplayId firstExternalDisplayId = + generateEdidDisplayId(firstExternalDisplayEdidOpt.value()); + + const auto secondExternalDisplayEdidOpt = parseEdid(getExternalEedid()); + ASSERT_TRUE(secondExternalDisplayEdidOpt); + const PhysicalDisplayId secondExternalDisplayId = + generateEdidDisplayId(secondExternalDisplayEdidOpt.value()); + + // Display IDs should be unique. + EXPECT_EQ(4067182673952280501u, firstExternalDisplayId.value); + EXPECT_EQ(14712168404707886855u, secondExternalDisplayId.value); +} + TEST(DisplayIdentificationTest, deviceProductInfo) { using ManufactureYear = DeviceProductInfo::ManufactureYear; using ManufactureWeekAndYear = DeviceProductInfo::ManufactureWeekAndYear; @@ -462,18 +478,6 @@ TEST(DisplayIdentificationTest, deviceProductInfo) { } } -TEST(DisplayIdentificationTest, fromPort) { - // Manufacturer ID should be invalid. - ASSERT_FALSE(getPnpId(PhysicalDisplayId::fromPort(0))); - ASSERT_FALSE(getPnpId(PhysicalDisplayId::fromPort(0xffu))); -} - -TEST(DisplayIdentificationTest, getVirtualDisplayId) { - // Manufacturer ID should be invalid. - ASSERT_FALSE(getPnpId(getVirtualDisplayId(0))); - ASSERT_FALSE(getPnpId(getVirtualDisplayId(0xffff'ffffu))); -} - } // namespace android // TODO(b/129481165): remove the #pragma below and fix conversion issues diff --git a/libs/ui/tests/RingBuffer_test.cpp b/libs/ui/tests/RingBuffer_test.cpp new file mode 100644 index 0000000000..9839492b9f --- /dev/null +++ b/libs/ui/tests/RingBuffer_test.cpp @@ -0,0 +1,70 @@ +/* + * Copyright 2025 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 <gtest/gtest.h> +#include <ui/RingBuffer.h> + +namespace android::ui { + +TEST(RingBuffer, basic) { + RingBuffer<int, 5> rb; + + rb.next() = 1; + ASSERT_EQ(1, rb.size()); + ASSERT_EQ(1, rb.back()); + ASSERT_EQ(1, rb.front()); + + rb.next() = 2; + ASSERT_EQ(2, rb.size()); + ASSERT_EQ(2, rb.back()); + ASSERT_EQ(1, rb.front()); + ASSERT_EQ(1, rb[-1]); + + rb.next() = 3; + ASSERT_EQ(3, rb.size()); + ASSERT_EQ(3, rb.back()); + ASSERT_EQ(1, rb.front()); + ASSERT_EQ(2, rb[-1]); + ASSERT_EQ(1, rb[-2]); + + rb.next() = 4; + ASSERT_EQ(4, rb.size()); + ASSERT_EQ(4, rb.back()); + ASSERT_EQ(1, rb.front()); + ASSERT_EQ(3, rb[-1]); + ASSERT_EQ(2, rb[-2]); + ASSERT_EQ(1, rb[-3]); + + rb.next() = 5; + ASSERT_EQ(5, rb.size()); + ASSERT_EQ(5, rb.back()); + ASSERT_EQ(1, rb.front()); + ASSERT_EQ(4, rb[-1]); + ASSERT_EQ(3, rb[-2]); + ASSERT_EQ(2, rb[-3]); + ASSERT_EQ(1, rb[-4]); + + rb.next() = 6; + ASSERT_EQ(5, rb.size()); + ASSERT_EQ(6, rb.back()); + ASSERT_EQ(2, rb.front()); + ASSERT_EQ(5, rb[-1]); + ASSERT_EQ(4, rb[-2]); + ASSERT_EQ(3, rb[-3]); + ASSERT_EQ(2, rb[-4]); +} + +} // namespace android::ui
\ No newline at end of file |