diff options
Diffstat (limited to 'libs')
194 files changed, 5946 insertions, 2456 deletions
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index 9d9ae31ccf..ea5343a2a0 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 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 f7e0915f15..1c1b6f30a4 100644 --- a/libs/binder/IPCThreadState.cpp +++ b/libs/binder/IPCThreadState.cpp @@ -630,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()"); @@ -807,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 " @@ -844,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); @@ -1498,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; diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index 9f5d822c55..777c22a63e 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -156,7 +156,7 @@ enum { #ifdef BINDER_WITH_KERNEL_IPC static void acquire_object(const sp<ProcessState>& proc, const flat_binder_object& obj, - const void* who) { + const void* who, bool tagFds) { switch (obj.hdr.type) { case BINDER_TYPE_BINDER: if (obj.binder) { @@ -173,7 +173,7 @@ static void acquire_object(const sp<ProcessState>& proc, const flat_binder_objec return; } case BINDER_TYPE_FD: { - if (obj.cookie != 0) { // owned + if (tagFds && obj.cookie != 0) { // owned FdTag(obj.handle, nullptr, who); } return; @@ -616,7 +616,7 @@ status_t Parcel::appendFrom(const Parcel* parcel, size_t offset, size_t len) { } } - acquire_object(proc, *flat, this); + acquire_object(proc, *flat, this, true /*tagFds*/); } } #else @@ -1800,13 +1800,22 @@ restart_write: // Need to write meta-data? if (nullMetaData || val.binder != 0) { kernelFields->mObjects[kernelFields->mObjectsSize] = mDataPos; - acquire_object(ProcessState::self(), val, this); + acquire_object(ProcessState::self(), val, this, true /*tagFds*/); kernelFields->mObjectsSize++; } return finishWrite(sizeof(flat_binder_object)); } + if (mOwner) { + // continueWrite does have the logic to convert this from an + // owned to an unowned Parcel. However, this is pretty inefficient, + // and it's really strange to need to do so, so prefer to avoid + // these paths than try to support them. + ALOGE("writing objects not supported on owned Parcels"); + return PERMISSION_DENIED; + } + if (!enoughData) { const status_t err = growData(sizeof(val)); if (err != NO_ERROR) return err; @@ -2723,6 +2732,65 @@ size_t Parcel::ipcObjectsCount() const return 0; } +static void do_nothing_release_func(const uint8_t* data, size_t dataSize, + const binder_size_t* objects, size_t objectsCount) { + (void)data; + (void)dataSize; + (void)objects; + (void)objectsCount; +} +static void delete_data_release_func(const uint8_t* data, size_t dataSize, + const binder_size_t* objects, size_t objectsCount) { + delete[] data; + (void)dataSize; + (void)objects; + (void)objectsCount; +} + +void Parcel::makeDangerousViewOf(Parcel* p) { + if (p->isForRpc()) { + // warning: this must match the logic in rpcSetDataReference + auto* rf = p->maybeRpcFields(); + LOG_ALWAYS_FATAL_IF(rf == nullptr); + std::vector<std::variant<binder::unique_fd, binder::borrowed_fd>> fds; + if (rf->mFds) { + fds.reserve(rf->mFds->size()); + for (const auto& fd : *rf->mFds) { + fds.push_back(binder::borrowed_fd(toRawFd(fd))); + } + } + status_t result = + rpcSetDataReference(rf->mSession, p->mData, p->mDataSize, + rf->mObjectPositions.data(), rf->mObjectPositions.size(), + std::move(fds), do_nothing_release_func); + LOG_ALWAYS_FATAL_IF(result != OK, "Failed: %s", statusToString(result).c_str()); + } else { +#ifdef BINDER_WITH_KERNEL_IPC + // warning: this must match the logic in ipcSetDataReference + auto* kf = p->maybeKernelFields(); + LOG_ALWAYS_FATAL_IF(kf == nullptr); + + // Ownership of FDs is passed to the Parcel from kernel binder. This should be refactored + // to move this ownership out of Parcel and into release_func. However, today, Parcel + // always assums it can own and close FDs today. So, for purposes of testing consistency, + // , create new FDs it can own. + + uint8_t* newData = new uint8_t[p->mDataSize]; // deleted by delete_data_release_func + memcpy(newData, p->mData, p->mDataSize); + for (size_t i = 0; i < kf->mObjectsSize; i++) { + flat_binder_object* flat = + reinterpret_cast<flat_binder_object*>(newData + kf->mObjects[i]); + if (flat->hdr.type == BINDER_TYPE_FD) { + flat->handle = fcntl(flat->handle, F_DUPFD_CLOEXEC, 0); + } + } + + ipcSetDataReference(newData, p->mDataSize, kf->mObjects, kf->mObjectsSize, + delete_data_release_func); +#endif // BINDER_WITH_KERNEL_IPC + } +} + void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize, const binder_size_t* objects, size_t objectsCount, release_func relFunc) { // this code uses 'mOwner == nullptr' to understand whether it owns memory @@ -2733,6 +2801,7 @@ void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize, const bin auto* kernelFields = maybeKernelFields(); LOG_ALWAYS_FATAL_IF(kernelFields == nullptr); // guaranteed by freeData. + // must match makeDangerousViewOf mData = const_cast<uint8_t*>(data); mDataSize = mDataCapacity = dataSize; kernelFields->mObjects = const_cast<binder_size_t*>(objects); @@ -2811,6 +2880,7 @@ status_t Parcel::rpcSetDataReference( auto* rpcFields = maybeRpcFields(); LOG_ALWAYS_FATAL_IF(rpcFields == nullptr); // guaranteed by markForRpc. + // must match makeDangerousViewOf mData = const_cast<uint8_t*>(data); mDataSize = mDataCapacity = dataSize; mOwner = relFunc; @@ -2878,15 +2948,17 @@ void Parcel::releaseObjects() #endif // BINDER_WITH_KERNEL_IPC } -void Parcel::acquireObjects() -{ +void Parcel::reacquireObjects(size_t objectsSize) { auto* kernelFields = maybeKernelFields(); if (kernelFields == nullptr) { return; } #ifdef BINDER_WITH_KERNEL_IPC - size_t i = kernelFields->mObjectsSize; + LOG_ALWAYS_FATAL_IF(objectsSize > kernelFields->mObjectsSize, + "Object size %zu out of range of %zu", objectsSize, + kernelFields->mObjectsSize); + size_t i = objectsSize; if (i == 0) { return; } @@ -2896,8 +2968,10 @@ void Parcel::acquireObjects() while (i > 0) { i--; const flat_binder_object* flat = reinterpret_cast<flat_binder_object*>(data + objects[i]); - acquire_object(proc, *flat, this); + acquire_object(proc, *flat, this, false /*tagFds*/); // they are already tagged } +#else + (void) objectsSize; #endif // BINDER_WITH_KERNEL_IPC } @@ -3114,12 +3188,8 @@ status_t Parcel::continueWrite(size_t desired) return NO_MEMORY; } - // Little hack to only acquire references on objects - // we will be keeping. - size_t oldObjectsSize = kernelFields->mObjectsSize; - kernelFields->mObjectsSize = objectsSize; - acquireObjects(); - kernelFields->mObjectsSize = oldObjectsSize; + // only acquire references on objects we are keeping + reacquireObjects(objectsSize); } if (rpcFields) { if (status_t status = truncateRpcObjects(objectsSize); status != OK) { 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/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/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h index 9ef4e694dd..f7465e2c1a 100644 --- a/libs/binder/include/binder/IPCThreadState.h +++ b/libs/binder/include/binder/IPCThreadState.h @@ -64,7 +64,10 @@ public: * Returns the PID of the process which has made the current binder * call. If not in a binder call, this will return getpid. * - * Warning: oneway transactions do not receive PID. Even if you expect + * Warning do not use this as a security identifier! PID is unreliable + * as it may be re-used. This should mostly be used for debugging. + * + * oneway transactions do not receive PID. Even if you expect * a transaction to be synchronous, a misbehaving client could send it * as an asynchronous call and result in a 0 PID here. Additionally, if * there is a race and the calling process dies, the PID may still be diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h index 1154211582..6c4c6fe573 100644 --- a/libs/binder/include/binder/Parcel.h +++ b/libs/binder/include/binder/Parcel.h @@ -651,6 +651,11 @@ public: LIBBINDER_EXPORTED void print(std::ostream& to, uint32_t flags = 0) const; + // This API is to quickly become a view of another Parcel, so that we can also + // test 'owner' paths quickly. It's extremely dangerous to use this API in + // practice, and you should never ever do it. + LIBBINDER_EXPORTED void makeDangerousViewOf(Parcel* p); + private: // Close all file descriptors in the parcel at object positions >= newObjectsSize. void closeFileDescriptors(size_t newObjectsSize); @@ -666,7 +671,7 @@ private: void ipcSetDataReference(const uint8_t* data, size_t dataSize, const binder_size_t* objects, size_t objectsCount, release_func relFunc); // Takes ownership even when an error is returned. - status_t rpcSetDataReference( + [[nodiscard]] status_t rpcSetDataReference( const sp<RpcSession>& session, const uint8_t* data, size_t dataSize, const uint32_t* objectTable, size_t objectTableSize, std::vector<std::variant<binder::unique_fd, binder::borrowed_fd>>&& ancillaryFds, @@ -674,7 +679,7 @@ private: status_t finishWrite(size_t len); void releaseObjects(); - void acquireObjects(); + void reacquireObjects(size_t objectSize); status_t growData(size_t len); // Clear the Parcel and set the capacity to `desired`. // Doesn't reset the RPC session association. 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/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h index bd46c473b4..d69d318a28 100644 --- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h +++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h @@ -419,6 +419,9 @@ binder_status_t AIBinder_unlinkToDeath(AIBinder* binder, AIBinder_DeathRecipient * This can be used with higher-level system services to determine the caller's identity and check * permissions. * + * Warning do not use this as a security identifier! PID is unreliable as it may be re-used. This + * should mostly be used for debugging. + * * Available since API level 29. * * \return calling uid or the current process's UID if this thread isn't processing a transaction. 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/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/src/state.rs b/libs/binder/rust/src/state.rs index 145ae651d7..609334eb53 100644 --- a/libs/binder/rust/src/state.rs +++ b/libs/binder/rust/src/state.rs @@ -102,7 +102,10 @@ impl ThreadState { /// dies and is replaced with another process with elevated permissions and /// the same PID. /// - /// Warning: oneway transactions do not receive PID. Even if you expect + /// Warning: do not use this as a security identifier! PID is unreliable + /// as it may be re-used. This should mostly be used for debugging. + /// + /// oneway transactions do not receive PID. Even if you expect /// a transaction to be synchronous, a misbehaving client could send it /// as a synchronous call and result in a 0 PID here. Additionally, if /// there is a race and the calling process dies, the PID may still be diff --git a/libs/binder/tests/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp index a41726c313..b2ba1ae38d 100644 --- a/libs/binder/tests/parcel_fuzzer/binder.cpp +++ b/libs/binder/tests/parcel_fuzzer/binder.cpp @@ -121,6 +121,11 @@ std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS { PARCEL_READ_NO_STATUS(size_t, hasFileDescriptors), PARCEL_READ_NO_STATUS(std::vector<android::sp<android::IBinder>>, debugReadAllStrongBinders), PARCEL_READ_NO_STATUS(std::vector<int>, debugReadAllFileDescriptors), + [] (const ::android::Parcel& p, FuzzedDataProvider&) { + FUZZ_LOG() << "about to markSensitive"; + p.markSensitive(); + FUZZ_LOG() << "markSensitive done"; + }, [] (const ::android::Parcel& p, FuzzedDataProvider& provider) { std::string interface = provider.ConsumeRandomLengthString(); FUZZ_LOG() << "about to enforceInterface: " << interface; diff --git a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h index 2812da79fa..11fcb061f6 100644 --- a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h +++ b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h @@ -28,6 +28,9 @@ struct RandomParcelOptions { std::function<void(Parcel* p, FuzzedDataProvider& provider)> writeHeader; std::vector<sp<IBinder>> extraBinders; std::vector<binder::unique_fd> extraFds; + + // internal state owned by fillRandomParcel, for Parcel views + std::vector<std::unique_ptr<Parcel>> extraParcels; }; /** diff --git a/libs/binder/tests/parcel_fuzzer/main.cpp b/libs/binder/tests/parcel_fuzzer/main.cpp index 192f9d5dce..d06b2d9020 100644 --- a/libs/binder/tests/parcel_fuzzer/main.cpp +++ b/libs/binder/tests/parcel_fuzzer/main.cpp @@ -70,7 +70,7 @@ void doTransactFuzz(const char* backend, const sp<B>& binder, FuzzedDataProvider uint32_t code = provider.ConsumeIntegral<uint32_t>(); uint32_t flag = provider.ConsumeIntegral<uint32_t>(); - FUZZ_LOG() << "backend: " << backend; + FUZZ_LOG() << "doTransactFuzz backend: " << backend; RandomParcelOptions options; @@ -101,7 +101,7 @@ void doReadFuzz(const char* backend, const std::vector<ParcelRead<P>>& reads, // since we are only using a byte to index CHECK_LE(reads.size(), 255u) << reads.size(); - FUZZ_LOG() << "backend: " << backend; + FUZZ_LOG() << "doReadFuzz backend: " << backend; FUZZ_LOG() << "input: " << HexString(p.data(), p.dataSize()); FUZZ_LOG() << "instructions: " << HexString(instructions.data(), instructions.size()); @@ -122,10 +122,15 @@ void doReadWriteFuzz(const char* backend, const std::vector<ParcelRead<P>>& read RandomParcelOptions options; P p; + // small amount of initial Parcel data, since fillRandomParcel uses makeDangerousViewOf + std::vector<uint8_t> parcelData = + provider.ConsumeBytes<uint8_t>(provider.ConsumeIntegralInRange<size_t>(0, 20)); + fillRandomParcel(&p, FuzzedDataProvider(parcelData.data(), parcelData.size()), &options); + // since we are only using a byte to index CHECK_LE(reads.size() + writes.size(), 255u) << reads.size(); - FUZZ_LOG() << "backend: " << backend; + FUZZ_LOG() << "doReadWriteFuzz backend: " << backend; while (provider.remaining_bytes() > 0) { uint8_t idx = provider.ConsumeIntegralInRange<uint8_t>(0, reads.size() + writes.size() - 1); diff --git a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp index 7c196142e8..61b9612ba5 100644 --- a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp +++ b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp @@ -17,6 +17,7 @@ #include <fuzzbinder/random_parcel.h> #include <android-base/logging.h> +#include <binder/Functional.h> #include <binder/RpcSession.h> #include <binder/RpcTransportRaw.h> #include <fuzzbinder/random_binder.h> @@ -32,10 +33,39 @@ static void fillRandomParcelData(Parcel* p, FuzzedDataProvider&& provider) { CHECK(OK == p->write(data.data(), data.size())); } -void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider, RandomParcelOptions* options) { +void fillRandomParcel(Parcel* outputParcel, FuzzedDataProvider&& provider, + RandomParcelOptions* options) { CHECK_NE(options, nullptr); - if (provider.ConsumeBool()) { + const uint8_t fuzzerParcelOptions = provider.ConsumeIntegral<uint8_t>(); + const bool resultShouldBeView = fuzzerParcelOptions & 1; + const bool resultShouldBeRpc = fuzzerParcelOptions & 2; + const bool resultShouldMarkSensitive = fuzzerParcelOptions & 4; + + auto sensitivity_guard = binder::impl::make_scope_guard([&]() { + if (resultShouldMarkSensitive) { + outputParcel->markSensitive(); + } + }); + + Parcel* p; + if (resultShouldBeView) { + options->extraParcels.push_back(std::make_unique<Parcel>()); + // held for duration of test, so that view will be valid + p = options->extraParcels[options->extraParcels.size() - 1].get(); + } else { + p = outputParcel; // directly fill out the output Parcel + } + + // must be last guard, so outputParcel gets setup as view before + // other guards + auto viewify_guard = binder::impl::make_scope_guard([&]() { + if (resultShouldBeView) { + outputParcel->makeDangerousViewOf(p); + } + }); + + if (resultShouldBeRpc) { auto session = RpcSession::make(RpcTransportCtxFactoryRaw::make()); CHECK_EQ(OK, session->addNullDebuggingClient()); // Set the protocol version so that we don't crash if the session diff --git a/libs/binder/tests/parcel_fuzzer/random_parcel_seeds.cpp b/libs/binder/tests/parcel_fuzzer/random_parcel_seeds.cpp index 0ed8a554ac..3cb628925c 100644 --- a/libs/binder/tests/parcel_fuzzer/random_parcel_seeds.cpp +++ b/libs/binder/tests/parcel_fuzzer/random_parcel_seeds.cpp @@ -281,9 +281,9 @@ void generateSeedsFromRecording(borrowed_fd fd, // This buffer holds the bytes which will be used for fillRandomParcel API std::vector<uint8_t> fillParcelBuffer; - // Don't take rpc path - uint8_t rpcBranch = 0; - impl::writeReversedBuffer(fillParcelBuffer, rpcBranch); + // Use all default options. + uint8_t parcelOptions = 0; + impl::writeReversedBuffer(fillParcelBuffer, parcelOptions); // Implicit branch on this path -> options->writeHeader(p, provider) uint8_t writeHeaderInternal = 0; 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 4874dbde9c..626581cc2a 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 { @@ -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::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::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..ac66362242 --- /dev/null +++ b/libs/graphicsenv/graphicsenv_flags.aconfig @@ -0,0 +1,9 @@ +package: "com.android.graphics.graphicsenv.flags" +container: "system" + +flag { + name: "feature_overrides" + namespace: "core_graphics" + description: "This flag controls the 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 452e48bb75..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> @@ -120,6 +121,9 @@ public: // 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..5ab31dbaba 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", diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index 5e6de78553..fa971426a7 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 @@ -242,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()) { @@ -419,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, @@ -622,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); @@ -645,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; { @@ -855,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) { @@ -1022,7 +1015,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, @@ -1030,7 +1023,7 @@ void BLASTBufferQueue::mergeWithNextTransaction(SurfaceComposerClient::Transacti std::lock_guard _lock{mMutex}; if (mLastAcquiredFrameNumber >= frameNumber) { // Apply the transaction since we have already acquired the desired frame. - t->apply(); + t->setApplyToken(mApplyToken).apply(); } else { mPendingTransactions.emplace_back(frameNumber, *t); // Clear the transaction so it can't be applied elsewhere. @@ -1128,10 +1121,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(); }); } @@ -1185,7 +1178,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); } @@ -1225,6 +1218,7 @@ 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; @@ -1251,6 +1245,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 @@ -1342,6 +1344,17 @@ void BLASTBufferQueue::setApplyToken(sp<IBinder> applyToken) { mApplyToken = std::move(applyToken); } +void BLASTBufferQueue::setWaitForBufferReleaseCallback( + std::function<void(const nsecs_t)> callback) { + std::lock_guard _lock{mWaitForBufferReleaseMutex}; + mWaitForBufferReleaseCallback = std::move(callback); +} + +std::function<void(const nsecs_t)> BLASTBufferQueue::getWaitForBufferReleaseCallback() const { + std::lock_guard _lock{mWaitForBufferReleaseMutex}; + return mWaitForBufferReleaseCallback; +} + #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) void BLASTBufferQueue::updateBufferReleaseProducer() { 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..1585aae45c 100644 --- a/libs/gui/BufferItemConsumer.cpp +++ b/libs/gui/BufferItemConsumer.cpp @@ -24,6 +24,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 +36,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) diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp index b0f6e69115..f21ac18f3c 100644 --- a/libs/gui/BufferQueue.cpp +++ b/libs/gui/BufferQueue.cpp @@ -108,6 +108,15 @@ void BufferQueue::ProxyConsumerListener::onSetFrameRate(float frameRate, int8_t } #endif +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) +void BufferQueue::ProxyConsumerListener::onSlotCountChanged(int slotCount) { + sp<ConsumerListener> listener(mConsumerListener.promote()); + if (listener != nullptr) { + listener->onSlotCountChanged(slotCount); + } +} +#endif + void BufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer, sp<IGraphicBufferConsumer>* outConsumer, bool consumerIsSurfaceFlinger) { @@ -116,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 9855b5bca4..270bfbdc64 100644 --- a/libs/gui/BufferQueueConsumer.cpp +++ b/libs/gui/BufferQueueConsumer.cpp @@ -341,9 +341,9 @@ status_t BufferQueueConsumer::detachBuffer(int slot) { return BAD_VALUE; } - if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { - BQ_LOGE("detachBuffer: slot index %d out of range [0, %d)", - slot, BufferQueueDefs::NUM_BUFFER_SLOTS); + const int totalSlotCount = mCore->getTotalSlotCountLocked(); + if (slot < 0 || slot >= totalSlotCount) { + BQ_LOGE("detachBuffer: slot index %d out of range [0, %d)", slot, totalSlotCount); return BAD_VALUE; } else if (!mSlots[slot].mBufferState.isAcquired()) { BQ_LOGE("detachBuffer: slot %d is not owned by the consumer " @@ -477,44 +477,38 @@ 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); - if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS || - releaseFence == nullptr) { - BQ_LOGE("releaseBuffer: slot %d out of range or fence %p NULL", slot, - releaseFence.get()); + const int totalSlotCount = mCore->getTotalSlotCountLocked(); + if (slot < 0 || slot >= totalSlotCount) { + BQ_LOGE("releaseBuffer: slot index %d out of range [0, %d)", slot, totalSlotCount); 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); + if (releaseFence == nullptr) { + BQ_LOGE("releaseBuffer: slot %d fence %p NULL", slot, releaseFence.get()); + return BAD_VALUE; } -#endif sp<IProducerListener> listener; { // Autolock scope std::lock_guard<std::mutex> lock(mCore->mMutex); + const int totalSlotCount = mCore->getTotalSlotCountLocked(); + if (slot < 0 || slot >= totalSlotCount || releaseFence == nullptr) { + BQ_LOGE("releaseBuffer: slot %d out of range [0, %d) or fence %p NULL", slot, + totalSlotCount, releaseFence.get()); + return BAD_VALUE; + } + // If the frame number has changed because the buffer has been reallocated, // we can ignore this releaseBuffer for the old buffer. // Ignore this for the shared buffer where the frame number can easily @@ -661,6 +655,43 @@ status_t BufferQueueConsumer::getReleasedBuffers(uint64_t *outSlotMask) { return NO_ERROR; } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) +status_t BufferQueueConsumer::getReleasedBuffersExtended(std::vector<bool>* outSlotMask) { + ATRACE_CALL(); + + if (outSlotMask == nullptr) { + BQ_LOGE("getReleasedBuffersExtended: outSlotMask may not be NULL"); + return BAD_VALUE; + } + + std::lock_guard<std::mutex> lock(mCore->mMutex); + + if (mCore->mIsAbandoned) { + BQ_LOGE("getReleasedBuffersExtended: BufferQueue has been abandoned"); + return NO_INIT; + } + + const int totalSlotCount = mCore->getTotalSlotCountLocked(); + outSlotMask->resize(totalSlotCount); + for (int s = 0; s < totalSlotCount; ++s) { + (*outSlotMask)[s] = !mSlots[s].mAcquireCalled; + } + + // Remove from the mask queued buffers for which acquire has been called, + // since the consumer will not receive their buffer addresses and so must + // retain their cached information + BufferQueueCore::Fifo::iterator current(mCore->mQueue.begin()); + while (current != mCore->mQueue.end()) { + if (current->mAcquireCalled) { + (*outSlotMask)[current->mSlot] = false; + } + ++current; + } + + return NO_ERROR; +} +#endif + status_t BufferQueueConsumer::setDefaultBufferSize(uint32_t width, uint32_t height) { ATRACE_CALL(); @@ -679,6 +710,28 @@ status_t BufferQueueConsumer::setDefaultBufferSize(uint32_t width, return NO_ERROR; } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) +status_t BufferQueueConsumer::allowUnlimitedSlots(bool allowUnlimitedSlots) { + ATRACE_CALL(); + BQ_LOGV("allowUnlimitedSlots: %d", allowUnlimitedSlots); + std::lock_guard<std::mutex> lock(mCore->mMutex); + + if (mCore->mIsAbandoned) { + BQ_LOGE("allowUnlimitedSlots: BufferQueue has been abandoned"); + return NO_INIT; + } + + if (mCore->mConnectedApi != BufferQueueCore::NO_CONNECTED_API) { + BQ_LOGE("allowUnlimitedSlots: BufferQueue already connected"); + return INVALID_OPERATION; + } + + mCore->mAllowExtendedSlotCount = allowUnlimitedSlots; + + return OK; +} +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + status_t BufferQueueConsumer::setMaxBufferCount(int bufferCount) { ATRACE_CALL(); @@ -718,16 +771,23 @@ status_t BufferQueueConsumer::setMaxAcquiredBufferCount( int maxAcquiredBuffers) { ATRACE_FORMAT("%s(%d)", __func__, maxAcquiredBuffers); - if (maxAcquiredBuffers < 1 || - maxAcquiredBuffers > BufferQueueCore::MAX_MAX_ACQUIRED_BUFFERS) { - BQ_LOGE("setMaxAcquiredBufferCount: invalid count %d", - maxAcquiredBuffers); - return BAD_VALUE; - } - sp<IConsumerListener> listener; { // Autolock scope std::unique_lock<std::mutex> lock(mCore->mMutex); + + // We reserve two slots in order to guarantee that the producer and + // consumer can run asynchronously. + int maxMaxAcquiredBuffers = +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + mCore->getTotalSlotCountLocked() - 2; +#else + BufferQueueCore::MAX_MAX_ACQUIRED_BUFFERS; +#endif + if (maxAcquiredBuffers < 1 || maxAcquiredBuffers > maxMaxAcquiredBuffers) { + BQ_LOGE("setMaxAcquiredBufferCount: invalid count %d", maxAcquiredBuffers); + return BAD_VALUE; + } + mCore->waitWhileAllocatingLocked(lock); if (mCore->mIsAbandoned) { diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp index 5a093995b4..6c79904475 100644 --- a/libs/gui/BufferQueueCore.cpp +++ b/libs/gui/BufferQueueCore.cpp @@ -38,6 +38,8 @@ #include <system/window.h> +#include <ui/BufferQueueDefs.h> + namespace android { // Macros for include BufferQueueCore information in log messages @@ -97,7 +99,11 @@ BufferQueueCore::BufferQueueCore() mConnectedProducerListener(), mBufferReleasedCbEnabled(false), mBufferAttachedCbEnabled(false), +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + mSlots(BufferQueueDefs::NUM_BUFFER_SLOTS), +#else mSlots(), +#endif mQueue(), mFreeSlots(), mFreeBuffers(), @@ -111,6 +117,9 @@ BufferQueueCore::BufferQueueCore() mDefaultWidth(1), mDefaultHeight(1), mDefaultBufferDataSpace(HAL_DATASPACE_UNKNOWN), +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + mAllowExtendedSlotCount(false), +#endif mMaxBufferCount(BufferQueueDefs::NUM_BUFFER_SLOTS), mMaxAcquiredBufferCount(1), mMaxDequeuedBufferCount(1), @@ -221,6 +230,14 @@ void BufferQueueCore::dumpState(const String8& prefix, String8* outResult) const } } +int BufferQueueCore::getTotalSlotCountLocked() const { +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + return mAllowExtendedSlotCount ? mMaxBufferCount : BufferQueueDefs::NUM_BUFFER_SLOTS; +#else + return BufferQueueDefs::NUM_BUFFER_SLOTS; +#endif +} + int BufferQueueCore::getMinUndequeuedBufferCountLocked() const { // If dequeueBuffer is allowed to error out, we don't have to add an // extra buffer. @@ -253,6 +270,26 @@ int BufferQueueCore::getMaxBufferCountLocked() const { return maxBufferCount; } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) +status_t BufferQueueCore::extendSlotCountLocked(int size) { + int previousSize = (int)mSlots.size(); + if (previousSize > size) { + return BAD_VALUE; + } + if (previousSize == size) { + return NO_ERROR; + } + + mSlots.resize(size); + for (int i = previousSize; i < size; i++) { + mUnusedSlots.push_back(i); + } + + mMaxBufferCount = size; + return NO_ERROR; +} +#endif + void BufferQueueCore::clearBufferSlotLocked(int slot) { BQ_LOGV("clearBufferSlotLocked: slot %d", slot); @@ -383,7 +420,7 @@ void BufferQueueCore::notifyBufferReleased() const { void BufferQueueCore::validateConsistencyLocked() const { static const useconds_t PAUSE_TIME = 0; int allocatedSlots = 0; - for (int slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) { + for (int slot = 0; slot < getTotalSlotCountLocked(); ++slot) { bool isInFreeSlots = mFreeSlots.count(slot) != 0; bool isInFreeBuffers = std::find(mFreeBuffers.cbegin(), mFreeBuffers.cend(), slot) != diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp index 2e7cef0847..5961b41478 100644 --- a/libs/gui/BufferQueueProducer.cpp +++ b/libs/gui/BufferQueueProducer.cpp @@ -40,6 +40,7 @@ #include <gui/TraceUtils.h> #include <private/gui/BufferQueueThreadState.h> +#include <utils/Errors.h> #include <utils/Log.h> #include <utils/Trace.h> @@ -108,9 +109,9 @@ status_t BufferQueueProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) { return NO_INIT; } - if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { - BQ_LOGE("requestBuffer: slot index %d out of range [0, %d)", - slot, BufferQueueDefs::NUM_BUFFER_SLOTS); + int maxSlot = mCore->getTotalSlotCountLocked(); + if (slot < 0 || slot >= maxSlot) { + BQ_LOGE("requestBuffer: slot index %d out of range [0, %d)", slot, maxSlot); return BAD_VALUE; } else if (!mSlots[slot].mBufferState.isDequeued()) { BQ_LOGE("requestBuffer: slot %d is not owned by the producer " @@ -123,6 +124,49 @@ status_t BufferQueueProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) { return NO_ERROR; } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) +status_t BufferQueueProducer::extendSlotCount(int size) { + ATRACE_CALL(); + + sp<IConsumerListener> listener; + { + std::lock_guard<std::mutex> lock(mCore->mMutex); + BQ_LOGV("extendSlotCount: size %d", size); + + if (mCore->mIsAbandoned) { + BQ_LOGE("extendSlotCount: BufferQueue has been abandoned"); + return NO_INIT; + } + + if (!mCore->mAllowExtendedSlotCount) { + BQ_LOGE("extendSlotCount: Consumer did not allow unlimited slots"); + return INVALID_OPERATION; + } + + int maxBeforeExtension = mCore->mMaxBufferCount; + + if (size == maxBeforeExtension) { + return NO_ERROR; + } + + if (size < maxBeforeExtension) { + return BAD_VALUE; + } + + if (status_t ret = mCore->extendSlotCountLocked(size); ret != OK) { + return ret; + } + listener = mCore->mConsumerListener; + } + + if (listener) { + listener->onSlotCountChanged(size); + } + + return NO_ERROR; +} +#endif + status_t BufferQueueProducer::setMaxDequeuedBufferCount( int maxDequeuedBuffers) { int maxBufferCount; @@ -170,9 +214,10 @@ status_t BufferQueueProducer::setMaxDequeuedBufferCount(int maxDequeuedBuffers, int bufferCount = mCore->getMinUndequeuedBufferCountLocked(); bufferCount += maxDequeuedBuffers; - if (bufferCount > BufferQueueDefs::NUM_BUFFER_SLOTS) { + if (bufferCount > mCore->getTotalSlotCountLocked()) { BQ_LOGE("setMaxDequeuedBufferCount: bufferCount %d too large " - "(max %d)", bufferCount, BufferQueueDefs::NUM_BUFFER_SLOTS); + "(max %d)", + bufferCount, mCore->getTotalSlotCountLocked()); return BAD_VALUE; } @@ -648,11 +693,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(); @@ -756,9 +801,9 @@ status_t BufferQueueProducer::detachBuffer(int slot) { return BAD_VALUE; } - if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { - BQ_LOGE("detachBuffer: slot index %d out of range [0, %d)", - slot, BufferQueueDefs::NUM_BUFFER_SLOTS); + const int totalSlotCount = mCore->getTotalSlotCountLocked(); + if (slot < 0 || slot >= totalSlotCount) { + BQ_LOGE("detachBuffer: slot index %d out of range [0, %d)", slot, totalSlotCount); return BAD_VALUE; } else if (!mSlots[slot].mBufferState.isDequeued()) { // TODO(http://b/140581935): This message is BQ_LOGW because it @@ -993,9 +1038,9 @@ status_t BufferQueueProducer::queueBuffer(int slot, return NO_INIT; } - if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { - BQ_LOGE("queueBuffer: slot index %d out of range [0, %d)", - slot, BufferQueueDefs::NUM_BUFFER_SLOTS); + const int totalSlotCount = mCore->getTotalSlotCountLocked(); + if (slot < 0 || slot >= totalSlotCount) { + BQ_LOGE("queueBuffer: slot index %d out of range [0, %d)", slot, totalSlotCount); return BAD_VALUE; } else if (!mSlots[slot].mBufferState.isDequeued()) { BQ_LOGE("queueBuffer: slot %d is not owned by the producer " @@ -1239,9 +1284,9 @@ status_t BufferQueueProducer::cancelBuffer(int slot, const sp<Fence>& fence) { return BAD_VALUE; } - if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { - BQ_LOGE("cancelBuffer: slot index %d out of range [0, %d)", slot, - BufferQueueDefs::NUM_BUFFER_SLOTS); + const int totalSlotCount = mCore->getTotalSlotCountLocked(); + if (slot < 0 || slot >= totalSlotCount) { + BQ_LOGE("cancelBuffer: slot index %d out of range [0, %d)", slot, totalSlotCount); return BAD_VALUE; } else if (!mSlots[slot].mBufferState.isDequeued()) { BQ_LOGE("cancelBuffer: slot %d is not owned by the producer " @@ -1409,6 +1454,9 @@ status_t BufferQueueProducer::connect(const sp<IProducerListener>& listener, output->nextFrameNumber = mCore->mFrameCounter + 1; output->bufferReplaced = false; output->maxBufferCount = mCore->mMaxBufferCount; +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + output->isSlotExpansionAllowed = mCore->mAllowExtendedSlotCount; +#endif if (listener != nullptr) { // Set up a death notification so that we can disconnect @@ -1416,7 +1464,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); @@ -1505,8 +1553,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 = @@ -1637,11 +1684,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 602bba8dab..117a362661 100644 --- a/libs/gui/ConsumerBase.cpp +++ b/libs/gui/ConsumerBase.cpp @@ -31,12 +31,15 @@ #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> #include <private/gui/ComposerService.h> +#include <ui/BufferQueueDefs.h> + #include <log/log.h> #include <utils/Log.h> #include <utils/String8.h> @@ -59,51 +62,74 @@ static int32_t createProcessUniqueId() { return android_atomic_inc(&globalCounter); } -ConsumerBase::ConsumerBase(const sp<IGraphicBufferConsumer>& bufferQueue, bool controlledByApp) : +ConsumerBase::ConsumerBase(const sp<IGraphicBufferConsumer>& bufferQueue, bool controlledByApp) + : +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + mSlots(BufferQueueDefs::NUM_BUFFER_SLOTS), +#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) ConsumerBase::ConsumerBase(bool controlledByApp, bool consumerIsSurfaceFlinger) - : mAbandoned(false), mPrevFinalReleaseFence(Fence::NO_FENCE) { + : +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + mSlots(BufferQueueDefs::NUM_BUFFER_SLOTS), +#endif + mAbandoned(false), + 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, const sp<IGraphicBufferConsumer>& consumer, bool controlledByApp) - : mAbandoned(false), + : +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + mSlots(BufferQueueDefs::NUM_BUFFER_SLOTS), +#endif + 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); - } else { - mConsumer->setConsumerName(mName); + return; + } + + mConsumer->setConsumerName(mName); +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + if (err = mConsumer->allowUnlimitedSlots(true); err != NO_ERROR) { + CB_LOGE("ConsumerBase: error marking as allowed to have unlimited slots: %s (%d)", + strerror(-err), err); } +#endif } ConsumerBase::~ConsumerBase() { @@ -130,7 +156,11 @@ int ConsumerBase::getSlotForBufferLocked(const sp<GraphicBuffer>& buffer) { } uint64_t id = buffer->getId(); - for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; i++) { +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + for (int i = 0; i < (int)mSlots.size(); ++i) { +#else + for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { +#endif auto& slot = mSlots[i]; if (slot.mGraphicBuffer && slot.mGraphicBuffer->getId() == id) { return i; @@ -242,6 +272,15 @@ void ConsumerBase::onBuffersReleased() { return; } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + std::vector<bool> mask; + mConsumer->getReleasedBuffersExtended(&mask); + for (size_t i = 0; i < mSlots.size(); i++) { + if (mask[i]) { + freeBufferLocked(i); + } + } +#else uint64_t mask = 0; mConsumer->getReleasedBuffers(&mask); for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { @@ -249,11 +288,23 @@ void ConsumerBase::onBuffersReleased() { freeBufferLocked(i); } } +#endif } void ConsumerBase::onSidebandStreamChanged() { } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) +void ConsumerBase::onSlotCountChanged(int slotCount) { + CB_LOGV("onSlotCountChanged: %d", slotCount); + Mutex::Autolock lock(mMutex); + + if (slotCount > (int)mSlots.size()) { + mSlots.resize(slotCount); + } +} +#endif + void ConsumerBase::abandon() { CB_LOGV("abandon"); Mutex::Autolock lock(mMutex); @@ -270,7 +321,11 @@ void ConsumerBase::abandonLocked() { CB_LOGE("abandonLocked: ConsumerBase is abandoned!"); return; } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + for (int i = 0; i < (int)mSlots.size(); ++i) { +#else for (int i =0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { +#endif freeBufferLocked(i); } // disconnect from the BufferQueue @@ -334,6 +389,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) { @@ -387,6 +462,15 @@ status_t ConsumerBase::setMaxBufferCount(int bufferCount) { CB_LOGE("setMaxBufferCount: ConsumerBase is abandoned!"); return NO_INIT; } + +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + if (status_t err = mConsumer->allowUnlimitedSlots(false); err != NO_ERROR) { + CB_LOGE("ConsumerBase: error marking as not allowed to have unlimited slots: %s (%d)", + strerror(-err), err); + return err; + } +#endif + return mConsumer->setMaxBufferCount(bufferCount); } #endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) @@ -400,7 +484,6 @@ status_t ConsumerBase::setMaxAcquiredBufferCount(int maxAcquiredBuffers) { return mConsumer->setMaxAcquiredBufferCount(maxAcquiredBuffers); } -#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) status_t ConsumerBase::setConsumerIsProtected(bool isProtected) { Mutex::Autolock lock(mMutex); if (mAbandoned) { @@ -409,7 +492,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); @@ -448,6 +530,15 @@ status_t ConsumerBase::discardFreeBuffers() { if (err != OK) { return err; } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + std::vector<bool> mask; + mConsumer->getReleasedBuffersExtended(&mask); + for (int i = 0; i < (int)mSlots.size(); i++) { + if (mask[i]) { + freeBufferLocked(i); + } + } +#else uint64_t mask; mConsumer->getReleasedBuffers(&mask); for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { @@ -455,6 +546,8 @@ status_t ConsumerBase::discardFreeBuffers() { freeBufferLocked(i); } } +#endif + return OK; } @@ -585,9 +678,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; @@ -596,13 +693,20 @@ status_t ConsumerBase::releaseBufferLocked( // buffer on the same slot), the buffer producer is definitely no longer // tracking it. if (!stillTracking(slot, graphicBuffer)) { + CB_LOGV("releaseBufferLocked: Not tracking, exiting without calling releaseBuffer for " + "slot=%d/%" PRIu64, + slot, mSlots[slot].mFrameNumber); return OK; } 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); } @@ -615,7 +719,11 @@ status_t ConsumerBase::releaseBufferLocked( bool ConsumerBase::stillTracking(int slot, const sp<GraphicBuffer> graphicBuffer) { +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + if (slot < 0 || slot >= (int)mSlots.size()) { +#else if (slot < 0 || slot >= BufferQueue::NUM_BUFFER_SLOTS) { +#endif return false; } return (mSlots[slot].mGraphicBuffer != nullptr && 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/Flags.cpp b/libs/gui/Flags.cpp index 85ee2cddad..ee2802f706 100644 --- a/libs/gui/Flags.cpp +++ b/libs/gui/Flags.cpp @@ -29,6 +29,14 @@ sp<SurfaceType> surfaceToSurfaceType(const sp<Surface>& surface) { #endif } +ParcelableSurfaceType surfaceToParcelableSurfaceType(const sp<Surface>& surface) { +#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES + return view::Surface::fromSurface(surface); +#else + return surface->getIGraphicBufferProducer(); +#endif +} + sp<IGraphicBufferProducer> surfaceTypeToIGBP(const sp<SurfaceType>& surface) { #if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES return surface->getIGraphicBufferProducer(); diff --git a/libs/gui/FrameRateUtils.cpp b/libs/gui/FrameRateUtils.cpp index 5c4879c1bd..1b2354e942 100644 --- a/libs/gui/FrameRateUtils.cpp +++ b/libs/gui/FrameRateUtils.cpp @@ -42,7 +42,7 @@ bool ValidateFrameRate(float frameRate, int8_t compatibility, int8_t changeFrame if (compatibility != ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT && compatibility != ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE && - compatibility != ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE && + compatibility != ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_AT_LEAST && (!privileged || (compatibility != ANATIVEWINDOW_FRAME_RATE_EXACT && compatibility != ANATIVEWINDOW_FRAME_RATE_NO_VOTE))) { diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp index f2173cd740..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), @@ -119,6 +164,9 @@ GLConsumer::GLConsumer(uint32_t tex, uint32_t texTarget, bool useFenceSync, bool mTexTarget(texTarget), mEglDisplay(EGL_NO_DISPLAY), mEglContext(EGL_NO_CONTEXT), +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + mEglSlots(BufferQueueDefs::NUM_BUFFER_SLOTS), +#endif mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT), mAttached(true) { GLC_LOGV("GLConsumer"); @@ -129,27 +177,29 @@ GLConsumer::GLConsumer(uint32_t tex, uint32_t texTarget, bool useFenceSync, bool } #endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) -GLConsumer::GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t tex, - uint32_t texTarget, bool useFenceSync, bool isControlledByApp) : - ConsumerBase(bq, isControlledByApp), - mCurrentCrop(Rect::EMPTY_RECT), - mCurrentTransform(0), - mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), - mCurrentFence(Fence::NO_FENCE), - mCurrentTimestamp(0), - mCurrentDataSpace(HAL_DATASPACE_UNKNOWN), - mCurrentFrameNumber(0), - mDefaultWidth(1), - mDefaultHeight(1), - mFilteringEnabled(true), - mTexName(tex), - mUseFenceSync(useFenceSync), - mTexTarget(texTarget), - mEglDisplay(EGL_NO_DISPLAY), - mEglContext(EGL_NO_CONTEXT), - mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT), - mAttached(true) -{ +GLConsumer::GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t tex, uint32_t texTarget, + bool useFenceSync, bool isControlledByApp) + : ConsumerBase(bq, isControlledByApp), + mCurrentCrop(Rect::EMPTY_RECT), + mCurrentTransform(0), + mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), + mCurrentFence(Fence::NO_FENCE), + mCurrentTimestamp(0), + mCurrentDataSpace(HAL_DATASPACE_UNKNOWN), + mCurrentFrameNumber(0), + mDefaultWidth(1), + mDefaultHeight(1), + mFilteringEnabled(true), + mTexName(tex), + mUseFenceSync(useFenceSync), + mTexTarget(texTarget), + mEglDisplay(EGL_NO_DISPLAY), + mEglContext(EGL_NO_CONTEXT), +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + mEglSlots(BufferQueueDefs::NUM_BUFFER_SLOTS), +#endif + mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT), + mAttached(true) { GLC_LOGV("GLConsumer"); memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), @@ -176,6 +226,9 @@ GLConsumer::GLConsumer(uint32_t texTarget, bool useFenceSync, bool isControlledB mTexTarget(texTarget), mEglDisplay(EGL_NO_DISPLAY), mEglContext(EGL_NO_CONTEXT), +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + mEglSlots(BufferQueueDefs::NUM_BUFFER_SLOTS), +#endif mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT), mAttached(false) { GLC_LOGV("GLConsumer"); @@ -204,6 +257,9 @@ GLConsumer::GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t texTarget, mTexTarget(texTarget), mEglDisplay(EGL_NO_DISPLAY), mEglContext(EGL_NO_CONTEXT), +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + mEglSlots(BufferQueueDefs::NUM_BUFFER_SLOTS), +#endif mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT), mAttached(false) { GLC_LOGV("GLConsumer"); @@ -322,7 +378,7 @@ status_t GLConsumer::releaseTexImage() { } if (mReleasedTexImage == nullptr) { - mReleasedTexImage = new EglImage(getDebugTexImageBuffer()); + mReleasedTexImage = sp<EglImage>::make(getDebugTexImageBuffer()); } mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT; @@ -354,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(); @@ -389,24 +445,35 @@ 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; } -status_t GLConsumer::releaseBufferLocked(int buf, - sp<GraphicBuffer> graphicBuffer, - EGLDisplay display, EGLSyncKHR eglFence) { +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) +void GLConsumer::onSlotCountChanged(int slotCount) { + ConsumerBase::onSlotCountChanged(slotCount); + + Mutex::Autolock lock(mMutex); + if (slotCount > (int)mEglSlots.size()) { + mEglSlots.resize(slotCount); + } +} +#endif + +#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) @@ -468,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); @@ -479,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; } } @@ -713,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) { @@ -722,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 @@ -751,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 f3bd90cffb..0000000000 --- a/libs/gui/IConsumerListener.cpp +++ /dev/null @@ -1,122 +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, - LAST = ON_FRAME_DETACHED, -}; - -} // 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"); - } -}; - -// 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); - } -} - -} // namespace android diff --git a/libs/gui/IGraphicBufferConsumer.cpp b/libs/gui/IGraphicBufferConsumer.cpp deleted file mode 100644 index 282957b940..0000000000 --- a/libs/gui/IGraphicBufferConsumer.cpp +++ /dev/null @@ -1,234 +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 <gui/BufferItem.h> -#include <gui/IConsumerListener.h> - -#include <binder/Parcel.h> - -#include <ui/Fence.h> -#include <ui/GraphicBuffer.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, - LAST = DUMP_STATE, -}; - -} // 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); - } - - 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); - } - - 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); - } - } -} - -} // namespace android diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp index 09144806ee..1d1910eb08 100644 --- a/libs/gui/IGraphicBufferProducer.cpp +++ b/libs/gui/IGraphicBufferProducer.cpp @@ -81,6 +81,7 @@ enum { GET_LAST_QUEUED_BUFFER2, SET_FRAME_RATE, SET_ADDITIONAL_OPTIONS, + SET_MAX_BUFER_COUNT_EXTENDED, }; class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer> @@ -103,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(); @@ -149,6 +150,20 @@ public: return result; } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + status_t extendSlotCount(int size) override { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); + data.writeInt32(size); + status_t result = remote()->transact(SET_MAX_BUFER_COUNT_EXTENDED, data, &reply); + if (result != NO_ERROR) { + return result; + } + result = reply.readInt32(); + return result; + } +#endif + virtual status_t setAsyncMode(bool async) { Parcel data, reply; data.writeInterfaceToken( @@ -182,7 +197,7 @@ public: } *buf = reply.readInt32(); - *fence = new Fence(); + *fence = sp<Fence>::make(); result = reply.read(**fence); if (result != NO_ERROR) { fence->clear(); @@ -278,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(); @@ -287,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(); @@ -625,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); @@ -635,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); @@ -672,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); @@ -685,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); @@ -981,6 +996,14 @@ IMPLEMENT_HYBRID_META_INTERFACE(GraphicBufferProducer, // ---------------------------------------------------------------------- +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) +status_t IGraphicBufferProducer::extendSlotCount(int size) { + // No-op for IGBP other than BufferQueue. + (void)size; + return INVALID_OPERATION; +} +#endif + status_t IGraphicBufferProducer::setLegacyBufferDrop(bool drop) { // No-op for IGBP other than BufferQueue. (void) drop; @@ -1209,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) { @@ -1227,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; @@ -1283,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); @@ -1582,6 +1605,15 @@ status_t BnGraphicBufferProducer::onTransact( return NO_ERROR; } #endif +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + case SET_MAX_BUFER_COUNT_EXTENDED: { + CHECK_INTERFACE(IGraphicBufferProducer, data, reply); + int size = data.readInt32(); + status_t result = extendSlotCount(size); + reply->writeInt32(result); + return NO_ERROR; + } +#endif } return BBinder::onTransact(code, data, reply, flags); } diff --git a/libs/gui/IGraphicBufferProducerFlattenables.cpp b/libs/gui/IGraphicBufferProducerFlattenables.cpp index 4e92a39973..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; @@ -128,7 +128,7 @@ status_t IGraphicBufferProducer::QueueBufferInput::unflatten( constexpr size_t IGraphicBufferProducer::QueueBufferOutput::minFlattenedSize() { return sizeof(width) + sizeof(height) + sizeof(transformHint) + sizeof(numPendingBuffers) + sizeof(nextFrameNumber) + sizeof(bufferReplaced) + sizeof(maxBufferCount) + - sizeof(result); + sizeof(result) + sizeof(isSlotExpansionAllowed); } size_t IGraphicBufferProducer::QueueBufferOutput::getFlattenedSize() const { return minFlattenedSize() + frameTimestamps.getFlattenedSize(); @@ -152,6 +152,7 @@ status_t IGraphicBufferProducer::QueueBufferOutput::flatten( FlattenableUtils::write(buffer, size, nextFrameNumber); FlattenableUtils::write(buffer, size, bufferReplaced); FlattenableUtils::write(buffer, size, maxBufferCount); + FlattenableUtils::write(buffer, size, isSlotExpansionAllowed); status_t result = frameTimestamps.flatten(buffer, size, fds, count); if (result != NO_ERROR) { @@ -175,6 +176,7 @@ status_t IGraphicBufferProducer::QueueBufferOutput::unflatten( FlattenableUtils::read(buffer, size, nextFrameNumber); FlattenableUtils::read(buffer, size, bufferReplaced); FlattenableUtils::read(buffer, size, maxBufferCount); + FlattenableUtils::read(buffer, size, isSlotExpansionAllowed); status_t result = frameTimestamps.unflatten(buffer, size, fds, count); if (result != NO_ERROR) { @@ -226,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) { @@ -321,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; @@ -393,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/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 e41f9bbf43..63dcfbcb9b 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -38,6 +38,7 @@ #include <utils/NativeHandle.h> #include <utils/Trace.h> +#include <ui/BufferQueueDefs.h> #include <ui/DynamicDisplayInfo.h> #include <ui/Fence.h> #include <ui/GraphicBuffer.h> @@ -98,7 +99,10 @@ Surface::Surface(const sp<IGraphicBufferProducer>& bufferProducer, bool controll : mGraphicBufferProducer(bufferProducer), #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) mSurfaceDeathListener(nullptr), -#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) +#endif +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + mSlots(NUM_BUFFER_SLOTS), +#endif mCrop(Rect::EMPTY_RECT), mBufferAge(0), mGenerationNumber(0), @@ -192,7 +196,7 @@ void Surface::allocateBuffers() { status_t Surface::allowAllocation(bool allowAllocation) { return mGraphicBufferProducer->allowAllocation(allowAllocation); } -#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) +#endif status_t Surface::setGenerationNumber(uint32_t generation) { status_t result = mGraphicBufferProducer->setGenerationNumber(generation); @@ -505,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", @@ -658,7 +662,11 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) { return result; } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + if (buf < 0 || buf >= (int)mSlots.size()) { +#else if (buf < 0 || buf >= NUM_BUFFER_SLOTS) { +#endif ALOGE("dequeueBuffer: IGraphicBufferProducer returned invalid slot number %d", buf); android_errorWriteLog(0x534e4554, "36991414"); // SafetyNet logging return FAILED_TRANSACTION; @@ -757,7 +765,11 @@ status_t Surface::detachBuffer(const sp<GraphicBuffer>& buffer) { Mutex::Autolock lock(mMutex); uint64_t bufferId = buffer->getId(); +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + for (int slot = 0; slot < (int)mSlots.size(); ++slot) { +#else for (int slot = 0; slot < Surface::NUM_BUFFER_SLOTS; ++slot) { +#endif auto& bufferSlot = mSlots[slot]; if (bufferSlot.buffer != nullptr && bufferSlot.buffer->getId() == bufferId) { bufferSlot.buffer = nullptr; @@ -840,7 +852,11 @@ int Surface::dequeueBuffers(std::vector<BatchBuffer>* buffers) { return output.result; } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + if (output.slot < 0 || output.slot >= (int)mSlots.size()) { +#else if (output.slot < 0 || output.slot >= NUM_BUFFER_SLOTS) { +#endif mGraphicBufferProducer->cancelBuffers(cancelBufferInputs, &cancelBufferOutputs); ALOGE("%s: IGraphicBufferProducer returned invalid slot number %d", __FUNCTION__, output.slot); @@ -963,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) { @@ -1001,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; } @@ -1027,7 +1043,11 @@ int Surface::getSlotFromBufferLocked( return BAD_VALUE; } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + for (int i = 0; i < (int)mSlots.size(); i++) { +#else for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { +#endif if (mSlots[i].buffer != nullptr && mSlots[i].buffer->handle == buffer->handle) { return i; @@ -1058,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, @@ -2072,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); } @@ -2084,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 = @@ -2094,6 +2114,9 @@ int Surface::connect(int api, const sp<SurfaceListener>& listener, bool reportBu mDefaultHeight = output.height; mNextFrameNumber = output.nextFrameNumber; mMaxBufferCount = output.maxBufferCount; +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + mIsSlotExpansionAllowed = output.isSlotExpansionAllowed; +#endif // Ignore transform hint if sticky transform is set or transform to display inverse flag is // set. Transform hint should be ignored if the client is expected to always submit buffers @@ -2190,7 +2213,11 @@ int Surface::detachNextBuffer(sp<GraphicBuffer>* outBuffer, *outFence = Fence::NO_FENCE; } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + for (int i = 0; i < (int)mSlots.size(); i++) { +#else for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { +#endif if (mSlots[i].buffer != nullptr && mSlots[i].buffer->getId() == buffer->getId()) { if (mReportRemovedBuffers) { @@ -2292,8 +2319,35 @@ int Surface::setMaxDequeuedBufferCount(int maxDequeuedBuffers) { ALOGV("Surface::setMaxDequeuedBufferCount"); Mutex::Autolock lock(mMutex); - status_t err = mGraphicBufferProducer->setMaxDequeuedBufferCount( - maxDequeuedBuffers); +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + if (maxDequeuedBuffers > BufferQueueDefs::NUM_BUFFER_SLOTS && !mIsSlotExpansionAllowed) { + return BAD_VALUE; + } + + int minUndequeuedBuffers = 0; + status_t err = mGraphicBufferProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, + &minUndequeuedBuffers); + if (err != OK) { + ALOGE("IGraphicBufferProducer::query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS) returned %s", + strerror(-err)); + return err; + } + + if (maxDequeuedBuffers > (int)mSlots.size()) { + int newSlotCount = minUndequeuedBuffers + maxDequeuedBuffers; + err = mGraphicBufferProducer->extendSlotCount(newSlotCount); + if (err != OK) { + ALOGE("IGraphicBufferProducer::extendSlotCount(%d) returned %s", newSlotCount, + strerror(-err)); + return err; + } + + mSlots.resize(newSlotCount); + } + err = mGraphicBufferProducer->setMaxDequeuedBufferCount(maxDequeuedBuffers); +#else + status_t err = mGraphicBufferProducer->setMaxDequeuedBufferCount(maxDequeuedBuffers); +#endif ALOGE_IF(err, "IGraphicBufferProducer::setMaxDequeuedBufferCount(%d) " "returned %s", maxDequeuedBuffers, strerror(-err)); @@ -2501,7 +2555,11 @@ void Surface::freeAllBuffers() { ALOGE("%s: %zu buffers were freed while being dequeued!", __FUNCTION__, mDequeuedSlots.size()); } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + for (int i = 0; i < (int)mSlots.size(); i++) { +#else for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { +#endif mSlots[i].buffer = nullptr; } } @@ -2510,7 +2568,11 @@ status_t Surface::getAndFlushBuffersFromSlots(const std::vector<int32_t>& slots, std::vector<sp<GraphicBuffer>>* outBuffers) { ALOGV("Surface::getAndFlushBuffersFromSlots"); for (int32_t i : slots) { +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + if (i < 0 || i >= (int)mSlots.size()) { +#else if (i < 0 || i >= NUM_BUFFER_SLOTS) { +#endif ALOGE("%s: Invalid slotIndex: %d", __FUNCTION__, i); return BAD_VALUE; } @@ -2670,7 +2732,11 @@ status_t Surface::lock( newDirtyRegion.set(bounds); mDirtyRegion.clear(); Mutex::Autolock lock(mMutex); - for (size_t i=0 ; i<NUM_BUFFER_SLOTS ; i++) { +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + for (int i = 0; i < (int)mSlots.size(); i++) { +#else + for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { +#endif mSlots[i].dirtyRegion.clear(); } } diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index be88b11b9c..e407a63d10 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()); @@ -829,9 +830,7 @@ SurfaceComposerClient::Transaction::Transaction() { SurfaceComposerClient::Transaction::Transaction(const Transaction& other) : mId(other.mId), - mAnimation(other.mAnimation), - mEarlyWakeupStart(other.mEarlyWakeupStart), - mEarlyWakeupEnd(other.mEarlyWakeupEnd), + mFlags(other.mFlags), mMayContainBuffer(other.mMayContainBuffer), mDesiredPresentTime(other.mDesiredPresentTime), mIsAutoTimestamp(other.mIsAutoTimestamp), @@ -846,7 +845,7 @@ SurfaceComposerClient::Transaction::Transaction(const Transaction& other) void SurfaceComposerClient::Transaction::sanitize(int pid, int uid) { uint32_t permissions = LayerStatePermissions::getTransactionPermissions(pid, uid); - for (auto & [handle, composerState] : mComposerStates) { + for (auto& composerState : mComposerStates) { composerState.state.sanitize(permissions); } if (!mInputWindowCommands.empty() && @@ -868,9 +867,7 @@ 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 uint32_t flags = parcel->readUint32(); const int64_t desiredPresentTime = parcel->readInt64(); const bool isAutoTimestamp = parcel->readBool(); const bool logCallPoints = parcel->readBool(); @@ -883,7 +880,7 @@ status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel if (count > parcel->dataSize()) { return BAD_VALUE; } - SortedVector<DisplayState> displayStates; + Vector<DisplayState> displayStates; displayStates.setCapacity(count); for (size_t i = 0; i < count; i++) { DisplayState displayState; @@ -926,17 +923,14 @@ status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel if (count > parcel->dataSize()) { return BAD_VALUE; } - std::unordered_map<sp<IBinder>, ComposerState, IBinderHash> composerStates; - composerStates.reserve(count); + Vector<ComposerState> composerStates; + composerStates.setCapacity(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; + composerStates.add(composerState); } InputWindowCommands inputWindowCommands; @@ -965,15 +959,13 @@ status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel // Parsing was successful. Update the object. mId = transactionId; - mAnimation = animation; - mEarlyWakeupStart = earlyWakeupStart; - mEarlyWakeupEnd = earlyWakeupEnd; + mFlags = flags; mDesiredPresentTime = desiredPresentTime; mIsAutoTimestamp = isAutoTimestamp; mFrameTimelineInfo = frameTimelineInfo; - mDisplayStates = displayStates; + mDisplayStates = std::move(displayStates); mListenerCallbacks = listenerCallbacks; - mComposerStates = composerStates; + mComposerStates = std::move(composerStates); mInputWindowCommands = inputWindowCommands; mApplyToken = applyToken; mUncacheBuffers = std::move(uncacheBuffers); @@ -996,9 +988,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->writeUint32(mFlags); parcel->writeInt64(mDesiredPresentTime); parcel->writeBool(mIsAutoTimestamp); parcel->writeBool(mLogCallPoints); @@ -1023,8 +1013,7 @@ 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); + for (auto const& composerState : mComposerStates) { composerState.write(*parcel); } @@ -1081,23 +1070,31 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::merge(Tr } 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); + 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) { + releaseBufferIfOverwriting(it->state); } - mComposerStates[handle].state.merge(composerState.state); + it->state.merge(otherState.state); + } else { + mComposerStates.add(otherState); } } for (auto const& state : other.mDisplayStates) { - ssize_t index = mDisplayStates.indexOf(state); - if (index < 0) { - mDisplayStates.add(state); + 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.editItemAt(static_cast<size_t>(index)).merge(state); + mDisplayStates.add(state); } } @@ -1131,8 +1128,7 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::merge(Tr mInputWindowCommands.merge(other.mInputWindowCommands); mMayContainBuffer |= other.mMayContainBuffer; - mEarlyWakeupStart = mEarlyWakeupStart || other.mEarlyWakeupStart; - mEarlyWakeupEnd = mEarlyWakeupEnd || other.mEarlyWakeupEnd; + mFlags |= other.mFlags; mApplyToken = other.mApplyToken; mergeFrameTimelineInfo(mFrameTimelineInfo, other.mFrameTimelineInfo); @@ -1154,15 +1150,13 @@ void SurfaceComposerClient::Transaction::clear() { mInputWindowCommands.clear(); mUncacheBuffers.clear(); mMayContainBuffer = false; - mAnimation = false; - mEarlyWakeupStart = false; - mEarlyWakeupEnd = false; mDesiredPresentTime = 0; mIsAutoTimestamp = true; mFrameTimelineInfo = {}; mApplyToken = nullptr; mMergedTransactionIds.clear(); mLogCallPoints = false; + mFlags = 0; } uint64_t SurfaceComposerClient::Transaction::getId() { @@ -1197,8 +1191,8 @@ void SurfaceComposerClient::Transaction::cacheBuffers() { } size_t count = 0; - for (auto& [handle, cs] : mComposerStates) { - layer_state_t* s = &(mComposerStates[handle].state); + for (auto& cs : mComposerStates) { + layer_state_t* s = &cs.state; if (!(s->what & layer_state_t::eBufferChanged)) { continue; } else if (s->bufferData && @@ -1323,42 +1317,26 @@ status_t SurfaceComposerClient::Transaction::apply(bool synchronous, bool oneWay 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; + 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; + uint32_t wakeupFlags = ISurfaceComposer::eEarlyWakeupStart | ISurfaceComposer::eEarlyWakeupEnd; + if ((mFlags & wakeupFlags) == wakeupFlags) { + mFlags &= ~(wakeupFlags); } - if (mEarlyWakeupEnd && !mEarlyWakeupStart) { - flags |= ISurfaceComposer::eEarlyWakeupEnd; - } - sp<IBinder> applyToken = mApplyToken ? mApplyToken : getDefaultApplyToken(); sp<ISurfaceComposer> sf(ComposerService::getComposerService()); status_t binderStatus = - sf->setTransactionState(mFrameTimelineInfo, composerStates, displayStates, flags, + sf->setTransactionState(mFrameTimelineInfo, mComposerStates, mDisplayStates, mFlags, applyToken, mInputWindowCommands, mDesiredPresentTime, mIsAutoTimestamp, mUncacheBuffers, hasListenerCallbacks, listenerCallbacks, mId, mMergedTransactionIds); @@ -1379,7 +1357,7 @@ status_t SurfaceComposerClient::Transaction::apply(bool synchronous, bool oneWay return binderStatus; } -sp<IBinder> SurfaceComposerClient::Transaction::sApplyToken = new BBinder(); +sp<IBinder> SurfaceComposerClient::Transaction::sApplyToken = sp<BBinder>::make(); std::mutex SurfaceComposerClient::Transaction::sApplyTokenMutex; @@ -1413,11 +1391,16 @@ void SurfaceComposerClient::Transaction::enableDebugLogCallPoints() { // --------------------------------------------------------------------------- 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 +1420,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 +1443,34 @@ std::optional<gui::StalledTransactionInfo> SurfaceComposerClient::getStalledTran } void SurfaceComposerClient::Transaction::setAnimationTransaction() { - mAnimation = true; + mFlags |= ISurfaceComposer::eAnimation; } void SurfaceComposerClient::Transaction::setEarlyWakeupStart() { - mEarlyWakeupStart = true; + mFlags |= ISurfaceComposer::eEarlyWakeupStart; } void SurfaceComposerClient::Transaction::setEarlyWakeupEnd() { - mEarlyWakeupEnd = true; + 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; + 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; } - return &(mComposerStates[handle].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.add(s); + + return &mComposerStates.editItemAt(mComposerStates.size() - 1).state; } void SurfaceComposerClient::Transaction::registerSurfaceControlForCallback( @@ -1543,11 +1528,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 +1558,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 +1674,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 +1720,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; } @@ -1961,9 +1950,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 +1961,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 +2006,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; } @@ -2138,21 +2125,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); + mInputWindowCommands.addFocusRequest(request); return *this; } SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::addWindowInfosReportedListener( sp<gui::IWindowInfosReportedListener> windowInfosReportedListener) { - mInputWindowCommands.windowInfosReportedListeners.insert(windowInfosReportedListener); + mInputWindowCommands.addWindowInfosReportedListener(windowInfosReportedListener); return *this; } @@ -2372,10 +2358,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 +2469,17 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setConte // --------------------------------------------------------------------------- DisplayState& SurfaceComposerClient::Transaction::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; - 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)); + mDisplayStates.add(s); + return mDisplayStates.editItemAt(mDisplayStates.size() - 1); } status_t SurfaceComposerClient::Transaction::setDisplaySurface(const sp<IBinder>& token, @@ -2578,8 +2562,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 +2580,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 +2673,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 +2691,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 +2702,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; } @@ -3293,10 +3279,17 @@ status_t SurfaceComposerClient::removeHdrLayerInfoListener( return statusTFromBinderStatus(status); } -status_t SurfaceComposerClient::setActivePictureListener( +status_t SurfaceComposerClient::addActivePictureListener( + const sp<gui::IActivePictureListener>& listener) { + binder::Status status = + ComposerServiceAIDL::getComposerService()->addActivePictureListener(listener); + return statusTFromBinderStatus(status); +} + +status_t SurfaceComposerClient::removeActivePictureListener( const sp<gui::IActivePictureListener>& listener) { binder::Status status = - ComposerServiceAIDL::getComposerService()->setActivePictureListener(listener); + ComposerServiceAIDL::getComposerService()->removeActivePictureListener(listener); return statusTFromBinderStatus(status); } 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/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/CaptureArgs.aidl b/libs/gui/aidl/android/gui/CaptureArgs.aidl index 4920344e0e..2bbed2b9d6 100644 --- a/libs/gui/aidl/android/gui/CaptureArgs.aidl +++ b/libs/gui/aidl/android/gui/CaptureArgs.aidl @@ -69,10 +69,5 @@ parcelable CaptureArgs { // exact colorspace is not an appropriate intermediate result. // Note that if the caller is requesting a specific dataspace, this hint does nothing. boolean hintForSeamlessTransition = false; - - // Allows the screenshot to attach a gainmap, which allows for a per-pixel - // transformation of the screenshot to another luminance range, typically - // mapping an SDR base image into HDR. - boolean attachGainmap = false; } diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl index 8c19bbbba9..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. @@ -607,8 +616,14 @@ interface ISurfaceComposer { oneway void removeJankListener(int layerId, IJankListener listener, long afterVsync); /** - * Sets the listener used to monitor visible content that is being processed with picture + * Adds a listener used to monitor visible content that is being processed with picture + * profiles. + */ + oneway void addActivePictureListener(IActivePictureListener listener); + + /** + * Removes a listener used to monitor visible content that is being processed with picture * profiles. */ - oneway void setActivePictureListener(IActivePictureListener listener); + oneway void removeActivePictureListener(IActivePictureListener listener); } 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 07558aa49d..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; } @@ -144,13 +147,24 @@ public: */ void setTransactionHangCallback(std::function<void(const std::string&)> callback); void setApplyToken(sp<IBinder>); + + void setWaitForBufferReleaseCallback(std::function<void(const nsecs_t)> callback) + EXCLUDES(mWaitForBufferReleaseMutex); + std::function<void(const nsecs_t)> getWaitForBufferReleaseCallback() const + EXCLUDES(mWaitForBufferReleaseMutex); + virtual ~BLASTBufferQueue(); 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 @@ -186,6 +200,7 @@ private: sp<SurfaceControl> mSurfaceControl GUARDED_BY(mMutex); mutable std::mutex mMutex; + mutable std::mutex mWaitForBufferReleaseMutex; std::condition_variable mCallbackCV; // BufferQueue internally allows 1 more than @@ -201,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 @@ -286,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. @@ -324,6 +339,8 @@ private: std::unordered_set<uint64_t> mSyncedFrameNumbers GUARDED_BY(mMutex); + 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..0bfa7b222e 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 diff --git a/libs/gui/include/gui/BufferQueue.h b/libs/gui/include/gui/BufferQueue.h index 0948c4d076..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; @@ -76,6 +76,9 @@ public: void onSetFrameRate(float frameRate, int8_t compatibility, int8_t changeFrameRateStrategy) override; #endif +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + void onSlotCountChanged(int slotCount) override; +#endif private: // mConsumerListener is a weak reference to the IConsumerListener. This is // the raison d'etre of ProxyConsumerListener. diff --git a/libs/gui/include/gui/BufferQueueConsumer.h b/libs/gui/include/gui/BufferQueueConsumer.h index 6aa801ab86..ab1231ad6b 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 @@ -96,11 +96,26 @@ public: // This should be called from the onBuffersReleased() callback. virtual status_t getReleasedBuffers(uint64_t* outSlotMask); +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + // getReleasedBuffers sets the values pointed to by outSlotMask to the bits + // indicating which buffer slots have been released by the BufferQueue + // but have not yet been released by the consumer. + // + // This should be called from the onBuffersReleased() callback when + // allowUnlimitedSlots has been called. + virtual status_t getReleasedBuffersExtended(std::vector<bool>* outSlotMask) override; +#endif + // setDefaultBufferSize is used to set the size of buffers returned by // dequeueBuffer when a width and height of zero is requested. Default // is 1x1. virtual status_t setDefaultBufferSize(uint32_t width, uint32_t height); +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + // see IGraphicBufferConsumer::allowUnlimitedSlots + virtual status_t allowUnlimitedSlots(bool allowUnlimitedSlots) override; +#endif + // see IGraphicBufferConsumer::setMaxBufferCount virtual status_t setMaxBufferCount(int bufferCount); @@ -152,6 +167,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. @@ -161,6 +177,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/BufferQueueCore.h b/libs/gui/include/gui/BufferQueueCore.h index 77cdf2c9f3..7f92a46053 100644 --- a/libs/gui/include/gui/BufferQueueCore.h +++ b/libs/gui/include/gui/BufferQueueCore.h @@ -32,10 +32,11 @@ #include <utils/Trace.h> #include <utils/Vector.h> +#include <condition_variable> #include <list> -#include <set> #include <mutex> -#include <condition_variable> +#include <set> +#include <vector> #define ATRACE_BUFFER_INDEX(index) \ do { \ @@ -91,6 +92,10 @@ private: // Dump our state in a string void dumpState(const String8& prefix, String8* outResult) const; + // getTotalSlotCountLocked returns the total number of slots in use by the + // buffer queue at this time. + int getTotalSlotCountLocked() const; + // getMinUndequeuedBufferCountLocked returns the minimum number of buffers // that must remain in a state other than DEQUEUED. The async parameter // tells whether we're in asynchronous mode. @@ -120,6 +125,10 @@ private: int getMaxBufferCountLocked(bool asyncMode, bool dequeueBufferCannotBlock, int maxBufferCount) const; +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + // This resizes mSlots to the given size, but only if it's increasing. + status_t extendSlotCountLocked(int size); +#endif // clearBufferSlotLocked frees the GraphicBuffer and sync resources for the // given slot. void clearBufferSlotLocked(int slot); @@ -204,7 +213,7 @@ private: // mConnectedProducerListener will not trigger onBufferAttached() callback. bool mBufferAttachedCbEnabled; - // mSlots is an array of buffer slots that must be mirrored on the producer + // mSlots is a collection of buffer slots that must be mirrored on the producer // side. This allows buffer ownership to be transferred between the producer // and consumer without sending a GraphicBuffer over Binder. The entire // array is initialized to NULL at construction time, and buffers are @@ -266,8 +275,14 @@ private: // is specified. android_dataspace mDefaultBufferDataSpace; +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + // mAllowExtendedSlotCount is set by the consumer to permit the producer to + // request an unlimited number of slots. + bool mAllowExtendedSlotCount; +#endif + // mMaxBufferCount is the limit on the number of buffers that will be - // allocated at one time. This limit can be set by the consumer. + // allocated at one time. int mMaxBufferCount; // mMaxAcquiredBufferCount is the number of buffers that the consumer may diff --git a/libs/gui/include/gui/BufferQueueDefs.h b/libs/gui/include/gui/BufferQueueDefs.h index ffafb49615..42cf439450 100644 --- a/libs/gui/include/gui/BufferQueueDefs.h +++ b/libs/gui/include/gui/BufferQueueDefs.h @@ -17,6 +17,7 @@ #ifndef ANDROID_GUI_BUFFERQUEUECOREDEFS_H #define ANDROID_GUI_BUFFERQUEUECOREDEFS_H +#include <com_android_graphics_libgui_flags.h> #include <gui/BufferSlot.h> #include <ui/BufferQueueDefs.h> @@ -24,7 +25,11 @@ namespace android { class BufferQueueCore; namespace BufferQueueDefs { - typedef BufferSlot SlotsType[NUM_BUFFER_SLOTS]; +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + typedef std::vector<BufferSlot> SlotsType; +#else + typedef BufferSlot SlotsType[NUM_BUFFER_SLOTS]; +#endif } // namespace BufferQueueDefs } // namespace android diff --git a/libs/gui/include/gui/BufferQueueProducer.h b/libs/gui/include/gui/BufferQueueProducer.h index 086ce7ce56..50abadb922 100644 --- a/libs/gui/include/gui/BufferQueueProducer.h +++ b/libs/gui/include/gui/BufferQueueProducer.h @@ -47,6 +47,11 @@ public: // flags indicating that previously-returned buffers are no longer valid. virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf); +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + // see IGraphicsBufferProducer::extendSlotCount + virtual status_t extendSlotCount(int size) override; +#endif + // see IGraphicsBufferProducer::setMaxDequeuedBufferCount virtual status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers); 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 e976aa48be..fd67f09a4a 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 @@ -185,7 +186,9 @@ protected: virtual void onFrameDetached(const uint64_t bufferId) override; virtual void onBuffersReleased() override; virtual void onSidebandStreamChanged() override; - +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + virtual void onSlotCountChanged(int slotCount) override; +#endif virtual int getSlotForBufferLocked(const sp<GraphicBuffer>& buffer); virtual status_t detachBufferLocked(int slotIndex); @@ -243,9 +246,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); @@ -284,7 +294,11 @@ protected: // slot that has not yet been used. The buffer allocated to a slot will also // be replaced if the requested buffer usage or geometry differs from that // of the buffer allocated to a slot. +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + std::vector<Slot> mSlots; +#else Slot mSlots[BufferQueueDefs::NUM_BUFFER_SLOTS]; +#endif // mAbandoned indicates that the BufferQueue will no longer be used to // consume images buffers pushed to it using the IGraphicBufferProducer @@ -318,6 +332,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/Flags.h b/libs/gui/include/gui/Flags.h index 845bc54c71..446841bcd2 100644 --- a/libs/gui/include/gui/Flags.h +++ b/libs/gui/include/gui/Flags.h @@ -46,6 +46,7 @@ typedef android::sp<android::IGraphicBufferProducer> ParcelableSurfaceType; namespace flagtools { sp<SurfaceType> surfaceToSurfaceType(const sp<Surface>& surface); +ParcelableSurfaceType surfaceToParcelableSurfaceType(const sp<Surface>& surface); ParcelableSurfaceType toParcelableSurfaceType(const view::Surface& surface); sp<IGraphicBufferProducer> surfaceTypeToIGBP(const sp<SurfaceType>& surface); bool isSurfaceTypeValid(const sp<SurfaceType>& surface); diff --git a/libs/gui/include/gui/GLConsumer.h b/libs/gui/include/gui/GLConsumer.h index 8a66dc0cf1..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); @@ -266,8 +280,12 @@ protected: virtual status_t acquireBufferLocked(BufferItem *item, nsecs_t presentWhen, uint64_t maxFrameNumber = 0) override; +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + virtual void onSlotCountChanged(int slotCount) override; +#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; @@ -276,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, @@ -465,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 @@ -496,8 +514,11 @@ private: // slot that has not yet been used. The buffer allocated to a slot will also // be replaced if the requested buffer usage or geometry differs from that // of the buffer allocated to a slot. +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + std::vector<EglSlot> mEglSlots; +#else EglSlot mEglSlots[BufferQueueDefs::NUM_BUFFER_SLOTS]; - +#endif // mCurrentTexture is the buffer slot index of the buffer that is currently // bound to the OpenGL texture. It is initialized to INVALID_BUFFER_SLOT, // indicating that no buffer slot is currently bound to the texture. Note, diff --git a/libs/gui/include/gui/IConsumerListener.h b/libs/gui/include/gui/IConsumerListener.h index 51d3959de7..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> @@ -98,27 +95,18 @@ public: virtual void onSetFrameRate(float /*frameRate*/, int8_t /*compatibility*/, int8_t /*changeFrameRateStrategy*/) {} #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; +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + // Notifies the consumer that IGraphicBufferProducer::extendSlotCount has + // been called and the total slot count has increased. + // + // This will only ever be called if + // IGraphicBufferConsumer::allowUnlimitedSlots has been called on the + // consumer. + virtual void onSlotCountChanged(int /* slotCount */) {} +#endif }; -#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 18f5488173..8272a591da 100644 --- a/libs/gui/include/gui/IGraphicBufferConsumer.h +++ b/libs/gui/include/gui/IGraphicBufferConsumer.h @@ -16,6 +16,7 @@ #pragma once +#include <com_android_graphics_libgui_flags.h> #include <gui/OccupancyTracker.h> #include <binder/IInterface.h> @@ -35,15 +36,12 @@ class Fence; class GraphicBuffer; class IConsumerListener; class NativeHandle; -#ifndef NO_BINDER -class IGraphicBufferConsumer : public IInterface { -public: - DECLARE_META_INTERFACE(GraphicBufferConsumer) -#else + +/* + * See IGraphicBufferProducer for details on SLOT_COUNT. + */ 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. @@ -92,7 +90,7 @@ public: // // Return of a value other than NO_ERROR means an error has occurred: // * BAD_VALUE - the given slot number is invalid, either because it is out of the range - // [0, NUM_BUFFER_SLOTS) or because the slot it refers to is not + // [0, SLOT_COUNT) or because the slot it refers to is not // currently acquired. virtual status_t detachBuffer(int slot) = 0; @@ -134,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, @@ -173,6 +176,19 @@ public: // * NO_INIT - the BufferQueue has been abandoned. virtual status_t getReleasedBuffers(uint64_t* slotMask) = 0; +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + // getReleasedBuffersExtended for each slot, sets slotMask[slot] to 1 if it + // corresponds to a released buffer slot. In particular, a released buffer + // is one that has been released by the BufferQueue but has not yet been + // released by the consumer. + // + // This should be called from the onBuffersReleased() callback. + // + // Return of a value other than NO_ERROR means an error has occurred: + // * NO_INIT - the BufferQueue has been abandoned. + virtual status_t getReleasedBuffersExtended(std::vector<bool>* slotMask) = 0; +#endif + // setDefaultBufferSize is used to set the size of buffers returned by dequeueBuffer when a // width and height of zero is requested. Default is 1x1. // @@ -180,6 +196,26 @@ public: // * BAD_VALUE - either w or h was zero virtual status_t setDefaultBufferSize(uint32_t w, uint32_t h) = 0; +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + // allowUnlimitedSlots allows the producer to set the upper bound on slots. + // + // Must be called before the producer is connected. If the producer + // increases the slot count, an IConsumerListener::onSlotCountChanged + // update is sent. + // + // This can not be used with setMaxBufferCount. Calls after + // setMaxBufferCount will fail and calls to setMaxBufferCount after setting + // this to true will fail. + // + // Return of a value other than NO_ERROR means an error has occurred: + // * NO_INIT - the BufferQueue has been abandoned + // * INVALID_OPERATION - one of the following errors has occurred: + // * Producer has been connected + // * setMaxBufferCount has been called and shrunk the + // BufferQueue. + virtual status_t allowUnlimitedSlots(bool allowUnlimitedSlots) = 0; +#endif + // setMaxBufferCount sets the maximum value for the number of buffers used in the BufferQueue // (the initial default is NUM_BUFFER_SLOTS). If a call to setMaxAcquiredBufferCount (by the // consumer), or a call to setAsyncMode or setMaxDequeuedBufferCount (by the producer), would @@ -279,18 +315,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/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h index a42ddc466c..7accca6298 100644 --- a/libs/gui/include/gui/IGraphicBufferProducer.h +++ b/libs/gui/include/gui/IGraphicBufferProducer.h @@ -72,6 +72,14 @@ using HGraphicBufferProducerV2_0 = * dequeueBuffer() to get an empty buffer, fills it with data, then * calls queueBuffer() to make it available to the consumer. * + * BufferQueues have a size, which we'll refer to in other comments as + * SLOT_COUNT. Its default is 64 (NUM_BUFFER_SLOTS). It can be adjusted by + * the IGraphicBufferConsumer::setMaxBufferCount, or when + * IGraphicBufferConsumer::allowUnlimitedSlots is set to true, by + * IGraphicBufferProducer::extendSlotCount. The actual number of buffers in use + * is a function of various configurations, including whether we're in single + * buffer mode, the maximum dequeuable/aquirable buffers, and SLOT_COUNT. + * * This class was previously called ISurfaceTexture. */ #ifndef NO_BINDER @@ -106,7 +114,7 @@ public: // slot->buffer mapping so that it's not necessary to transfer a // GraphicBuffer for every dequeue operation. // - // The slot must be in the range of [0, NUM_BUFFER_SLOTS). + // The slot must be in the range of [0, SLOT_COUNT). // // Return of a value other than NO_ERROR means an error has occurred: // * NO_INIT - the buffer queue has been abandoned or the producer is not @@ -116,6 +124,30 @@ public: // * buffer specified by the slot is not dequeued virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf) = 0; +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + // extendSlotCount sets the maximum slot count (SLOT_COUNT) to the given + // size. This feature must be enabled by the consumer to function via + // IGraphicBufferConsumer::allowUnlimitedSlots. This must be called before + // the producer connects. + // + // After calling this, any slot can be returned in the [0, size) range. + // Callers are responsible for the allocation of the appropriate slots + // array for their own buffer cache. + // + // On success, the consumer is notified (so that it can increase its own + // slot cache). + // + // Return of a value other than NO_ERROR means that an error has occurred: + // * NO_INIT - the buffer queue has been abandoned + // * INVALID_OPERATION - one of the following conditions has occurred: + // * The producer is connected already + // * The consumer didn't call allowUnlimitedSlots + // * BAD_VALUE - The value is smaller than the previous max size + // (initialized to 64, then whatever the last call to this + // was) + virtual status_t extendSlotCount(int size); +#endif + // setMaxDequeuedBufferCount sets the maximum number of buffers that can be // dequeued by the producer at one time. If this method succeeds, any new // buffer slots will be both unallocated and owned by the BufferQueue object @@ -129,7 +161,7 @@ public: // will result in a BAD_VALUE error. // // The buffer count should be at least 1 (inclusive), but at most - // (NUM_BUFFER_SLOTS - the minimum undequeued buffer count) (exclusive). The + // (SLOT_COUNT - the minimum undequeued buffer count) (exclusive). The // minimum undequeued buffer count can be obtained by calling // query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS). // @@ -239,8 +271,8 @@ public: // * NO_INIT - the buffer queue has been abandoned or the producer is not // connected. // * BAD_VALUE - the given slot number is invalid, either because it is - // out of the range [0, NUM_BUFFER_SLOTS), or because the slot - // it refers to is not currently dequeued and requested. + // out of the range [0, SLOT_COUNT), or because the slot it + // refers to is not currently dequeued and requested. virtual status_t detachBuffer(int slot) = 0; // detachNextBuffer is equivalent to calling dequeueBuffer, requestBuffer, @@ -415,6 +447,7 @@ public: FrameEventHistoryDelta frameTimestamps; bool bufferReplaced{false}; int maxBufferCount{BufferQueueDefs::NUM_BUFFER_SLOTS}; + bool isSlotExpansionAllowed{false}; status_t result{NO_ERROR}; }; @@ -430,7 +463,7 @@ public: // below). Any other properties (zero point, etc) // are client-dependent, and should be documented by the client. // - // The slot must be in the range of [0, NUM_BUFFER_SLOTS). + // The slot must be in the range of [0, SLOT_COUNT). // // Upon success, the output will be filled with meaningful values // (refer to the documentation below). @@ -460,7 +493,7 @@ public: // // The buffer is not queued for use by the consumer. // - // The slot must be in the range of [0, NUM_BUFFER_SLOTS). + // The slot must be in the range of [0, SLOT_COUNT). // // The buffer will not be overwritten until the fence signals. The fence // will usually be the one obtained from dequeueBuffer. 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 1c31e46cb4..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 @@ -425,7 +468,7 @@ struct layer_state_t { PictureProfileHandle pictureProfileHandle{PictureProfileHandle::NONE}; // A value indicating the significance of the layer's content to the app's desired user - // experience. A lower priority will result in more likelihood of getting access to limited + // experience. A higher value will result in more likelihood of getting access to limited // resources, such as picture processing hardware. int32_t appContentPriority = 0; @@ -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/Surface.h b/libs/gui/include/gui/Surface.h index 14a351316d..755674d9e6 100644 --- a/libs/gui/include/gui/Surface.h +++ b/libs/gui/include/gui/Surface.h @@ -558,7 +558,11 @@ protected: // slot that has not yet been used. The buffer allocated to a slot will also // be replaced if the requested buffer usage or geometry differs from that // of the buffer allocated to a slot. +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + std::vector<BufferSlot> mSlots; +#else BufferSlot mSlots[NUM_BUFFER_SLOTS]; +#endif // mReqWidth is the buffer width that will be requested at the next dequeue // operation. It is initialized to 1. @@ -732,6 +736,10 @@ protected: std::vector<sp<GraphicBuffer>> mRemovedBuffers; int mMaxBufferCount; +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + bool mIsSlotExpansionAllowed; +#endif + sp<IProducerListener> mListenerProxy; // Get and flush the buffers of given slots, if the buffer in the slot diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index 0ce0c0a9c3..4fda8deb9c 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -298,7 +298,9 @@ public: static status_t removeHdrLayerInfoListener(const sp<IBinder>& displayToken, const sp<gui::IHdrLayerInfoListener>& listener); - static status_t setActivePictureListener(const sp<gui::IActivePictureListener>& listener); + static status_t addActivePictureListener(const sp<gui::IActivePictureListener>& listener); + + static status_t removeActivePictureListener(const sp<gui::IActivePictureListener>& listener); /* * Sends a power boost to the composer. This function is asynchronous. @@ -343,8 +345,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. * @@ -394,6 +394,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); @@ -453,8 +454,8 @@ public: bool mLogCallPoints = false; protected: - std::unordered_map<sp<IBinder>, ComposerState, IBinderHash> mComposerStates; - SortedVector<DisplayState> mDisplayStates; + Vector<ComposerState> mComposerStates; + Vector<DisplayState> mDisplayStates; std::unordered_map<sp<ITransactionCompletedListener>, CallbackInfo, TCLHash> mListenerCallbacks; std::vector<client_cache_t> mUncacheBuffers; @@ -465,10 +466,7 @@ public: std::vector<uint64_t> mMergedTransactionIds; uint64_t mId; - - bool mAnimation = false; - bool mEarlyWakeupStart = false; - bool mEarlyWakeupEnd = false; + uint32_t mFlags = 0; // 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 @@ -565,6 +563,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, @@ -616,7 +619,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/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..24d26b12a1 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,18 +26,24 @@ 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)); diff --git a/libs/gui/libgui_flags.aconfig b/libs/gui/libgui_flags.aconfig index 6bf38c05f1..534f05e987 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" @@ -138,4 +130,15 @@ flag { description: "Remove BufferQueueProducer::dequeue's wait on this fence (or the fence entirely) to prevent deadlocks" bug: "339705065" is_fixed_read_only: true + metadata { + purpose: PURPOSE_BUGFIX + } } # 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 diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp index f07747f32f..87051a7aac 100644 --- a/libs/gui/tests/Android.bp +++ b/libs/gui/tests/Android.bp @@ -55,6 +55,7 @@ cc_test { "-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_BQ_EXTENDEDALLOCATE=true", "-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_WB_CONSUMER_BASE_OWNS_BQ=true", "-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_WB_PLATFORM_API_IMPROVEMENTS=true", + "-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_WB_UNLIMITED_SLOTS=true", ], srcs: [ 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 3b6a66efe9..b980f882ff 100644 --- a/libs/gui/tests/BufferItemConsumer_test.cpp +++ b/libs/gui/tests/BufferItemConsumer_test.cpp @@ -22,8 +22,11 @@ #include <gui/BufferItemConsumer.h> #include <gui/IProducerListener.h> #include <gui/Surface.h> +#include <ui/BufferQueueDefs.h> #include <ui/GraphicBuffer.h> +#include <unordered_set> + namespace android { static constexpr int kWidth = 100; @@ -57,14 +60,17 @@ class BufferItemConsumerTest : public ::testing::Test { }; void SetUp() override { - mBIC = new BufferItemConsumer(kUsage, kMaxLockedBuffers, true); + mBuffers.resize(BufferQueueDefs::NUM_BUFFER_SLOTS); + + 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, @@ -137,6 +143,11 @@ class BufferItemConsumerTest : public ::testing::Test { ASSERT_EQ(NO_ERROR, ret); } + void DetachBuffer(int slot) { + ALOGD("detachBuffer: slot=%d", slot); + status_t ret = mBIC->detachBuffer(mBuffers[slot]); + ASSERT_EQ(NO_ERROR, ret); + } std::mutex mMutex; int mFreedBufferCount{0}; @@ -146,7 +157,7 @@ class BufferItemConsumerTest : public ::testing::Test { sp<BufferFreedListener> mBFL; sp<IGraphicBufferProducer> mProducer; sp<IGraphicBufferConsumer> mConsumer; - sp<GraphicBuffer> mBuffers[BufferQueueDefs::NUM_BUFFER_SLOTS]; + std::vector<sp<GraphicBuffer>> mBuffers; }; // Test that detaching buffer from consumer side triggers onBufferFreed. @@ -239,4 +250,52 @@ TEST_F(BufferItemConsumerTest, DetachBufferWithBuffer) { } #endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) +TEST_F(BufferItemConsumerTest, UnlimitedSlots_AcquireReleaseAll) { + ASSERT_EQ(OK, mProducer->extendSlotCount(256)); + mBuffers.resize(256); + + ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(100)); + + std::unordered_set<int> slots; + for (int i = 0; i < 100; i++) { + int slot; + DequeueBuffer(&slot); + slots.insert(slot); + } + EXPECT_EQ(100u, slots.size()); + + for (int dequeuedSlot : slots) { + QueueBuffer(dequeuedSlot); + + int slot; + AcquireBuffer(&slot); + ReleaseBuffer(slot); + } +} + +TEST_F(BufferItemConsumerTest, UnlimitedSlots_AcquireDetachAll) { + ASSERT_EQ(OK, mProducer->extendSlotCount(256)); + mBuffers.resize(256); + + ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(100)); + + std::unordered_set<int> slots; + for (int i = 0; i < 100; i++) { + int slot; + DequeueBuffer(&slot); + slots.insert(slot); + } + EXPECT_EQ(100u, slots.size()); + + for (int dequeuedSlot : slots) { + QueueBuffer(dequeuedSlot); + + int slot; + AcquireBuffer(&slot); + DetachBuffer(slot); + } +} +#endif + } // namespace android diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp index 16060990bd..cfbb2e7386 100644 --- a/libs/gui/tests/BufferQueue_test.cpp +++ b/libs/gui/tests/BufferQueue_test.cpp @@ -20,6 +20,8 @@ #include "Constants.h" #include "MockConsumer.h" +#include <EGL/egl.h> + #include <gui/BufferItem.h> #include <gui/BufferItemConsumer.h> #include <gui/BufferQueue.h> @@ -30,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> @@ -43,8 +46,11 @@ #include <gmock/gmock.h> #include <gtest/gtest.h> +#include <csignal> #include <future> +#include <optional> #include <thread> +#include <unordered_map> #include <com_android_graphics_libgui_flags.h> @@ -61,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, @@ -95,6 +110,7 @@ protected: sp<IGraphicBufferProducer> mProducer; sp<IGraphicBufferConsumer> mConsumer; + std::vector<std::function<void()>> mTeardownFns; }; static const uint32_t TEST_DATA = 0x12345678u; @@ -102,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); @@ -119,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)); @@ -159,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) { @@ -1612,4 +1653,221 @@ TEST_F(BufferQueueTest, PassesThroughPictureProfileHandle) { } } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) +struct MockUnlimitedSlotConsumer : public MockConsumer { + virtual void onSlotCountChanged(int size) override { mSize = size; } + + std::optional<int> mSize; +}; + +TEST_F(BufferQueueTest, UnlimitedSlots_FailsWhenNotAllowed) { + createBufferQueue(); + + sp<MockUnlimitedSlotConsumer> mc = sp<MockUnlimitedSlotConsumer>::make(); + EXPECT_EQ(OK, mConsumer->consumerConnect(mc, false)); + + EXPECT_EQ(INVALID_OPERATION, mProducer->extendSlotCount(64)); + EXPECT_EQ(INVALID_OPERATION, mProducer->extendSlotCount(32)); + EXPECT_EQ(INVALID_OPERATION, mProducer->extendSlotCount(128)); + + EXPECT_EQ(std::nullopt, mc->mSize); +} + +TEST_F(BufferQueueTest, UnlimitedSlots_OnlyAllowedForExtensions) { + createBufferQueue(); + + sp<MockUnlimitedSlotConsumer> consumerListener = sp<MockUnlimitedSlotConsumer>::make(); + EXPECT_EQ(OK, mConsumer->consumerConnect(consumerListener, false)); + EXPECT_EQ(OK, mConsumer->allowUnlimitedSlots(true)); + + EXPECT_EQ(BAD_VALUE, mProducer->extendSlotCount(32)); + EXPECT_EQ(OK, mProducer->extendSlotCount(64)); + EXPECT_EQ(OK, mProducer->extendSlotCount(128)); + EXPECT_EQ(128, *consumerListener->mSize); + + EXPECT_EQ(OK, mProducer->extendSlotCount(128)); + EXPECT_EQ(BAD_VALUE, mProducer->extendSlotCount(127)); +} + +class BufferQueueUnlimitedTest : public BufferQueueTest { +protected: + static constexpr auto kMaxBufferCount = 128; + static constexpr auto kAcquirableBufferCount = 2; + static constexpr auto kDequeableBufferCount = kMaxBufferCount - kAcquirableBufferCount; + + virtual void SetUp() override { + BufferQueueTest::SetUp(); + + createBufferQueue(); + setUpConsumer(); + setUpProducer(); + } + + void setUpConsumer() { + EXPECT_EQ(OK, mConsumer->consumerConnect(mConsumerListener, false)); + +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + EXPECT_EQ(OK, mConsumer->allowUnlimitedSlots(true)); +#endif + EXPECT_EQ(OK, mConsumer->setConsumerUsageBits(GraphicBuffer::USAGE_SW_READ_OFTEN)); + EXPECT_EQ(OK, mConsumer->setDefaultBufferSize(10, 10)); + EXPECT_EQ(OK, mConsumer->setDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888)); + EXPECT_EQ(OK, mConsumer->setMaxAcquiredBufferCount(kAcquirableBufferCount)); + } + + void setUpProducer() { + EXPECT_EQ(OK, mProducer->extendSlotCount(kMaxBufferCount)); + + IGraphicBufferProducer::QueueBufferOutput output; + EXPECT_EQ(OK, + mProducer->connect(mProducerListener, NATIVE_WINDOW_API_CPU, + /*producerControlledByApp*/ true, &output)); +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + ASSERT_TRUE(output.isSlotExpansionAllowed); +#endif + ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(kDequeableBufferCount)); + ASSERT_EQ(OK, mProducer->allowAllocation(true)); + } + + std::unordered_map<int, sp<Fence>> dequeueAll() { + std::unordered_map<int, sp<Fence>> slotsToFences; + + for (int i = 0; i < kDequeableBufferCount; ++i) { + int slot; + sp<Fence> fence; + sp<GraphicBuffer> buffer; + + status_t ret = + mProducer->dequeueBuffer(&slot, &fence, /*w*/ 0, /*h*/ 0, /*format*/ 0, + /*uint64_t*/ 0, + /*outBufferAge*/ nullptr, /*outTimestamps*/ nullptr); + if (ret & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) { + EXPECT_EQ(OK, mProducer->requestBuffer(slot, &buffer)) + << "Unable to request buffer for slot " << slot; + } + EXPECT_FALSE(slotsToFences.contains(slot)); + slotsToFences.emplace(slot, fence); + } + EXPECT_EQ(kDequeableBufferCount, (int)slotsToFences.size()); + return slotsToFences; + } + + sp<MockUnlimitedSlotConsumer> mConsumerListener = sp<MockUnlimitedSlotConsumer>::make(); + sp<StubProducerListener> mProducerListener = sp<StubProducerListener>::make(); +}; + +TEST_F(BufferQueueUnlimitedTest, ExpandOverridesConsumerMaxBuffers) { + createBufferQueue(); + setUpConsumer(); + EXPECT_EQ(OK, mConsumer->setMaxBufferCount(10)); + + setUpProducer(); + + EXPECT_EQ(kDequeableBufferCount, (int)dequeueAll().size()); +} + +TEST_F(BufferQueueUnlimitedTest, CanDetachAll) { + auto slotsToFences = dequeueAll(); + for (auto& [slot, fence] : slotsToFences) { + EXPECT_EQ(OK, mProducer->detachBuffer(slot)); + } +} + +TEST_F(BufferQueueUnlimitedTest, CanCancelAll) { + auto slotsToFences = dequeueAll(); + for (auto& [slot, fence] : slotsToFences) { + EXPECT_EQ(OK, mProducer->cancelBuffer(slot, fence)); + } +} + +TEST_F(BufferQueueUnlimitedTest, CanAcquireAndReleaseAll) { + auto slotsToFences = dequeueAll(); + for (auto& [slot, fence] : slotsToFences) { + IGraphicBufferProducer::QueueBufferInput input; + input.fence = fence; + + IGraphicBufferProducer::QueueBufferOutput output; + EXPECT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); + + BufferItem buffer; + EXPECT_EQ(OK, mConsumer->acquireBuffer(&buffer, 0)); + EXPECT_EQ(OK, + mConsumer->releaseBuffer(buffer.mSlot, buffer.mFrameNumber, EGL_NO_DISPLAY, + EGL_NO_SYNC, buffer.mFence)); + } +} + +TEST_F(BufferQueueUnlimitedTest, CanAcquireAndDetachAll) { + auto slotsToFences = dequeueAll(); + for (auto& [slot, fence] : slotsToFences) { + IGraphicBufferProducer::QueueBufferInput input; + input.fence = fence; + + IGraphicBufferProducer::QueueBufferOutput output; + EXPECT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); + + BufferItem buffer; + EXPECT_EQ(OK, mConsumer->acquireBuffer(&buffer, 0)); + EXPECT_EQ(OK, mConsumer->detachBuffer(buffer.mSlot)); + } +} + +TEST_F(BufferQueueUnlimitedTest, GetReleasedBuffersExtended) { + // First, acquire and release all the buffers so the consumer "knows" about + // them + auto slotsToFences = dequeueAll(); + + std::vector<bool> releasedSlots; + EXPECT_EQ(OK, mConsumer->getReleasedBuffersExtended(&releasedSlots)); + for (auto& [slot, _] : slotsToFences) { + EXPECT_TRUE(releasedSlots[slot]) + << "Slots that haven't been acquired will show up as released."; + } + for (auto& [slot, fence] : slotsToFences) { + IGraphicBufferProducer::QueueBufferInput input; + input.fence = fence; + + IGraphicBufferProducer::QueueBufferOutput output; + EXPECT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); + + BufferItem buffer; + EXPECT_EQ(OK, mConsumer->acquireBuffer(&buffer, 0)); + EXPECT_EQ(OK, + mConsumer->releaseBuffer(buffer.mSlot, buffer.mFrameNumber, EGL_NO_DISPLAY, + EGL_NO_SYNC_KHR, buffer.mFence)); + } + + EXPECT_EQ(OK, mConsumer->getReleasedBuffersExtended(&releasedSlots)); + for (auto& [slot, _] : slotsToFences) { + EXPECT_FALSE(releasedSlots[slot]) + << "Slots that have been acquired will show up as not released."; + } + + // Then, alternatively cancel and detach (release) buffers. Only detached + // buffers should be returned by getReleasedBuffersExtended + slotsToFences = dequeueAll(); + std::set<int> cancelledSlots; + std::set<int> detachedSlots; + bool cancel; + for (auto& [slot, fence] : slotsToFences) { + if (cancel) { + EXPECT_EQ(OK, mProducer->cancelBuffer(slot, fence)); + cancelledSlots.insert(slot); + } else { + EXPECT_EQ(OK, mProducer->detachBuffer(slot)); + detachedSlots.insert(slot); + } + cancel = !cancel; + } + + EXPECT_EQ(OK, mConsumer->getReleasedBuffersExtended(&releasedSlots)); + for (int slot : detachedSlots) { + EXPECT_TRUE(releasedSlots[slot]) << "Slots that are detached are released."; + } + for (int slot : cancelledSlots) { + EXPECT_FALSE(releasedSlots[slot]) + << "Slots that are still held in the queue are not released."; + } +} +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) } // namespace android 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 f4239cb69e..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; } @@ -803,6 +802,27 @@ INSTANTIATE_TEST_CASE_P(Rgba8888Tests, ::testing::ValuesIn(rgba8888TestSets)); #endif +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) +TEST(CpuConsumerSlotTest, UnlimitedSlots_AcquireReleaseAll) { + sp<CpuConsumer> cpuConsumer = sp<CpuConsumer>::make(3); + sp<Surface> surface = cpuConsumer->getSurface(); + sp<SurfaceListener> listener = sp<StubSurfaceListener>::make(); + ASSERT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, listener)); + ASSERT_EQ(OK, surface->setMaxDequeuedBufferCount(256)); + std::vector<Surface::BatchBuffer> buffers(256); + EXPECT_EQ(OK, surface->dequeueBuffers(&buffers)); + + for (auto& buffer : buffers) { + sp<GraphicBuffer> graphicBuffer = GraphicBuffer::from(buffer.buffer); + sp<Fence> fence = sp<Fence>::make(buffer.fenceFd); + EXPECT_EQ(OK, surface->queueBuffer(graphicBuffer, fence)); + + CpuConsumer::LockedBuffer nativeBuffer; + EXPECT_EQ(OK, cpuConsumer->lockNextBuffer(&nativeBuffer)); + EXPECT_EQ(OK, cpuConsumer->unlockBuffer(nativeBuffer)); + } +} +#endif } // namespace android 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/FillBuffer.cpp b/libs/gui/tests/FillBuffer.cpp index b60995a624..11383d906b 100644 --- a/libs/gui/tests/FillBuffer.cpp +++ b/libs/gui/tests/FillBuffer.cpp @@ -76,7 +76,7 @@ void fillYV12BufferRect(uint8_t* buf, int w, int h, int stride, } void fillRGBA8Buffer(uint8_t* buf, int w, int h, int stride) { - const size_t PIXEL_SIZE = 4; + constexpr size_t PIXEL_SIZE = 4; for (int x = 0; x < w; x++) { for (int y = 0; y < h; y++) { off_t offset = (y * stride + x) * PIXEL_SIZE; @@ -89,6 +89,21 @@ void fillRGBA8Buffer(uint8_t* buf, int w, int h, int stride) { } } +void fillRGBA8Buffer(uint8_t* buf, int w, int h, int stride, uint8_t r, uint8_t g, uint8_t b, + uint8_t a) { + constexpr size_t PIXEL_SIZE = 4; + + for (int x = 0; x < w; x++) { + for (int y = 0; y < h; y++) { + off_t offset = (y * stride + x) * PIXEL_SIZE; + buf[offset] = r; + buf[offset + 1] = g; + buf[offset + 2] = b; + buf[offset + 3] = a; + } + } +} + void produceOneRGBA8Frame(const sp<ANativeWindow>& anw) { android_native_buffer_t* anb; ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(anw.get(), diff --git a/libs/gui/tests/FillBuffer.h b/libs/gui/tests/FillBuffer.h index b584179318..f5d6b8bbda 100644 --- a/libs/gui/tests/FillBuffer.h +++ b/libs/gui/tests/FillBuffer.h @@ -30,6 +30,8 @@ void fillYV12BufferRect(uint8_t* buf, int w, int h, int stride, const android_native_rect_t& rect); void fillRGBA8Buffer(uint8_t* buf, int w, int h, int stride); +void fillRGBA8Buffer(uint8_t* buf, int w, int h, int stride, uint8_t r, uint8_t g, uint8_t b, + uint8_t a); // Produce a single RGBA8 frame by filling a buffer with a checkerboard pattern // using the CPU. This assumes that the ANativeWindow is already configured to diff --git a/libs/gui/tests/FrameRateUtilsTest.cpp b/libs/gui/tests/FrameRateUtilsTest.cpp index 9ffe91f460..c5025331e1 100644 --- a/libs/gui/tests/FrameRateUtilsTest.cpp +++ b/libs/gui/tests/FrameRateUtilsTest.cpp @@ -34,7 +34,7 @@ TEST(FrameRateUtilsTest, ValidateFrameRate) { ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS, "")); EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, "")); - EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE, + EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_AT_LEAST, ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, "")); // Privileged APIs. 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/SurfaceTextureGL_test.cpp b/libs/gui/tests/SurfaceTextureGL_test.cpp index 449533aa57..b22b85332c 100644 --- a/libs/gui/tests/SurfaceTextureGL_test.cpp +++ b/libs/gui/tests/SurfaceTextureGL_test.cpp @@ -17,6 +17,8 @@ #define LOG_TAG "SurfaceTextureGL_test" //#define LOG_NDEBUG 0 +#include <gmock/gmock.h> + #include "SurfaceTextureGL.h" #include "DisconnectWaiter.h" @@ -735,4 +737,30 @@ TEST_F(SurfaceTextureGLTest, InvalidWidthOrHeightFails) { ASSERT_NE(NO_ERROR, mST->updateTexImage()); } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) +TEST_F(SurfaceTextureGLTest, TestUnlimitedSlots) { + ASSERT_EQ(OK, mSTC->connect(NATIVE_WINDOW_API_CPU, sp<StubSurfaceListener>::make())); + ASSERT_EQ(OK, mSTC->setMaxDequeuedBufferCount(256)); + + std::vector<Surface::BatchBuffer> buffers(256); + ASSERT_EQ(OK, mSTC->dequeueBuffers(&buffers)); + ASSERT_EQ(256u, buffers.size()); + ASSERT_THAT(buffers, Each(Field(&Surface::BatchBuffer::buffer, ::testing::NotNull()))); + + for (size_t i = 0; i < buffers.size(); ++i) { + sp<GraphicBuffer> graphicBuffer = GraphicBuffer::from(buffers[i].buffer); + sp<Fence> fence = sp<Fence>::make(buffers[i].fenceFd); + + void* buf; + ASSERT_EQ(OK, graphicBuffer->lock(AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN, &buf)); + fillRGBA8Buffer((uint8_t*)buf, graphicBuffer->getWidth(), graphicBuffer->getHeight(), + graphicBuffer->getStride(), i, i, i, i); + graphicBuffer->unlock(); + + ASSERT_EQ(OK, mSTC->queueBuffer(graphicBuffer, fence)); + ASSERT_EQ(OK, mST->updateTexImage()); + checkPixel(0, 0, i, i, i, i); + } +} +#endif } // namespace android diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index 76362ff272..e7690e2530 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -14,10 +14,6 @@ * limitations under the License. */ -#include "gui/view/Surface.h" -#include "Constants.h" -#include "MockConsumer.h" - #include <gtest/gtest.h> #include <SurfaceFlingerProperties.h> @@ -36,10 +32,13 @@ #include <gui/IConsumerListener.h> #include <gui/IGraphicBufferConsumer.h> #include <gui/IGraphicBufferProducer.h> +#include <gui/IProducerListener.h> #include <gui/ISurfaceComposer.h> #include <gui/Surface.h> #include <gui/SurfaceComposerClient.h> #include <gui/SyncScreenCaptureListener.h> +#include <gui/view/Surface.h> +#include <nativebase/nativebase.h> #include <private/gui/ComposerService.h> #include <private/gui/ComposerServiceAIDL.h> #include <sys/types.h> @@ -47,6 +46,7 @@ #include <ui/BufferQueueDefs.h> #include <ui/DisplayMode.h> #include <ui/GraphicBuffer.h> +#include <ui/PixelFormat.h> #include <ui/Rect.h> #include <utils/Errors.h> #include <utils/String8.h> @@ -55,9 +55,12 @@ #include <cstddef> #include <cstdint> #include <future> +#include <iterator> #include <limits> #include <thread> +#include "Constants.h" +#include "MockConsumer.h" #include "testserver/TestServerClient.h" namespace android { @@ -291,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; @@ -306,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; @@ -322,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 @@ -600,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 {} @@ -689,10 +687,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(); } @@ -1016,7 +1015,11 @@ public: return binder::Status::ok(); } - binder::Status setActivePictureListener(const sp<gui::IActivePictureListener>&) { + binder::Status addActivePictureListener(const sp<gui::IActivePictureListener>&) { + return binder::Status::ok(); + } + + binder::Status removeActivePictureListener(const sp<gui::IActivePictureListener>&) { return binder::Status::ok(); } @@ -2173,8 +2176,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(); @@ -2222,8 +2224,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(); @@ -2370,8 +2371,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); @@ -2529,4 +2529,128 @@ TEST_F(SurfaceTest, QueueBufferOutput_TracksReplacements_Plural) { } #endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) +TEST_F(SurfaceTest, UnlimitedSlots_FailsOnIncompatibleConsumer) { + sp<IGraphicBufferProducer> producer; + sp<IGraphicBufferConsumer> consumer; + BufferQueue::createBufferQueue(&producer, &consumer); + + sp<IConsumerListener> consumerListener = sp<FakeConsumer>::make(); + + EXPECT_EQ(OK, consumer->allowUnlimitedSlots(false)); + EXPECT_EQ(OK, consumer->consumerConnect(consumerListener, /* consumerListener */ true)); + + sp<Surface> surface = sp<Surface>::make(producer); + sp<SurfaceListener> surfaceListener = sp<StubSurfaceListener>::make(); + EXPECT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, surfaceListener)); + + EXPECT_NE(OK, surface->setMaxDequeuedBufferCount(128)) + << "We shouldn't be able to set high max buffer counts if the consumer doesn't allow " + "it"; +} + +TEST_F(SurfaceTest, UnlimitedSlots_CanDequeueAndQueueMoreThanOldMaximum) { + sp<IGraphicBufferProducer> producer; + sp<IGraphicBufferConsumer> consumer; + BufferQueue::createBufferQueue(&producer, &consumer); + + sp<IConsumerListener> consumerListener = sp<FakeConsumer>::make(); + + EXPECT_EQ(OK, consumer->allowUnlimitedSlots(true)); + EXPECT_EQ(OK, consumer->consumerConnect(consumerListener, /* consumerListener */ true)); + EXPECT_EQ(OK, consumer->setDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888)); + EXPECT_EQ(OK, consumer->setConsumerUsageBits(AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN)); + + sp<Surface> surface = sp<Surface>::make(producer); + sp<SurfaceListener> surfaceListener = sp<StubSurfaceListener>::make(); + EXPECT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, surfaceListener)); + + EXPECT_EQ(OK, surface->setMaxDequeuedBufferCount(128)) + << "If unlimited slots are allowed, we should be able increase the max dequeued buffer " + "count arbitrarily"; + + std::vector<std::tuple<sp<GraphicBuffer>, sp<Fence>, int>> buffers; + for (int i = 0; i < 128; i++) { + sp<GraphicBuffer> buffer; + sp<Fence> fence; + ASSERT_EQ(OK, surface->dequeueBuffer(&buffer, &fence)) << "Unable to dequeue buffer #" << i; + buffers.push_back({buffer, fence, i}); + } + + for (auto& [buffer, fence, idx] : buffers) { + ASSERT_EQ(OK, surface->queueBuffer(buffer, fence)) << "Unable to queue buffer #" << idx; + } +} + +TEST_F(SurfaceTest, UnlimitedSlots_CanDequeueAndDetachMoreThanOldMaximum) { + sp<IGraphicBufferProducer> producer; + sp<IGraphicBufferConsumer> consumer; + BufferQueue::createBufferQueue(&producer, &consumer); + + sp<IConsumerListener> consumerListener = sp<FakeConsumer>::make(); + + EXPECT_EQ(OK, consumer->allowUnlimitedSlots(true)); + EXPECT_EQ(OK, consumer->consumerConnect(consumerListener, /* consumerListener */ true)); + EXPECT_EQ(OK, consumer->setDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888)); + EXPECT_EQ(OK, consumer->setConsumerUsageBits(AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN)); + + sp<Surface> surface = sp<Surface>::make(producer); + sp<SurfaceListener> surfaceListener = sp<StubSurfaceListener>::make(); + EXPECT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, surfaceListener)); + + EXPECT_EQ(OK, surface->setMaxDequeuedBufferCount(128)) + << "If unlimited slots are allowed, we should be able increase the max dequeued buffer " + "count arbitrarily"; + + std::vector<std::tuple<sp<GraphicBuffer>, sp<Fence>, int>> buffers; + for (int i = 0; i < 128; i++) { + sp<GraphicBuffer> buffer; + sp<Fence> fence; + ASSERT_EQ(OK, surface->dequeueBuffer(&buffer, &fence)) << "Unable to dequeue buffer #" << i; + buffers.push_back({buffer, fence, i}); + } + + for (auto& [buffer, _, idx] : buffers) { + ASSERT_EQ(OK, surface->detachBuffer(buffer)) << "Unable to detach buffer #" << idx; + } +} + +TEST_F(SurfaceTest, UnlimitedSlots_BatchOperations) { + sp<IGraphicBufferProducer> producer; + sp<IGraphicBufferConsumer> consumer; + BufferQueue::createBufferQueue(&producer, &consumer); + + sp<IConsumerListener> consumerListener = sp<FakeConsumer>::make(); + + EXPECT_EQ(OK, consumer->allowUnlimitedSlots(true)); + EXPECT_EQ(OK, consumer->consumerConnect(consumerListener, /* consumerListener */ true)); + EXPECT_EQ(OK, consumer->setDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888)); + EXPECT_EQ(OK, consumer->setConsumerUsageBits(AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN)); + + sp<Surface> surface = sp<Surface>::make(producer); + sp<SurfaceListener> surfaceListener = sp<StubSurfaceListener>::make(); + EXPECT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, surfaceListener)); + + EXPECT_EQ(OK, surface->setMaxDequeuedBufferCount(128)) + << "If unlimited slots are allowed, we should be able increase the max dequeued buffer " + "count arbitrarily"; + + std::vector<Surface::BatchBuffer> buffers(128); + EXPECT_EQ(OK, surface->dequeueBuffers(&buffers)); + EXPECT_EQ(128u, buffers.size()); + + std::vector<Surface::BatchQueuedBuffer> queuedBuffers; + std::transform(buffers.begin(), buffers.end(), std::back_inserter(queuedBuffers), + [](Surface::BatchBuffer& buffer) { + Surface::BatchQueuedBuffer out; + out.buffer = buffer.buffer; + out.fenceFd = buffer.fenceFd; + return out; + }); + + std::vector<SurfaceQueueBufferOutput> outputs; + EXPECT_EQ(OK, surface->queueBuffers(queuedBuffers, &outputs)); + EXPECT_EQ(128u, outputs.size()); +} +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) } // 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/benchmarks/Android.bp b/libs/gui/tests/benchmarks/Android.bp new file mode 100644 index 0000000000..a728bef977 --- /dev/null +++ b/libs/gui/tests/benchmarks/Android.bp @@ -0,0 +1,27 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + +cc_benchmark { + name: "libgui_benchmarks", + srcs: [ + "*.cpp", + ], + defaults: ["libgui-defaults"], + static_libs: [ + "libgmock", + "libgtest", + ], + shared_libs: [ + "libgui", + ], + header_libs: [ + "libsurfaceflinger_mocks_headers", + "surfaceflinger_tests_common_headers", + ], +} diff --git a/libs/gui/tests/benchmarks/Transaction_benchmarks.cpp b/libs/gui/tests/benchmarks/Transaction_benchmarks.cpp new file mode 100644 index 0000000000..0a5189538b --- /dev/null +++ b/libs/gui/tests/benchmarks/Transaction_benchmarks.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <benchmark/benchmark.h> +#include <cstddef> +#include <optional> +#include <vector> +#include "binder/Parcel.h" +#include "gui/SurfaceComposerClient.h" +#include "gui/SurfaceControl.h" +#include "log/log_main.h" + +namespace android { +namespace { +using android::hardware::graphics::common::V1_1::BufferUsage; + +std::vector<sp<SurfaceControl>> createSurfaceControl(const char* name, size_t num) { + sp<SurfaceComposerClient> client = sp<SurfaceComposerClient>::make(); + LOG_FATAL_IF(client->initCheck() != OK, "Could not init SurfaceComposerClient"); + std::vector<sp<SurfaceControl>> surfaceControls; + for (size_t i = 0; i < num; i++) { + surfaceControls.push_back( + client->createSurface(String8(name), 0, 0, PIXEL_FORMAT_RGBA_8888, + ISurfaceComposerClient::eFXSurfaceBufferState)); + } + return surfaceControls; +} + +void applyTransaction(benchmark::State& state) { + std::vector<sp<SurfaceControl>> surfaceControls = createSurfaceControl(__func__, 5 /* num */); + for (auto _ : state) { + SurfaceComposerClient::Transaction t; + for (auto& sc : surfaceControls) { + t.setCrop(sc, FloatRect{1, 2, 3, 4}); + t.setAutoRefresh(sc, true); + t.hide(sc); + t.setAlpha(sc, 0.5); + t.setCornerRadius(sc, 0.8); + } + Parcel p; + t.writeToParcel(&p); + t.clear(); + benchmark::DoNotOptimize(t); + } +} +BENCHMARK(applyTransaction); + +// Mimic a buffer transaction with callbacks +void applyBufferTransaction(benchmark::State& state) { + std::vector<sp<SurfaceControl>> surfaceControls = createSurfaceControl(__func__, 5 /* num */); + std::vector<sp<GraphicBuffer>> buffers; + for (size_t i = 0; i < surfaceControls.size(); i++) { + int64_t usageFlags = BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | + BufferUsage::COMPOSER_OVERLAY | BufferUsage::GPU_TEXTURE; + buffers.emplace_back( + sp<GraphicBuffer>::make(5, 5, PIXEL_FORMAT_RGBA_8888, 1, usageFlags, "test")); + } + + for (auto _ : state) { + SurfaceComposerClient::Transaction t; + int i = 0; + for (auto& sc : surfaceControls) { + std::function<void(const ReleaseCallbackId&, const sp<Fence>& /*releaseFence*/, + std::optional<uint32_t> currentMaxAcquiredBufferCount)> + releaseBufferCallback; + t.setBuffer(sc, buffers[i], std::nullopt, std::nullopt, 5, releaseBufferCallback); + } + Parcel p; + // proxy for applying the transaction + t.writeToParcel(&p); + t.clear(); + benchmark::DoNotOptimize(t); + } +} +BENCHMARK(applyBufferTransaction); + +void mergeTransaction(benchmark::State& state) { + std::vector<sp<SurfaceControl>> surfaceControls = createSurfaceControl(__func__, 5 /* num */); + for (auto _ : state) { + SurfaceComposerClient::Transaction t1; + for (auto& sc : surfaceControls) { + t1.setCrop(sc, FloatRect{1, 2, 3, 4}); + t1.setAutoRefresh(sc, true); + t1.hide(sc); + t1.setAlpha(sc, 0.5); + t1.setCornerRadius(sc, 0.8); + } + + SurfaceComposerClient::Transaction t2; + for (auto& sc : surfaceControls) { + t2.hide(sc); + t2.setAlpha(sc, 0.5); + t2.setCornerRadius(sc, 0.8); + t2.setBackgroundBlurRadius(sc, 5); + } + t1.merge(std::move(t2)); + benchmark::DoNotOptimize(t1); + } +} +BENCHMARK(mergeTransaction); + +void readTransactionFromParcel(benchmark::State& state) { + std::vector<sp<SurfaceControl>> surfaceControls = createSurfaceControl(__func__, 5 /* num */); + SurfaceComposerClient::Transaction t; + for (auto& sc : surfaceControls) { + t.setCrop(sc, FloatRect{1, 2, 3, 4}); + t.setAutoRefresh(sc, true); + t.hide(sc); + t.setAlpha(sc, 0.5); + t.setCornerRadius(sc, 0.8); + } + Parcel p; + t.writeToParcel(&p); + t.clear(); + + for (auto _ : state) { + SurfaceComposerClient::Transaction t2; + t2.readFromParcel(&p); + p.setDataPosition(0); + benchmark::DoNotOptimize(t2); + } +} +BENCHMARK(readTransactionFromParcel); + +} // namespace +} // namespace android diff --git a/libs/gui/tests/benchmarks/main.cpp b/libs/gui/tests/benchmarks/main.cpp new file mode 100644 index 0000000000..685c7c6a26 --- /dev/null +++ b/libs/gui/tests/benchmarks/main.cpp @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <benchmark/benchmark.h> +BENCHMARK_MAIN(); 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 0a92a71596..1ed9794105 100644 --- a/libs/input/AccelerationCurve.cpp +++ b/libs/input/AccelerationCurve.cpp @@ -40,6 +40,18 @@ static_assert(kSegments.back().maxPointerSpeedMmPerS == std::numeric_limits<doub constexpr std::array<double, 15> kSensitivityFactors = {1, 2, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16, 18, 20}; +// Calculates the base gain for a given pointer sensitivity value. +// +// The base gain is a scaling factor that is applied to the pointer movement. +// Higher sensitivity values result in larger base gains, which in turn result +// 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 [1.0, 3.5]. +double calculateBaseGain(int32_t sensitivity) { + return 1.0 + (sensitivity + 7) * (3.5 - 1.0) / (7 + 7); +} + } // namespace std::vector<AccelerationCurveSegment> createAccelerationCurveForPointerSensitivity( @@ -60,4 +72,13 @@ std::vector<AccelerationCurveSegment> createAccelerationCurveForPointerSensitivi return output; } +std::vector<AccelerationCurveSegment> createFlatAccelerationCurve(int32_t sensitivity) { + LOG_ALWAYS_FATAL_IF(sensitivity < -7 || sensitivity > 7, "Invalid pointer sensitivity value"); + std::vector<AccelerationCurveSegment> output = { + AccelerationCurveSegment{std::numeric_limits<double>::infinity(), + calculateBaseGain(sensitivity), + /* reciprocal = */ 0}}; + return output; +} + } // namespace android
\ No newline at end of file diff --git a/libs/input/Android.bp b/libs/input/Android.bp index 389fb7f6ab..ff26184a33 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,15 +212,15 @@ 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", @@ -223,12 +230,13 @@ cc_library { "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 +270,13 @@ cc_library { shared_libs: [ "android.companion.virtualdevice.flags-aconfig-cc", + "libPlatformProperties", "libaconfig_storage_read_api_cc", "libbase", "libbinder", "libbinder_ndk", "libcutils", "liblog", - "libPlatformProperties", "libtinyxml2", "libutils", "libz", // needed by libkernelconfigs @@ -287,15 +295,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 +318,8 @@ cc_library { target: { android: { required: [ - "motion_predictor_model_prebuilt", "motion_predictor_model_config", + "motion_predictor_model_prebuilt", ], static_libs: [ "libstatslog_libinput", @@ -372,9 +380,9 @@ cc_defaults { cpp_std: "c++20", host_supported: true, shared_libs: [ - "libutils", "libbase", "liblog", + "libutils", ], } 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..f866f9b8f0 --- /dev/null +++ b/libs/input/InputFlags.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 <input/InputFlags.h> + +#include <android-base/logging.h> +#include <com_android_input_flags.h> +#include <cutils/properties.h> + +#include <string> + +namespace android { + +bool InputFlags::connectedDisplaysCursorEnabled() { + 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 6a55726db1..d388d48e8d 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -327,8 +327,8 @@ std::unique_ptr<InputChannel> InputChannel::create(const std::string& name, android::base::unique_fd fd, sp<IBinder> token) { const int result = fcntl(fd, F_SETFL, O_NONBLOCK); if (result != 0) { - LOG_ALWAYS_FATAL("channel '%s' ~ Could not make socket non-blocking: %s", name.c_str(), - strerror(errno)); + LOG_ALWAYS_FATAL("channel '%s' ~ Could not make socket (%d) non-blocking: %s", name.c_str(), + fd.get(), strerror(errno)); return nullptr; } // using 'new' to access a non-public constructor @@ -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/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp index 90d29dd190..d2c49b113d 100644 --- a/libs/input/KeyCharacterMap.cpp +++ b/libs/input/KeyCharacterMap.cpp @@ -615,7 +615,7 @@ std::unique_ptr<KeyCharacterMap> KeyCharacterMap::readFromParcel(Parcel* parcel) ALOGE("%s: Null parcel", __func__); return nullptr; } - std::string loadFileName = parcel->readCString(); + std::string loadFileName = parcel->readString8().c_str(); std::unique_ptr<KeyCharacterMap> map = std::make_unique<KeyCharacterMap>(KeyCharacterMap(loadFileName)); map->mType = static_cast<KeyCharacterMap::KeyboardType>(parcel->readInt32()); @@ -704,7 +704,7 @@ void KeyCharacterMap::writeToParcel(Parcel* parcel) const { ALOGE("%s: Null parcel", __func__); return; } - parcel->writeCString(mLoadFileName.c_str()); + parcel->writeString8(String8(mLoadFileName.c_str())); parcel->writeInt32(static_cast<int32_t>(mType)); parcel->writeBool(mLayoutOverlayApplied); diff --git a/libs/input/android/os/IInputConstants.aidl b/libs/input/android/os/IInputConstants.aidl index 31592cd6e3..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. @@ -76,6 +92,9 @@ interface IInputConstants /* The default pointer acceleration value. */ const int DEFAULT_POINTER_ACCELERATION = 3; + /* The default mouse wheel acceleration value. */ + const int DEFAULT_MOUSE_WHEEL_ACCELERATION = 4; + /** * Use the default Velocity Tracker Strategy. Different axes may use different default * strategies. 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 fd7704815f..fc859cb0f8 100644 --- a/libs/input/input_flags.aconfig +++ b/libs/input/input_flags.aconfig @@ -16,6 +16,16 @@ flag { } flag { + name: "enable_button_state_verification" + namespace: "input" + description: "Set to true to enable crashing whenever bad inbound events are going into InputDispatcher" + bug: "392870542" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "remove_input_channel_from_windowstate" namespace: "input" description: "Do not store a copy of input channel inside WindowState." @@ -37,9 +47,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,13 +61,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" @@ -79,13 +82,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" @@ -109,13 +105,6 @@ flag { } flag { - name: "enable_touchpad_fling_stop" - namespace: "input" - description: "Enable fling scrolling to be stopped by putting a finger on the touchpad again" - bug: "281106755" -} - -flag { name: "enable_prediction_pruning_via_jerk_thresholding" namespace: "input" description: "Enable prediction pruning based on jerk thresholds." @@ -159,13 +148,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." @@ -195,6 +177,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." @@ -231,3 +223,40 @@ 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 + } +} 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/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 0167c433cb..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,33 +53,41 @@ cc_test { ], cflags: [ "-Wall", - "-Wextra", "-Werror", + "-Wextra", "-Wno-unused-parameter", ], sanitize: { + address: true, hwaddress: true, undefined: true, all_undefined: true, diag: { + cfi: true, + integer_overflow: true, + memtag_heap: true, undefined: true, + misc_undefined: [ + "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, @@ -92,11 +100,6 @@ cc_test { "libstatssocket_lazy", ], }, - host: { - sanitize: { - address: true, - }, - }, }, native_coverage: false, } @@ -114,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/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/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h index ed3e8c1a62..10abb7c927 100644 --- a/libs/nativewindow/include/android/native_window.h +++ b/libs/nativewindow/include/android/native_window.h @@ -258,11 +258,11 @@ enum ANativeWindow_FrameRateCompatibility { ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE = 1, /** - * The window requests a frame rate that is greater than or equal to the specified frame rate. + * The window requests a frame rate that is at least the specified frame rate. * This value should be used for UIs, animations, scrolling, and anything that is not a game * or video. */ - ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE = 2 + ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_AT_LEAST = 2 }; /** 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 243532bc4d..a22c975588 100644 --- a/libs/permission/include/binder/AppOpsManager.h +++ b/libs/permission/include/binder/AppOpsManager.h @@ -148,7 +148,10 @@ public: OP_BLUETOOTH_ADVERTISE = 114, OP_RECORD_INCOMING_PHONE_AUDIO = 115, OP_NEARBY_WIFI_DEVICES = 116, - _NUM_OP = 117 + // 116 - 154 omitted due to lack of use in native + OP_CONTROL_AUDIO = 154, + OP_CONTROL_AUDIO_PARTIAL = 155, + _NUM_OP = 156, }; enum { @@ -177,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/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp index 907590a236..873fc67c65 100644 --- a/libs/renderengine/RenderEngine.cpp +++ b/libs/renderengine/RenderEngine.cpp @@ -107,16 +107,15 @@ ftl::Future<FenceResult> RenderEngine::drawLayers(const DisplaySettings& display return resultFuture; } -ftl::Future<FenceResult> RenderEngine::drawGainmap( - const std::shared_ptr<ExternalTexture>& sdr, base::borrowed_fd&& sdrFence, +ftl::Future<FenceResult> RenderEngine::tonemapAndDrawGainmap( const std::shared_ptr<ExternalTexture>& hdr, base::borrowed_fd&& hdrFence, - float hdrSdrRatio, ui::Dataspace dataspace, + float hdrSdrRatio, ui::Dataspace dataspace, const std::shared_ptr<ExternalTexture>& sdr, const std::shared_ptr<ExternalTexture>& gainmap) { const auto resultPromise = std::make_shared<std::promise<FenceResult>>(); std::future<FenceResult> resultFuture = resultPromise->get_future(); updateProtectedContext({}, {sdr.get(), hdr.get(), gainmap.get()}); - drawGainmapInternal(std::move(resultPromise), sdr, std::move(sdrFence), hdr, - std::move(hdrFence), hdrSdrRatio, dataspace, gainmap); + tonemapAndDrawGainmapInternal(std::move(resultPromise), hdr, std::move(hdrFence), hdrSdrRatio, + dataspace, sdr, gainmap); return resultFuture; } 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/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h index 95c4d033e2..c2dd4ae97e 100644 --- a/libs/renderengine/include/renderengine/RenderEngine.h +++ b/libs/renderengine/include/renderengine/RenderEngine.h @@ -217,12 +217,17 @@ public: const std::shared_ptr<ExternalTexture>& buffer, base::unique_fd&& bufferFence); - virtual ftl::Future<FenceResult> drawGainmap(const std::shared_ptr<ExternalTexture>& sdr, - base::borrowed_fd&& sdrFence, - const std::shared_ptr<ExternalTexture>& hdr, - base::borrowed_fd&& hdrFence, float hdrSdrRatio, - ui::Dataspace dataspace, - const std::shared_ptr<ExternalTexture>& gainmap); + // Tonemaps an HDR input image and draws an SDR rendition, plus a gainmap + // describing how to recover the HDR image. + // + // The HDR input image is ALWAYS encoded with an sRGB transfer function and + // is a floating point format. Accordingly, the hdrSdrRatio describes the + // max luminance in the HDR input image above SDR, and the dataspace + // describes the input primaries. + virtual ftl::Future<FenceResult> tonemapAndDrawGainmap( + const std::shared_ptr<ExternalTexture>& hdr, base::borrowed_fd&& hdrFence, + float hdrSdrRatio, ui::Dataspace dataspace, const std::shared_ptr<ExternalTexture>& sdr, + const std::shared_ptr<ExternalTexture>& gainmap); // Clean-up method that should be called on the main thread after the // drawFence returned by drawLayers fires. This method will free up @@ -310,11 +315,10 @@ protected: const DisplaySettings& display, const std::vector<LayerSettings>& layers, const std::shared_ptr<ExternalTexture>& buffer, base::unique_fd&& bufferFence) = 0; - virtual void drawGainmapInternal( + virtual void tonemapAndDrawGainmapInternal( const std::shared_ptr<std::promise<FenceResult>>&& resultPromise, - const std::shared_ptr<ExternalTexture>& sdr, base::borrowed_fd&& sdrFence, const std::shared_ptr<ExternalTexture>& hdr, base::borrowed_fd&& hdrFence, - float hdrSdrRatio, ui::Dataspace dataspace, + float hdrSdrRatio, ui::Dataspace dataspace, const std::shared_ptr<ExternalTexture>& sdr, const std::shared_ptr<ExternalTexture>& gainmap) = 0; }; diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h index fb8331d870..c42e4034e0 100644 --- a/libs/renderengine/include/renderengine/mock/RenderEngine.h +++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h @@ -46,17 +46,16 @@ public: ftl::Future<FenceResult>(const DisplaySettings&, const std::vector<LayerSettings>&, const std::shared_ptr<ExternalTexture>&, base::unique_fd&&)); - MOCK_METHOD7(drawGainmap, + MOCK_METHOD6(tonemapAndDrawGainmap, ftl::Future<FenceResult>(const std::shared_ptr<ExternalTexture>&, - base::borrowed_fd&&, - const std::shared_ptr<ExternalTexture>&, base::borrowed_fd&&, float, ui::Dataspace, + const std::shared_ptr<ExternalTexture>&, const std::shared_ptr<ExternalTexture>&)); - MOCK_METHOD8(drawGainmapInternal, + MOCK_METHOD7(tonemapAndDrawGainmapInternal, void(const std::shared_ptr<std::promise<FenceResult>>&&, - const std::shared_ptr<ExternalTexture>&, base::borrowed_fd&&, const std::shared_ptr<ExternalTexture>&, base::borrowed_fd&&, float, - ui::Dataspace, const std::shared_ptr<ExternalTexture>&)); + ui::Dataspace, const std::shared_ptr<ExternalTexture>&, + const std::shared_ptr<ExternalTexture>&)); MOCK_METHOD5(drawLayersInternal, void(const std::shared_ptr<std::promise<FenceResult>>&&, const DisplaySettings&, const std::vector<LayerSettings>&, const std::shared_ptr<ExternalTexture>&, diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp index 3b0f03671b..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 { @@ -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 b9869672de..5f2d1b1be6 100644 --- a/libs/renderengine/skia/SkiaRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaRenderEngine.cpp @@ -544,9 +544,18 @@ sk_sp<SkShader> SkiaRenderEngine::createRuntimeEffectShader( } if (graphicBuffer && parameters.layer.luts) { + const bool dimInLinearSpace = parameters.display.dimmingStage != + aidl::android::hardware::graphics::composer3::DimmingStage::GAMMA_OETF; + const ui::Dataspace runtimeEffectDataspace = !dimInLinearSpace + ? static_cast<ui::Dataspace>( + (parameters.outputDataSpace & ui::Dataspace::STANDARD_MASK) | + ui::Dataspace::TRANSFER_GAMMA2_2 | + (parameters.outputDataSpace & ui::Dataspace::RANGE_MASK)) + : parameters.outputDataSpace; + shader = mLutShader.lutShader(shader, parameters.layer.luts, parameters.layer.sourceDataspace, - toSkColorSpace(parameters.outputDataSpace)); + toSkColorSpace(runtimeEffectDataspace)); } if (parameters.requiresLinearEffect) { @@ -567,9 +576,7 @@ sk_sp<SkShader> SkiaRenderEngine::createRuntimeEffectShader( if (usingLocalTonemap) { const float inputRatio = hdrType == HdrRenderType::GENERIC_HDR ? 1.0f : parameters.layerDimmingRatio; - static MouriMap kMapper; - shader = kMapper.mouriMap(getActiveContext(), shader, inputRatio, - parameters.display.targetHdrSdrRatio); + shader = localTonemap(shader, inputRatio, parameters.display.targetHdrSdrRatio); } // disable tonemapping if we already locally tonemapped @@ -610,6 +617,12 @@ sk_sp<SkShader> SkiaRenderEngine::createRuntimeEffectShader( return shader; } +sk_sp<SkShader> SkiaRenderEngine::localTonemap(sk_sp<SkShader> shader, float inputMultiplier, + float targetHdrSdrRatio) { + static MouriMap kMapper; + return kMapper.mouriMap(getActiveContext(), shader, inputMultiplier, targetHdrSdrRatio); +} + void SkiaRenderEngine::initCanvas(SkCanvas* canvas, const DisplaySettings& display) { if (CC_UNLIKELY(mCapture->isCaptureRunning())) { // Record display settings when capture is running. @@ -825,8 +838,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 @@ -840,12 +852,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); @@ -878,12 +887,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()); @@ -907,6 +910,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, @@ -1212,44 +1238,58 @@ void SkiaRenderEngine::drawLayersInternal( resultPromise->set_value(std::move(drawFence)); } -void SkiaRenderEngine::drawGainmapInternal( +void SkiaRenderEngine::tonemapAndDrawGainmapInternal( const std::shared_ptr<std::promise<FenceResult>>&& resultPromise, - const std::shared_ptr<ExternalTexture>& sdr, base::borrowed_fd&& sdrFence, const std::shared_ptr<ExternalTexture>& hdr, base::borrowed_fd&& hdrFence, - float hdrSdrRatio, ui::Dataspace dataspace, + float hdrSdrRatio, ui::Dataspace dataspace, const std::shared_ptr<ExternalTexture>& sdr, const std::shared_ptr<ExternalTexture>& gainmap) { std::lock_guard<std::mutex> lock(mRenderingMutex); auto context = getActiveContext(); - auto surfaceTextureRef = getOrCreateBackendTexture(gainmap->getBuffer(), true); - sk_sp<SkSurface> dstSurface = - surfaceTextureRef->getOrCreateSurface(ui::Dataspace::V0_SRGB_LINEAR); - - waitFence(context, sdrFence); - const auto sdrTextureRef = getOrCreateBackendTexture(sdr->getBuffer(), false); - const auto sdrImage = sdrTextureRef->makeImage(dataspace, kPremul_SkAlphaType); - const auto sdrShader = - sdrImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, - SkSamplingOptions({SkFilterMode::kLinear, SkMipmapMode::kNone}), - nullptr); + auto gainmapTextureRef = getOrCreateBackendTexture(gainmap->getBuffer(), true); + sk_sp<SkSurface> gainmapSurface = + gainmapTextureRef->getOrCreateSurface(ui::Dataspace::V0_SRGB_LINEAR); + + auto sdrTextureRef = getOrCreateBackendTexture(sdr->getBuffer(), true); + sk_sp<SkSurface> sdrSurface = sdrTextureRef->getOrCreateSurface(dataspace); + waitFence(context, hdrFence); const auto hdrTextureRef = getOrCreateBackendTexture(hdr->getBuffer(), false); const auto hdrImage = hdrTextureRef->makeImage(dataspace, kPremul_SkAlphaType); const auto hdrShader = hdrImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, - SkSamplingOptions({SkFilterMode::kLinear, SkMipmapMode::kNone}), + SkSamplingOptions({SkFilterMode::kNearest, SkMipmapMode::kNone}), nullptr); + const auto tonemappedShader = localTonemap(hdrShader, 1.0f, 1.0f); + static GainmapFactory kGainmapFactory; - const auto gainmapShader = kGainmapFactory.createSkShader(sdrShader, hdrShader, hdrSdrRatio); + const auto gainmapShader = + kGainmapFactory.createSkShader(tonemappedShader, hdrShader, hdrSdrRatio); - const auto canvas = dstSurface->getCanvas(); - SkPaint paint; - paint.setShader(gainmapShader); - paint.setBlendMode(SkBlendMode::kSrc); - canvas->drawPaint(paint); + sp<Fence> drawFence; - auto drawFence = sp<Fence>::make(flushAndSubmit(context, dstSurface)); - trace(drawFence); + { + const auto canvas = sdrSurface->getCanvas(); + SkPaint paint; + paint.setShader(tonemappedShader); + paint.setBlendMode(SkBlendMode::kSrc); + canvas->drawPaint(paint); + + drawFence = sp<Fence>::make(flushAndSubmit(context, sdrSurface)); + trace(drawFence); + } + + { + const auto canvas = gainmapSurface->getCanvas(); + SkPaint paint; + paint.setShader(gainmapShader); + paint.setBlendMode(SkBlendMode::kSrc); + canvas->drawPaint(paint); + + auto gmFence = sp<Fence>::make(flushAndSubmit(context, gainmapSurface)); + trace(gmFence); + drawFence = Fence::merge("gm-ss", drawFence, gmFence); + } resultPromise->set_value(std::move(drawFence)); } diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h index 7be4c253e7..92b7af985c 100644 --- a/libs/renderengine/skia/SkiaRenderEngine.h +++ b/libs/renderengine/skia/SkiaRenderEngine.h @@ -143,13 +143,11 @@ private: const std::vector<LayerSettings>& layers, const std::shared_ptr<ExternalTexture>& buffer, base::unique_fd&& bufferFence) override final; - void drawGainmapInternal(const std::shared_ptr<std::promise<FenceResult>>&& resultPromise, - const std::shared_ptr<ExternalTexture>& sdr, - base::borrowed_fd&& sdrFence, - const std::shared_ptr<ExternalTexture>& hdr, - base::borrowed_fd&& hdrFence, float hdrSdrRatio, - ui::Dataspace dataspace, - const std::shared_ptr<ExternalTexture>& gainmap) override final; + void tonemapAndDrawGainmapInternal( + const std::shared_ptr<std::promise<FenceResult>>&& resultPromise, + const std::shared_ptr<ExternalTexture>& hdr, base::borrowed_fd&& hdrFence, + float hdrSdrRatio, ui::Dataspace dataspace, const std::shared_ptr<ExternalTexture>& sdr, + const std::shared_ptr<ExternalTexture>& gainmap) override final; void dump(std::string& result) override final; @@ -168,6 +166,8 @@ private: }; sk_sp<SkShader> createRuntimeEffectShader(const RuntimeEffectShaderParameters&); + sk_sp<SkShader> localTonemap(sk_sp<SkShader>, float inputMultiplier, float targetHdrSdrRatio); + const PixelFormat mDefaultPixelFormat; // Identifier used for various mappings of layers to various diff --git a/libs/renderengine/skia/VulkanInterface.cpp b/libs/renderengine/skia/VulkanInterface.cpp index 37b69f6590..7331bbc418 100644 --- a/libs/renderengine/skia/VulkanInterface.cpp +++ b/libs/renderengine/skia/VulkanInterface.cpp @@ -204,10 +204,10 @@ static skgpu::VulkanGetProc sGetProc = [](const char* proc_name, BAIL("[%s] null", #expr); \ } -#define VK_CHECK(expr) \ - if ((expr) != VK_SUCCESS) { \ - BAIL("[%s] failed. err = %d", #expr, expr); \ - return; \ +#define VK_CHECK(expr) \ + if (VkResult result = (expr); result != VK_SUCCESS) { \ + BAIL("[%s] failed. err = %d", #expr, result); \ + return; \ } #define VK_GET_PROC(F) \ 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/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 da47aae15b..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,46 +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); -} - -static sk_sp<SkSurface> makeSurface(SkiaGpuContext* context, const SkRect& origRect, int scale) { - SkImageInfo scaledInfo = - SkImageInfo::MakeN32Premul(ceil(static_cast<float>(origRect.width()) / scale), - ceil(static_cast<float>(origRect.height()) / scale)); - return context->createRenderTarget(scaledInfo); + 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); @@ -124,11 +148,20 @@ sk_sp<SkImage> KawaseBlurDualFilter::generate(SkiaGpuContext* context, const uin const float filterDepth = std::min(kMaxSurfaces - 1.0f, radius * kInputScale / 2.5f); const int filterPasses = std::min(kMaxSurfaces - 1, static_cast<int>(ceil(filterDepth))); + auto makeSurface = [&](float scale) -> sk_sp<SkSurface> { + 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. sk_sp<SkSurface> surfaces[kMaxSurfaces] = - {filterPasses >= 0 ? makeSurface(context, blurRect, 1 * kInverseInputScale) : nullptr, - filterPasses >= 1 ? makeSurface(context, blurRect, 2 * kInverseInputScale) : nullptr, - filterPasses >= 2 ? makeSurface(context, blurRect, 4 * kInverseInputScale) : nullptr}; + {filterPasses >= 0 ? makeSurface(1 * kInverseInputScale) : nullptr, + filterPasses >= 1 ? makeSurface(2 * kInverseInputScale) : nullptr, + filterPasses >= 2 ? makeSurface(4 * kInverseInputScale) : nullptr}; // These weights for scaling offsets per-pass are handpicked to look good at 1 <= radius <= 250. static const float kWeights[5] = { @@ -161,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 5e9dfbba3e..f262158f2c 100644 --- a/libs/renderengine/skia/filters/LutShader.cpp +++ b/libs/renderengine/skia/filters/LutShader.cpp @@ -39,10 +39,13 @@ static const SkString kShader = SkString(R"( uniform int key; uniform int dimension; uniform vec3 luminanceCoefficients; // for CIE_Y + // for hlg/pq transfer function, we need normalize it to [0.0, 1.0] + // we use `normalizeScalar` to do so + uniform float normalizeScalar; vec4 main(vec2 xy) { float4 rgba = image.eval(xy); - float3 linear = toLinearSrgb(rgba.rgb); + float3 linear = toLinearSrgb(rgba.rgb) * normalizeScalar; if (dimension == 1) { // RGB if (key == 0) { @@ -52,19 +55,19 @@ static const SkString kShader = SkString(R"( float gainR = lut.eval(vec2(indexR, 0.0) + 0.5).r; float gainG = lut.eval(vec2(indexG, 0.0) + 0.5).r; float gainB = lut.eval(vec2(indexB, 0.0) + 0.5).r; - return float4(linear.r * gainR, linear.g * gainG, linear.b * gainB, rgba.a); + linear = float3(linear.r * gainR, linear.g * gainG, linear.b * gainB); // MAX_RGB } else if (key == 1) { float maxRGB = max(linear.r, max(linear.g, linear.b)); float index = maxRGB * float(size - 1); float gain = lut.eval(vec2(index, 0.0) + 0.5).r; - return float4(linear * gain, rgba.a); + linear = linear * gain; // CIE_Y } else if (key == 2) { float y = dot(linear, luminanceCoefficients) / 3.0; float index = y * float(size - 1); float gain = lut.eval(vec2(index, 0.0) + 0.5).r; - return float4(linear * gain, rgba.a); + linear = linear * gain; } } else if (dimension == 3) { if (key == 0) { @@ -110,12 +113,10 @@ static const SkString kShader = SkString(R"( float3 c0 = mix(c00, c10, linear.g); float3 c1 = mix(c01, c11, linear.g); - float3 val = mix(c0, c1, linear.b); - - return float4(val, rgba.a); + linear = mix(c0, c1, linear.b); } } - return rgba; + return float4(linear, rgba.a); })"); // same as shader::toColorSpace function @@ -181,15 +182,21 @@ 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())) { - LOG_ALWAYS_FATAL("unable to install pixels"); + ALOGW("bitmap.installPixels failed, skip this Lut!"); + return input; } sk_sp<SkImage> lutImage = SkImages::RasterFromBitmap(bitmap); + if (!lutImage) { + ALOGW("Got a nullptr from SkImages::RasterFromBitmap, skip this Lut!"); + return input; + } + mBuilder->child("image") = input; mBuilder->child("lut") = lutImage->makeRawShader(SkTileMode::kClamp, SkTileMode::kClamp, @@ -197,9 +204,22 @@ sk_sp<SkShader> LutShader::generateLutShader(sk_sp<SkShader> input, ? SkSamplingOptions(SkFilterMode::kLinear) : SkSamplingOptions()); + float normalizeScalar = 1.0; + switch (srcDataspace & HAL_DATASPACE_TRANSFER_MASK) { + case HAL_DATASPACE_TRANSFER_HLG: + normalizeScalar = 0.203; + break; + case HAL_DATASPACE_TRANSFER_ST2084: + normalizeScalar = 0.0203; + break; + default: + normalizeScalar = 1.0; + } const int uSize = static_cast<int>(size); const int uKey = static_cast<int>(samplingKey); const int uDimension = static_cast<int>(dimension); + const float uNormalizeScalar = static_cast<float>(normalizeScalar); + if (static_cast<LutProperties::SamplingKey>(samplingKey) == LutProperties::SamplingKey::CIE_Y) { // Use predefined colorspaces of input dataspace so that we can get D65 illuminant mat3 toXYZMatrix(toColorSpace(srcDataspace).getRGBtoXYZ()); @@ -211,6 +231,7 @@ sk_sp<SkShader> LutShader::generateLutShader(sk_sp<SkShader> input, mBuilder->uniform("size") = uSize; mBuilder->uniform("key") = uKey; mBuilder->uniform("dimension") = uDimension; + mBuilder->uniform("normalizeScalar") = uNormalizeScalar; return mBuilder->makeShader(); } diff --git a/libs/renderengine/skia/filters/MouriMap.cpp b/libs/renderengine/skia/filters/MouriMap.cpp index b099bcf3d7..5dc36e6358 100644 --- a/libs/renderengine/skia/filters/MouriMap.cpp +++ b/libs/renderengine/skia/filters/MouriMap.cpp @@ -30,12 +30,12 @@ sk_sp<SkRuntimeEffect> makeEffect(const SkString& sksl) { } const SkString kCrosstalkAndChunk16x16(R"( uniform shader bitmap; - uniform float hdrSdrRatio; + uniform float inputMultiplier; vec4 main(vec2 xy) { float maximum = 0.0; for (int y = 0; y < 16; y++) { for (int x = 0; x < 16; x++) { - float3 linear = toLinearSrgb(bitmap.eval((xy - 0.5) * 16 + 0.5 + vec2(x, y)).rgb) * hdrSdrRatio; + float3 linear = toLinearSrgb(bitmap.eval((xy - 0.5) * 16 + 0.5 + vec2(x, y)).rgb) * inputMultiplier; float maxRGB = max(linear.r, max(linear.g, linear.b)); maximum = max(maximum, log2(max(maxRGB, 1.0))); } @@ -77,12 +77,12 @@ const SkString kTonemap(R"( uniform shader image; uniform shader lux; uniform float scaleFactor; - uniform float hdrSdrRatio; + uniform float inputMultiplier; uniform float targetHdrSdrRatio; vec4 main(vec2 xy) { float localMax = lux.eval(xy * scaleFactor).r; float4 rgba = image.eval(xy); - float3 linear = toLinearSrgb(rgba.rgb) * hdrSdrRatio; + float3 linear = toLinearSrgb(rgba.rgb) * inputMultiplier; if (localMax <= targetHdrSdrRatio) { return float4(fromLinearSrgb(linear), rgba.a); @@ -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 @@ -116,19 +116,19 @@ MouriMap::MouriMap() mTonemap(makeEffect(kTonemap)) {} sk_sp<SkShader> MouriMap::mouriMap(SkiaGpuContext* context, sk_sp<SkShader> input, - float hdrSdrRatio, float targetHdrSdrRatio) { - auto downchunked = downchunk(context, input, hdrSdrRatio); + float inputMultiplier, float targetHdrSdrRatio) { + auto downchunked = downchunk(context, input, inputMultiplier); auto localLux = blur(context, downchunked.get()); - return tonemap(input, localLux.get(), hdrSdrRatio, targetHdrSdrRatio); + return tonemap(input, localLux.get(), inputMultiplier, targetHdrSdrRatio); } sk_sp<SkImage> MouriMap::downchunk(SkiaGpuContext* context, sk_sp<SkShader> input, - float hdrSdrRatio) const { + float inputMultiplier) const { SkMatrix matrix; SkImage* image = input->isAImage(&matrix, (SkTileMode*)nullptr); SkRuntimeShaderBuilder crosstalkAndChunk16x16Builder(mCrosstalkAndChunk16x16); crosstalkAndChunk16x16Builder.child("bitmap") = input; - crosstalkAndChunk16x16Builder.uniform("hdrSdrRatio") = hdrSdrRatio; + crosstalkAndChunk16x16Builder.uniform("inputMultiplier") = inputMultiplier; // TODO: fp16 might be overkill. Most practical surfaces use 8-bit RGB for HDR UI and 10-bit YUV // for HDR video. These downsample operations compute log2(max(linear RGB, 1.0)). So we don't // care about LDR precision since they all resolve to LDR-max. For appropriately mastered HDR @@ -168,7 +168,7 @@ sk_sp<SkImage> MouriMap::blur(SkiaGpuContext* context, SkImage* input) const { LOG_ALWAYS_FATAL_IF(!blurSurface, "%s: Failed to create surface!", __func__); return makeImage(blurSurface.get(), blurBuilder); } -sk_sp<SkShader> MouriMap::tonemap(sk_sp<SkShader> input, SkImage* localLux, float hdrSdrRatio, +sk_sp<SkShader> MouriMap::tonemap(sk_sp<SkShader> input, SkImage* localLux, float inputMultiplier, float targetHdrSdrRatio) const { static constexpr float kScaleFactor = 1.0f / 128.0f; SkRuntimeShaderBuilder tonemapBuilder(mTonemap); @@ -177,7 +177,7 @@ sk_sp<SkShader> MouriMap::tonemap(sk_sp<SkShader> input, SkImage* localLux, floa localLux->makeRawShader(SkTileMode::kClamp, SkTileMode::kClamp, SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone)); tonemapBuilder.uniform("scaleFactor") = kScaleFactor; - tonemapBuilder.uniform("hdrSdrRatio") = hdrSdrRatio; + tonemapBuilder.uniform("inputMultiplier") = inputMultiplier; tonemapBuilder.uniform("targetHdrSdrRatio") = targetHdrSdrRatio; return tonemapBuilder.makeShader(); } diff --git a/libs/renderengine/skia/filters/MouriMap.h b/libs/renderengine/skia/filters/MouriMap.h index 9ba2b6ff9d..f4bfa1549e 100644 --- a/libs/renderengine/skia/filters/MouriMap.h +++ b/libs/renderengine/skia/filters/MouriMap.h @@ -62,10 +62,13 @@ class MouriMap { public: MouriMap(); // Apply the MouriMap tonemmaping operator to the input. - // The HDR/SDR ratio describes the luminace range of the input. 1.0 means SDR. Anything larger - // then 1.0 means that there is headroom above the SDR region. - // Similarly, the target HDR/SDR ratio describes the luminance range of the output. - sk_sp<SkShader> mouriMap(SkiaGpuContext* context, sk_sp<SkShader> input, float inputHdrSdrRatio, + // The inputMultiplier informs how to interpret the luminance encoding of the input. + // For a fixed point input, this is necessary to interpret what "1.0" means for the input + // pixels, so that the luminance can be scaled appropriately, such that the operator can + // transform SDR values to be within 1.0. For a floating point input, "1.0" always means SDR, + // so the caller must pass 1.0. + // The target HDR/SDR ratio describes the luminance range of the output. + sk_sp<SkShader> mouriMap(SkiaGpuContext* context, sk_sp<SkShader> input, float inputMultiplier, float targetHdrSdrRatio); private: diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp index c187f93089..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 { @@ -249,11 +250,10 @@ void RenderEngineThreaded::drawLayersInternal( return; } -void RenderEngineThreaded::drawGainmapInternal( +void RenderEngineThreaded::tonemapAndDrawGainmapInternal( const std::shared_ptr<std::promise<FenceResult>>&& resultPromise, - const std::shared_ptr<ExternalTexture>& sdr, base::borrowed_fd&& sdrFence, const std::shared_ptr<ExternalTexture>& hdr, base::borrowed_fd&& hdrFence, - float hdrSdrRatio, ui::Dataspace dataspace, + float hdrSdrRatio, ui::Dataspace dataspace, const std::shared_ptr<ExternalTexture>& sdr, const std::shared_ptr<ExternalTexture>& gainmap) { resultPromise->set_value(Fence::NO_FENCE); return; @@ -281,10 +281,9 @@ ftl::Future<FenceResult> RenderEngineThreaded::drawLayers( return resultFuture; } -ftl::Future<FenceResult> RenderEngineThreaded::drawGainmap( - const std::shared_ptr<ExternalTexture>& sdr, base::borrowed_fd&& sdrFence, +ftl::Future<FenceResult> RenderEngineThreaded::tonemapAndDrawGainmap( const std::shared_ptr<ExternalTexture>& hdr, base::borrowed_fd&& hdrFence, - float hdrSdrRatio, ui::Dataspace dataspace, + float hdrSdrRatio, ui::Dataspace dataspace, const std::shared_ptr<ExternalTexture>& sdr, const std::shared_ptr<ExternalTexture>& gainmap) { SFTRACE_CALL(); const auto resultPromise = std::make_shared<std::promise<FenceResult>>(); @@ -292,13 +291,14 @@ ftl::Future<FenceResult> RenderEngineThreaded::drawGainmap( { std::lock_guard lock(mThreadMutex); mNeedsPostRenderCleanup = true; - mFunctionCalls.push([resultPromise, sdr, sdrFence = std::move(sdrFence), hdr, - hdrFence = std::move(hdrFence), hdrSdrRatio, dataspace, + mFunctionCalls.push([resultPromise, hdr, hdrFence = std::move(hdrFence), hdrSdrRatio, + dataspace, sdr, gainmap](renderengine::RenderEngine& instance) mutable { - SFTRACE_NAME("REThreaded::drawGainmap"); - instance.updateProtectedContext({}, {sdr.get(), hdr.get(), gainmap.get()}); - instance.drawGainmapInternal(std::move(resultPromise), sdr, std::move(sdrFence), hdr, - std::move(hdrFence), hdrSdrRatio, dataspace, gainmap); + SFTRACE_NAME("REThreaded::tonemapAndDrawGainmap"); + instance.updateProtectedContext({}, {hdr.get(), sdr.get(), gainmap.get()}); + instance.tonemapAndDrawGainmapInternal(std::move(resultPromise), hdr, + std::move(hdrFence), hdrSdrRatio, dataspace, sdr, + gainmap); }); } mCondition.notify_one(); diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h index cb6e924d81..8554b55030 100644 --- a/libs/renderengine/threaded/RenderEngineThreaded.h +++ b/libs/renderengine/threaded/RenderEngineThreaded.h @@ -55,12 +55,10 @@ public: const std::vector<LayerSettings>& layers, const std::shared_ptr<ExternalTexture>& buffer, base::unique_fd&& bufferFence) override; - ftl::Future<FenceResult> drawGainmap(const std::shared_ptr<ExternalTexture>& sdr, - base::borrowed_fd&& sdrFence, - const std::shared_ptr<ExternalTexture>& hdr, - base::borrowed_fd&& hdrFence, float hdrSdrRatio, - ui::Dataspace dataspace, - const std::shared_ptr<ExternalTexture>& gainmap) override; + ftl::Future<FenceResult> tonemapAndDrawGainmap( + const std::shared_ptr<ExternalTexture>& hdr, base::borrowed_fd&& hdrFence, + float hdrSdrRatio, ui::Dataspace dataspace, const std::shared_ptr<ExternalTexture>& sdr, + const std::shared_ptr<ExternalTexture>& gainmap) override; int getContextPriority() override; bool supportsBackgroundBlur() override; @@ -77,13 +75,11 @@ protected: const std::vector<LayerSettings>& layers, const std::shared_ptr<ExternalTexture>& buffer, base::unique_fd&& bufferFence) override; - void drawGainmapInternal(const std::shared_ptr<std::promise<FenceResult>>&& resultPromise, - const std::shared_ptr<ExternalTexture>& sdr, - base::borrowed_fd&& sdrFence, - const std::shared_ptr<ExternalTexture>& hdr, - base::borrowed_fd&& hdrFence, float hdrSdrRatio, - ui::Dataspace dataspace, - const std::shared_ptr<ExternalTexture>& gainmap) override; + void tonemapAndDrawGainmapInternal( + const std::shared_ptr<std::promise<FenceResult>>&& resultPromise, + const std::shared_ptr<ExternalTexture>& hdr, base::borrowed_fd&& hdrFence, + float hdrSdrRatio, ui::Dataspace dataspace, const std::shared_ptr<ExternalTexture>& sdr, + const std::shared_ptr<ExternalTexture>& gainmap) override; private: void threadMain(CreateInstanceFactory factory); diff --git a/libs/tracing_perfetto/Android.bp b/libs/tracing_perfetto/Android.bp index 9a2d4f7463..1ef83a4706 100644 --- a/libs/tracing_perfetto/Android.bp +++ b/libs/tracing_perfetto/Android.bp @@ -21,7 +21,7 @@ package { default_applicable_licenses: ["frameworks_native_license"], } -cc_library_shared { +cc_library { name: "libtracing_perfetto", export_include_dirs: [ "include", @@ -37,13 +37,17 @@ cc_library_shared { srcs: [ "tracing_perfetto.cpp", "tracing_perfetto_internal.cpp", + "tracing_sdk.cpp", ], shared_libs: [ "libbase", "libcutils", "libperfetto_c", - "android.os.flags-aconfig-cc-host", + ], + + export_shared_lib_headers: [ + "libperfetto_c", ], host_supported: true, diff --git a/libs/tracing_perfetto/include/tracing_sdk.h b/libs/tracing_perfetto/include/tracing_sdk.h new file mode 100644 index 0000000000..271d7c8563 --- /dev/null +++ b/libs/tracing_perfetto/include/tracing_sdk.h @@ -0,0 +1,451 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <android-base/logging.h> +#include <stdint.h> + +#include <optional> +#include <vector> + +#include "perfetto/public/producer.h" +#include "perfetto/public/te_category_macros.h" +#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 + * underlying native resources. + * + * The static methods prefixed with `delete_` are special. They are designed to be + * invoked by Java through the `NativeAllocationRegistry` when the + * corresponding Java object becomes unreachable. These methods act as + * callbacks to ensure proper deallocation of native resources. + */ +namespace tracing_perfetto { +/** + * @brief Represents extra data associated with a trace event. + * This class manages a collection of PerfettoTeHlExtra pointers. + */ +class Extra; + +/** + * @brief Emits a trace event. + * @param type The type of the event. + * @param cat The category of the event. + * @param name The name of the event. + * @param arg_ptr Pointer to Extra data. + */ +void trace_event(int type, const PerfettoTeCategory* cat, const char* name, + Extra* extra); + +/** + * @brief Gets the process track UUID. + */ +uint64_t get_process_track_uuid(); + +/** + * @brief Gets the thread track UUID for a given PID. + */ +uint64_t get_thread_track_uuid(pid_t tid); + +/** + * @brief Holder for all the other classes in the file. + */ +class Extra { + public: + Extra(); + void push_extra(PerfettoTeHlExtra* extra); + void pop_extra(); + void clear_extras(); + static void delete_extra(Extra* extra); + + PerfettoTeHlExtra* const* get() const; + + private: + DISALLOW_COPY_AND_ASSIGN(Extra); + + // These PerfettoTeHlExtra pointers are really pointers to all the other + // types of extras: Category, DebugArg, Counter etc. Those objects are + // individually managed by Java. + std::vector<PerfettoTeHlExtra*> extras_; +}; + +/** + * @brief Represents a trace event category. + */ +class Category { + public: + Category(const std::string& name, const std::string& tag, + const std::string& severity); + + ~Category(); + + void register_category(); + + void unregister_category(); + + bool is_category_enabled(); + + static void delete_category(Category* category); + + const PerfettoTeCategory* get() const; + + private: + DISALLOW_COPY_AND_ASSIGN(Category); + PerfettoTeCategory category_; + const std::string name_; + const std::string tag_; + const std::string severity_; +}; + +/** + * @brief Represents one end of a flow between two events. + */ +class Flow { + public: + Flow(); + + void set_process_flow(uint64_t id); + void set_process_terminating_flow(uint64_t id); + static void delete_flow(Flow* flow); + + const PerfettoTeHlExtraFlow* get() const; + + private: + DISALLOW_COPY_AND_ASSIGN(Flow); + PerfettoTeHlExtraFlow flow_; +}; + +/** + * @brief Represents a named track. + */ +class NamedTrack { + public: + NamedTrack(uint64_t id, uint64_t parent_uuid, const std::string& name); + + static void delete_track(NamedTrack* track); + + const PerfettoTeHlExtraNamedTrack* get() const; + + private: + DISALLOW_COPY_AND_ASSIGN(NamedTrack); + const std::string name_; + PerfettoTeHlExtraNamedTrack track_; +}; + +/** + * @brief Represents a registered track. + */ +class RegisteredTrack { + public: + RegisteredTrack(uint64_t id, uint64_t parent_uuid, const std::string& name, + bool is_counter); + ~RegisteredTrack(); + + void register_track(); + void unregister_track(); + static void delete_track(RegisteredTrack* track); + + const PerfettoTeHlExtraRegisteredTrack* get() const; + + private: + DISALLOW_COPY_AND_ASSIGN(RegisteredTrack); + PerfettoTeRegisteredTrack registered_track_; + PerfettoTeHlExtraRegisteredTrack track_; + const std::string name_; + const uint64_t id_; + const uint64_t parent_uuid_; + const bool is_counter_; +}; + +/** + * @brief Represents a counter track event. + * @tparam T The data type of the counter (int64_t or double). + */ +template <typename T> +class Counter { + public: + template <typename> + struct always_false : std::false_type {}; + + struct TypeMap { + using type = std::invoke_result_t<decltype([]() { + if constexpr (std::is_same_v<T, int64_t>) { + return std::type_identity<PerfettoTeHlExtraCounterInt64>{}; + } else if constexpr (std::is_same_v<T, double>) { + return std::type_identity<PerfettoTeHlExtraCounterDouble>{}; + } else { + return std::type_identity<void>{}; + } + })>::type; + + static constexpr int enum_value = []() { + if constexpr (std::is_same_v<T, int64_t>) { + return PERFETTO_TE_HL_EXTRA_TYPE_COUNTER_INT64; + } else if constexpr (std::is_same_v<T, double>) { + return PERFETTO_TE_HL_EXTRA_TYPE_COUNTER_DOUBLE; + } else { + static_assert(always_false<T>::value, "Unsupported type"); + return 0; // Never reached, just to satisfy return type + } + }(); + }; + + Counter() { + static_assert(!std::is_same_v<typename TypeMap::type, void>, + "Unsupported type for Counter"); + + typename TypeMap::type counter; + counter.header = {TypeMap::enum_value}; + counter_ = std::move(counter); + } + + void set_value(T value) { + if constexpr (std::is_same_v<T, int64_t>) { + counter_.value = value; + } else if constexpr (std::is_same_v<T, double>) { + counter_.value = value; + } + } + + static void delete_counter(Counter* counter) { + delete counter; + } + + const TypeMap::type* get() const { + return &counter_; + } + + private: + DISALLOW_COPY_AND_ASSIGN(Counter); + TypeMap::type counter_; +}; + +/** + * @brief Represents a debug argument for a trace event. + * @tparam T The data type of the argument (bool, int64_t, double, const char*). + */ +template <typename T> +class DebugArg { + public: + template <typename> + struct always_false : std::false_type {}; + + struct TypeMap { + using type = std::invoke_result_t<decltype([]() { + if constexpr (std::is_same_v<T, bool>) { + return std::type_identity<PerfettoTeHlExtraDebugArgBool>{}; + } else if constexpr (std::is_same_v<T, int64_t>) { + return std::type_identity<PerfettoTeHlExtraDebugArgInt64>{}; + } else if constexpr (std::is_same_v<T, double>) { + return std::type_identity<PerfettoTeHlExtraDebugArgDouble>{}; + } else if constexpr (std::is_same_v<T, const char*>) { + return std::type_identity<PerfettoTeHlExtraDebugArgString>{}; + } else { + return std::type_identity<void>{}; + } + })>::type; + + static constexpr int enum_value = []() { + if constexpr (std::is_same_v<T, bool>) { + return PERFETTO_TE_HL_EXTRA_TYPE_DEBUG_ARG_BOOL; + } else if constexpr (std::is_same_v<T, int64_t>) { + return PERFETTO_TE_HL_EXTRA_TYPE_DEBUG_ARG_INT64; + } else if constexpr (std::is_same_v<T, double>) { + return PERFETTO_TE_HL_EXTRA_TYPE_DEBUG_ARG_DOUBLE; + } else if constexpr (std::is_same_v<T, const char*>) { + return PERFETTO_TE_HL_EXTRA_TYPE_DEBUG_ARG_STRING; + } else { + static_assert(always_false<T>::value, "Unsupported type"); + return 0; // Never reached, just to satisfy return type + } + }(); + }; + + DebugArg(const std::string& name) : name_(name) { + static_assert(!std::is_same_v<typename TypeMap::type, void>, + "Unsupported type for DebugArg"); + + typename TypeMap::type arg; + arg.header = {TypeMap::enum_value}; + arg.name = name_.c_str(); + arg_ = std::move(arg); + } + + void set_value(T value) { + if constexpr (std::is_same_v<T, const char*>) { + arg_.value = value; + } else if constexpr (std::is_same_v<T, int64_t>) { + arg_.value = value; + } else if constexpr (std::is_same_v<T, bool>) { + arg_.value = value; + } else if constexpr (std::is_same_v<T, double>) { + arg_.value = value; + } + } + + static void delete_arg(DebugArg* arg) { + delete arg; + } + + const TypeMap::type* get() const { + return &arg_; + } + + private: + DISALLOW_COPY_AND_ASSIGN(DebugArg); + TypeMap::type arg_; + const std::string name_; +}; + +template <typename T> +class ProtoField { + public: + template <typename> + struct always_false : std::false_type {}; + + struct TypeMap { + using type = std::invoke_result_t<decltype([]() { + if constexpr (std::is_same_v<T, int64_t>) { + return std::type_identity<PerfettoTeHlProtoFieldVarInt>{}; + } else if constexpr (std::is_same_v<T, double>) { + return std::type_identity<PerfettoTeHlProtoFieldDouble>{}; + } else if constexpr (std::is_same_v<T, const char*>) { + return std::type_identity<PerfettoTeHlProtoFieldCstr>{}; + } else { + return std::type_identity<void>{}; + } + })>::type; + + static constexpr PerfettoTeHlProtoFieldType enum_value = []() { + if constexpr (std::is_same_v<T, int64_t>) { + return PERFETTO_TE_HL_PROTO_TYPE_VARINT; + } else if constexpr (std::is_same_v<T, double>) { + return PERFETTO_TE_HL_PROTO_TYPE_DOUBLE; + } else if constexpr (std::is_same_v<T, const char*>) { + return PERFETTO_TE_HL_PROTO_TYPE_CSTR; + } else { + static_assert(always_false<T>::value, "Unsupported type"); + return 0; // Never reached, just to satisfy return type + } + }(); + }; + + ProtoField() { + static_assert(!std::is_same_v<typename TypeMap::type, void>, + "Unsupported type for ProtoField"); + + typename TypeMap::type arg; + arg.header.type = TypeMap::enum_value; + arg_ = std::move(arg); + } + + void set_value(uint32_t id, T value) { + if constexpr (std::is_same_v<T, int64_t>) { + arg_.header.id = id; + arg_.value = value; + } else if constexpr (std::is_same_v<T, double>) { + arg_.header.id = id; + arg_.value = value; + } else if constexpr (std::is_same_v<T, const char*>) { + arg_.header.id = id; + arg_.str = value; + } + } + + static void delete_field(ProtoField* field) { + delete field; + } + + const TypeMap::type* get() const { + return &arg_; + } + + private: + DISALLOW_COPY_AND_ASSIGN(ProtoField); + TypeMap::type arg_; +}; + +class ProtoFieldNested { + public: + ProtoFieldNested(); + + void add_field(PerfettoTeHlProtoField* field); + void set_id(uint32_t id); + static void delete_field(ProtoFieldNested* field); + + const PerfettoTeHlProtoFieldNested* get() const; + + private: + DISALLOW_COPY_AND_ASSIGN(ProtoFieldNested); + PerfettoTeHlProtoFieldNested field_; + // These PerfettoTeHlProtoField pointers are really pointers to all the other + // types of protos: PerfettoTeHlProtoFieldVarInt, PerfettoTeHlProtoFieldVarInt, + // PerfettoTeHlProtoFieldVarInt, PerfettoTeHlProtoFieldNested. Those objects are + // individually managed by Java. + std::vector<PerfettoTeHlProtoField*> fields_; +}; + +class Proto { + public: + Proto(); + + void add_field(PerfettoTeHlProtoField* field); + void clear_fields(); + static void delete_proto(Proto* proto); + + const PerfettoTeHlExtraProtoFields* get() const; + + private: + DISALLOW_COPY_AND_ASSIGN(Proto); + PerfettoTeHlExtraProtoFields proto_; + // These PerfettoTeHlProtoField pointers are really pointers to all the other + // types of protos: PerfettoTeHlProtoFieldVarInt, PerfettoTeHlProtoFieldVarInt, + // PerfettoTeHlProtoFieldVarInt, PerfettoTeHlProtoFieldNested. Those objects are + // individually managed by Java. + 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. + * @param ttl_ms The time-to-live of the trigger in milliseconds. + */ +void activate_trigger(const char* name, uint32_t ttl_ms); +} // namespace tracing_perfetto diff --git a/libs/tracing_perfetto/tests/Android.bp b/libs/tracing_perfetto/tests/Android.bp index d203467783..79fb704906 100644 --- a/libs/tracing_perfetto/tests/Android.bp +++ b/libs/tracing_perfetto/tests/Android.bp @@ -36,7 +36,6 @@ cc_test { "android.os.flags-aconfig-cc-host", "libbase", "libperfetto_c", - "liblog", "libprotobuf-cpp-lite", "libtracing_perfetto", ], @@ -44,5 +43,8 @@ cc_test { "tracing_perfetto_test.cpp", "utils.cpp", ], + local_include_dirs: [ + "include", + ], test_suites: ["device-tests"], } diff --git a/libs/tracing_perfetto/tests/include/utils.h b/libs/tracing_perfetto/tests/include/utils.h new file mode 100644 index 0000000000..b2630e1829 --- /dev/null +++ b/libs/tracing_perfetto/tests/include/utils.h @@ -0,0 +1,124 @@ +/* + * 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. + */ + +// Copied from //external/perfetto/src/shared_lib/test/utils.h + +#ifndef UTILS_H +#define UTILS_H + +#include <cassert> +#include <condition_variable> +#include <cstdint> +#include <functional> +#include <iterator> +#include <memory> +#include <mutex> +#include <ostream> +#include <string> +#include <vector> + +#include "perfetto/public/abi/pb_decoder_abi.h" +#include "perfetto/public/pb_utils.h" +#include "perfetto/public/tracing_session.h" + +// Pretty printer for gtest +void PrintTo(const PerfettoPbDecoderField& field, std::ostream*); + +namespace perfetto { +namespace shlib { +namespace test_utils { + +class WaitableEvent { + public: + WaitableEvent() = default; + void Notify() { + std::unique_lock<std::mutex> lock(m_); + notified_ = true; + cv_.notify_one(); + } + bool WaitForNotification() { + std::unique_lock<std::mutex> lock(m_); + cv_.wait(lock, [this] { return notified_; }); + return notified_; + } + bool IsNotified() { + std::unique_lock<std::mutex> lock(m_); + return notified_; + } + + private: + std::mutex m_; + std::condition_variable cv_; + bool notified_ = false; +}; + +class TracingSession { + public: + class Builder { + public: + Builder() = default; + Builder& add_enabled_category(std::string category) { + enabled_categories_.push_back(std::move(category)); + return *this; + } + Builder& add_disabled_category(std::string category) { + disabled_categories_.push_back(std::move(category)); + return *this; + } + Builder& add_atrace_category(std::string category) { + atrace_categories_.push_back(std::move(category)); + return *this; + } + Builder& add_atrace_category_prefer_sdk(std::string category) { + atrace_categories_prefer_sdk_.push_back(std::move(category)); + return *this; + } + TracingSession Build(); + + private: + std::vector<std::string> enabled_categories_; + std::vector<std::string> disabled_categories_; + std::vector<std::string> atrace_categories_; + std::vector<std::string> atrace_categories_prefer_sdk_; + }; + + static TracingSession Adopt(struct PerfettoTracingSessionImpl*); + static TracingSession FromBytes(void *buf, size_t len); + + TracingSession(TracingSession&&) noexcept; + + ~TracingSession(); + + struct PerfettoTracingSessionImpl* session() const { + return session_; + } + + bool FlushBlocking(uint32_t timeout_ms); + void WaitForStopped(); + void StopBlocking(); + std::vector<uint8_t> ReadBlocking(); + + private: + TracingSession() = default; + struct PerfettoTracingSessionImpl* session_; + std::unique_ptr<WaitableEvent> stopped_; +}; + +} // namespace test_utils +} // namespace shlib +} // namespace perfetto + +#endif // UTILS_H diff --git a/libs/tracing_perfetto/tests/tracing_perfetto_test.cpp b/libs/tracing_perfetto/tests/tracing_perfetto_test.cpp index e9fee2e6cf..b21a090677 100644 --- a/libs/tracing_perfetto/tests/tracing_perfetto_test.cpp +++ b/libs/tracing_perfetto/tests/tracing_perfetto_test.cpp @@ -22,6 +22,7 @@ #include <unistd.h> #include "gtest/gtest.h" + #include "perfetto/public/abi/data_source_abi.h" #include "perfetto/public/abi/heap_buffer.h" #include "perfetto/public/abi/pb_decoder_abi.h" diff --git a/libs/tracing_perfetto/tests/utils.cpp b/libs/tracing_perfetto/tests/utils.cpp index 8c4d4a8925..af61bc2192 100644 --- a/libs/tracing_perfetto/tests/utils.cpp +++ b/libs/tracing_perfetto/tests/utils.cpp @@ -34,36 +34,17 @@ namespace perfetto { namespace shlib { namespace test_utils { -namespace { - -std::string ToHexChars(uint8_t val) { - std::string ret; - uint8_t high_nibble = (val & 0xF0) >> 4; - uint8_t low_nibble = (val & 0xF); - static const char hex_chars[] = "0123456789ABCDEF"; - ret.push_back(hex_chars[high_nibble]); - ret.push_back(hex_chars[low_nibble]); - return ret; -} - -} // namespace - TracingSession TracingSession::Builder::Build() { perfetto::protos::TraceConfig trace_config; trace_config.add_buffers()->set_size_kb(1024); - auto* track_event_ds_config = trace_config.add_data_sources()->mutable_config(); - auto* ftrace_ds_config = trace_config.add_data_sources()->mutable_config(); - - track_event_ds_config->set_name("track_event"); - track_event_ds_config->set_target_buffer(0); - - ftrace_ds_config->set_name("linux.ftrace"); - ftrace_ds_config->set_target_buffer(0); - { - auto* ftrace_config = ftrace_ds_config->mutable_ftrace_config(); if (!atrace_categories_.empty()) { + auto* ftrace_ds_config = trace_config.add_data_sources()->mutable_config(); + ftrace_ds_config->set_name("linux.ftrace"); + ftrace_ds_config->set_target_buffer(0); + + auto* ftrace_config = ftrace_ds_config->mutable_ftrace_config(); ftrace_config->add_ftrace_events("ftrace/print"); for (const std::string& cat : atrace_categories_) { ftrace_config->add_atrace_categories(cat); @@ -76,8 +57,14 @@ TracingSession TracingSession::Builder::Build() { } { - auto* track_event_config = track_event_ds_config->mutable_track_event_config(); if (!enabled_categories_.empty() || !disabled_categories_.empty()) { + auto* track_event_ds_config = trace_config.add_data_sources()->mutable_config(); + + track_event_ds_config->set_name("track_event"); + track_event_ds_config->set_target_buffer(0); + + auto* track_event_config = track_event_ds_config->mutable_track_event_config(); + for (const std::string& cat : enabled_categories_) { track_event_config->add_enabled_categories(cat); } @@ -88,13 +75,17 @@ TracingSession TracingSession::Builder::Build() { } } - struct PerfettoTracingSessionImpl* ts = - PerfettoTracingSessionCreate(PERFETTO_BACKEND_SYSTEM); - std::string trace_config_string; trace_config.SerializeToString(&trace_config_string); - PerfettoTracingSessionSetup(ts, trace_config_string.data(), trace_config_string.length()); + return TracingSession::FromBytes(trace_config_string.data(), trace_config_string.length()); +} + +TracingSession TracingSession::FromBytes(void *buf, size_t len) { + struct PerfettoTracingSessionImpl* ts = + PerfettoTracingSessionCreate(PERFETTO_BACKEND_SYSTEM); + + PerfettoTracingSessionSetup(ts, buf, len); // Fails to start here PerfettoTracingSessionStartBlocking(ts); @@ -177,39 +168,3 @@ std::vector<uint8_t> TracingSession::ReadBlocking() { } // namespace test_utils } // namespace shlib } // namespace perfetto - -void PrintTo(const PerfettoPbDecoderField& field, std::ostream* pos) { - std::ostream& os = *pos; - PerfettoPbDecoderStatus status = - static_cast<PerfettoPbDecoderStatus>(field.status); - switch (status) { - case PERFETTO_PB_DECODER_ERROR: - os << "MALFORMED PROTOBUF"; - break; - case PERFETTO_PB_DECODER_DONE: - os << "DECODER DONE"; - break; - case PERFETTO_PB_DECODER_OK: - switch (field.wire_type) { - case PERFETTO_PB_WIRE_TYPE_DELIMITED: - os << "\""; - for (size_t i = 0; i < field.value.delimited.len; i++) { - os << perfetto::shlib::test_utils::ToHexChars( - field.value.delimited.start[i]) - << " "; - } - os << "\""; - break; - case PERFETTO_PB_WIRE_TYPE_VARINT: - os << "varint: " << field.value.integer64; - break; - case PERFETTO_PB_WIRE_TYPE_FIXED32: - os << "fixed32: " << field.value.integer32; - break; - case PERFETTO_PB_WIRE_TYPE_FIXED64: - os << "fixed64: " << field.value.integer64; - break; - } - break; - } -} diff --git a/libs/tracing_perfetto/tests/utils.h b/libs/tracing_perfetto/tests/utils.h deleted file mode 100644 index 8edb4143ee..0000000000 --- a/libs/tracing_perfetto/tests/utils.h +++ /dev/null @@ -1,457 +0,0 @@ -/* - * 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. - */ - -// Copied from //external/perfetto/src/shared_lib/test/utils.h - -#ifndef UTILS_H -#define UTILS_H - -#include <cassert> -#include <condition_variable> -#include <cstdint> -#include <functional> -#include <iterator> -#include <memory> -#include <mutex> -#include <ostream> -#include <string> -#include <vector> - -#include "gmock/gmock-matchers.h" -#include "gmock/gmock-more-matchers.h" -#include "gtest/gtest-matchers.h" -#include "gtest/gtest.h" -#include "perfetto/public/abi/pb_decoder_abi.h" -#include "perfetto/public/pb_utils.h" -#include "perfetto/public/tracing_session.h" - -// Pretty printer for gtest -void PrintTo(const PerfettoPbDecoderField& field, std::ostream*); - -namespace perfetto { -namespace shlib { -namespace test_utils { - -class WaitableEvent { - public: - WaitableEvent() = default; - void Notify() { - std::unique_lock<std::mutex> lock(m_); - notified_ = true; - cv_.notify_one(); - } - bool WaitForNotification() { - std::unique_lock<std::mutex> lock(m_); - cv_.wait(lock, [this] { return notified_; }); - return notified_; - } - bool IsNotified() { - std::unique_lock<std::mutex> lock(m_); - return notified_; - } - - private: - std::mutex m_; - std::condition_variable cv_; - bool notified_ = false; -}; - -class TracingSession { - public: - class Builder { - public: - Builder() = default; - Builder& add_enabled_category(std::string category) { - enabled_categories_.push_back(std::move(category)); - return *this; - } - Builder& add_disabled_category(std::string category) { - disabled_categories_.push_back(std::move(category)); - return *this; - } - Builder& add_atrace_category(std::string category) { - atrace_categories_.push_back(std::move(category)); - return *this; - } - Builder& add_atrace_category_prefer_sdk(std::string category) { - atrace_categories_prefer_sdk_.push_back(std::move(category)); - return *this; - } - TracingSession Build(); - - private: - std::vector<std::string> enabled_categories_; - std::vector<std::string> disabled_categories_; - std::vector<std::string> atrace_categories_; - std::vector<std::string> atrace_categories_prefer_sdk_; - }; - - static TracingSession Adopt(struct PerfettoTracingSessionImpl*); - - TracingSession(TracingSession&&) noexcept; - - ~TracingSession(); - - struct PerfettoTracingSessionImpl* session() const { - return session_; - } - - bool FlushBlocking(uint32_t timeout_ms); - void WaitForStopped(); - void StopBlocking(); - std::vector<uint8_t> ReadBlocking(); - - private: - TracingSession() = default; - struct PerfettoTracingSessionImpl* session_; - std::unique_ptr<WaitableEvent> stopped_; -}; - -template <typename FieldSkipper> -class FieldViewBase { - public: - class Iterator { - public: - using iterator_category = std::input_iterator_tag; - using value_type = const PerfettoPbDecoderField; - using pointer = value_type; - using reference = value_type; - reference operator*() const { - struct PerfettoPbDecoder decoder; - decoder.read_ptr = read_ptr_; - decoder.end_ptr = end_ptr_; - struct PerfettoPbDecoderField field; - do { - field = PerfettoPbDecoderParseField(&decoder); - } while (field.status == PERFETTO_PB_DECODER_OK && - skipper_.ShouldSkip(field)); - return field; - } - Iterator& operator++() { - struct PerfettoPbDecoder decoder; - decoder.read_ptr = read_ptr_; - decoder.end_ptr = end_ptr_; - PerfettoPbDecoderSkipField(&decoder); - read_ptr_ = decoder.read_ptr; - AdvanceToFirstInterestingField(); - return *this; - } - Iterator operator++(int) { - Iterator tmp = *this; - ++(*this); - return tmp; - } - - friend bool operator==(const Iterator& a, const Iterator& b) { - return a.read_ptr_ == b.read_ptr_; - } - friend bool operator!=(const Iterator& a, const Iterator& b) { - return a.read_ptr_ != b.read_ptr_; - } - - private: - Iterator(const uint8_t* read_ptr, const uint8_t* end_ptr, - const FieldSkipper& skipper) - : read_ptr_(read_ptr), end_ptr_(end_ptr), skipper_(skipper) { - AdvanceToFirstInterestingField(); - } - void AdvanceToFirstInterestingField() { - struct PerfettoPbDecoder decoder; - decoder.read_ptr = read_ptr_; - decoder.end_ptr = end_ptr_; - struct PerfettoPbDecoderField field; - const uint8_t* prev_read_ptr; - do { - prev_read_ptr = decoder.read_ptr; - field = PerfettoPbDecoderParseField(&decoder); - } while (field.status == PERFETTO_PB_DECODER_OK && - skipper_.ShouldSkip(field)); - if (field.status == PERFETTO_PB_DECODER_OK) { - read_ptr_ = prev_read_ptr; - } else { - read_ptr_ = decoder.read_ptr; - } - } - friend class FieldViewBase<FieldSkipper>; - const uint8_t* read_ptr_; - const uint8_t* end_ptr_; - const FieldSkipper& skipper_; - }; - using value_type = const PerfettoPbDecoderField; - using const_iterator = Iterator; - template <typename... Args> - explicit FieldViewBase(const uint8_t* begin, const uint8_t* end, Args... args) - : begin_(begin), end_(end), s_(args...) { - } - template <typename... Args> - explicit FieldViewBase(const std::vector<uint8_t>& data, Args... args) - : FieldViewBase(data.data(), data.data() + data.size(), args...) { - } - template <typename... Args> - explicit FieldViewBase(const struct PerfettoPbDecoderField& field, - Args... args) - : s_(args...) { - if (field.wire_type != PERFETTO_PB_WIRE_TYPE_DELIMITED) { - abort(); - } - begin_ = field.value.delimited.start; - end_ = begin_ + field.value.delimited.len; - } - Iterator begin() const { - return Iterator(begin_, end_, s_); - } - Iterator end() const { - return Iterator(end_, end_, s_); - } - PerfettoPbDecoderField front() const { - return *begin(); - } - - size_t size() const { - size_t count = 0; - for (auto field : *this) { - (void)field; - count++; - } - return count; - } - - bool ok() const { - for (auto field : *this) { - if (field.status != PERFETTO_PB_DECODER_OK) { - return false; - } - } - return true; - } - - private: - const uint8_t* begin_; - const uint8_t* end_; - FieldSkipper s_; -}; - -// Pretty printer for gtest -template <typename FieldSkipper> -void PrintTo(const FieldViewBase<FieldSkipper>& field_view, std::ostream* pos) { - std::ostream& os = *pos; - os << "{"; - for (PerfettoPbDecoderField f : field_view) { - PrintTo(f, pos); - os << ", "; - } - os << "}"; -} - -class IdFieldSkipper { - public: - explicit IdFieldSkipper(uint32_t id) : id_(id) { - } - explicit IdFieldSkipper(int32_t id) : id_(static_cast<uint32_t>(id)) { - } - bool ShouldSkip(const struct PerfettoPbDecoderField& field) const { - return field.id != id_; - } - - private: - uint32_t id_; -}; - -class NoFieldSkipper { - public: - NoFieldSkipper() = default; - bool ShouldSkip(const struct PerfettoPbDecoderField&) const { - return false; - } -}; - -// View over all the fields of a contiguous serialized protobuf message. -// -// Examples: -// -// for (struct PerfettoPbDecoderField field : FieldView(msg_begin, msg_end)) { -// //... -// } -// FieldView fields2(/*PerfettoPbDecoderField*/ nested_field); -// FieldView fields3(/*std::vector<uint8_t>*/ data); -// size_t num = fields1.size(); // The number of fields. -// bool ok = fields1.ok(); // Checks that the message is not malformed. -using FieldView = FieldViewBase<NoFieldSkipper>; - -// Like `FieldView`, but only considers fields with a specific id. -// -// Examples: -// -// IdFieldView fields(msg_begin, msg_end, id) -using IdFieldView = FieldViewBase<IdFieldSkipper>; - -// Matches a PerfettoPbDecoderField with the specified id. Accepts another -// matcher to match the contents of the field. -// -// Example: -// PerfettoPbDecoderField field = ... -// EXPECT_THAT(field, PbField(900, VarIntField(5))); -template <typename M> -auto PbField(int32_t id, M m) { - return testing::AllOf( - testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK), - testing::Field(&PerfettoPbDecoderField::id, id), m); -} - -// Matches a PerfettoPbDecoderField submessage field. Accepts a container -// matcher for the subfields. -// -// Example: -// PerfettoPbDecoderField field = ... -// EXPECT_THAT(field, MsgField(ElementsAre(...))); -template <typename M> -auto MsgField(M m) { - auto f = [](const PerfettoPbDecoderField& field) { return FieldView(field); }; - return testing::AllOf( - testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK), - testing::Field(&PerfettoPbDecoderField::wire_type, - PERFETTO_PB_WIRE_TYPE_DELIMITED), - testing::ResultOf(f, m)); -} - -// Matches a PerfettoPbDecoderField length delimited field. Accepts a string -// matcher. -// -// Example: -// PerfettoPbDecoderField field = ... -// EXPECT_THAT(field, StringField("string")); -template <typename M> -auto StringField(M m) { - auto f = [](const PerfettoPbDecoderField& field) { - return std::string( - reinterpret_cast<const char*>(field.value.delimited.start), - field.value.delimited.len); - }; - return testing::AllOf( - testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK), - testing::Field(&PerfettoPbDecoderField::wire_type, - PERFETTO_PB_WIRE_TYPE_DELIMITED), - testing::ResultOf(f, m)); -} - -// Matches a PerfettoPbDecoderField VarInt field. Accepts an integer matcher -// -// Example: -// PerfettoPbDecoderField field = ... -// EXPECT_THAT(field, VarIntField(1))); -template <typename M> -auto VarIntField(M m) { - auto f = [](const PerfettoPbDecoderField& field) { - return field.value.integer64; - }; - return testing::AllOf( - testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK), - testing::Field(&PerfettoPbDecoderField::wire_type, - PERFETTO_PB_WIRE_TYPE_VARINT), - testing::ResultOf(f, m)); -} - -// Matches a PerfettoPbDecoderField fixed64 field. Accepts an integer matcher -// -// Example: -// PerfettoPbDecoderField field = ... -// EXPECT_THAT(field, Fixed64Field(1))); -template <typename M> -auto Fixed64Field(M m) { - auto f = [](const PerfettoPbDecoderField& field) { - return field.value.integer64; - }; - return testing::AllOf( - testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK), - testing::Field(&PerfettoPbDecoderField::wire_type, - PERFETTO_PB_WIRE_TYPE_FIXED64), - testing::ResultOf(f, m)); -} - -// Matches a PerfettoPbDecoderField fixed32 field. Accepts an integer matcher -// -// Example: -// PerfettoPbDecoderField field = ... -// EXPECT_THAT(field, Fixed32Field(1))); -template <typename M> -auto Fixed32Field(M m) { - auto f = [](const PerfettoPbDecoderField& field) { - return field.value.integer32; - }; - return testing::AllOf( - testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK), - testing::Field(&PerfettoPbDecoderField::wire_type, - PERFETTO_PB_WIRE_TYPE_FIXED32), - testing::ResultOf(f, m)); -} - -// Matches a PerfettoPbDecoderField double field. Accepts a double matcher -// -// Example: -// PerfettoPbDecoderField field = ... -// EXPECT_THAT(field, DoubleField(1.0))); -template <typename M> -auto DoubleField(M m) { - auto f = [](const PerfettoPbDecoderField& field) { - return field.value.double_val; - }; - return testing::AllOf( - testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK), - testing::Field(&PerfettoPbDecoderField::wire_type, - PERFETTO_PB_WIRE_TYPE_FIXED64), - testing::ResultOf(f, m)); -} - -// Matches a PerfettoPbDecoderField float field. Accepts a float matcher -// -// Example: -// PerfettoPbDecoderField field = ... -// EXPECT_THAT(field, FloatField(1.0))); -template <typename M> -auto FloatField(M m) { - auto f = [](const PerfettoPbDecoderField& field) { - return field.value.float_val; - }; - return testing::AllOf( - testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK), - testing::Field(&PerfettoPbDecoderField::wire_type, - PERFETTO_PB_WIRE_TYPE_FIXED32), - testing::ResultOf(f, m)); -} - -// Matches a PerfettoPbDecoderField submessage field. Accepts a container -// matcher for the subfields. -// -// Example: -// PerfettoPbDecoderField field = ... -// EXPECT_THAT(field, AllFieldsWithId(900, ElementsAre(...))); -template <typename M> -auto AllFieldsWithId(int32_t id, M m) { - auto f = [id](const PerfettoPbDecoderField& field) { - return IdFieldView(field, id); - }; - return testing::AllOf( - testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK), - testing::Field(&PerfettoPbDecoderField::wire_type, - PERFETTO_PB_WIRE_TYPE_DELIMITED), - testing::ResultOf(f, m)); -} - -} // namespace test_utils -} // namespace shlib -} // namespace perfetto - -#endif // UTILS_H diff --git a/libs/tracing_perfetto/tracing_perfetto.cpp b/libs/tracing_perfetto/tracing_perfetto.cpp index c35e078b02..4b7021393f 100644 --- a/libs/tracing_perfetto/tracing_perfetto.cpp +++ b/libs/tracing_perfetto/tracing_perfetto.cpp @@ -17,6 +17,7 @@ #include "tracing_perfetto.h" #include <cutils/trace.h> + #include <cstdarg> #include "perfetto/public/te_category_macros.h" @@ -43,8 +44,10 @@ void traceBegin(uint64_t category, const char* name) { void traceFormatBegin(uint64_t category, const char* fmt, ...) { struct PerfettoTeCategory* perfettoTeCategory = internal::toPerfettoCategory(category); - const bool preferAtrace = internal::shouldPreferAtrace(perfettoTeCategory, category); - const bool preferPerfetto = internal::isPerfettoCategoryEnabled(perfettoTeCategory); + const bool preferAtrace = + internal::shouldPreferAtrace(perfettoTeCategory, category); + const bool preferPerfetto = + internal::isPerfettoCategoryEnabled(perfettoTeCategory); if (CC_LIKELY(!(preferAtrace || preferPerfetto))) { return; } @@ -57,7 +60,6 @@ void traceFormatBegin(uint64_t category, const char* fmt, ...) { vsnprintf(buf, BUFFER_SIZE, fmt, ap); va_end(ap); - if (preferAtrace) { atrace_begin(category, buf); } else if (preferPerfetto) { @@ -99,26 +101,28 @@ void traceAsyncEnd(uint64_t category, const char* name, int32_t cookie) { } void traceAsyncBeginForTrack(uint64_t category, const char* name, - const char* trackName, int32_t cookie) { + const char* trackName, int32_t cookie) { struct PerfettoTeCategory* perfettoTeCategory = internal::toPerfettoCategory(category); if (internal::shouldPreferAtrace(perfettoTeCategory, category)) { atrace_async_for_track_begin(category, trackName, name, cookie); } else if (internal::isPerfettoCategoryEnabled(perfettoTeCategory)) { - internal::perfettoTraceAsyncBeginForTrack(*perfettoTeCategory, name, trackName, cookie); + internal::perfettoTraceAsyncBeginForTrack(*perfettoTeCategory, name, + trackName, cookie); } } void traceAsyncEndForTrack(uint64_t category, const char* trackName, - int32_t cookie) { + int32_t cookie) { struct PerfettoTeCategory* perfettoTeCategory = internal::toPerfettoCategory(category); if (internal::shouldPreferAtrace(perfettoTeCategory, category)) { atrace_async_for_track_end(category, trackName, cookie); } else if (internal::isPerfettoCategoryEnabled(perfettoTeCategory)) { - internal::perfettoTraceAsyncEndForTrack(*perfettoTeCategory, trackName, cookie); + internal::perfettoTraceAsyncEndForTrack(*perfettoTeCategory, trackName, + cookie); } } @@ -136,8 +140,10 @@ void traceInstant(uint64_t category, const char* name) { void traceFormatInstant(uint64_t category, const char* fmt, ...) { struct PerfettoTeCategory* perfettoTeCategory = internal::toPerfettoCategory(category); - const bool preferAtrace = internal::shouldPreferAtrace(perfettoTeCategory, category); - const bool preferPerfetto = internal::isPerfettoCategoryEnabled(perfettoTeCategory); + const bool preferAtrace = + internal::shouldPreferAtrace(perfettoTeCategory, category); + const bool preferPerfetto = + internal::isPerfettoCategoryEnabled(perfettoTeCategory); if (CC_LIKELY(!(preferAtrace || preferPerfetto))) { return; } @@ -158,7 +164,7 @@ void traceFormatInstant(uint64_t category, const char* fmt, ...) { } void traceInstantForTrack(uint64_t category, const char* trackName, - const char* name) { + const char* name) { struct PerfettoTeCategory* perfettoTeCategory = internal::toPerfettoCategory(category); @@ -181,20 +187,21 @@ void traceCounter(uint64_t category, const char* name, int64_t value) { } void traceCounter32(uint64_t category, const char* name, int32_t value) { - struct PerfettoTeCategory* perfettoTeCategory = internal::toPerfettoCategory(category); + struct PerfettoTeCategory* perfettoTeCategory = + internal::toPerfettoCategory(category); if (internal::shouldPreferAtrace(perfettoTeCategory, category)) { atrace_int(category, name, value); } else if (internal::isPerfettoCategoryEnabled(perfettoTeCategory)) { internal::perfettoTraceCounter(*perfettoTeCategory, name, - static_cast<int64_t>(value)); + static_cast<int64_t>(value)); } } bool isTagEnabled(uint64_t category) { struct PerfettoTeCategory* perfettoTeCategory = internal::toPerfettoCategory(category); - return internal::isPerfettoCategoryEnabled(perfettoTeCategory) - || atrace_is_tag_enabled(category); + return internal::isPerfettoCategoryEnabled(perfettoTeCategory) || + atrace_is_tag_enabled(category); } } // namespace tracing_perfetto diff --git a/libs/tracing_perfetto/tracing_perfetto_internal.cpp b/libs/tracing_perfetto/tracing_perfetto_internal.cpp index a58bc77131..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") \ @@ -47,7 +48,6 @@ #include <atomic> #include <mutex> -#include <android_os.h> #include <android-base/properties.h> #include <cutils/trace.h> #include <inttypes.h> @@ -228,10 +228,6 @@ struct PerfettoTeCategory* toPerfettoCategory(uint64_t category) { } void registerWithPerfetto(bool test) { - if (!android::os::perfetto_sdk_tracing()) { - return; - } - static std::once_flag registration; std::call_once(registration, [test]() { struct PerfettoProducerInitArgs args = PERFETTO_PRODUCER_INIT_ARGS_INIT(); 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 new file mode 100644 index 0000000000..70b8be981b --- /dev/null +++ b/libs/tracing_perfetto/tracing_sdk.cpp @@ -0,0 +1,302 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "tracing_sdk.h" + +#include <android-base/logging.h> +#include <cutils/trace.h> + +#include <cstdarg> +#include <cstdlib> + +#include "perfetto/public/abi/producer_abi.h" +#include "perfetto/public/te_category_macros.h" +#include "perfetto/public/te_macros.h" +#include "perfetto/public/track_event.h" +#include "tracing_perfetto.h" + +namespace tracing_perfetto { +void trace_event(int type, const PerfettoTeCategory* perfettoTeCategory, + const char* name, tracing_perfetto::Extra* extra) { + bool enabled = PERFETTO_UNLIKELY(PERFETTO_ATOMIC_LOAD_EXPLICIT( + perfettoTeCategory->enabled, PERFETTO_MEMORY_ORDER_RELAXED)); + if (enabled) { + extra->push_extra(nullptr); + PerfettoTeHlEmitImpl(perfettoTeCategory->impl, type, + type == PERFETTO_TE_TYPE_COUNTER ? nullptr : name, + extra->get()); + extra->clear_extras(); + } +} + +uint64_t get_process_track_uuid() { + return PerfettoTeProcessTrackUuid(); +} + +uint64_t get_thread_track_uuid(pid_t tid) { + // Cating a signed pid_t to unsigned + return PerfettoTeProcessTrackUuid() ^ PERFETTO_STATIC_CAST(uint64_t, tid); +} + +Extra::Extra() { +} + +void Extra::push_extra(PerfettoTeHlExtra* ptr) { + extras_.push_back(ptr); +} + +void Extra::pop_extra() { + extras_.pop_back(); +} + +void Extra::clear_extras() { + extras_.clear(); +} + +void Extra::delete_extra(Extra* ptr) { + delete ptr; +} + +PerfettoTeHlExtra* const* Extra::get() const { + return extras_.data(); +} + +Category::Category(const std::string& name, const std::string& tag, + const std::string& severity) + : category_({.enabled = &perfetto_atomic_false}), + name_(name), + tag_(tag), + severity_(severity) { +} + +Category::~Category() { + unregister_category(); +} + +void Category::register_category() { + if (category_.impl) return; + + std::vector<const char*> tags; + if (!tag_.empty()) tags.push_back(tag_.data()); + if (!severity_.empty()) tags.push_back(severity_.data()); + + category_.desc = {name_.c_str(), name_.c_str(), tags.data(), tags.size()}; + + PerfettoTeCategoryRegister(&category_); + PerfettoTePublishCategories(); +} + +void Category::unregister_category() { + if (!category_.impl) return; + + PerfettoTeCategoryUnregister(&category_); + PerfettoTePublishCategories(); +} + +bool Category::is_category_enabled() { + return PERFETTO_UNLIKELY(PERFETTO_ATOMIC_LOAD_EXPLICIT( + (category_).enabled, PERFETTO_MEMORY_ORDER_RELAXED)); +} + +const PerfettoTeCategory* Category::get() const { + return &category_; +} + +void Category::delete_category(Category* ptr) { + delete ptr; +} + +Flow::Flow() : flow_{} { +} + +void Flow::set_process_flow(uint64_t id) { + flow_.header.type = PERFETTO_TE_HL_EXTRA_TYPE_FLOW; + PerfettoTeFlow ret = PerfettoTeProcessScopedFlow(id); + flow_.id = ret.id; +} + +void Flow::set_process_terminating_flow(uint64_t id) { + flow_.header.type = PERFETTO_TE_HL_EXTRA_TYPE_TERMINATING_FLOW; + PerfettoTeFlow ret = PerfettoTeProcessScopedFlow(id); + flow_.id = ret.id; +} + +const PerfettoTeHlExtraFlow* Flow::get() const { + return &flow_; +} + +void Flow::delete_flow(Flow* ptr) { + delete ptr; +} + +NamedTrack::NamedTrack(uint64_t id, uint64_t parent_uuid, + const std::string& name) + : name_(name), + track_{{PERFETTO_TE_HL_EXTRA_TYPE_NAMED_TRACK}, + name_.data(), + id, + parent_uuid} { +} + +const PerfettoTeHlExtraNamedTrack* NamedTrack::get() const { + return &track_; +} + +void NamedTrack::delete_track(NamedTrack* ptr) { + delete ptr; +} + +RegisteredTrack::RegisteredTrack(uint64_t id, uint64_t parent_uuid, + const std::string& name, bool is_counter) + : registered_track_{}, + track_{{PERFETTO_TE_HL_EXTRA_TYPE_REGISTERED_TRACK}, + &(registered_track_.impl)}, + name_(name), + id_(id), + parent_uuid_(parent_uuid), + is_counter_(is_counter) { + register_track(); +} + +RegisteredTrack::~RegisteredTrack() { + unregister_track(); +} + +void RegisteredTrack::register_track() { + if (registered_track_.impl.descriptor) return; + + if (is_counter_) { + PerfettoTeCounterTrackRegister(®istered_track_, name_.data(), + parent_uuid_); + } else { + PerfettoTeNamedTrackRegister(®istered_track_, name_.data(), id_, + parent_uuid_); + } +} + +void RegisteredTrack::unregister_track() { + if (!registered_track_.impl.descriptor) return; + PerfettoTeRegisteredTrackUnregister(®istered_track_); +} + +const PerfettoTeHlExtraRegisteredTrack* RegisteredTrack::get() const { + return &track_; +} + +void RegisteredTrack::delete_track(RegisteredTrack* ptr) { + delete ptr; +} + +Proto::Proto() : proto_({PERFETTO_TE_HL_EXTRA_TYPE_PROTO_FIELDS}, nullptr) { +} + +void Proto::add_field(PerfettoTeHlProtoField* ptr) { + if (!fields_.empty()) { + fields_.pop_back(); + } + + fields_.push_back(ptr); + fields_.push_back(nullptr); + proto_.fields = fields_.data(); +} + +void Proto::clear_fields() { + fields_.clear(); + proto_.fields = nullptr; +} + +void Proto::delete_proto(Proto* ptr) { + delete ptr; +} + +const PerfettoTeHlExtraProtoFields* Proto::get() const { + return &proto_; +} + +ProtoFieldNested::ProtoFieldNested() + : field_({PERFETTO_TE_HL_PROTO_TYPE_NESTED}, nullptr) { +} + +void ProtoFieldNested::add_field(PerfettoTeHlProtoField* ptr) { + if (!fields_.empty()) { + fields_.pop_back(); + } + + fields_.push_back(ptr); + fields_.push_back(nullptr); + field_.fields = fields_.data(); +} + +void ProtoFieldNested::set_id(uint32_t id) { + fields_.clear(); + field_.header.id = id; + field_.fields = nullptr; +} + +void ProtoFieldNested::delete_field(ProtoFieldNested* ptr) { + delete ptr; +} + +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); +} +} // namespace tracing_perfetto diff --git a/libs/ui/DisplayIdentification.cpp b/libs/ui/DisplayIdentification.cpp index 8d6f74b605..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()) { @@ -417,6 +414,7 @@ std::optional<DisplayIdentificationInfo> parseDisplayIdentificationData( return DisplayIdentificationInfo{ .id = displayId, .name = std::string(edid->displayName), + .port = port, .deviceProductInfo = buildDeviceProductInfo(*edid), .preferredDetailedTimingDescriptor = edid->preferredDetailedTimingDescriptor, }; @@ -426,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/include/ui/DisplayId.h b/libs/ui/include/ui/DisplayId.h index 8a14db8ffa..937e3f1486 100644 --- a/libs/ui/include/ui/DisplayId.h +++ b/libs/ui/include/ui/DisplayId.h @@ -20,7 +20,6 @@ #include <ostream> #include <string> -#include <ftl/hash.h> #include <ftl/optional.h> namespace android { @@ -31,31 +30,16 @@ 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; + static constexpr DisplayId fromValue(uint64_t value) { return DisplayId(value); } constexpr bool isVirtual() const { return value & FLAG_VIRTUAL; } - constexpr bool isStable() const { return value & FLAG_STABLE; } 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,6 +63,9 @@ 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 { + // TODO: b/162612135 - Remove default constructor. + PhysicalDisplayId() = default; + static constexpr ftl::Optional<PhysicalDisplayId> tryCast(DisplayId id) { if (id.isVirtual()) { return std::nullopt; @@ -86,7 +73,7 @@ struct PhysicalDisplayId : DisplayId { return PhysicalDisplayId(id); } - // 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 +86,18 @@ struct PhysicalDisplayId : DisplayId { return PhysicalDisplayId(0, port, kManufacturerId, kModelHash); } - // TODO(b/162612135) Remove default constructor - PhysicalDisplayId() = default; + static constexpr PhysicalDisplayId fromValue(uint64_t value) { + return PhysicalDisplayId(value); + } - constexpr uint16_t getManufacturerId() const { return static_cast<uint16_t>(value >> 40); } constexpr uint8_t getPort() const { return static_cast<uint8_t>(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) | @@ -127,8 +119,15 @@ struct VirtualDisplayId : DisplayId { 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) {} }; @@ -142,20 +141,17 @@ struct HalVirtualDisplayId : VirtualDisplayId { 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); @@ -163,12 +159,12 @@ struct GpuVirtualDisplayId : VirtualDisplayId { return std::nullopt; } -private: - struct HashTag {}; // Disambiguate with BaseId constructor. - constexpr GpuVirtualDisplayId(HashTag, uint64_t hash) - : VirtualDisplayId(FLAG_STABLE | FLAG_GPU | hash) {} + static constexpr GpuVirtualDisplayId fromValue(uint64_t value) { + return GpuVirtualDisplayId(SkipVirtualFlag{}, value); + } - explicit constexpr GpuVirtualDisplayId(DisplayId other) : VirtualDisplayId(other) {} +private: + using VirtualDisplayId::VirtualDisplayId; }; // HalDisplayId is the ID of a display which is managed by HWC. @@ -184,20 +180,13 @@ struct HalDisplayId : DisplayId { 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 {}; -} - static_assert(sizeof(DisplayId) == sizeof(uint64_t)); static_assert(sizeof(HalDisplayId) == sizeof(uint64_t)); static_assert(sizeof(VirtualDisplayId) == sizeof(uint64_t)); diff --git a/libs/ui/include/ui/DisplayIdentification.h b/libs/ui/include/ui/DisplayIdentification.h index cf67d7bf93..1e3449c004 100644 --- a/libs/ui/include/ui/DisplayIdentification.h +++ b/libs/ui/include/ui/DisplayIdentification.h @@ -42,6 +42,7 @@ struct DetailedTimingDescriptor { struct DisplayIdentificationInfo { PhysicalDisplayId id; std::string name; + uint8_t port; std::optional<DeviceProductInfo> deviceProductInfo; std::optional<DetailedTimingDescriptor> preferredDetailedTimingDescriptor; }; @@ -73,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; @@ -84,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_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..2b11786df3 100644 --- a/libs/ui/tests/Android.bp +++ b/libs/ui/tests/Android.bp @@ -144,6 +144,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 index ef686dfc83..209acba672 100644 --- a/libs/ui/tests/DisplayId_test.cpp +++ b/libs/ui/tests/DisplayId_test.cpp @@ -26,7 +26,6 @@ TEST(DisplayIdTest, createPhysicalIdFromEdid) { 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)); @@ -34,7 +33,7 @@ TEST(DisplayIdTest, createPhysicalIdFromEdid) { EXPECT_TRUE(HalDisplayId::tryCast(id)); EXPECT_EQ(id, DisplayId::fromValue(id.value)); - EXPECT_EQ(id, DisplayId::fromValue<PhysicalDisplayId>(id.value)); + EXPECT_EQ(id, PhysicalDisplayId::fromValue(id.value)); } TEST(DisplayIdTest, createPhysicalIdFromPort) { @@ -48,7 +47,7 @@ TEST(DisplayIdTest, createPhysicalIdFromPort) { EXPECT_TRUE(HalDisplayId::tryCast(id)); EXPECT_EQ(id, DisplayId::fromValue(id.value)); - EXPECT_EQ(id, DisplayId::fromValue<PhysicalDisplayId>(id.value)); + EXPECT_EQ(id, PhysicalDisplayId::fromValue(id.value)); } TEST(DisplayIdTest, createGpuVirtualId) { @@ -60,7 +59,7 @@ TEST(DisplayIdTest, createGpuVirtualId) { EXPECT_FALSE(HalDisplayId::tryCast(id)); EXPECT_EQ(id, DisplayId::fromValue(id.value)); - EXPECT_EQ(id, DisplayId::fromValue<GpuVirtualDisplayId>(id.value)); + EXPECT_EQ(id, GpuVirtualDisplayId::fromValue(id.value)); } TEST(DisplayIdTest, createVirtualIdFromGpuVirtualId) { @@ -75,21 +74,6 @@ TEST(DisplayIdTest, createVirtualIdFromGpuVirtualId) { 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)); @@ -99,7 +83,7 @@ TEST(DisplayIdTest, createHalVirtualId) { EXPECT_TRUE(HalDisplayId::tryCast(id)); EXPECT_EQ(id, DisplayId::fromValue(id.value)); - EXPECT_EQ(id, DisplayId::fromValue<HalVirtualDisplayId>(id.value)); + EXPECT_EQ(id, HalVirtualDisplayId::fromValue(id.value)); } TEST(DisplayIdTest, createVirtualIdFromHalVirtualId) { 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 |