Merge tag 'android-16.0.0_r1' of https://android.googlesource.com/platform/frameworks/native into HEAD
Android 16.0.0 release 1
Change-Id: Iacaf25353d3020d7edcd8b9b6c484a94d875105f
diff --git a/libs/attestation/OWNERS b/libs/attestation/OWNERS
index 4dbb0ea..76811f2 100644
--- a/libs/attestation/OWNERS
+++ b/libs/attestation/OWNERS
@@ -1,2 +1 @@
-chaviw@google.com
-svv@google.com
\ No newline at end of file
+svv@google.com
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index aac369d..a3499c1 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -269,6 +269,7 @@
"-Wzero-as-null-pointer-constant",
"-Wreorder-init-list",
"-Wunused-const-variable",
+ "-Wunused-result",
"-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION",
"-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
// Hide symbols by default and set the BUILDING_LIBBINDER macro so that
@@ -276,15 +277,6 @@
"-fvisibility=hidden",
"-DBUILDING_LIBBINDER",
],
-
- target: {
- vendor: {
- // Trimming the exported symbols reveals a bug in vendor code, so
- // disable it for the vendor variant for now. http://b/349657329
- // TODO: Fix the issue and remove this override.
- cflags: ["-fvisibility=default"],
- },
- },
}
cc_defaults {
@@ -826,6 +818,7 @@
// so we restrict its visibility to the Trusty-specific packages.
visibility: [
":__subpackages__",
+ "//hardware/interfaces/security/see:__subpackages__",
"//system/core/trusty:__subpackages__",
"//vendor:__subpackages__",
],
diff --git a/libs/binder/BackendUnifiedServiceManager.cpp b/libs/binder/BackendUnifiedServiceManager.cpp
index 34d5a09..b1c8994 100644
--- a/libs/binder/BackendUnifiedServiceManager.cpp
+++ b/libs/binder/BackendUnifiedServiceManager.cpp
@@ -15,6 +15,7 @@
*/
#include "BackendUnifiedServiceManager.h"
+#include <android-base/strings.h>
#include <android/os/IAccessor.h>
#include <android/os/IServiceManager.h>
#include <binder/RpcSession.h>
@@ -47,6 +48,9 @@
using android::os::IAccessor;
using binder::Status;
+static const char* kUnsupportedOpNoServiceManager =
+ "Unsupported operation without a kernel binder servicemanager process";
+
static const char* kStaticCachableList[] = {
// go/keep-sorted start
"accessibility",
@@ -126,7 +130,13 @@
bool BinderCacheWithInvalidation::isClientSideCachingEnabled(const std::string& serviceName) {
sp<ProcessState> self = ProcessState::selfOrNull();
- if (!self || self->getThreadPoolMaxTotalThreadCount() <= 0) {
+ // Should not cache if process state could not be found, or if thread pool
+ // max could is not greater than zero.
+ if (!self) {
+ ALOGW("Service retrieved before binder threads started. If they are to be started, "
+ "consider starting binder threads earlier.");
+ return false;
+ } else if (self->getThreadPoolMaxTotalThreadCount() <= 0) {
ALOGW("Thread Pool max thread count is 0. Cannot cache binder as linkToDeath cannot be "
"implemented. serviceName: %s",
serviceName.c_str());
@@ -211,7 +221,9 @@
sp<IBinder>* _aidl_return) {
os::Service service;
Status status = getService2(name, &service);
- *_aidl_return = service.get<os::Service::Tag::serviceWithMetadata>().service;
+ if (status.isOk()) {
+ *_aidl_return = service.get<os::Service::Tag::serviceWithMetadata>().service;
+ }
return status;
}
@@ -220,7 +232,10 @@
return Status::ok();
}
os::Service service;
- Status status = mTheRealServiceManager->getService2(name, &service);
+ Status status = Status::ok();
+ if (mTheRealServiceManager) {
+ status = mTheRealServiceManager->getService2(name, &service);
+ }
if (status.isOk()) {
status = toBinderService(name, service, _out);
@@ -231,13 +246,26 @@
return status;
}
-Status BackendUnifiedServiceManager::checkService(const ::std::string& name, os::Service* _out) {
+Status BackendUnifiedServiceManager::checkService(const ::std::string& name,
+ sp<IBinder>* _aidl_return) {
+ os::Service service;
+ Status status = checkService2(name, &service);
+ if (status.isOk()) {
+ *_aidl_return = service.get<os::Service::Tag::serviceWithMetadata>().service;
+ }
+ return status;
+}
+
+Status BackendUnifiedServiceManager::checkService2(const ::std::string& name, os::Service* _out) {
os::Service service;
if (returnIfCached(name, _out)) {
return Status::ok();
}
- Status status = mTheRealServiceManager->checkService(name, &service);
+ Status status = Status::ok();
+ if (mTheRealServiceManager) {
+ status = mTheRealServiceManager->checkService2(name, &service);
+ }
if (status.isOk()) {
status = toBinderService(name, service, _out);
if (status.isOk()) {
@@ -315,66 +343,156 @@
Status BackendUnifiedServiceManager::addService(const ::std::string& name,
const sp<IBinder>& service, bool allowIsolated,
int32_t dumpPriority) {
- Status status = mTheRealServiceManager->addService(name, service, allowIsolated, dumpPriority);
- // mEnableAddServiceCache is true by default.
- if (kUseCacheInAddService && mEnableAddServiceCache && status.isOk()) {
- return updateCache(name, service,
- dumpPriority & android::os::IServiceManager::FLAG_IS_LAZY_SERVICE);
+ if (mTheRealServiceManager) {
+ Status status =
+ mTheRealServiceManager->addService(name, service, allowIsolated, dumpPriority);
+ // mEnableAddServiceCache is true by default.
+ if (kUseCacheInAddService && mEnableAddServiceCache && status.isOk()) {
+ return updateCache(name, service,
+ dumpPriority & android::os::IServiceManager::FLAG_IS_LAZY_SERVICE);
+ }
+ return status;
}
- return status;
+ return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION,
+ kUnsupportedOpNoServiceManager);
}
Status BackendUnifiedServiceManager::listServices(int32_t dumpPriority,
::std::vector<::std::string>* _aidl_return) {
- return mTheRealServiceManager->listServices(dumpPriority, _aidl_return);
+ Status status = Status::ok();
+ if (mTheRealServiceManager) {
+ status = mTheRealServiceManager->listServices(dumpPriority, _aidl_return);
+ }
+ if (!status.isOk()) return status;
+
+ appendInjectedAccessorServices(_aidl_return);
+
+ return status;
}
Status BackendUnifiedServiceManager::registerForNotifications(
const ::std::string& name, const sp<os::IServiceCallback>& callback) {
- return mTheRealServiceManager->registerForNotifications(name, callback);
+ if (mTheRealServiceManager) {
+ return mTheRealServiceManager->registerForNotifications(name, callback);
+ }
+ return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION,
+ kUnsupportedOpNoServiceManager);
}
Status BackendUnifiedServiceManager::unregisterForNotifications(
const ::std::string& name, const sp<os::IServiceCallback>& callback) {
- return mTheRealServiceManager->unregisterForNotifications(name, callback);
+ if (mTheRealServiceManager) {
+ return mTheRealServiceManager->unregisterForNotifications(name, callback);
+ }
+ return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION,
+ kUnsupportedOpNoServiceManager);
}
Status BackendUnifiedServiceManager::isDeclared(const ::std::string& name, bool* _aidl_return) {
- return mTheRealServiceManager->isDeclared(name, _aidl_return);
+ Status status = Status::ok();
+ if (mTheRealServiceManager) {
+ status = mTheRealServiceManager->isDeclared(name, _aidl_return);
+ }
+ if (!status.isOk()) return status;
+
+ if (!*_aidl_return) {
+ forEachInjectedAccessorService([&](const std::string& instance) {
+ if (name == instance) {
+ *_aidl_return = true;
+ }
+ });
+ }
+
+ return status;
}
Status BackendUnifiedServiceManager::getDeclaredInstances(
const ::std::string& iface, ::std::vector<::std::string>* _aidl_return) {
- return mTheRealServiceManager->getDeclaredInstances(iface, _aidl_return);
+ Status status = Status::ok();
+ if (mTheRealServiceManager) {
+ status = mTheRealServiceManager->getDeclaredInstances(iface, _aidl_return);
+ }
+ if (!status.isOk()) return status;
+
+ forEachInjectedAccessorService([&](const std::string& instance) {
+ // Declared instances have the format
+ // <interface>/instance like foo.bar.ISomething/instance
+ // If it does not have that format, consider the instance to be ""
+ std::string_view name(instance);
+ if (base::ConsumePrefix(&name, iface + "/")) {
+ _aidl_return->emplace_back(name);
+ } else if (iface == instance) {
+ _aidl_return->push_back("");
+ }
+ });
+
+ return status;
}
Status BackendUnifiedServiceManager::updatableViaApex(
const ::std::string& name, ::std::optional<::std::string>* _aidl_return) {
- return mTheRealServiceManager->updatableViaApex(name, _aidl_return);
+ if (mTheRealServiceManager) {
+ return mTheRealServiceManager->updatableViaApex(name, _aidl_return);
+ }
+ return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION,
+ kUnsupportedOpNoServiceManager);
}
Status BackendUnifiedServiceManager::getUpdatableNames(const ::std::string& apexName,
::std::vector<::std::string>* _aidl_return) {
- return mTheRealServiceManager->getUpdatableNames(apexName, _aidl_return);
+ if (mTheRealServiceManager) {
+ return mTheRealServiceManager->getUpdatableNames(apexName, _aidl_return);
+ }
+ return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION,
+ kUnsupportedOpNoServiceManager);
}
Status BackendUnifiedServiceManager::getConnectionInfo(
const ::std::string& name, ::std::optional<os::ConnectionInfo>* _aidl_return) {
- return mTheRealServiceManager->getConnectionInfo(name, _aidl_return);
+ if (mTheRealServiceManager) {
+ return mTheRealServiceManager->getConnectionInfo(name, _aidl_return);
+ }
+ return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION,
+ kUnsupportedOpNoServiceManager);
}
Status BackendUnifiedServiceManager::registerClientCallback(
const ::std::string& name, const sp<IBinder>& service,
const sp<os::IClientCallback>& callback) {
- return mTheRealServiceManager->registerClientCallback(name, service, callback);
+ if (mTheRealServiceManager) {
+ return mTheRealServiceManager->registerClientCallback(name, service, callback);
+ }
+ return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION,
+ kUnsupportedOpNoServiceManager);
}
Status BackendUnifiedServiceManager::tryUnregisterService(const ::std::string& name,
const sp<IBinder>& service) {
- return mTheRealServiceManager->tryUnregisterService(name, service);
+ if (mTheRealServiceManager) {
+ return mTheRealServiceManager->tryUnregisterService(name, service);
+ }
+ return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION,
+ kUnsupportedOpNoServiceManager);
}
Status BackendUnifiedServiceManager::getServiceDebugInfo(
::std::vector<os::ServiceDebugInfo>* _aidl_return) {
- return mTheRealServiceManager->getServiceDebugInfo(_aidl_return);
+ if (mTheRealServiceManager) {
+ return mTheRealServiceManager->getServiceDebugInfo(_aidl_return);
+ }
+ return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION,
+ kUnsupportedOpNoServiceManager);
}
[[clang::no_destroy]] static std::once_flag gUSmOnce;
[[clang::no_destroy]] static sp<BackendUnifiedServiceManager> gUnifiedServiceManager;
+static bool hasOutOfProcessServiceManager() {
+#ifndef BINDER_WITH_KERNEL_IPC
+ return false;
+#else
+#if defined(__BIONIC__) && !defined(__ANDROID_VNDK__)
+ return android::base::GetBoolProperty("servicemanager.installed", true);
+#else
+ return true;
+#endif
+#endif // BINDER_WITH_KERNEL_IPC
+}
+
sp<BackendUnifiedServiceManager> getBackendUnifiedServiceManager() {
std::call_once(gUSmOnce, []() {
#if defined(__BIONIC__) && !defined(__ANDROID_VNDK__)
- /* wait for service manager */ {
+ /* wait for service manager */
+ if (hasOutOfProcessServiceManager()) {
using std::literals::chrono_literals::operator""s;
using android::base::WaitForProperty;
while (!WaitForProperty("servicemanager.ready", "true", 1s)) {
@@ -384,7 +502,7 @@
#endif
sp<AidlServiceManager> sm = nullptr;
- while (sm == nullptr) {
+ while (hasOutOfProcessServiceManager() && sm == nullptr) {
sm = interface_cast<AidlServiceManager>(
ProcessState::self()->getContextObject(nullptr));
if (sm == nullptr) {
diff --git a/libs/binder/BackendUnifiedServiceManager.h b/libs/binder/BackendUnifiedServiceManager.h
index 6a0d06a..c14f280 100644
--- a/libs/binder/BackendUnifiedServiceManager.h
+++ b/libs/binder/BackendUnifiedServiceManager.h
@@ -122,7 +122,8 @@
binder::Status getService(const ::std::string& name, sp<IBinder>* _aidl_return) override;
binder::Status getService2(const ::std::string& name, os::Service* out) override;
- binder::Status checkService(const ::std::string& name, os::Service* out) override;
+ binder::Status checkService(const ::std::string& name, sp<IBinder>* _aidl_return) override;
+ binder::Status checkService2(const ::std::string& name, os::Service* out) override;
binder::Status addService(const ::std::string& name, const sp<IBinder>& service,
bool allowIsolated, int32_t dumpPriority) override;
binder::Status listServices(int32_t dumpPriority,
@@ -167,5 +168,9 @@
sp<BackendUnifiedServiceManager> getBackendUnifiedServiceManager();
android::binder::Status getInjectedAccessor(const std::string& name, android::os::Service* service);
+void appendInjectedAccessorServices(std::vector<std::string>* list);
+// Do not call any other service manager APIs that might take the accessor
+// mutex because this will be holding it!
+void forEachInjectedAccessorService(const std::function<void(const std::string&)>& f);
} // namespace android
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index 53bd08d..9883eb2 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -38,6 +38,7 @@
#endif
#include "BuildFlags.h"
+#include "Constants.h"
#include "OS.h"
#include "RpcState.h"
@@ -70,8 +71,6 @@
constexpr bool kEnableRecording = false;
#endif
-// Log any reply transactions for which the data exceeds this size
-#define LOG_REPLIES_OVER_SIZE (300 * 1024)
// ---------------------------------------------------------------------------
IBinder::IBinder()
@@ -288,7 +287,7 @@
// for below objects
RpcMutex mLock;
std::set<sp<RpcServerLink>> mRpcServerLinks;
- BpBinder::ObjectManager mObjects;
+ BpBinder::ObjectManager mObjectMgr;
unique_fd mRecordingFd;
};
@@ -412,7 +411,7 @@
// In case this is being transacted on in the same process.
if (reply != nullptr) {
reply->setDataPosition(0);
- if (reply->dataSize() > LOG_REPLIES_OVER_SIZE) {
+ if (reply->dataSize() > binder::kLogTransactionsOverBytes) {
ALOGW("Large reply transaction of %zu bytes, interface descriptor %s, code %d",
reply->dataSize(), String8(getInterfaceDescriptor()).c_str(), code);
}
@@ -446,6 +445,9 @@
const sp<DeathRecipient>& /*recipient*/, void* /*cookie*/,
uint32_t /*flags*/)
{
+ // BBinder::linkToDeath is invalid because this process owns this binder.
+ // The DeathRecipient is called on BpBinders when the process owning the
+ // binder node dies.
return INVALID_OPERATION;
}
@@ -468,7 +470,7 @@
LOG_ALWAYS_FATAL_IF(!e, "no memory");
RpcMutexUniqueLock _l(e->mLock);
- return e->mObjects.attach(objectID, object, cleanupCookie, func);
+ return e->mObjectMgr.attach(objectID, object, cleanupCookie, func);
}
void* BBinder::findObject(const void* objectID) const
@@ -477,7 +479,7 @@
if (!e) return nullptr;
RpcMutexUniqueLock _l(e->mLock);
- return e->mObjects.find(objectID);
+ return e->mObjectMgr.find(objectID);
}
void* BBinder::detachObject(const void* objectID) {
@@ -485,7 +487,7 @@
if (!e) return nullptr;
RpcMutexUniqueLock _l(e->mLock);
- return e->mObjects.detach(objectID);
+ return e->mObjectMgr.detach(objectID);
}
void BBinder::withLock(const std::function<void()>& doWithLock) {
@@ -501,7 +503,7 @@
Extras* e = getOrCreateExtras();
LOG_ALWAYS_FATAL_IF(!e, "no memory");
RpcMutexUniqueLock _l(e->mLock);
- return e->mObjects.lookupOrCreateWeak(objectID, make, makeArgs);
+ return e->mObjectMgr.lookupOrCreateWeak(objectID, make, makeArgs);
}
BBinder* BBinder::localBinder()
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index 3758b65..c13e0f9 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -28,6 +28,7 @@
#include <stdio.h>
#include "BuildFlags.h"
+#include "Constants.h"
#include "file.h"
//#undef ALOGV
@@ -63,9 +64,6 @@
static constexpr uint32_t kBinderProxyCountWarnInterval = 5000;
-// Log any transactions for which the data exceeds this size
-#define LOG_TRANSACTIONS_OVER_SIZE (300 * 1024)
-
enum {
LIMIT_REACHED_MASK = 0x80000000, // A flag denoting that the limit has been reached
WARNING_REACHED_MASK = 0x40000000, // A flag denoting that the warning has been reached
@@ -78,7 +76,16 @@
BpBinder::ObjectManager::~ObjectManager()
{
- kill();
+ const size_t N = mObjects.size();
+ ALOGV("Killing %zu objects in manager %p", N, this);
+ for (auto i : mObjects) {
+ const entry_t& e = i.second;
+ if (e.func != nullptr) {
+ e.func(i.first, e.object, e.cleanupCookie);
+ }
+ }
+
+ mObjects.clear();
}
void* BpBinder::ObjectManager::attach(const void* objectID, void* object, void* cleanupCookie,
@@ -144,20 +151,6 @@
return newObj;
}
-void BpBinder::ObjectManager::kill()
-{
- const size_t N = mObjects.size();
- ALOGV("Killing %zu objects in manager %p", N, this);
- for (auto i : mObjects) {
- const entry_t& e = i.second;
- if (e.func != nullptr) {
- e.func(i.first, e.object, e.cleanupCookie);
- }
- }
-
- mObjects.clear();
-}
-
// ---------------------------------------------------------------------------
sp<BpBinder> BpBinder::create(int32_t handle, std::function<void()>* postTask) {
@@ -408,9 +401,11 @@
status = IPCThreadState::self()->transact(binderHandle(), code, data, reply, flags);
}
- if (data.dataSize() > LOG_TRANSACTIONS_OVER_SIZE) {
+
+ if (data.dataSize() > binder::kLogTransactionsOverBytes) {
RpcMutexUniqueLock _l(mLock);
- ALOGW("Large outgoing transaction of %zu bytes, interface descriptor %s, code %d",
+ ALOGW("Large outgoing transaction of %zu bytes, interface descriptor %s, code %d was "
+ "sent",
data.dataSize(), String8(mDescriptorCache).c_str(), code);
}
@@ -697,19 +692,19 @@
void* BpBinder::attachObject(const void* objectID, void* object, void* cleanupCookie,
object_cleanup_func func) {
RpcMutexUniqueLock _l(mLock);
- ALOGV("Attaching object %p to binder %p (manager=%p)", object, this, &mObjects);
- return mObjects.attach(objectID, object, cleanupCookie, func);
+ ALOGV("Attaching object %p to binder %p (manager=%p)", object, this, &mObjectMgr);
+ return mObjectMgr.attach(objectID, object, cleanupCookie, func);
}
void* BpBinder::findObject(const void* objectID) const
{
RpcMutexUniqueLock _l(mLock);
- return mObjects.find(objectID);
+ return mObjectMgr.find(objectID);
}
void* BpBinder::detachObject(const void* objectID) {
RpcMutexUniqueLock _l(mLock);
- return mObjects.detach(objectID);
+ return mObjectMgr.detach(objectID);
}
void BpBinder::withLock(const std::function<void()>& doWithLock) {
@@ -720,7 +715,7 @@
sp<IBinder> BpBinder::lookupOrCreateWeak(const void* objectID, object_make_func make,
const void* makeArgs) {
RpcMutexUniqueLock _l(mLock);
- return mObjects.lookupOrCreateWeak(objectID, make, makeArgs);
+ return mObjectMgr.lookupOrCreateWeak(objectID, make, makeArgs);
}
BpBinder* BpBinder::remoteBinder()
diff --git a/libs/binder/Constants.h b/libs/binder/Constants.h
new file mode 100644
index 0000000..b75493c
--- /dev/null
+++ b/libs/binder/Constants.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+namespace android::binder {
+
+/**
+ * See also BINDER_VM_SIZE. In kernel binder, the sum of all transactions must be allocated in this
+ * space. Large transactions are very error prone. In general, we should work to reduce this limit.
+ * The same limit is used in RPC binder for consistency.
+ */
+constexpr size_t kLogTransactionsOverBytes = 300 * 1024;
+
+/**
+ * See b/392575419 - this limit is chosen for a specific usecase, because RPC binder does not have
+ * support for shared memory in the Android Baklava timeframe. This was 100 KB during and before
+ * Android V.
+ *
+ * Keeping this low helps preserve overall system performance. Transactions of this size are far too
+ * expensive to make multiple copies over binder or sockets, and they should be avoided if at all
+ * possible and transition to shared memory.
+ */
+constexpr size_t kRpcTransactionLimitBytes = 600 * 1024;
+
+} // namespace android::binder
diff --git a/libs/binder/IActivityManager.cpp b/libs/binder/IActivityManager.cpp
index 152c815..83f4719 100644
--- a/libs/binder/IActivityManager.cpp
+++ b/libs/binder/IActivityManager.cpp
@@ -147,9 +147,11 @@
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 @@
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 @@
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 @@
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 @@
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 6698d0c..1c1b6f3 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -38,6 +38,10 @@
#include "Utils.h"
#include "binder_module.h"
+#if (defined(__ANDROID__) || defined(__Fuchsia__)) && !defined(BINDER_WITH_KERNEL_IPC)
+#error Android and Fuchsia are expected to have BINDER_WITH_KERNEL_IPC
+#endif
+
#if LOG_NDEBUG
#define IF_LOG_TRANSACTIONS() if (false)
@@ -626,12 +630,22 @@
{
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()");
@@ -775,6 +789,7 @@
{
LOG_THREADPOOL("**** THREAD %p (PID %d) IS JOINING THE THREAD POOL\n", (void*)pthread_self(),
getpid());
+ mProcess->checkExpectingThreadPoolStart();
mProcess->mCurrentThreads++;
mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);
@@ -802,7 +817,11 @@
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 "
@@ -839,7 +858,7 @@
void IPCThreadState::stopProcess(bool /*immediate*/)
{
//ALOGI("**** STOPPING PROCESS");
- flushCommands();
+ (void)flushCommands();
int fd = mProcess->mDriverFD;
mProcess->mDriverFD = -1;
close(fd);
@@ -1214,7 +1233,7 @@
std::string message = logStream.str();
ALOGI("%s", message.c_str());
}
-#if defined(__ANDROID__)
+#if defined(BINDER_WITH_KERNEL_IPC)
if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
err = NO_ERROR;
else
@@ -1493,7 +1512,14 @@
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;
@@ -1603,7 +1629,7 @@
IPCThreadState* const self = static_cast<IPCThreadState*>(st);
if (self) {
self->flushCommands();
-#if defined(__ANDROID__)
+#if defined(BINDER_WITH_KERNEL_IPC)
if (self->mProcess->mDriverFD >= 0) {
ioctl(self->mProcess->mDriverFD, BINDER_THREAD_EXIT, 0);
}
@@ -1619,7 +1645,7 @@
binder_frozen_status_info info = {};
info.pid = pid;
-#if defined(__ANDROID__)
+#if defined(BINDER_WITH_KERNEL_IPC)
if (ioctl(self()->mProcess->mDriverFD, BINDER_GET_FROZEN_INFO, &info) < 0)
ret = -errno;
#endif
@@ -1638,7 +1664,7 @@
info.timeout_ms = timeout_ms;
-#if defined(__ANDROID__)
+#if defined(BINDER_WITH_KERNEL_IPC)
if (ioctl(self()->mProcess->mDriverFD, BINDER_FREEZE, &info) < 0)
ret = -errno;
#endif
@@ -1656,7 +1682,7 @@
if (!ProcessState::isDriverFeatureEnabled(ProcessState::DriverFeature::EXTENDED_ERROR))
return;
-#if defined(__ANDROID__)
+#if defined(BINDER_WITH_KERNEL_IPC)
if (ioctl(self()->mProcess->mDriverFD, BINDER_GET_EXTENDED_ERROR, &ee) < 0) {
ALOGE("Failed to get extended error: %s", strerror(errno));
return;
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index 53435c3..c9ca646 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -43,7 +43,11 @@
#include <binder/IPermissionController.h>
#endif
-#ifdef __ANDROID__
+#if !(defined(__ANDROID__) || defined(__FUCHSIA))
+#define BINDER_SERVICEMANAGEMENT_DELEGATION_SUPPORT
+#endif
+
+#if !defined(BINDER_SERVICEMANAGEMENT_DELEGATION_SUPPORT)
#include <cutils/properties.h>
#else
#include "ServiceManagerHost.h"
@@ -304,6 +308,25 @@
return android::binder::Status::ok();
}
+void appendInjectedAccessorServices(std::vector<std::string>* list) {
+ LOG_ALWAYS_FATAL_IF(list == nullptr,
+ "Attempted to get list of services from Accessors with nullptr");
+ std::lock_guard<std::mutex> lock(gAccessorProvidersMutex);
+ for (const auto& entry : gAccessorProviders) {
+ list->insert(list->end(), entry.mProvider->instances().begin(),
+ entry.mProvider->instances().end());
+ }
+}
+
+void forEachInjectedAccessorService(const std::function<void(const std::string&)>& f) {
+ std::lock_guard<std::mutex> lock(gAccessorProvidersMutex);
+ for (const auto& entry : gAccessorProviders) {
+ for (const auto& instance : entry.mProvider->instances()) {
+ f(instance);
+ }
+ }
+}
+
sp<IServiceManager> defaultServiceManager()
{
std::call_once(gSmOnce, []() {
@@ -605,7 +628,7 @@
sp<IBinder> CppBackendShim::checkService(const String16& name) const {
Service ret;
- if (!mUnifiedServiceManager->checkService(String8(name).c_str(), &ret).isOk()) {
+ if (!mUnifiedServiceManager->checkService2(String8(name).c_str(), &ret).isOk()) {
return nullptr;
}
return ret.get<Service::Tag::serviceWithMetadata>().service;
@@ -883,7 +906,7 @@
return ret;
}
-#ifndef __ANDROID__
+#if defined(BINDER_SERVICEMANAGEMENT_DELEGATION_SUPPORT)
// CppBackendShim for host. Implements the old libbinder android::IServiceManager API.
// The internal implementation of the AIDL interface android::os::IServiceManager calls into
// on-device service manager.
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index a5f416f..2c37624 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -156,7 +156,7 @@
#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 @@
return;
}
case BINDER_TYPE_FD: {
- if (obj.cookie != 0) { // owned
+ if (tagFds && obj.cookie != 0) { // owned
FdTag(obj.handle, nullptr, who);
}
return;
@@ -299,8 +299,13 @@
obj.handle = handle;
obj.cookie = 0;
} else {
+#if __linux__
int policy = local->getMinSchedulerPolicy();
int priority = local->getMinSchedulerPriority();
+#else
+ int policy = 0;
+ int priority = 0;
+#endif
if (policy != 0 || priority != 0) {
// override value, since it is set explicitly
@@ -537,7 +542,7 @@
return BAD_VALUE;
}
- if ((mDataSize+len) > mDataCapacity) {
+ if ((mDataPos + len) > mDataCapacity) {
// grow data
err = growData(len);
if (err != NO_ERROR) {
@@ -611,11 +616,12 @@
}
}
- acquire_object(proc, *flat, this);
+ acquire_object(proc, *flat, this, true /*tagFds*/);
}
}
#else
LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ (void)kernelFields;
return INVALID_OPERATION;
#endif // BINDER_WITH_KERNEL_IPC
} else {
@@ -797,6 +803,7 @@
setDataPosition(initPosition);
#else
LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ (void)kernelFields;
#endif
} else if (const auto* rpcFields = maybeRpcFields(); rpcFields && rpcFields->mFds) {
for (const auto& fd : *rpcFields->mFds) {
@@ -839,9 +846,10 @@
}
#else
LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ (void)kernelFields;
return INVALID_OPERATION;
#endif // BINDER_WITH_KERNEL_IPC
- } else if (const auto* rpcFields = maybeRpcFields()) {
+ } else if (maybeRpcFields()) {
return INVALID_OPERATION;
}
return NO_ERROR;
@@ -879,6 +887,7 @@
}
#else
LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ (void)kernelFields;
return INVALID_OPERATION;
#endif // BINDER_WITH_KERNEL_IPC
} else if (const auto* rpcFields = maybeRpcFields()) {
@@ -971,6 +980,7 @@
writeInt32(kHeader);
#else // BINDER_WITH_KERNEL_IPC
LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ (void)kernelFields;
return INVALID_OPERATION;
#endif // BINDER_WITH_KERNEL_IPC
}
@@ -1061,6 +1071,7 @@
#else // BINDER_WITH_KERNEL_IPC
LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
(void)threadState;
+ (void)kernelFields;
return false;
#endif // BINDER_WITH_KERNEL_IPC
}
@@ -1293,10 +1304,6 @@
status_t Parcel::writeUniqueFileDescriptorVector(const std::optional<std::vector<unique_fd>>& val) {
return writeData(val);
}
-status_t Parcel::writeUniqueFileDescriptorVector(
- const std::unique_ptr<std::vector<unique_fd>>& val) {
- return writeData(val);
-}
status_t Parcel::writeStrongBinderVector(const std::vector<sp<IBinder>>& val) { return writeData(val); }
status_t Parcel::writeStrongBinderVector(const std::optional<std::vector<sp<IBinder>>>& val) { return writeData(val); }
@@ -1352,10 +1359,6 @@
status_t Parcel::readUniqueFileDescriptorVector(std::optional<std::vector<unique_fd>>* val) const {
return readData(val);
}
-status_t Parcel::readUniqueFileDescriptorVector(
- std::unique_ptr<std::vector<unique_fd>>* val) const {
- return readData(val);
-}
status_t Parcel::readUniqueFileDescriptorVector(std::vector<unique_fd>* val) const {
return readData(val);
}
@@ -1797,13 +1800,22 @@
// 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;
@@ -2687,6 +2699,7 @@
#else // BINDER_WITH_KERNEL_IPC
(void)newObjectsSize;
LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ (void)kernelFields;
#endif // BINDER_WITH_KERNEL_IPC
} else if (auto* rpcFields = maybeRpcFields()) {
rpcFields->mFds.reset();
@@ -2719,6 +2732,65 @@
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
@@ -2729,6 +2801,7 @@
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);
@@ -2807,6 +2880,7 @@
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;
@@ -2874,15 +2948,17 @@
#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;
}
@@ -2892,8 +2968,10 @@
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
}
@@ -3110,12 +3188,8 @@
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) {
@@ -3327,14 +3401,6 @@
}
#ifdef BINDER_WITH_KERNEL_IPC
-size_t Parcel::getBlobAshmemSize() const
-{
- // This used to return the size of all blobs that were written to ashmem, now we're returning
- // the ashmem currently referenced by this Parcel, which should be equivalent.
- // TODO(b/202029388): Remove method once ABI can be changed.
- return getOpenAshmemSize();
-}
-
size_t Parcel::getOpenAshmemSize() const
{
auto* kernelFields = maybeKernelFields();
diff --git a/libs/binder/PersistableBundle.cpp b/libs/binder/PersistableBundle.cpp
index abb6612..99f9726 100644
--- a/libs/binder/PersistableBundle.cpp
+++ b/libs/binder/PersistableBundle.cpp
@@ -119,6 +119,9 @@
}
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 @@
}
}
}
+ // result intentional ignored since it will always be false;
+ RETURN_IF_FAILED(parcel->readBool());
return NO_ERROR;
}
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 5e7f151..0bec379 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -48,6 +48,10 @@
#define DEFAULT_MAX_BINDER_THREADS 15
#define DEFAULT_ENABLE_ONEWAY_SPAM_DETECTION 1
+#if defined(__ANDROID__) || defined(__Fuchsia__)
+#define EXPECT_BINDER_OPEN_SUCCESS
+#endif
+
#ifdef __ANDROID_VNDK__
const char* kDefaultDriver = "/dev/vndbinder";
#else
@@ -501,6 +505,21 @@
return mThreadPoolStarted;
}
+void ProcessState::checkExpectingThreadPoolStart() const {
+ if (mThreadPoolStarted) return;
+
+ // this is also racey, but you should setup the threadpool in the main thread. If that is an
+ // issue, we can check if we are the process leader, but haven't seen the issue in practice.
+ size_t requestedThreads = mMaxThreads.load();
+
+ // if it's manually set to the default, we do ignore it here...
+ if (requestedThreads == DEFAULT_MAX_BINDER_THREADS) return;
+ if (requestedThreads == 0) return;
+
+ ALOGW("Thread pool configuration of size %zu requested, but startThreadPool was not called.",
+ requestedThreads);
+}
+
#define DRIVER_FEATURES_PATH "/dev/binderfs/features/"
bool ProcessState::isDriverFeatureEnabled(const DriverFeature feature) {
// Use static variable to cache the results.
@@ -598,7 +617,7 @@
}
}
-#ifdef __ANDROID__
+#if defined(EXPECT_BINDER_OPEN_SUCCESS)
LOG_ALWAYS_FATAL_IF(!opened.ok(),
"Binder driver '%s' could not be opened. Error: %s. Terminating.",
driver, error.c_str());
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
index 16023ff..1f3a45a 100644
--- a/libs/binder/RpcSession.cpp
+++ b/libs/binder/RpcSession.cpp
@@ -188,7 +188,9 @@
}
status_t RpcSession::setupPreconnectedClient(unique_fd fd, std::function<unique_fd()>&& request) {
- return setupClient([&](const std::vector<uint8_t>& sessionId, bool incoming) -> status_t {
+ return setupClient([&, fd = std::move(fd),
+ request = std::move(request)](const std::vector<uint8_t>& sessionId,
+ bool incoming) mutable -> status_t {
if (!fd.ok()) {
fd = request();
if (!fd.ok()) return BAD_VALUE;
@@ -476,8 +478,10 @@
return server;
}
-status_t RpcSession::setupClient(const std::function<status_t(const std::vector<uint8_t>& sessionId,
- bool incoming)>& connectAndInit) {
+template <typename Fn,
+ typename /* = std::enable_if_t<std::is_invocable_r_v<
+ status_t, Fn, const std::vector<uint8_t>&, bool>> */>
+status_t RpcSession::setupClient(Fn&& connectAndInit) {
{
RpcMutexLockGuard _l(mMutex);
LOG_ALWAYS_FATAL_IF(mStartedSetup, "Must only setup session once");
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index fe6e1a3..03d974d 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -23,6 +23,7 @@
#include <binder/IPCThreadState.h>
#include <binder/RpcServer.h>
+#include "Constants.h"
#include "Debug.h"
#include "RpcWireFormat.h"
#include "Utils.h"
@@ -337,6 +338,8 @@
}
RpcState::CommandData::CommandData(size_t size) : mSize(size) {
+ if (size == 0) return;
+
// The maximum size for regular binder is 1MB for all concurrent
// transactions. A very small proportion of transactions are even
// larger than a page, but we need to avoid allocating too much
@@ -348,11 +351,11 @@
// transaction (in some cases, additional fixed size amounts are added),
// though for rough consistency, we should avoid cases where this data type
// is used for multiple dynamic allocations for a single transaction.
- constexpr size_t kMaxTransactionAllocation = 100 * 1000;
- if (size == 0) return;
- if (size > kMaxTransactionAllocation) {
- ALOGW("Transaction requested too much data allocation %zu", size);
+ if (size > binder::kRpcTransactionLimitBytes) {
+ ALOGE("Transaction requested too much data allocation: %zu bytes, failing.", size);
return;
+ } else if (size > binder::kLogTransactionsOverBytes) {
+ ALOGW("Transaction too large: inefficient and in danger of breaking: %zu bytes.", size);
}
mData.reset(new (std::nothrow) uint8_t[size]);
}
diff --git a/libs/binder/RpcTransportTipcAndroid.cpp b/libs/binder/RpcTransportTipcAndroid.cpp
index 3819fb6..14c0bde 100644
--- a/libs/binder/RpcTransportTipcAndroid.cpp
+++ b/libs/binder/RpcTransportTipcAndroid.cpp
@@ -21,6 +21,7 @@
#include <log/log.h>
#include <poll.h>
#include <trusty/tipc.h>
+#include <type_traits>
#include "FdTrigger.h"
#include "RpcState.h"
@@ -32,6 +33,9 @@
namespace android {
+// Corresponds to IPC_MAX_MSG_HANDLES in the Trusty kernel
+constexpr size_t kMaxTipcHandles = 8;
+
// RpcTransport for writing Trusty IPC clients in Android.
class RpcTransportTipcAndroid : public RpcTransport {
public:
@@ -78,12 +82,28 @@
FdTrigger* fdTrigger, iovec* iovs, int niovs,
const std::optional<SmallFunction<status_t()>>& altPoll,
const std::vector<std::variant<unique_fd, borrowed_fd>>* ancillaryFds) override {
+ bool sentFds = false;
auto writeFn = [&](iovec* iovs, size_t niovs) -> ssize_t {
- // TODO: send ancillaryFds. For now, we just abort if anyone tries
- // to send any.
- LOG_ALWAYS_FATAL_IF(ancillaryFds != nullptr && !ancillaryFds->empty(),
- "File descriptors are not supported on Trusty yet");
- return TEMP_FAILURE_RETRY(tipc_send(mSocket.fd.get(), iovs, niovs, nullptr, 0));
+ trusty_shm shms[kMaxTipcHandles] = {{0}};
+ ssize_t shm_count = 0;
+
+ if (!sentFds && ancillaryFds != nullptr && !ancillaryFds->empty()) {
+ if (ancillaryFds->size() > kMaxTipcHandles) {
+ ALOGE("Too many file descriptors for TIPC: %zu", ancillaryFds->size());
+ errno = EINVAL;
+ return -1;
+ }
+ for (const auto& fdVariant : *ancillaryFds) {
+ shms[shm_count++] = {std::visit([](const auto& fd) { return fd.get(); },
+ fdVariant),
+ TRUSTY_SEND_SECURE_OR_SHARE};
+ }
+ }
+
+ auto ret = TEMP_FAILURE_RETRY(tipc_send(mSocket.fd.get(), iovs, niovs,
+ (shm_count == 0) ? nullptr : shms, shm_count));
+ sentFds |= ret >= 0;
+ return ret;
};
status_t status = interruptableReadOrWrite(mSocket, fdTrigger, iovs, niovs, writeFn,
diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING
index 9e5e79f..4332f8a 100644
--- a/libs/binder/TEST_MAPPING
+++ b/libs/binder/TEST_MAPPING
@@ -37,6 +37,9 @@
"name": "binderStabilityTest"
},
{
+ "name": "binderStabilityIntegrationTest"
+ },
+ {
"name": "binderRpcWireProtocolTest"
},
{
diff --git a/libs/binder/aidl/android/content/pm/OWNERS b/libs/binder/aidl/android/content/pm/OWNERS
index 3100518..2617a16 100644
--- a/libs/binder/aidl/android/content/pm/OWNERS
+++ b/libs/binder/aidl/android/content/pm/OWNERS
@@ -1,5 +1,4 @@
+michaelwr@google.com
narayan@google.com
patb@google.com
-svetoslavganov@google.com
-toddke@google.com
-patb@google.com
\ No newline at end of file
+schfan@google.com
diff --git a/libs/binder/aidl/android/os/IServiceManager.aidl b/libs/binder/aidl/android/os/IServiceManager.aidl
index 69edef8..6539238 100644
--- a/libs/binder/aidl/android/os/IServiceManager.aidl
+++ b/libs/binder/aidl/android/os/IServiceManager.aidl
@@ -83,11 +83,20 @@
/**
* Retrieve an existing service called @a name from the service
+ * manager. Non-blocking. Returns null if the service does not exist.
+ *
+ * @deprecated TODO(b/355394904): Use checkService2 instead. This does not
+ * return metadata that is included in ServiceWithMetadata
+ */
+ @UnsupportedAppUsage
+ @nullable IBinder checkService(@utf8InCpp String name);
+
+ /**
+ * Retrieve an existing service called @a name from the service
* manager. Non-blocking. Returns null if the service does not
* exist.
*/
- @UnsupportedAppUsage
- Service checkService(@utf8InCpp String name);
+ Service checkService2(@utf8InCpp String name);
/**
* Place a new @a service called @a name into the service
diff --git a/libs/binder/binder_module.h b/libs/binder/binder_module.h
index 65cdcd7..b3a2d9e 100644
--- a/libs/binder/binder_module.h
+++ b/libs/binder/binder_module.h
@@ -32,34 +32,4 @@
#include <linux/android/binder.h>
#include <sys/ioctl.h>
-struct binder_frozen_state_info {
- binder_uintptr_t cookie;
- __u32 is_frozen;
-};
-
-#ifndef BR_FROZEN_BINDER
-// Temporary definition of BR_FROZEN_BINDER until UAPI binder.h includes it.
-#define BR_FROZEN_BINDER _IOR('r', 21, struct binder_frozen_state_info)
-#endif // BR_FROZEN_BINDER
-
-#ifndef BR_CLEAR_FREEZE_NOTIFICATION_DONE
-// Temporary definition of BR_CLEAR_FREEZE_NOTIFICATION_DONE until UAPI binder.h includes it.
-#define BR_CLEAR_FREEZE_NOTIFICATION_DONE _IOR('r', 22, binder_uintptr_t)
-#endif // BR_CLEAR_FREEZE_NOTIFICATION_DONE
-
-#ifndef BC_REQUEST_FREEZE_NOTIFICATION
-// Temporary definition of BC_REQUEST_FREEZE_NOTIFICATION until UAPI binder.h includes it.
-#define BC_REQUEST_FREEZE_NOTIFICATION _IOW('c', 19, struct binder_handle_cookie)
-#endif // BC_REQUEST_FREEZE_NOTIFICATION
-
-#ifndef BC_CLEAR_FREEZE_NOTIFICATION
-// Temporary definition of BC_CLEAR_FREEZE_NOTIFICATION until UAPI binder.h includes it.
-#define BC_CLEAR_FREEZE_NOTIFICATION _IOW('c', 20, struct binder_handle_cookie)
-#endif // BC_CLEAR_FREEZE_NOTIFICATION
-
-#ifndef BC_FREEZE_NOTIFICATION_DONE
-// Temporary definition of BC_FREEZE_NOTIFICATION_DONE until UAPI binder.h includes it.
-#define BC_FREEZE_NOTIFICATION_DONE _IOW('c', 21, binder_uintptr_t)
-#endif // BC_FREEZE_NOTIFICATION_DONE
-
#endif // _BINDER_MODULE_H_
diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h
index 7518044..935bd8d 100644
--- a/libs/binder/include/binder/BpBinder.h
+++ b/libs/binder/include/binder/BpBinder.h
@@ -104,6 +104,7 @@
// Stop the current recording.
LIBBINDER_EXPORTED status_t stopRecordingBinder();
+ // Note: This class is not thread safe so protect uses of it when necessary
class ObjectManager {
public:
ObjectManager();
@@ -116,8 +117,6 @@
sp<IBinder> lookupOrCreateWeak(const void* objectID, IBinder::object_make_func make,
const void* makeArgs);
- void kill();
-
private:
ObjectManager(const ObjectManager&);
ObjectManager& operator=(const ObjectManager&);
@@ -224,7 +223,7 @@
volatile int32_t mObitsSent;
Vector<Obituary>* mObituaries;
std::unique_ptr<FrozenStateChange> mFrozen;
- ObjectManager mObjects;
+ ObjectManager mObjectMgr;
mutable String16 mDescriptorCache;
int32_t mTrackedUid;
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
index 5924a2d..993ad82 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -219,7 +219,6 @@
constexpr const char* const kManualInterfaces[] = {
"android.app.IActivityManager",
"android.app.IUidObserver",
- "android.drm.IDrm",
"android.gfx.tests.ICallback",
"android.gfx.tests.IIPCTest",
"android.gfx.tests.ISafeInterfaceTest",
@@ -233,22 +232,17 @@
"android.hardware.ICameraClient",
"android.hardware.ICameraRecordingProxy",
"android.hardware.ICameraRecordingProxyListener",
- "android.hardware.ICrypto",
"android.hardware.IOMXObserver",
"android.hardware.IStreamListener",
"android.hardware.IStreamSource",
"android.media.IAudioService",
"android.media.IDataSource",
- "android.media.IDrmClient",
"android.media.IMediaCodecList",
- "android.media.IMediaDrmService",
"android.media.IMediaExtractor",
- "android.media.IMediaExtractorService",
"android.media.IMediaHTTPConnection",
"android.media.IMediaHTTPService",
"android.media.IMediaLogService",
"android.media.IMediaMetadataRetriever",
- "android.media.IMediaMetricsService",
"android.media.IMediaPlayer",
"android.media.IMediaPlayerClient",
"android.media.IMediaPlayerService",
@@ -258,9 +252,6 @@
"android.media.IMediaSource",
"android.media.IRemoteDisplay",
"android.media.IRemoteDisplayClient",
- "android.media.IResourceManagerClient",
- "android.media.IResourceManagerService",
- "android.os.IComplexTypeInterface",
"android.os.IPermissionController",
"android.os.IProcessInfoService",
"android.os.ISchedulingPolicyService",
@@ -272,7 +263,6 @@
"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 9ef4e69..f7465e2 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -64,7 +64,10 @@
* 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/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h
index 7d79baa..d248f22 100644
--- a/libs/binder/include/binder/IServiceManager.h
+++ b/libs/binder/include/binder/IServiceManager.h
@@ -80,6 +80,14 @@
/**
* Register a service.
+ *
+ * Note:
+ * This status_t return value may be an exception code from an underlying
+ * Status type that doesn't have a representive error code in
+ * utils/Errors.h.
+ * One example of this is a return value of -7
+ * (Status::Exception::EX_UNSUPPORTED_OPERATION) when the service manager
+ * process is not installed on the device when addService is called.
*/
// NOLINTNEXTLINE(google-default-arguments)
virtual status_t addService(const String16& name, const sp<IBinder>& service,
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index 0c7366e..6c4c6fe 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -383,11 +383,13 @@
LIBBINDER_EXPORTED status_t
writeUniqueFileDescriptorVector(const std::optional<std::vector<binder::unique_fd>>& val);
LIBBINDER_EXPORTED status_t
- writeUniqueFileDescriptorVector(const std::unique_ptr<std::vector<binder::unique_fd>>& val)
- __attribute__((deprecated("use std::optional version instead")));
- LIBBINDER_EXPORTED status_t
writeUniqueFileDescriptorVector(const std::vector<binder::unique_fd>& val);
+ // WARNING: deprecated and incompatible with AIDL. You should use Parcelable
+ // definitions outside of Parcel to represent shared memory, such as
+ // IMemory or with ParcelFileDescriptor. We should remove this, or move it to be
+ // external to Parcel, it's not a very encapsulated API.
+ //
// Writes a blob to the parcel.
// If the blob is small, then it is stored in-place, otherwise it is
// transferred by way of an anonymous shared memory region. Prefer sending
@@ -401,8 +403,6 @@
// as long as it keeps a dup of the blob file descriptor handy for later.
LIBBINDER_EXPORTED status_t writeDupImmutableBlobFileDescriptor(int fd);
- LIBBINDER_EXPORTED status_t writeObject(const flat_binder_object& val, bool nullMetaData);
-
// Like Parcel.java's writeNoException(). Just writes a zero int32.
// Currently the native implementation doesn't do any of the StrictMode
// stack gathering and serialization that the Java implementation does.
@@ -627,11 +627,13 @@
LIBBINDER_EXPORTED status_t
readUniqueFileDescriptorVector(std::optional<std::vector<binder::unique_fd>>* val) const;
LIBBINDER_EXPORTED status_t
- readUniqueFileDescriptorVector(std::unique_ptr<std::vector<binder::unique_fd>>* val) const
- __attribute__((deprecated("use std::optional version instead")));
- LIBBINDER_EXPORTED status_t
readUniqueFileDescriptorVector(std::vector<binder::unique_fd>* val) const;
+ // WARNING: deprecated and incompatible with AIDL. You should use Parcelable
+ // definitions outside of Parcel to represent shared memory, such as
+ // IMemory or with ParcelFileDescriptor. We should remove this, or move it to be
+ // external to Parcel, it's not a very encapsulated API.
+ //
// Reads a blob from the parcel.
// The caller should call release() on the blob after reading its contents.
LIBBINDER_EXPORTED status_t readBlob(size_t len, ReadableBlob* outBlob) const;
@@ -649,6 +651,11 @@
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);
@@ -664,7 +671,7 @@
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,
@@ -672,7 +679,7 @@
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.
@@ -680,6 +687,7 @@
// Set the capacity to `desired`, truncating the Parcel if necessary.
status_t continueWrite(size_t desired);
status_t truncateRpcObjects(size_t newObjectsSize);
+ status_t writeObject(const flat_binder_object& val, bool nullMetaData);
status_t writePointer(uintptr_t val);
status_t readPointer(uintptr_t *pArg) const;
uintptr_t readPointer() const;
@@ -1480,14 +1488,15 @@
* Note: for historical reasons, this does not include ashmem memory which
* is referenced by this Parcel, but which this parcel doesn't own (e.g.
* writeFileDescriptor is called without 'takeOwnership' true).
+ *
+ * WARNING: you should not use this, but rather, unparcel, and inspect
+ * each FD independently. This counts ashmem size, but there may be
+ * other resources used for non-ashmem FDs, such as other types of
+ * shared memory, files, etc..
*/
LIBBINDER_EXPORTED size_t getOpenAshmemSize() const;
private:
- // TODO(b/202029388): Remove 'getBlobAshmemSize' once no prebuilts reference
- // this
- LIBBINDER_EXPORTED size_t getBlobAshmemSize() const;
-
// Needed so that we can save object metadata to the disk
friend class android::binder::debug::RecordedTransaction;
};
diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h
index 21bfd42..ced49c1 100644
--- a/libs/binder/include/binder/ProcessState.h
+++ b/libs/binder/include/binder/ProcessState.h
@@ -141,6 +141,8 @@
private:
static sp<ProcessState> init(const char* defaultDriver, bool requireDefault);
+ void checkExpectingThreadPoolStart() const;
+
static void onFork();
static void parentPostFork();
static void childPostFork();
diff --git a/libs/binder/include/binder/RpcSession.h b/libs/binder/include/binder/RpcSession.h
index af37bf2..c9f92da 100644
--- a/libs/binder/include/binder/RpcSession.h
+++ b/libs/binder/include/binder/RpcSession.h
@@ -25,6 +25,7 @@
#include <map>
#include <optional>
+#include <type_traits>
#include <vector>
namespace android {
@@ -291,9 +292,14 @@
// join on thread passed to preJoinThreadOwnership
static void join(sp<RpcSession>&& session, PreJoinSetupResult&& result);
- [[nodiscard]] status_t setupClient(
- const std::function<status_t(const std::vector<uint8_t>& sessionId, bool incoming)>&
- connectAndInit);
+ // This is a workaround to support move-only functors.
+ // TODO: use std::move_only_function when it becomes available.
+ template <typename Fn,
+ // Fn must be a callable type taking (const std::vector<uint8_t>&, bool) and returning
+ // status_t
+ typename = std::enable_if_t<
+ std::is_invocable_r_v<status_t, Fn, const std::vector<uint8_t>&, bool>>>
+ [[nodiscard]] status_t setupClient(Fn&& connectAndInit);
[[nodiscard]] status_t setupSocketClient(const RpcSocketAddress& address);
[[nodiscard]] status_t setupOneSocketConnection(const RpcSocketAddress& address,
const std::vector<uint8_t>& sessionId,
diff --git a/libs/binder/include/binder/RpcThreads.h b/libs/binder/include/binder/RpcThreads.h
index 99fa6b8..51b9716b 100644
--- a/libs/binder/include/binder/RpcThreads.h
+++ b/libs/binder/include/binder/RpcThreads.h
@@ -20,6 +20,7 @@
#include <condition_variable>
#include <functional>
#include <memory>
+#include <mutex>
#include <thread>
#include <binder/Common.h>
diff --git a/libs/binder/include/binder/SafeInterface.h b/libs/binder/include/binder/SafeInterface.h
index 0b4f196..e848385 100644
--- a/libs/binder/include/binder/SafeInterface.h
+++ b/libs/binder/include/binder/SafeInterface.h
@@ -34,6 +34,13 @@
namespace android {
namespace SafeInterface {
+/**
+ * WARNING: Prefer to use AIDL-generated interfaces. Using SafeInterface to generate interfaces
+ * does not support tracing, and many other AIDL features out of the box. The general direction
+ * we should go is to migrate safe interface users to AIDL and then remove this so that there
+ * is only one thing to learn/use/test/integrate, not this as well.
+ */
+
// ParcelHandler is responsible for writing/reading various types to/from a Parcel in a generic way
class LIBBINDER_EXPORTED ParcelHandler {
public:
@@ -72,7 +79,7 @@
template <typename T>
typename std::enable_if<std::is_base_of<Flattenable<T>, T>::value, status_t>::type read(
const Parcel& parcel, sp<T>* t) const {
- *t = new T{};
+ *t = sp<T>::make();
return callParcel("read(sp<Flattenable>)", [&]() { return parcel.read(*(t->get())); });
}
template <typename T>
diff --git a/libs/binder/include/binder/Stability.h b/libs/binder/include/binder/Stability.h
index cafb8aa..bfe0a5a 100644
--- a/libs/binder/include/binder/Stability.h
+++ b/libs/binder/include/binder/Stability.h
@@ -20,6 +20,8 @@
#include <binder/IBinder.h>
#include <string>
+class BinderStabilityIntegrationTest_ExpectedStabilityForItsPartition_Test;
+
namespace android {
class BpBinder;
@@ -127,6 +129,8 @@
// through Parcel)
friend ::android::ProcessState;
+ friend ::BinderStabilityIntegrationTest_ExpectedStabilityForItsPartition_Test;
+
static void tryMarkCompilationUnit(IBinder* binder);
// Currently, we use int16_t for Level so that it can fit in BBinder.
@@ -156,11 +160,11 @@
uint32_t flags);
// get stability information as encoded on the wire
- static int16_t getRepr(IBinder* binder);
+ LIBBINDER_EXPORTED static int16_t getRepr(IBinder* binder);
// whether a transaction on binder is allowed, if the transaction
// is done from a context with a specific stability level
- static bool check(int16_t provided, Level required);
+ LIBBINDER_EXPORTED static bool check(int16_t provided, Level required);
static bool isDeclaredLevel(int32_t level);
static std::string levelString(int32_t level);
diff --git a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
index 48c0ea6..1949cbb 100644
--- a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
+++ b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
@@ -134,9 +134,14 @@
//
// param will be passed to requestFd. Callers can use param to pass contexts to
// the requestFd function.
+//
+// paramDeleteFd will be called when requestFd and param are no longer in use.
+// Callers can pass a function deleting param if necessary. Callers can set
+// paramDeleteFd to null if callers doesn't need to clean up param.
AIBinder* ARpcSession_setupPreconnectedClient(ARpcSession* session,
int (*requestFd)(void* param),
- void* param);
+ void* param,
+ void (*paramDeleteFd)(void* param));
// Sets the file descriptor transport mode for this session.
void ARpcSession_setFileDescriptorTransportMode(ARpcSession* session,
diff --git a/libs/binder/libbinder_rpc_unstable.cpp b/libs/binder/libbinder_rpc_unstable.cpp
index a84a0c6..8177fb0 100644
--- a/libs/binder/libbinder_rpc_unstable.cpp
+++ b/libs/binder/libbinder_rpc_unstable.cpp
@@ -23,11 +23,8 @@
#ifndef __TRUSTY__
#include <cutils/sockets.h>
-#endif
-
-#ifdef __linux__
-#include <linux/vm_sockets.h>
-#endif // __linux__
+#include "vm_sockets.h"
+#endif // !__TRUSTY__
using android::OK;
using android::RpcServer;
@@ -251,9 +248,18 @@
#endif // __TRUSTY__
AIBinder* ARpcSession_setupPreconnectedClient(ARpcSession* handle, int (*requestFd)(void* param),
- void* param) {
+ void* param, void (*paramDeleteFd)(void* param)) {
auto session = handleToStrongPointer<RpcSession>(handle);
- auto request = [=] { return unique_fd{requestFd(param)}; };
+ auto deleter = [=](void* param) {
+ if (paramDeleteFd) {
+ paramDeleteFd(param);
+ }
+ };
+ // TODO: use unique_ptr once setupPreconnectedClient uses std::move_only_function.
+ std::shared_ptr<void> sharedParam(param, deleter);
+ auto request = [=, sharedParam = std::move(sharedParam)] {
+ return unique_fd{requestFd(sharedParam.get())};
+ };
if (status_t status = session->setupPreconnectedClient(unique_fd{}, request); status != OK) {
ALOGE("Failed to set up preconnected client. error: %s", statusToString(status).c_str());
return nullptr;
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
index bd46c47..d69d318 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 @@
* 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_ibinder_platform.h b/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h
index 89f21dd..783e11f 100644
--- a/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h
+++ b/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h
@@ -62,6 +62,8 @@
* This must be called before the object is sent to another process.
* Aborts on invalid values. Not thread safe.
*
+ * This overrides the setting in ABinderProcess_disableBackgroundScheduling.
+ *
* \param binder local server binder to set the policy for
* \param policy scheduler policy as defined in linux UAPI
* \param priority priority. [-20..19] for SCHED_NORMAL, [1..99] for RT
diff --git a/libs/binder/ndk/include_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h
index f5df8d5..2c2e2c8 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 @@
* not be added with this flag for privacy concerns.
*/
ADD_SERVICE_ALLOW_ISOLATED = 1 << 0,
+ /**
+ * Allows services to dump sections according to priorities and format
+ */
ADD_SERVICE_DUMP_FLAG_PRIORITY_CRITICAL = 1 << 1,
ADD_SERVICE_DUMP_FLAG_PRIORITY_HIGH = 1 << 2,
ADD_SERVICE_DUMP_FLAG_PRIORITY_NORMAL = 1 << 3,
ADD_SERVICE_DUMP_FLAG_PRIORITY_DEFAULT = 1 << 4,
+ ADD_SERVICE_DUMP_FLAG_PROTO = 1 << 5,
// All other bits are reserved for internal usage
};
diff --git a/libs/binder/ndk/include_platform/android/binder_process.h b/libs/binder/ndk/include_platform/android/binder_process.h
index 6aff994..2432099 100644
--- a/libs/binder/ndk/include_platform/android/binder_process.h
+++ b/libs/binder/ndk/include_platform/android/binder_process.h
@@ -75,6 +75,19 @@
void ABinderProcess_joinThreadPool(void);
/**
+ * Disables (or enables) background scheduling.
+ *
+ * By default, binder threads execute at a lower priority. However, this can cause
+ * priority inversion, so it is recommended to be disabled in high priority
+ * or in system processes.
+ *
+ * See also AIBinder_setMinSchedulerPolicy, which overrides this setting.
+ *
+ * \param disable whether to disable background scheduling
+ */
+void ABinderProcess_disableBackgroundScheduling(bool disable);
+
+/**
* This gives you an fd to wait on. Whenever data is available on the fd,
* ABinderProcess_handlePolledCommands can be called to handle binder queries.
* This is expected to be used in a single threaded process which waits on
diff --git a/libs/binder/ndk/include_platform/android/binder_stability.h b/libs/binder/ndk/include_platform/android/binder_stability.h
index 089c775..8050205 100644
--- a/libs/binder/ndk/include_platform/android/binder_stability.h
+++ b/libs/binder/ndk/include_platform/android/binder_stability.h
@@ -27,6 +27,10 @@
#if defined(__ANDROID_VENDOR__)
+#if defined(__ANDROID_PRODUCT__)
+#error "build bug: product is not part of the vendor half of the Treble system/vendor split"
+#endif
+
/**
* Private addition to binder_flag_t.
*/
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index a637165..d4eb8c7 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -229,6 +229,7 @@
AIBinder_fromPlatformBinder*;
AIBinder_toPlatformBinder*;
AParcel_viewPlatformParcel*;
+ ABinderProcess_disableBackgroundScheduling;
};
local:
*;
diff --git a/libs/binder/ndk/process.cpp b/libs/binder/ndk/process.cpp
index 0072ac3..bcdb959 100644
--- a/libs/binder/ndk/process.cpp
+++ b/libs/binder/ndk/process.cpp
@@ -36,6 +36,10 @@
IPCThreadState::self()->joinThreadPool();
}
+void ABinderProcess_disableBackgroundScheduling(bool disable) {
+ IPCThreadState::disableBackgroundScheduling(disable);
+}
+
binder_status_t ABinderProcess_setupPolling(int* fd) {
return IPCThreadState::self()->setupPolling(fd);
}
diff --git a/libs/binder/ndk/service_manager.cpp b/libs/binder/ndk/service_manager.cpp
index d6ac4ac..14bc5d2 100644
--- a/libs/binder/ndk/service_manager.cpp
+++ b/libs/binder/ndk/service_manager.cpp
@@ -63,6 +63,9 @@
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/ndk/tests/binderVendorDoubleLoadTest.cpp b/libs/binder/ndk/tests/binderVendorDoubleLoadTest.cpp
index 66be94f..fb92e05 100644
--- a/libs/binder/ndk/tests/binderVendorDoubleLoadTest.cpp
+++ b/libs/binder/ndk/tests/binderVendorDoubleLoadTest.cpp
@@ -30,6 +30,8 @@
#include <gtest/gtest.h>
#include <sys/prctl.h>
+static_assert(FLAG_PRIVATE_LOCAL != 0, "Build system configuration breaks stability");
+
using namespace android;
using ::android::binder::Status;
using ::android::internal::Stability;
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
index 8404a48..adef9ea 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -16,6 +16,7 @@
"libdowncast_rs",
"liblibc",
"liblog_rust",
+ "libzerocopy",
],
host_supported: true,
vendor_available: true,
@@ -205,6 +206,7 @@
"libdowncast_rs",
"liblibc",
"liblog_rust",
+ "libzerocopy",
],
}
diff --git a/libs/binder/rust/rpcbinder/Android.bp b/libs/binder/rust/rpcbinder/Android.bp
index 4036551..46651ce 100644
--- a/libs/binder/rust/rpcbinder/Android.bp
+++ b/libs/binder/rust/rpcbinder/Android.bp
@@ -26,6 +26,7 @@
],
visibility: [
"//device/google/cuttlefish/shared/minidroid/sample",
+ "//hardware/interfaces/security/see:__subpackages__",
"//packages/modules/Virtualization:__subpackages__",
],
apex_available: [
diff --git a/libs/binder/rust/rpcbinder/src/lib.rs b/libs/binder/rust/rpcbinder/src/lib.rs
index 7e5c9dd..774bb21 100644
--- a/libs/binder/rust/rpcbinder/src/lib.rs
+++ b/libs/binder/rust/rpcbinder/src/lib.rs
@@ -20,6 +20,8 @@
mod session;
pub use server::RpcServer;
+#[cfg(target_os = "trusty")]
+pub use server::RpcServerConnection;
#[cfg(not(target_os = "trusty"))]
pub use server::RpcServerRef;
pub use session::{FileDescriptorTransportMode, RpcSession, RpcSessionRef};
diff --git a/libs/binder/rust/rpcbinder/src/server/trusty.rs b/libs/binder/rust/rpcbinder/src/server/trusty.rs
index fe45dec..54d82d5 100644
--- a/libs/binder/rust/rpcbinder/src/server/trusty.rs
+++ b/libs/binder/rust/rpcbinder/src/server/trusty.rs
@@ -106,6 +106,10 @@
ctx: *mut c_void,
}
+// SAFETY: The opaque handle: `ctx` points into a dynamic allocation,
+// and not tied to anything specific to the current thread.
+unsafe impl Send for RpcServerConnection {}
+
impl Drop for RpcServerConnection {
fn drop(&mut self) {
// We do not need to close handle_fd since we do not own it.
diff --git a/libs/binder/rust/rpcbinder/src/session.rs b/libs/binder/rust/rpcbinder/src/session.rs
index 09688a2..411b9de 100644
--- a/libs/binder/rust/rpcbinder/src/session.rs
+++ b/libs/binder/rust/rpcbinder/src/session.rs
@@ -195,11 +195,13 @@
/// take ownership of) file descriptors already connected to it.
pub fn setup_preconnected_client<T: FromIBinder + ?Sized>(
&self,
- mut request_fd: impl FnMut() -> Option<RawFd>,
+ request_fd: impl FnMut() -> Option<RawFd>,
) -> Result<Strong<T>, StatusCode> {
- // Double reference the factory because trait objects aren't FFI safe.
- let mut request_fd_ref: RequestFd = &mut request_fd;
- let param = &mut request_fd_ref as *mut RequestFd as *mut c_void;
+ // Trait objects aren't FFI safe, so *mut c_void can't be converted back to
+ // *mut dyn FnMut() -> Option<RawFd>>. Double box the factory to make it possible to get
+ // the factory from *mut c_void (to *mut Box<dyn<...>>) in the callbacks.
+ let request_fd_box: Box<dyn FnMut() -> Option<RawFd>> = Box::new(request_fd);
+ let param = Box::into_raw(Box::new(request_fd_box)) as *mut c_void;
// SAFETY: AIBinder returned by RpcPreconnectedClient has correct reference count, and the
// ownership can be safely taken by new_spibinder. RpcPreconnectedClient does not take ownership
@@ -209,6 +211,7 @@
self.as_ptr(),
Some(request_fd_wrapper),
param,
+ Some(param_delete_fd_wrapper),
))
};
Self::get_interface(service)
@@ -225,13 +228,18 @@
}
}
-type RequestFd<'a> = &'a mut dyn FnMut() -> Option<RawFd>;
-
unsafe extern "C" fn request_fd_wrapper(param: *mut c_void) -> c_int {
- let request_fd_ptr = param as *mut RequestFd;
+ let request_fd_ptr = param as *mut Box<dyn FnMut() -> Option<RawFd>>;
// SAFETY: This is only ever called by RpcPreconnectedClient, within the lifetime of the
// BinderFdFactory reference, with param being a properly aligned non-null pointer to an
// initialized instance.
let request_fd = unsafe { request_fd_ptr.as_mut().unwrap() };
request_fd().unwrap_or(-1)
}
+
+unsafe extern "C" fn param_delete_fd_wrapper(param: *mut c_void) {
+ // SAFETY: This is only ever called by RpcPreconnectedClient, with param being the
+ // pointer returned from Box::into_raw.
+ let request_fd_box = unsafe { Box::from_raw(param as *mut Box<dyn FnMut() -> Option<RawFd>>) };
+ drop(request_fd_box);
+}
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index 8c0501b..771c65b 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -1160,6 +1160,12 @@
pub const fn enum_values() -> [Self; $size] {
[$(Self::$name),*]
}
+
+ #[inline(always)]
+ #[allow(missing_docs)]
+ pub const fn get(&self) -> $backing {
+ self.0
+ }
}
impl std::fmt::Debug for $enum {
@@ -1201,5 +1207,45 @@
Ok(v.map(|v| v.into_iter().map(Self).collect()))
}
}
+
+ impl std::ops::BitOr for $enum {
+ type Output = Self;
+ fn bitor(self, rhs: Self) -> Self {
+ Self(self.0 | rhs.0)
+ }
+ }
+
+ impl std::ops::BitOrAssign for $enum {
+ fn bitor_assign(&mut self, rhs: Self) {
+ self.0 = self.0 | rhs.0;
+ }
+ }
+
+ impl std::ops::BitAnd for $enum {
+ type Output = Self;
+ fn bitand(self, rhs: Self) -> Self {
+ Self(self.0 & rhs.0)
+ }
+ }
+
+ impl std::ops::BitAndAssign for $enum {
+ fn bitand_assign(&mut self, rhs: Self) {
+ self.0 = self.0 & rhs.0;
+ }
+ }
+
+ impl std::ops::BitXor for $enum {
+ type Output = Self;
+ fn bitxor(self, rhs: Self) -> Self {
+ Self(self.0 ^ rhs.0)
+ }
+ }
+
+ impl std::ops::BitXorAssign for $enum {
+ fn bitxor_assign(&mut self, rhs: Self) {
+ self.0 = self.0 ^ rhs.0;
+ }
+ }
+
};
}
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
index 1b24b0a..0026f21 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -99,12 +99,14 @@
mod error;
mod native;
mod parcel;
+#[cfg(not(trusty))]
+mod persistable_bundle;
mod proxy;
#[cfg(not(any(trusty, android_ndk)))]
mod service;
#[cfg(not(any(trusty, android_ndk)))]
mod state;
-#[cfg(not(any(android_vendor, android_ndk, android_vndk)))]
+#[cfg(not(any(android_vendor, android_ndk, android_vndk, trusty)))]
mod system_only;
use binder_ndk_sys as sys;
@@ -113,6 +115,8 @@
pub use binder::{BinderFeatures, FromIBinder, IBinder, Interface, Strong, Weak};
pub use error::{ExceptionCode, IntoBinderResult, Status, StatusCode};
pub use parcel::{ParcelFileDescriptor, Parcelable, ParcelableHolder};
+#[cfg(not(trusty))]
+pub use persistable_bundle::{PersistableBundle, ValueType};
pub use proxy::{DeathRecipient, SpIBinder, WpIBinder};
#[cfg(not(any(trusty, android_ndk)))]
pub use service::{
@@ -125,7 +129,7 @@
pub use service::{get_interface, get_service};
#[cfg(not(any(trusty, android_ndk)))]
pub use state::{ProcessState, ThreadState};
-#[cfg(not(any(android_vendor, android_vndk, android_ndk)))]
+#[cfg(not(any(android_vendor, android_vndk, android_ndk, trusty)))]
pub use system_only::{delegate_accessor, Accessor, AccessorProvider, ConnectionInfo};
/// Binder result containing a [`Status`] on error.
diff --git a/libs/binder/rust/src/parcel.rs b/libs/binder/rust/src/parcel.rs
index 485b0bd..2d40ced 100644
--- a/libs/binder/rust/src/parcel.rs
+++ b/libs/binder/rust/src/parcel.rs
@@ -184,7 +184,7 @@
/// Safety: The `BorrowedParcel` constructors guarantee that a `BorrowedParcel`
/// object will always contain a valid pointer to an `AParcel`.
-unsafe impl<'a> AsNative<sys::AParcel> for BorrowedParcel<'a> {
+unsafe impl AsNative<sys::AParcel> for BorrowedParcel<'_> {
fn as_native(&self) -> *const sys::AParcel {
self.ptr.as_ptr()
}
@@ -195,7 +195,7 @@
}
// Data serialization methods
-impl<'a> BorrowedParcel<'a> {
+impl BorrowedParcel<'_> {
/// Data written to parcelable is zero'd before being deleted or reallocated.
#[cfg(not(android_ndk))]
pub fn mark_sensitive(&mut self) {
@@ -334,7 +334,7 @@
/// A segment of a writable parcel, used for [`BorrowedParcel::sized_write`].
pub struct WritableSubParcel<'a>(BorrowedParcel<'a>);
-impl<'a> WritableSubParcel<'a> {
+impl WritableSubParcel<'_> {
/// Write a type that implements [`Serialize`] to the sub-parcel.
pub fn write<S: Serialize + ?Sized>(&mut self, parcelable: &S) -> Result<()> {
parcelable.serialize(&mut self.0)
@@ -440,7 +440,7 @@
}
// Data deserialization methods
-impl<'a> BorrowedParcel<'a> {
+impl BorrowedParcel<'_> {
/// Attempt to read a type that implements [`Deserialize`] from this parcel.
pub fn read<D: Deserialize>(&self) -> Result<D> {
D::deserialize(self)
@@ -565,7 +565,7 @@
end_position: i32,
}
-impl<'a> ReadableSubParcel<'a> {
+impl ReadableSubParcel<'_> {
/// Read a type that implements [`Deserialize`] from the sub-parcel.
pub fn read<D: Deserialize>(&self) -> Result<D> {
D::deserialize(&self.parcel)
@@ -649,7 +649,7 @@
}
// Internal APIs
-impl<'a> BorrowedParcel<'a> {
+impl BorrowedParcel<'_> {
pub(crate) fn write_binder(&mut self, binder: Option<&SpIBinder>) -> Result<()> {
// Safety: `BorrowedParcel` always contains a valid pointer to an
// `AParcel`. `AsNative` for `Option<SpIBinder`> will either return
@@ -702,7 +702,7 @@
}
}
-impl<'a> fmt::Debug for BorrowedParcel<'a> {
+impl fmt::Debug for BorrowedParcel<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("BorrowedParcel").finish()
}
diff --git a/libs/binder/rust/src/persistable_bundle.rs b/libs/binder/rust/src/persistable_bundle.rs
new file mode 100644
index 0000000..8639c0d
--- /dev/null
+++ b/libs/binder/rust/src/persistable_bundle.rs
@@ -0,0 +1,1076 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+use crate::{
+ binder::AsNative,
+ error::{status_result, StatusCode},
+ impl_deserialize_for_unstructured_parcelable, impl_serialize_for_unstructured_parcelable,
+ parcel::{BorrowedParcel, UnstructuredParcelable},
+};
+use binder_ndk_sys::{
+ APersistableBundle, APersistableBundle_delete, APersistableBundle_dup,
+ APersistableBundle_erase, APersistableBundle_getBoolean, APersistableBundle_getBooleanKeys,
+ APersistableBundle_getBooleanVector, APersistableBundle_getBooleanVectorKeys,
+ APersistableBundle_getDouble, APersistableBundle_getDoubleKeys,
+ APersistableBundle_getDoubleVector, APersistableBundle_getDoubleVectorKeys,
+ APersistableBundle_getInt, APersistableBundle_getIntKeys, APersistableBundle_getIntVector,
+ APersistableBundle_getIntVectorKeys, APersistableBundle_getLong,
+ APersistableBundle_getLongKeys, APersistableBundle_getLongVector,
+ APersistableBundle_getLongVectorKeys, APersistableBundle_getPersistableBundle,
+ APersistableBundle_getPersistableBundleKeys, APersistableBundle_getString,
+ APersistableBundle_getStringKeys, APersistableBundle_getStringVector,
+ APersistableBundle_getStringVectorKeys, APersistableBundle_isEqual, APersistableBundle_new,
+ APersistableBundle_putBoolean, APersistableBundle_putBooleanVector,
+ APersistableBundle_putDouble, APersistableBundle_putDoubleVector, APersistableBundle_putInt,
+ APersistableBundle_putIntVector, APersistableBundle_putLong, APersistableBundle_putLongVector,
+ APersistableBundle_putPersistableBundle, APersistableBundle_putString,
+ APersistableBundle_putStringVector, APersistableBundle_readFromParcel, APersistableBundle_size,
+ APersistableBundle_writeToParcel, APERSISTABLEBUNDLE_ALLOCATOR_FAILED,
+ APERSISTABLEBUNDLE_KEY_NOT_FOUND,
+};
+use std::ffi::{c_char, c_void, CStr, CString, NulError};
+use std::ptr::{null_mut, slice_from_raw_parts_mut, NonNull};
+use zerocopy::FromZeros;
+
+/// A mapping from string keys to values of various types.
+#[derive(Debug)]
+pub struct PersistableBundle(NonNull<APersistableBundle>);
+
+impl PersistableBundle {
+ /// Creates a new `PersistableBundle`.
+ pub fn new() -> Self {
+ // SAFETY: APersistableBundle_new doesn't actually have any safety requirements.
+ let bundle = unsafe { APersistableBundle_new() };
+ Self(NonNull::new(bundle).expect("Allocated APersistableBundle was null"))
+ }
+
+ /// Returns the number of mappings in the bundle.
+ pub fn size(&self) -> usize {
+ // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
+ // lifetime of the `PersistableBundle`.
+ unsafe { APersistableBundle_size(self.0.as_ptr()) }
+ .try_into()
+ .expect("APersistableBundle_size returned a negative size")
+ }
+
+ /// Removes any entry with the given key.
+ ///
+ /// Returns an error if the given key contains a NUL character, otherwise returns whether there
+ /// was any entry to remove.
+ pub fn remove(&mut self, key: &str) -> Result<bool, NulError> {
+ let key = CString::new(key)?;
+ // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
+ // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed
+ // to be valid for the duration of this call.
+ Ok(unsafe { APersistableBundle_erase(self.0.as_ptr(), key.as_ptr()) != 0 })
+ }
+
+ /// Inserts a key-value pair into the bundle.
+ ///
+ /// If the key is already present then its value will be overwritten by the given value.
+ ///
+ /// Returns an error if the key contains a NUL character.
+ pub fn insert_bool(&mut self, key: &str, value: bool) -> Result<(), NulError> {
+ let key = CString::new(key)?;
+ // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
+ // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed
+ // to be valid for the duration of this call.
+ unsafe {
+ APersistableBundle_putBoolean(self.0.as_ptr(), key.as_ptr(), value);
+ }
+ Ok(())
+ }
+
+ /// Inserts a key-value pair into the bundle.
+ ///
+ /// If the key is already present then its value will be overwritten by the given value.
+ ///
+ /// Returns an error if the key contains a NUL character.
+ pub fn insert_int(&mut self, key: &str, value: i32) -> Result<(), NulError> {
+ let key = CString::new(key)?;
+ // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
+ // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed
+ // to be valid for the duration of this call.
+ unsafe {
+ APersistableBundle_putInt(self.0.as_ptr(), key.as_ptr(), value);
+ }
+ Ok(())
+ }
+
+ /// Inserts a key-value pair into the bundle.
+ ///
+ /// If the key is already present then its value will be overwritten by the given value.
+ ///
+ /// Returns an error if the key contains a NUL character.
+ pub fn insert_long(&mut self, key: &str, value: i64) -> Result<(), NulError> {
+ let key = CString::new(key)?;
+ // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
+ // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed
+ // to be valid for the duration of this call.
+ unsafe {
+ APersistableBundle_putLong(self.0.as_ptr(), key.as_ptr(), value);
+ }
+ Ok(())
+ }
+
+ /// Inserts a key-value pair into the bundle.
+ ///
+ /// If the key is already present then its value will be overwritten by the given value.
+ ///
+ /// Returns an error if the key contains a NUL character.
+ pub fn insert_double(&mut self, key: &str, value: f64) -> Result<(), NulError> {
+ let key = CString::new(key)?;
+ // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
+ // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed
+ // to be valid for the duration of this call.
+ unsafe {
+ APersistableBundle_putDouble(self.0.as_ptr(), key.as_ptr(), value);
+ }
+ Ok(())
+ }
+
+ /// Inserts a key-value pair into the bundle.
+ ///
+ /// If the key is already present then its value will be overwritten by the given value.
+ ///
+ /// Returns an error if the key or value contains a NUL character.
+ pub fn insert_string(&mut self, key: &str, value: &str) -> Result<(), NulError> {
+ let key = CString::new(key)?;
+ let value = CString::new(value)?;
+ // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
+ // lifetime of the `PersistableBundle`. The pointer returned by `CStr::as_ptr` is guaranteed
+ // to be valid for the duration of this call.
+ unsafe {
+ APersistableBundle_putString(self.0.as_ptr(), key.as_ptr(), value.as_ptr());
+ }
+ Ok(())
+ }
+
+ /// Inserts a key-value pair into the bundle.
+ ///
+ /// If the key is already present then its value will be overwritten by the given value.
+ ///
+ /// Returns an error if the key contains a NUL character.
+ pub fn insert_bool_vec(&mut self, key: &str, value: &[bool]) -> Result<(), NulError> {
+ let key = CString::new(key)?;
+ // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
+ // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed
+ // to be valid for the duration of this call, and likewise the pointer returned by
+ // `value.as_ptr()` is guaranteed to be valid for at least `value.len()` values for the
+ // duration of the call.
+ unsafe {
+ APersistableBundle_putBooleanVector(
+ self.0.as_ptr(),
+ key.as_ptr(),
+ value.as_ptr(),
+ value.len().try_into().unwrap(),
+ );
+ }
+ Ok(())
+ }
+
+ /// Inserts a key-value pair into the bundle.
+ ///
+ /// If the key is already present then its value will be overwritten by the given value.
+ ///
+ /// Returns an error if the key contains a NUL character.
+ pub fn insert_int_vec(&mut self, key: &str, value: &[i32]) -> Result<(), NulError> {
+ let key = CString::new(key)?;
+ // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
+ // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed
+ // to be valid for the duration of this call, and likewise the pointer returned by
+ // `value.as_ptr()` is guaranteed to be valid for at least `value.len()` values for the
+ // duration of the call.
+ unsafe {
+ APersistableBundle_putIntVector(
+ self.0.as_ptr(),
+ key.as_ptr(),
+ value.as_ptr(),
+ value.len().try_into().unwrap(),
+ );
+ }
+ Ok(())
+ }
+
+ /// Inserts a key-value pair into the bundle.
+ ///
+ /// If the key is already present then its value will be overwritten by the given value.
+ ///
+ /// Returns an error if the key contains a NUL character.
+ pub fn insert_long_vec(&mut self, key: &str, value: &[i64]) -> Result<(), NulError> {
+ let key = CString::new(key)?;
+ // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
+ // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed
+ // to be valid for the duration of this call, and likewise the pointer returned by
+ // `value.as_ptr()` is guaranteed to be valid for at least `value.len()` values for the
+ // duration of the call.
+ unsafe {
+ APersistableBundle_putLongVector(
+ self.0.as_ptr(),
+ key.as_ptr(),
+ value.as_ptr(),
+ value.len().try_into().unwrap(),
+ );
+ }
+ Ok(())
+ }
+
+ /// Inserts a key-value pair into the bundle.
+ ///
+ /// If the key is already present then its value will be overwritten by the given value.
+ ///
+ /// Returns an error if the key contains a NUL character.
+ pub fn insert_double_vec(&mut self, key: &str, value: &[f64]) -> Result<(), NulError> {
+ let key = CString::new(key)?;
+ // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
+ // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed
+ // to be valid for the duration of this call, and likewise the pointer returned by
+ // `value.as_ptr()` is guaranteed to be valid for at least `value.len()` values for the
+ // duration of the call.
+ unsafe {
+ APersistableBundle_putDoubleVector(
+ self.0.as_ptr(),
+ key.as_ptr(),
+ value.as_ptr(),
+ value.len().try_into().unwrap(),
+ );
+ }
+ Ok(())
+ }
+
+ /// Inserts a key-value pair into the bundle.
+ ///
+ /// If the key is already present then its value will be overwritten by the given value.
+ ///
+ /// Returns an error if the key contains a NUL character.
+ pub fn insert_string_vec<'a, T: ToString + 'a>(
+ &mut self,
+ key: &str,
+ value: impl IntoIterator<Item = &'a T>,
+ ) -> Result<(), NulError> {
+ let key = CString::new(key)?;
+ // We need to collect the new `CString`s into something first so that they live long enough
+ // for their pointers to be valid for the `APersistableBundle_putStringVector` call below.
+ let c_strings = value
+ .into_iter()
+ .map(|s| CString::new(s.to_string()))
+ .collect::<Result<Vec<_>, NulError>>()?;
+ let char_pointers = c_strings.iter().map(|s| s.as_ptr()).collect::<Vec<_>>();
+ // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
+ // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed
+ // to be valid for the duration of this call, and likewise the pointer returned by
+ // `value.as_ptr()` is guaranteed to be valid for at least `value.len()` values for the
+ // duration of the call.
+ unsafe {
+ APersistableBundle_putStringVector(
+ self.0.as_ptr(),
+ key.as_ptr(),
+ char_pointers.as_ptr(),
+ char_pointers.len().try_into().unwrap(),
+ );
+ }
+ Ok(())
+ }
+
+ /// Inserts a key-value pair into the bundle.
+ ///
+ /// If the key is already present then its value will be overwritten by the given value.
+ ///
+ /// Returns an error if the key contains a NUL character.
+ pub fn insert_persistable_bundle(
+ &mut self,
+ key: &str,
+ value: &PersistableBundle,
+ ) -> Result<(), NulError> {
+ let key = CString::new(key)?;
+ // SAFETY: The wrapped `APersistableBundle` pointers are guaranteed to be valid for the
+ // lifetime of the `PersistableBundle`s. The pointer returned by `CStr::as_ptr` is
+ // guaranteed to be valid for the duration of this call, and
+ // `APersistableBundle_putPersistableBundle` does a deep copy so that is all that is
+ // required.
+ unsafe {
+ APersistableBundle_putPersistableBundle(
+ self.0.as_ptr(),
+ key.as_ptr(),
+ value.0.as_ptr(),
+ );
+ }
+ Ok(())
+ }
+
+ /// Gets the boolean value associated with the given key.
+ ///
+ /// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist
+ /// in the bundle.
+ pub fn get_bool(&self, key: &str) -> Result<Option<bool>, NulError> {
+ let key = CString::new(key)?;
+ let mut value = false;
+ // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
+ // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed
+ // to be valid for the duration of this call. The value pointer must be valid because it
+ // comes from a reference.
+ if unsafe { APersistableBundle_getBoolean(self.0.as_ptr(), key.as_ptr(), &mut value) } {
+ Ok(Some(value))
+ } else {
+ Ok(None)
+ }
+ }
+
+ /// Gets the i32 value associated with the given key.
+ ///
+ /// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist
+ /// in the bundle.
+ pub fn get_int(&self, key: &str) -> Result<Option<i32>, NulError> {
+ let key = CString::new(key)?;
+ let mut value = 0;
+ // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
+ // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed
+ // to be valid for the duration of this call. The value pointer must be valid because it
+ // comes from a reference.
+ if unsafe { APersistableBundle_getInt(self.0.as_ptr(), key.as_ptr(), &mut value) } {
+ Ok(Some(value))
+ } else {
+ Ok(None)
+ }
+ }
+
+ /// Gets the i64 value associated with the given key.
+ ///
+ /// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist
+ /// in the bundle.
+ pub fn get_long(&self, key: &str) -> Result<Option<i64>, NulError> {
+ let key = CString::new(key)?;
+ let mut value = 0;
+ // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
+ // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed
+ // to be valid for the duration of this call. The value pointer must be valid because it
+ // comes from a reference.
+ if unsafe { APersistableBundle_getLong(self.0.as_ptr(), key.as_ptr(), &mut value) } {
+ Ok(Some(value))
+ } else {
+ Ok(None)
+ }
+ }
+
+ /// Gets the f64 value associated with the given key.
+ ///
+ /// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist
+ /// in the bundle.
+ pub fn get_double(&self, key: &str) -> Result<Option<f64>, NulError> {
+ let key = CString::new(key)?;
+ let mut value = 0.0;
+ // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
+ // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed
+ // to be valid for the duration of this call. The value pointer must be valid because it
+ // comes from a reference.
+ if unsafe { APersistableBundle_getDouble(self.0.as_ptr(), key.as_ptr(), &mut value) } {
+ Ok(Some(value))
+ } else {
+ Ok(None)
+ }
+ }
+
+ /// Gets the string value associated with the given key.
+ ///
+ /// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist
+ /// in the bundle.
+ pub fn get_string(&self, key: &str) -> Result<Option<String>, NulError> {
+ let key = CString::new(key)?;
+ let mut value = null_mut();
+ let mut allocated_size: usize = 0;
+ // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
+ // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed
+ // to be valid for the lifetime of `key`. The value pointer must be valid because it comes
+ // from a reference.
+ let value_size_bytes = unsafe {
+ APersistableBundle_getString(
+ self.0.as_ptr(),
+ key.as_ptr(),
+ &mut value,
+ Some(string_allocator),
+ (&raw mut allocated_size).cast(),
+ )
+ };
+ match value_size_bytes {
+ APERSISTABLEBUNDLE_KEY_NOT_FOUND => Ok(None),
+ APERSISTABLEBUNDLE_ALLOCATOR_FAILED => {
+ panic!("APersistableBundle_getString failed to allocate string");
+ }
+ _ => {
+ let raw_slice = slice_from_raw_parts_mut(value.cast(), allocated_size);
+ // SAFETY: The pointer was returned from string_allocator, which used
+ // `Box::into_raw`, and we've got the appropriate size back from allocated_size.
+ let boxed_slice: Box<[u8]> = unsafe { Box::from_raw(raw_slice) };
+ assert_eq!(
+ allocated_size,
+ usize::try_from(value_size_bytes)
+ .expect("APersistableBundle_getString returned negative value size")
+ + 1
+ );
+ let c_string = CString::from_vec_with_nul(boxed_slice.into())
+ .expect("APersistableBundle_getString returned string missing NUL byte");
+ let string = c_string
+ .into_string()
+ .expect("APersistableBundle_getString returned invalid UTF-8");
+ Ok(Some(string))
+ }
+ }
+ }
+
+ /// Gets the vector of `T` associated with the given key.
+ ///
+ /// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist
+ /// in the bundle.
+ ///
+ /// `get_func` should be one of the `APersistableBundle_get*Vector` functions from
+ /// `binder_ndk_sys`.
+ ///
+ /// # Safety
+ ///
+ /// `get_func` must only require that the pointers it takes are valid for the duration of the
+ /// call. It must allow a null pointer for the buffer, and must return the size in bytes of
+ /// buffer it requires. If it is given a non-null buffer pointer it must write that number of
+ /// bytes to the buffer, which must be a whole number of valid `T` values.
+ unsafe fn get_vec<T: Clone>(
+ &self,
+ key: &str,
+ default: T,
+ get_func: unsafe extern "C" fn(
+ *const APersistableBundle,
+ *const c_char,
+ *mut T,
+ i32,
+ ) -> i32,
+ ) -> Result<Option<Vec<T>>, NulError> {
+ let key = CString::new(key)?;
+ // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
+ // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed
+ // to be valid for the lifetime of `key`. A null pointer is allowed for the buffer.
+ match unsafe { get_func(self.0.as_ptr(), key.as_ptr(), null_mut(), 0) } {
+ APERSISTABLEBUNDLE_KEY_NOT_FOUND => Ok(None),
+ APERSISTABLEBUNDLE_ALLOCATOR_FAILED => {
+ panic!("APersistableBundle_getStringVector failed to allocate string");
+ }
+ required_buffer_size => {
+ let mut value = vec![
+ default;
+ usize::try_from(required_buffer_size).expect(
+ "APersistableBundle_get*Vector returned invalid size"
+ ) / size_of::<T>()
+ ];
+ // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for
+ // the lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()`
+ // is guaranteed to be valid for the lifetime of `key`. The value buffer pointer is
+ // valid as it comes from the Vec we just allocated.
+ match unsafe {
+ get_func(
+ self.0.as_ptr(),
+ key.as_ptr(),
+ value.as_mut_ptr(),
+ (value.len() * size_of::<T>()).try_into().unwrap(),
+ )
+ } {
+ APERSISTABLEBUNDLE_KEY_NOT_FOUND => {
+ panic!("APersistableBundle_get*Vector failed to find key after first finding it");
+ }
+ APERSISTABLEBUNDLE_ALLOCATOR_FAILED => {
+ panic!("APersistableBundle_getStringVector failed to allocate string");
+ }
+ _ => Ok(Some(value)),
+ }
+ }
+ }
+ }
+
+ /// Gets the boolean vector value associated with the given key.
+ ///
+ /// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist
+ /// in the bundle.
+ pub fn get_bool_vec(&self, key: &str) -> Result<Option<Vec<bool>>, NulError> {
+ // SAFETY: APersistableBundle_getBooleanVector fulfils all the safety requirements of
+ // `get_vec`.
+ unsafe { self.get_vec(key, Default::default(), APersistableBundle_getBooleanVector) }
+ }
+
+ /// Gets the i32 vector value associated with the given key.
+ ///
+ /// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist
+ /// in the bundle.
+ pub fn get_int_vec(&self, key: &str) -> Result<Option<Vec<i32>>, NulError> {
+ // SAFETY: APersistableBundle_getIntVector fulfils all the safety requirements of
+ // `get_vec`.
+ unsafe { self.get_vec(key, Default::default(), APersistableBundle_getIntVector) }
+ }
+
+ /// Gets the i64 vector value associated with the given key.
+ ///
+ /// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist
+ /// in the bundle.
+ pub fn get_long_vec(&self, key: &str) -> Result<Option<Vec<i64>>, NulError> {
+ // SAFETY: APersistableBundle_getLongVector fulfils all the safety requirements of
+ // `get_vec`.
+ unsafe { self.get_vec(key, Default::default(), APersistableBundle_getLongVector) }
+ }
+
+ /// Gets the f64 vector value associated with the given key.
+ ///
+ /// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist
+ /// in the bundle.
+ pub fn get_double_vec(&self, key: &str) -> Result<Option<Vec<f64>>, NulError> {
+ // SAFETY: APersistableBundle_getDoubleVector fulfils all the safety requirements of
+ // `get_vec`.
+ unsafe { self.get_vec(key, Default::default(), APersistableBundle_getDoubleVector) }
+ }
+
+ /// Gets the string vector value associated with the given key.
+ ///
+ /// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist
+ /// in the bundle.
+ pub fn get_string_vec(&self, key: &str) -> Result<Option<Vec<String>>, NulError> {
+ if let Some(value) =
+ // SAFETY: `get_string_vector_with_allocator` fulfils all the safety requirements of
+ // `get_vec`.
+ unsafe { self.get_vec(key, null_mut(), get_string_vector_with_allocator) }?
+ {
+ Ok(Some(
+ value
+ .into_iter()
+ .map(|s| {
+ // SAFETY: The pointer was returned from `string_allocator`, which used
+ // `Box::into_raw`, and `APersistableBundle_getStringVector` should have
+ // written valid bytes to it including a NUL terminator in the last
+ // position.
+ let string_length = unsafe { CStr::from_ptr(s) }.count_bytes();
+ let raw_slice = slice_from_raw_parts_mut(s.cast(), string_length + 1);
+ // SAFETY: The pointer was returned from `string_allocator`, which used
+ // `Box::into_raw`, and we've got the appropriate size back by checking the
+ // length of the string.
+ let boxed_slice: Box<[u8]> = unsafe { Box::from_raw(raw_slice) };
+ let c_string = CString::from_vec_with_nul(boxed_slice.into()).expect(
+ "APersistableBundle_getStringVector returned string missing NUL byte",
+ );
+ c_string
+ .into_string()
+ .expect("APersistableBundle_getStringVector returned invalid UTF-8")
+ })
+ .collect(),
+ ))
+ } else {
+ Ok(None)
+ }
+ }
+
+ /// Gets the `PersistableBundle` value associated with the given key.
+ ///
+ /// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist
+ /// in the bundle.
+ pub fn get_persistable_bundle(&self, key: &str) -> Result<Option<Self>, NulError> {
+ let key = CString::new(key)?;
+ let mut value = null_mut();
+ // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
+ // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed
+ // to be valid for the lifetime of `key`. The value pointer must be valid because it comes
+ // from a reference.
+ if unsafe {
+ APersistableBundle_getPersistableBundle(self.0.as_ptr(), key.as_ptr(), &mut value)
+ } {
+ Ok(Some(Self(NonNull::new(value).expect(
+ "APersistableBundle_getPersistableBundle returned true but didn't set outBundle",
+ ))))
+ } else {
+ Ok(None)
+ }
+ }
+
+ /// Calls the appropriate `APersistableBundle_get*Keys` function for the given `value_type`,
+ /// with our `string_allocator` and a null context pointer.
+ ///
+ /// # Safety
+ ///
+ /// `out_keys` must either be null or point to a buffer of at least `buffer_size_bytes` bytes,
+ /// properly aligned for `T`, and not otherwise accessed for the duration of the call.
+ unsafe fn get_keys_raw(
+ &self,
+ value_type: ValueType,
+ out_keys: *mut *mut c_char,
+ buffer_size_bytes: i32,
+ ) -> i32 {
+ // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
+ // lifetime of the `PersistableBundle`. Our caller guarantees an appropriate value for
+ // `out_keys` and `buffer_size_bytes`.
+ unsafe {
+ match value_type {
+ ValueType::Boolean => APersistableBundle_getBooleanKeys(
+ self.0.as_ptr(),
+ out_keys,
+ buffer_size_bytes,
+ Some(string_allocator),
+ null_mut(),
+ ),
+ ValueType::Integer => APersistableBundle_getIntKeys(
+ self.0.as_ptr(),
+ out_keys,
+ buffer_size_bytes,
+ Some(string_allocator),
+ null_mut(),
+ ),
+ ValueType::Long => APersistableBundle_getLongKeys(
+ self.0.as_ptr(),
+ out_keys,
+ buffer_size_bytes,
+ Some(string_allocator),
+ null_mut(),
+ ),
+ ValueType::Double => APersistableBundle_getDoubleKeys(
+ self.0.as_ptr(),
+ out_keys,
+ buffer_size_bytes,
+ Some(string_allocator),
+ null_mut(),
+ ),
+ ValueType::String => APersistableBundle_getStringKeys(
+ self.0.as_ptr(),
+ out_keys,
+ buffer_size_bytes,
+ Some(string_allocator),
+ null_mut(),
+ ),
+ ValueType::BooleanVector => APersistableBundle_getBooleanVectorKeys(
+ self.0.as_ptr(),
+ out_keys,
+ buffer_size_bytes,
+ Some(string_allocator),
+ null_mut(),
+ ),
+ ValueType::IntegerVector => APersistableBundle_getIntVectorKeys(
+ self.0.as_ptr(),
+ out_keys,
+ buffer_size_bytes,
+ Some(string_allocator),
+ null_mut(),
+ ),
+ ValueType::LongVector => APersistableBundle_getLongVectorKeys(
+ self.0.as_ptr(),
+ out_keys,
+ buffer_size_bytes,
+ Some(string_allocator),
+ null_mut(),
+ ),
+ ValueType::DoubleVector => APersistableBundle_getDoubleVectorKeys(
+ self.0.as_ptr(),
+ out_keys,
+ buffer_size_bytes,
+ Some(string_allocator),
+ null_mut(),
+ ),
+ ValueType::StringVector => APersistableBundle_getStringVectorKeys(
+ self.0.as_ptr(),
+ out_keys,
+ buffer_size_bytes,
+ Some(string_allocator),
+ null_mut(),
+ ),
+ ValueType::PersistableBundle => APersistableBundle_getPersistableBundleKeys(
+ self.0.as_ptr(),
+ out_keys,
+ buffer_size_bytes,
+ Some(string_allocator),
+ null_mut(),
+ ),
+ }
+ }
+ }
+
+ /// Gets all the keys associated with values of the given type.
+ pub fn keys_for_type(&self, value_type: ValueType) -> Vec<String> {
+ // SAFETY: A null pointer is allowed for the buffer.
+ match unsafe { self.get_keys_raw(value_type, null_mut(), 0) } {
+ APERSISTABLEBUNDLE_ALLOCATOR_FAILED => {
+ panic!("APersistableBundle_get*Keys failed to allocate string");
+ }
+ required_buffer_size => {
+ let required_buffer_size_usize = usize::try_from(required_buffer_size)
+ .expect("APersistableBundle_get*Keys returned invalid size");
+ assert_eq!(required_buffer_size_usize % size_of::<*mut c_char>(), 0);
+ let mut keys =
+ vec![null_mut(); required_buffer_size_usize / size_of::<*mut c_char>()];
+ // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for
+ // the lifetime of the `PersistableBundle`. The keys buffer pointer is valid as it
+ // comes from the Vec we just allocated.
+ if unsafe { self.get_keys_raw(value_type, keys.as_mut_ptr(), required_buffer_size) }
+ == APERSISTABLEBUNDLE_ALLOCATOR_FAILED
+ {
+ panic!("APersistableBundle_get*Keys failed to allocate string");
+ }
+ keys.into_iter()
+ .map(|key| {
+ // SAFETY: The pointer was returned from `string_allocator`, which used
+ // `Box::into_raw`, and `APersistableBundle_getStringVector` should have
+ // written valid bytes to it including a NUL terminator in the last
+ // position.
+ let string_length = unsafe { CStr::from_ptr(key) }.count_bytes();
+ let raw_slice = slice_from_raw_parts_mut(key.cast(), string_length + 1);
+ // SAFETY: The pointer was returned from `string_allocator`, which used
+ // `Box::into_raw`, and we've got the appropriate size back by checking the
+ // length of the string.
+ let boxed_slice: Box<[u8]> = unsafe { Box::from_raw(raw_slice) };
+ let c_string = CString::from_vec_with_nul(boxed_slice.into())
+ .expect("APersistableBundle_get*Keys returned string missing NUL byte");
+ c_string
+ .into_string()
+ .expect("APersistableBundle_get*Keys returned invalid UTF-8")
+ })
+ .collect()
+ }
+ }
+ }
+
+ /// Returns an iterator over all keys in the bundle, along with the type of their associated
+ /// value.
+ pub fn keys(&self) -> impl Iterator<Item = (String, ValueType)> + use<'_> {
+ [
+ ValueType::Boolean,
+ ValueType::Integer,
+ ValueType::Long,
+ ValueType::Double,
+ ValueType::String,
+ ValueType::BooleanVector,
+ ValueType::IntegerVector,
+ ValueType::LongVector,
+ ValueType::DoubleVector,
+ ValueType::StringVector,
+ ValueType::PersistableBundle,
+ ]
+ .iter()
+ .flat_map(|value_type| {
+ self.keys_for_type(*value_type).into_iter().map(|key| (key, *value_type))
+ })
+ }
+}
+
+/// Wrapper around `APersistableBundle_getStringVector` to pass `string_allocator` and a null
+/// context pointer.
+///
+/// # Safety
+///
+/// * `bundle` must point to a valid `APersistableBundle` which is not modified for the duration of
+/// the call.
+/// * `key` must point to a valid NUL-terminated C string.
+/// * `buffer` must either be null or point to a buffer of at least `buffer_size_bytes` bytes,
+/// properly aligned for `T`, and not otherwise accessed for the duration of the call.
+unsafe extern "C" fn get_string_vector_with_allocator(
+ bundle: *const APersistableBundle,
+ key: *const c_char,
+ buffer: *mut *mut c_char,
+ buffer_size_bytes: i32,
+) -> i32 {
+ // SAFETY: The safety requirements are all guaranteed by our caller according to the safety
+ // documentation above.
+ unsafe {
+ APersistableBundle_getStringVector(
+ bundle,
+ key,
+ buffer,
+ buffer_size_bytes,
+ Some(string_allocator),
+ null_mut(),
+ )
+ }
+}
+
+// SAFETY: The underlying *APersistableBundle can be moved between threads.
+unsafe impl Send for PersistableBundle {}
+
+// SAFETY: The underlying *APersistableBundle can be read from multiple threads, and we require
+// `&mut PersistableBundle` for any operations which mutate it.
+unsafe impl Sync for PersistableBundle {}
+
+impl Default for PersistableBundle {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+impl Drop for PersistableBundle {
+ fn drop(&mut self) {
+ // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
+ // lifetime of this `PersistableBundle`.
+ unsafe { APersistableBundle_delete(self.0.as_ptr()) };
+ }
+}
+
+impl Clone for PersistableBundle {
+ fn clone(&self) -> Self {
+ // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
+ // lifetime of the `PersistableBundle`.
+ let duplicate = unsafe { APersistableBundle_dup(self.0.as_ptr()) };
+ Self(NonNull::new(duplicate).expect("Duplicated APersistableBundle was null"))
+ }
+}
+
+impl PartialEq for PersistableBundle {
+ fn eq(&self, other: &Self) -> bool {
+ // SAFETY: The wrapped `APersistableBundle` pointers are guaranteed to be valid for the
+ // lifetime of the `PersistableBundle`s.
+ unsafe { APersistableBundle_isEqual(self.0.as_ptr(), other.0.as_ptr()) }
+ }
+}
+
+impl UnstructuredParcelable for PersistableBundle {
+ fn write_to_parcel(&self, parcel: &mut BorrowedParcel) -> Result<(), StatusCode> {
+ let status =
+ // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
+ // lifetime of the `PersistableBundle`. `parcel.as_native_mut()` always returns a valid
+ // parcel pointer.
+ unsafe { APersistableBundle_writeToParcel(self.0.as_ptr(), parcel.as_native_mut()) };
+ status_result(status)
+ }
+
+ fn from_parcel(parcel: &BorrowedParcel) -> Result<Self, StatusCode> {
+ let mut bundle = null_mut();
+
+ // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
+ // lifetime of the `PersistableBundle`. `parcel.as_native()` always returns a valid parcel
+ // pointer.
+ let status = unsafe { APersistableBundle_readFromParcel(parcel.as_native(), &mut bundle) };
+ status_result(status)?;
+
+ Ok(Self(NonNull::new(bundle).expect(
+ "APersistableBundle_readFromParcel returned success but didn't allocate bundle",
+ )))
+ }
+}
+
+/// Allocates a boxed slice of the given size in bytes, returns a pointer to it and writes its size
+/// to `*context`.
+///
+/// # Safety
+///
+/// `context` must either be null or point to a `usize` to which we can write.
+unsafe extern "C" fn string_allocator(size: i32, context: *mut c_void) -> *mut c_char {
+ let Ok(size) = size.try_into() else {
+ return null_mut();
+ };
+ let Ok(boxed_slice) = <[c_char]>::new_box_zeroed_with_elems(size) else {
+ return null_mut();
+ };
+ if !context.is_null() {
+ // SAFETY: The caller promised that `context` is either null or points to a `usize` to which
+ // we can write, and we just checked that it's not null.
+ unsafe {
+ *context.cast::<usize>() = size;
+ }
+ }
+ Box::into_raw(boxed_slice).cast()
+}
+
+impl_deserialize_for_unstructured_parcelable!(PersistableBundle);
+impl_serialize_for_unstructured_parcelable!(PersistableBundle);
+
+/// The types which may be stored as values in a [`PersistableBundle`].
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub enum ValueType {
+ /// A `bool`.
+ Boolean,
+ /// An `i32`.
+ Integer,
+ /// An `i64`.
+ Long,
+ /// An `f64`.
+ Double,
+ /// A string.
+ String,
+ /// A vector of `bool`s.
+ BooleanVector,
+ /// A vector of `i32`s.
+ IntegerVector,
+ /// A vector of `i64`s.
+ LongVector,
+ /// A vector of `f64`s.
+ DoubleVector,
+ /// A vector of strings.
+ StringVector,
+ /// A nested `PersistableBundle`.
+ PersistableBundle,
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ #[test]
+ fn create_delete() {
+ let bundle = PersistableBundle::new();
+ drop(bundle);
+ }
+
+ #[test]
+ fn duplicate_equal() {
+ let bundle = PersistableBundle::new();
+ let duplicate = bundle.clone();
+ assert_eq!(bundle, duplicate);
+ }
+
+ #[test]
+ fn get_empty() {
+ let bundle = PersistableBundle::new();
+ assert_eq!(bundle.get_bool("foo"), Ok(None));
+ assert_eq!(bundle.get_int("foo"), Ok(None));
+ assert_eq!(bundle.get_long("foo"), Ok(None));
+ assert_eq!(bundle.get_double("foo"), Ok(None));
+ assert_eq!(bundle.get_bool_vec("foo"), Ok(None));
+ assert_eq!(bundle.get_int_vec("foo"), Ok(None));
+ assert_eq!(bundle.get_long_vec("foo"), Ok(None));
+ assert_eq!(bundle.get_double_vec("foo"), Ok(None));
+ assert_eq!(bundle.get_string("foo"), Ok(None));
+ }
+
+ #[test]
+ fn remove_empty() {
+ let mut bundle = PersistableBundle::new();
+ assert_eq!(bundle.remove("foo"), Ok(false));
+ }
+
+ #[test]
+ fn insert_get_primitives() {
+ let mut bundle = PersistableBundle::new();
+
+ assert_eq!(bundle.insert_bool("bool", true), Ok(()));
+ assert_eq!(bundle.insert_int("int", 42), Ok(()));
+ assert_eq!(bundle.insert_long("long", 66), Ok(()));
+ assert_eq!(bundle.insert_double("double", 123.4), Ok(()));
+
+ assert_eq!(bundle.get_bool("bool"), Ok(Some(true)));
+ assert_eq!(bundle.get_int("int"), Ok(Some(42)));
+ assert_eq!(bundle.get_long("long"), Ok(Some(66)));
+ assert_eq!(bundle.get_double("double"), Ok(Some(123.4)));
+ assert_eq!(bundle.size(), 4);
+
+ // Getting the wrong type should return nothing.
+ assert_eq!(bundle.get_int("bool"), Ok(None));
+ assert_eq!(bundle.get_long("bool"), Ok(None));
+ assert_eq!(bundle.get_double("bool"), Ok(None));
+ assert_eq!(bundle.get_bool("int"), Ok(None));
+ assert_eq!(bundle.get_long("int"), Ok(None));
+ assert_eq!(bundle.get_double("int"), Ok(None));
+ assert_eq!(bundle.get_bool("long"), Ok(None));
+ assert_eq!(bundle.get_int("long"), Ok(None));
+ assert_eq!(bundle.get_double("long"), Ok(None));
+ assert_eq!(bundle.get_bool("double"), Ok(None));
+ assert_eq!(bundle.get_int("double"), Ok(None));
+ assert_eq!(bundle.get_long("double"), Ok(None));
+
+ // If they are removed they should no longer be present.
+ assert_eq!(bundle.remove("bool"), Ok(true));
+ assert_eq!(bundle.remove("int"), Ok(true));
+ assert_eq!(bundle.remove("long"), Ok(true));
+ assert_eq!(bundle.remove("double"), Ok(true));
+ assert_eq!(bundle.get_bool("bool"), Ok(None));
+ assert_eq!(bundle.get_int("int"), Ok(None));
+ assert_eq!(bundle.get_long("long"), Ok(None));
+ assert_eq!(bundle.get_double("double"), Ok(None));
+ assert_eq!(bundle.size(), 0);
+ }
+
+ #[test]
+ fn insert_get_string() {
+ let mut bundle = PersistableBundle::new();
+
+ assert_eq!(bundle.insert_string("string", "foo"), Ok(()));
+ assert_eq!(bundle.insert_string("empty", ""), Ok(()));
+ assert_eq!(bundle.size(), 2);
+
+ assert_eq!(bundle.get_string("string"), Ok(Some("foo".to_string())));
+ assert_eq!(bundle.get_string("empty"), Ok(Some("".to_string())));
+ }
+
+ #[test]
+ fn insert_get_vec() {
+ let mut bundle = PersistableBundle::new();
+
+ assert_eq!(bundle.insert_bool_vec("bool", &[]), Ok(()));
+ assert_eq!(bundle.insert_int_vec("int", &[42]), Ok(()));
+ assert_eq!(bundle.insert_long_vec("long", &[66, 67, 68]), Ok(()));
+ assert_eq!(bundle.insert_double_vec("double", &[123.4]), Ok(()));
+ assert_eq!(bundle.insert_string_vec("string", &["foo", "bar", "baz"]), Ok(()));
+ assert_eq!(
+ bundle.insert_string_vec(
+ "string",
+ &[&"foo".to_string(), &"bar".to_string(), &"baz".to_string()]
+ ),
+ Ok(())
+ );
+ assert_eq!(
+ bundle.insert_string_vec(
+ "string",
+ &["foo".to_string(), "bar".to_string(), "baz".to_string()]
+ ),
+ Ok(())
+ );
+
+ assert_eq!(bundle.size(), 5);
+
+ assert_eq!(bundle.get_bool_vec("bool"), Ok(Some(vec![])));
+ assert_eq!(bundle.get_int_vec("int"), Ok(Some(vec![42])));
+ assert_eq!(bundle.get_long_vec("long"), Ok(Some(vec![66, 67, 68])));
+ assert_eq!(bundle.get_double_vec("double"), Ok(Some(vec![123.4])));
+ assert_eq!(
+ bundle.get_string_vec("string"),
+ Ok(Some(vec!["foo".to_string(), "bar".to_string(), "baz".to_string()]))
+ );
+ }
+
+ #[test]
+ fn insert_get_bundle() {
+ let mut bundle = PersistableBundle::new();
+
+ let mut sub_bundle = PersistableBundle::new();
+ assert_eq!(sub_bundle.insert_int("int", 42), Ok(()));
+ assert_eq!(sub_bundle.size(), 1);
+ assert_eq!(bundle.insert_persistable_bundle("bundle", &sub_bundle), Ok(()));
+
+ assert_eq!(bundle.get_persistable_bundle("bundle"), Ok(Some(sub_bundle)));
+ }
+
+ #[test]
+ fn get_keys() {
+ let mut bundle = PersistableBundle::new();
+
+ assert_eq!(bundle.keys_for_type(ValueType::Boolean), Vec::<String>::new());
+ assert_eq!(bundle.keys_for_type(ValueType::Integer), Vec::<String>::new());
+ assert_eq!(bundle.keys_for_type(ValueType::StringVector), Vec::<String>::new());
+
+ assert_eq!(bundle.insert_bool("bool1", false), Ok(()));
+ assert_eq!(bundle.insert_bool("bool2", true), Ok(()));
+ assert_eq!(bundle.insert_int("int", 42), Ok(()));
+
+ assert_eq!(
+ bundle.keys_for_type(ValueType::Boolean),
+ vec!["bool1".to_string(), "bool2".to_string()]
+ );
+ assert_eq!(bundle.keys_for_type(ValueType::Integer), vec!["int".to_string()]);
+ assert_eq!(bundle.keys_for_type(ValueType::StringVector), Vec::<String>::new());
+
+ assert_eq!(
+ bundle.keys().collect::<Vec<_>>(),
+ vec![
+ ("bool1".to_string(), ValueType::Boolean),
+ ("bool2".to_string(), ValueType::Boolean),
+ ("int".to_string(), ValueType::Integer),
+ ]
+ );
+ }
+}
diff --git a/libs/binder/rust/src/state.rs b/libs/binder/rust/src/state.rs
index 8a06274..609334e 100644
--- a/libs/binder/rust/src/state.rs
+++ b/libs/binder/rust/src/state.rs
@@ -28,8 +28,9 @@
/// `num_threads` additional threads as specified by
/// [`set_thread_pool_max_thread_count`](Self::set_thread_pool_max_thread_count).
///
- /// This should be done before creating any Binder client or server. If
- /// neither this nor [`join_thread_pool`](Self::join_thread_pool) are
+ /// If this is called, it must be done before creating any Binder client or server.
+ ///
+ /// If neither this nor [`join_thread_pool`](Self::join_thread_pool) are
/// called, then some things (such as callbacks and
/// [`IBinder::link_to_death`](crate::IBinder::link_to_death)) will silently
/// not work: the callbacks will be queued but never called as there is no
@@ -101,7 +102,10 @@
/// 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/rust/src/system_only.rs b/libs/binder/rust/src/system_only.rs
index 1a58d6b..50aa336 100644
--- a/libs/binder/rust/src/system_only.rs
+++ b/libs/binder/rust/src/system_only.rs
@@ -23,22 +23,17 @@
use std::os::raw::c_char;
use libc::{sockaddr, sockaddr_un, sockaddr_vm, socklen_t};
-use std::sync::Arc;
-use std::{fmt, mem, ptr};
+use std::boxed::Box;
+use std::{mem, ptr};
/// Rust wrapper around ABinderRpc_Accessor objects for RPC binder service management.
///
/// Dropping the `Accessor` will drop the underlying object and the binder it owns.
+#[derive(Debug)]
pub struct Accessor {
accessor: *mut sys::ABinderRpc_Accessor,
}
-impl fmt::Debug for Accessor {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "ABinderRpc_Accessor({:p})", self.accessor)
- }
-}
-
/// Socket connection info required for libbinder to connect to a service.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConnectionInfo {
@@ -70,7 +65,7 @@
where
F: Fn(&str) -> Option<ConnectionInfo> + Send + Sync + 'static,
{
- let callback: *mut c_void = Arc::into_raw(Arc::new(callback)) as *mut c_void;
+ let callback: *mut c_void = Box::into_raw(Box::new(callback)) as *mut c_void;
let inst = CString::new(instance).unwrap();
// Safety: The function pointer is a valid connection_info callback.
@@ -154,7 +149,7 @@
/// the string within isize::MAX from the pointer. The memory must not be mutated for
/// the duration of this function call and must be valid for reads from the pointer
/// to the null terminator.
- /// - The `cookie` parameter must be the cookie for an `Arc<F>` and
+ /// - The `cookie` parameter must be the cookie for a `Box<F>` and
/// the caller must hold a ref-count to it.
unsafe extern "C" fn connection_info<F>(
instance: *const c_char,
@@ -167,7 +162,7 @@
log::error!("Cookie({cookie:p}) or instance({instance:p}) is null!");
return ptr::null_mut();
}
- // Safety: The caller promises that `cookie` is for an Arc<F>.
+ // Safety: The caller promises that `cookie` is for a Box<F>.
let callback = unsafe { (cookie as *const F).as_ref().unwrap() };
// Safety: The caller in libbinder_ndk will have already verified this is a valid
@@ -212,19 +207,19 @@
}
}
- /// Callback that decrements the ref-count.
+ /// Callback that drops the `Box<F>`.
/// This is invoked from C++ when a binder is unlinked.
///
/// # Safety
///
- /// - The `cookie` parameter must be the cookie for an `Arc<F>` and
+ /// - The `cookie` parameter must be the cookie for a `Box<F>` and
/// the owner must give up a ref-count to it.
unsafe extern "C" fn cookie_decr_refcount<F>(cookie: *mut c_void)
where
F: Fn(&str) -> Option<ConnectionInfo> + Send + Sync + 'static,
{
- // Safety: The caller promises that `cookie` is for an Arc<F>.
- unsafe { Arc::decrement_strong_count(cookie as *const F) };
+ // Safety: The caller promises that `cookie` is for a Box<F>.
+ unsafe { std::mem::drop(Box::from_raw(cookie as *mut F)) };
}
}
@@ -301,7 +296,7 @@
where
F: Fn(&str) -> Option<Accessor> + Send + Sync + 'static,
{
- let callback: *mut c_void = Arc::into_raw(Arc::new(provider)) as *mut c_void;
+ let callback: *mut c_void = Box::into_raw(Box::new(provider)) as *mut c_void;
let c_str_instances: Vec<CString> =
instances.iter().map(|s| CString::new(s.as_bytes()).unwrap()).collect();
let mut c_instances: Vec<*const c_char> =
@@ -351,7 +346,7 @@
log::error!("Cookie({cookie:p}) or instance({instance:p}) is null!");
return ptr::null_mut();
}
- // Safety: The caller promises that `cookie` is for an Arc<F>.
+ // Safety: The caller promises that `cookie` is for a Box<F>.
let callback = unsafe { (cookie as *const F).as_ref().unwrap() };
let inst = {
@@ -382,14 +377,14 @@
///
/// # Safety
///
- /// - The `cookie` parameter must be the cookie for an `Arc<F>` and
+ /// - The `cookie` parameter must be the cookie for a `Box<F>` and
/// the owner must give up a ref-count to it.
unsafe extern "C" fn accessor_cookie_decr_refcount<F>(cookie: *mut c_void)
where
F: Fn(&str) -> Option<Accessor> + Send + Sync + 'static,
{
- // Safety: The caller promises that `cookie` is for an Arc<F>.
- unsafe { Arc::decrement_strong_count(cookie as *const F) };
+ // Safety: The caller promises that `cookie` is for a Box<F>.
+ unsafe { std::mem::drop(Box::from_raw(cookie as *mut F)) };
}
}
diff --git a/libs/binder/rust/sys/BinderBindings.hpp b/libs/binder/rust/sys/BinderBindings.hpp
index 557f0e8..c19e375 100644
--- a/libs/binder/rust/sys/BinderBindings.hpp
+++ b/libs/binder/rust/sys/BinderBindings.hpp
@@ -17,6 +17,7 @@
#include <android/binder_ibinder.h>
#include <android/binder_parcel.h>
#include <android/binder_status.h>
+#include <android/persistable_bundle.h>
/* Platform only */
#if defined(ANDROID_PLATFORM) || defined(__ANDROID_VENDOR__)
@@ -91,6 +92,11 @@
#endif
};
+enum {
+ APERSISTABLEBUNDLE_KEY_NOT_FOUND = APERSISTABLEBUNDLE_KEY_NOT_FOUND,
+ APERSISTABLEBUNDLE_ALLOCATOR_FAILED = APERSISTABLEBUNDLE_ALLOCATOR_FAILED,
+};
+
} // namespace consts
} // namespace c_interface
diff --git a/libs/binder/servicedispatcher.cpp b/libs/binder/servicedispatcher.cpp
index be99065..78fe2a8 100644
--- a/libs/binder/servicedispatcher.cpp
+++ b/libs/binder/servicedispatcher.cpp
@@ -127,7 +127,12 @@
// We can't send BpBinder for regular binder over RPC.
return android::binder::Status::fromStatusT(android::INVALID_OPERATION);
}
- android::binder::Status checkService(const std::string&, android::os::Service*) override {
+ android::binder::Status checkService(const std::string&,
+ android::sp<android::IBinder>*) override {
+ // We can't send BpBinder for regular binder over RPC.
+ return android::binder::Status::fromStatusT(android::INVALID_OPERATION);
+ }
+ android::binder::Status checkService2(const std::string&, android::os::Service*) override {
// We can't send BpBinder for regular binder over RPC.
return android::binder::Status::fromStatusT(android::INVALID_OPERATION);
}
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index c21d7c6..f412dfb 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -803,6 +803,28 @@
}
cc_test {
+ name: "binderStabilityIntegrationTest",
+ defaults: ["binder_test_defaults"],
+ srcs: [
+ "binderStabilityIntegrationTest.cpp",
+ ],
+
+ shared_libs: [
+ "libbinder",
+ "libutils",
+ ],
+ static_libs: [
+ "libprocpartition",
+ ],
+
+ test_suites: [
+ "general-tests",
+ "vts",
+ ],
+ require_root: true,
+}
+
+cc_test {
name: "binderAllocationLimits",
defaults: ["binder_test_defaults"],
srcs: ["binderAllocationLimits.cpp"],
diff --git a/libs/binder/tests/IBinderRpcTest.aidl b/libs/binder/tests/IBinderRpcTest.aidl
index 1164767..dcd6461 100644
--- a/libs/binder/tests/IBinderRpcTest.aidl
+++ b/libs/binder/tests/IBinderRpcTest.aidl
@@ -34,6 +34,8 @@
void holdBinder(@nullable IBinder binder);
@nullable IBinder getHeldBinder();
+ byte[] repeatBytes(in byte[] bytes);
+
// Idea is client creates its own instance of IBinderRpcTest and calls this,
// and the server calls 'binder' with (calls - 1) passing itself as 'binder',
// going back and forth until calls = 0
diff --git a/libs/binder/tests/binderAllocationLimits.cpp b/libs/binder/tests/binderAllocationLimits.cpp
index c0c0aae..339ce4b 100644
--- a/libs/binder/tests/binderAllocationLimits.cpp
+++ b/libs/binder/tests/binderAllocationLimits.cpp
@@ -22,17 +22,33 @@
#include <binder/RpcServer.h>
#include <binder/RpcSession.h>
#include <cutils/trace.h>
+#include <gtest/gtest-spi.h>
#include <gtest/gtest.h>
#include <utils/CallStack.h>
#include <malloc.h>
+#include <atomic>
#include <functional>
+#include <numeric>
#include <vector>
using namespace android::binder::impl;
static android::String8 gEmpty(""); // make sure first allocation from optimization runs
+struct State {
+ State(std::vector<size_t>&& expectedMallocs) : expectedMallocs(std::move(expectedMallocs)) {}
+ ~State() {
+ size_t num = numMallocs.load();
+ if (expectedMallocs.size() != num) {
+ ADD_FAILURE() << "Expected " << expectedMallocs.size() << " allocations, but got "
+ << num;
+ }
+ }
+ const std::vector<size_t> expectedMallocs;
+ std::atomic<size_t> numMallocs;
+};
+
struct DestructionAction {
DestructionAction(std::function<void()> f) : mF(std::move(f)) {}
~DestructionAction() { mF(); };
@@ -95,8 +111,7 @@
// Action to execute when malloc is hit. Supports nesting. Malloc is not
// restricted when the allocation hook is being processed.
-__attribute__((warn_unused_result))
-DestructionAction OnMalloc(LambdaHooks::AllocationHook f) {
+__attribute__((warn_unused_result)) DestructionAction OnMalloc(LambdaHooks::AllocationHook f) {
MallocHooks before = MallocHooks::save();
LambdaHooks::lambdas.emplace_back(std::move(f));
LambdaHooks::lambda_malloc_hooks.overwrite();
@@ -106,6 +121,22 @@
});
}
+DestructionAction setExpectedMallocs(std::vector<size_t>&& expected) {
+ auto state = std::make_shared<State>(std::move(expected));
+ return OnMalloc([state = state](size_t bytes) {
+ size_t num = state->numMallocs.fetch_add(1);
+ if (num >= state->expectedMallocs.size() || state->expectedMallocs[num] != bytes) {
+ ADD_FAILURE() << "Unexpected allocation number " << num << " of size " << bytes
+ << " bytes" << std::endl
+ << android::CallStack::stackToString("UNEXPECTED ALLOCATION",
+ android::CallStack::getCurrent(
+ 4 /*ignoreDepth*/)
+ .get())
+ << std::endl;
+ }
+ });
+}
+
// exported symbol, to force compiler not to optimize away pointers we set here
const void* imaginary_use;
@@ -119,16 +150,53 @@
imaginary_use = new int[10];
}
+ delete[] reinterpret_cast<const int*>(imaginary_use);
EXPECT_EQ(mallocs, 1u);
}
+TEST(TestTheTest, OnMallocWithExpectedMallocs) {
+ std::vector<size_t> expectedMallocs = {
+ 4,
+ 16,
+ 8,
+ };
+ {
+ const auto on_malloc = setExpectedMallocs(std::move(expectedMallocs));
+ imaginary_use = new int32_t[1];
+ delete[] reinterpret_cast<const int*>(imaginary_use);
+ imaginary_use = new int32_t[4];
+ delete[] reinterpret_cast<const int*>(imaginary_use);
+ imaginary_use = new int32_t[2];
+ delete[] reinterpret_cast<const int*>(imaginary_use);
+ }
+}
+
+TEST(TestTheTest, OnMallocWithExpectedMallocsWrongSize) {
+ std::vector<size_t> expectedMallocs = {
+ 4,
+ 16,
+ 100000,
+ };
+ EXPECT_NONFATAL_FAILURE(
+ {
+ const auto on_malloc = setExpectedMallocs(std::move(expectedMallocs));
+ imaginary_use = new int32_t[1];
+ delete[] reinterpret_cast<const int*>(imaginary_use);
+ imaginary_use = new int32_t[4];
+ delete[] reinterpret_cast<const int*>(imaginary_use);
+ imaginary_use = new int32_t[2];
+ delete[] reinterpret_cast<const int*>(imaginary_use);
+ },
+ "Unexpected allocation number 2 of size 8 bytes");
+}
__attribute__((warn_unused_result))
DestructionAction ScopeDisallowMalloc() {
return OnMalloc([&](size_t bytes) {
- ADD_FAILURE() << "Unexpected allocation: " << bytes;
+ FAIL() << "Unexpected allocation: " << bytes;
using android::CallStack;
- std::cout << CallStack::stackToString("UNEXPECTED ALLOCATION", CallStack::getCurrent(4 /*ignoreDepth*/).get())
+ std::cout << CallStack::stackToString("UNEXPECTED ALLOCATION",
+ CallStack::getCurrent(4 /*ignoreDepth*/).get())
<< std::endl;
});
}
@@ -224,6 +292,51 @@
EXPECT_EQ(mallocs, 1u);
}
+TEST(BinderAccessorAllocation, AddAccessorCheckService) {
+ // Need to call defaultServiceManager() before checking malloc because it
+ // will allocate an instance in the call_once
+ const auto sm = defaultServiceManager();
+ const std::string kInstanceName1 = "foo.bar.IFoo/default";
+ const std::string kInstanceName2 = "foo.bar.IFoo2/default";
+ const String16 kInstanceName16(kInstanceName1.c_str());
+ std::vector<size_t> expectedMallocs = {
+ // addAccessorProvider
+ 112, // new AccessorProvider
+ 16, // new AccessorProviderEntry
+ // checkService
+ 45, // String8 from String16 in CppShim::checkService
+ 128, // writeInterfaceToken
+ 16, // getInjectedAccessor, new AccessorProviderEntry
+ 66, // getInjectedAccessor, String16
+ 45, // String8 from String16 in AccessorProvider::provide
+ };
+ std::set<std::string> supportedInstances = {kInstanceName1, kInstanceName2};
+ auto onMalloc = setExpectedMallocs(std::move(expectedMallocs));
+
+ auto receipt =
+ android::addAccessorProvider(std::move(supportedInstances),
+ [&](const String16&) -> sp<IBinder> { return nullptr; });
+ EXPECT_FALSE(receipt.expired());
+
+ sp<IBinder> binder = sm->checkService(kInstanceName16);
+
+ status_t status = android::removeAccessorProvider(receipt);
+}
+
+TEST(BinderAccessorAllocation, AddAccessorEmpty) {
+ std::vector<size_t> expectedMallocs = {
+ 48, // From ALOGE with empty set of instances
+ };
+ std::set<std::string> supportedInstances = {};
+ auto onMalloc = setExpectedMallocs(std::move(expectedMallocs));
+
+ auto receipt =
+ android::addAccessorProvider(std::move(supportedInstances),
+ [&](const String16&) -> sp<IBinder> { return nullptr; });
+
+ EXPECT_TRUE(receipt.expired());
+}
+
TEST(RpcBinderAllocation, SetupRpcServer) {
std::string tmp = getenv("TMPDIR") ?: "/tmp";
std::string addr = tmp + "/binderRpcBenchmark";
@@ -255,6 +368,7 @@
}
int main(int argc, char** argv) {
+ LOG(INFO) << "Priming static log variables for binderAllocationLimits.";
if (getenv("LIBC_HOOKS_ENABLE") == nullptr) {
CHECK(0 == setenv("LIBC_HOOKS_ENABLE", "1", true /*overwrite*/));
execv(argv[0], argv);
diff --git a/libs/binder/tests/binderCacheUnitTest.cpp b/libs/binder/tests/binderCacheUnitTest.cpp
index 19395c2..121e5ae 100644
--- a/libs/binder/tests/binderCacheUnitTest.cpp
+++ b/libs/binder/tests/binderCacheUnitTest.cpp
@@ -74,7 +74,7 @@
public:
MockAidlServiceManager() : innerSm() {}
- binder::Status checkService(const ::std::string& name, os::Service* _out) override {
+ binder::Status checkService2(const ::std::string& name, os::Service* _out) override {
os::ServiceWithMetadata serviceWithMetadata = os::ServiceWithMetadata();
serviceWithMetadata.service = innerSm.getService(String16(name.c_str()));
serviceWithMetadata.isLazyService = false;
@@ -98,7 +98,7 @@
public:
MockAidlServiceManager2() : innerSm() {}
- binder::Status checkService(const ::std::string& name, os::Service* _out) override {
+ binder::Status checkService2(const ::std::string& name, os::Service* _out) override {
os::ServiceWithMetadata serviceWithMetadata = os::ServiceWithMetadata();
serviceWithMetadata.service = innerSm.getService(String16(name.c_str()));
serviceWithMetadata.isLazyService = true;
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 08fa03c..891c0a2 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -44,6 +44,7 @@
#include <processgroup/processgroup.h>
#include <utils/Flattenable.h>
#include <utils/SystemClock.h>
+#include "binder/IServiceManagerUnitTestHelper.h"
#include <linux/sched.h>
#include <sys/epoll.h>
@@ -344,7 +345,12 @@
}
bool checkFreezeSupport() {
- std::ifstream freezer_file("/sys/fs/cgroup/uid_0/cgroup.freeze");
+ std::string path;
+ if (!CgroupGetAttributePathForTask("FreezerState", getpid(), &path)) {
+ return false;
+ }
+
+ std::ifstream freezer_file(path);
// Pass test on devices where the cgroup v2 freezer is not supported
if (freezer_file.fail()) {
return false;
@@ -580,14 +586,14 @@
EXPECT_EQ(NO_ERROR, sm->addService(String16("binderLibTest-manager"), binder));
}
+class LocalRegistrationCallbackImpl : public virtual IServiceManager::LocalRegistrationCallback {
+ void onServiceRegistration(const String16&, const sp<IBinder>&) override {}
+ virtual ~LocalRegistrationCallbackImpl() {}
+};
+
TEST_F(BinderLibTest, RegisterForNotificationsFailure) {
auto sm = defaultServiceManager();
- using LocalRegistrationCallback = IServiceManager::LocalRegistrationCallback;
- class LocalRegistrationCallbackImpl : public virtual LocalRegistrationCallback {
- void onServiceRegistration(const String16&, const sp<IBinder>&) override {}
- virtual ~LocalRegistrationCallbackImpl() {}
- };
- sp<LocalRegistrationCallback> cb = sp<LocalRegistrationCallbackImpl>::make();
+ sp<IServiceManager::LocalRegistrationCallback> cb = sp<LocalRegistrationCallbackImpl>::make();
EXPECT_EQ(BAD_VALUE, sm->registerForNotifications(String16("ValidName"), nullptr));
EXPECT_EQ(UNKNOWN_ERROR, sm->registerForNotifications(String16("InvalidName!$"), cb));
@@ -595,12 +601,7 @@
TEST_F(BinderLibTest, UnregisterForNotificationsFailure) {
auto sm = defaultServiceManager();
- using LocalRegistrationCallback = IServiceManager::LocalRegistrationCallback;
- class LocalRegistrationCallbackImpl : public virtual LocalRegistrationCallback {
- void onServiceRegistration(const String16&, const sp<IBinder>&) override {}
- virtual ~LocalRegistrationCallbackImpl() {}
- };
- sp<LocalRegistrationCallback> cb = sp<LocalRegistrationCallbackImpl>::make();
+ sp<IServiceManager::LocalRegistrationCallback> cb = sp<LocalRegistrationCallbackImpl>::make();
EXPECT_EQ(OK, sm->registerForNotifications(String16("ValidName"), cb));
@@ -1783,6 +1784,43 @@
EXPECT_EQ(sm->unregisterForNotifications(String16("RogerRafa"), cb), OK);
}
+// Make sure all IServiceManager APIs will function without an AIDL service
+// manager registered on the device.
+TEST(ServiceManagerNoAidlServer, SanityCheck) {
+ String16 kServiceName("no_services_exist");
+ // This is what clients will see when there is no servicemanager process
+ // that registers itself as context object 0.
+ // Can't use setDefaultServiceManager() here because these test cases run in
+ // the same process and will abort when called twice or before/after
+ // defaultServiceManager().
+ sp<IServiceManager> sm = getServiceManagerShimFromAidlServiceManagerForTests(nullptr);
+ auto status = sm->addService(kServiceName, sp<BBinder>::make());
+ // CppBackendShim returns Status::exceptionCode as the status_t
+ EXPECT_EQ(status, Status::Exception::EX_UNSUPPORTED_OPERATION) << statusToString(status);
+ auto service = sm->checkService(String16("no_services_exist"));
+ EXPECT_TRUE(service == nullptr);
+ auto list = sm->listServices(android::IServiceManager::DUMP_FLAG_PRIORITY_ALL);
+ EXPECT_TRUE(list.isEmpty());
+ bool declared = sm->isDeclared(kServiceName);
+ EXPECT_FALSE(declared);
+ list = sm->getDeclaredInstances(kServiceName);
+ EXPECT_TRUE(list.isEmpty());
+ auto updatable = sm->updatableViaApex(kServiceName);
+ EXPECT_EQ(updatable, std::nullopt);
+ list = sm->getUpdatableNames(kServiceName);
+ EXPECT_TRUE(list.isEmpty());
+ auto conInfo = sm->getConnectionInfo(kServiceName);
+ EXPECT_EQ(conInfo, std::nullopt);
+ auto cb = sp<LocalRegistrationCallbackImpl>::make();
+ status = sm->registerForNotifications(kServiceName, cb);
+ EXPECT_EQ(status, UNKNOWN_ERROR) << statusToString(status);
+ status = sm->unregisterForNotifications(kServiceName, cb);
+ EXPECT_EQ(status, BAD_VALUE) << statusToString(status);
+ auto dbgInfos = sm->getServiceDebugInfo();
+ EXPECT_TRUE(dbgInfos.empty());
+ sm->enableAddServiceCache(true);
+}
+
TEST_F(BinderLibTest, ThreadPoolAvailableThreads) {
Parcel data, reply;
sp<IBinder> server = addServer();
diff --git a/libs/binder/tests/binderParcelUnitTest.cpp b/libs/binder/tests/binderParcelUnitTest.cpp
index 6259d9d..a71da3f 100644
--- a/libs/binder/tests/binderParcelUnitTest.cpp
+++ b/libs/binder/tests/binderParcelUnitTest.cpp
@@ -197,6 +197,17 @@
ASSERT_EQ(2, p2.readInt32());
}
+TEST(Parcel, AppendWithBadDataPos) {
+ Parcel p1;
+ p1.writeInt32(1);
+ p1.writeInt32(1);
+ Parcel p2;
+ p2.setDataCapacity(8);
+ p2.setDataPosition(10000);
+
+ EXPECT_EQ(android::BAD_VALUE, p2.appendFrom(&p1, 0, 8));
+}
+
TEST(Parcel, HasBinders) {
sp<IBinder> b1 = sp<BBinder>::make();
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index da5a8e3..7c9c452 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -382,7 +382,7 @@
sockaddr_un addr_un{};
addr_un.sun_family = AF_UNIX;
strcpy(addr_un.sun_path, serverConfig.addr.c_str());
- addr = *reinterpret_cast<sockaddr_storage*>(&addr_un);
+ std::memcpy(&addr, &addr_un, sizeof(sockaddr_un));
addrLen = sizeof(sockaddr_un);
status = session->setupPreconnectedClient({}, [=]() {
@@ -394,7 +394,7 @@
sockaddr_un addr_un{};
addr_un.sun_family = AF_UNIX;
strcpy(addr_un.sun_path, serverConfig.addr.c_str());
- addr = *reinterpret_cast<sockaddr_storage*>(&addr_un);
+ std::memcpy(&addr, &addr_un, sizeof(sockaddr_un));
addrLen = sizeof(sockaddr_un);
status = session->setupUnixDomainClient(serverConfig.addr.c_str());
@@ -409,7 +409,7 @@
.svm_port = static_cast<unsigned int>(serverInfo.port),
.svm_cid = VMADDR_CID_LOCAL,
};
- addr = *reinterpret_cast<sockaddr_storage*>(&addr_vm);
+ std::memcpy(&addr, &addr_vm, sizeof(sockaddr_vm));
addrLen = sizeof(sockaddr_vm);
status = session->setupVsockClient(VMADDR_CID_LOCAL, serverInfo.port);
@@ -420,7 +420,7 @@
addr_in.sin_family = AF_INET;
addr_in.sin_port = htons(serverInfo.port);
inet_aton(ip_addr.c_str(), &addr_in.sin_addr);
- addr = *reinterpret_cast<sockaddr_storage*>(&addr_in);
+ std::memcpy(&addr, &addr_in, sizeof(sockaddr_in));
addrLen = sizeof(sockaddr_in);
status = session->setupInetClient(ip_addr.c_str(), serverInfo.port);
@@ -711,6 +711,35 @@
proc.proc->sessions.erase(proc.proc->sessions.begin() + 1);
}
+// TODO(b/392717039): can we move this to universal tests?
+TEST_P(BinderRpc, SendTooLargeVector) {
+ if (GetParam().singleThreaded) {
+ GTEST_SKIP() << "Requires multi-threaded server to test one of the sessions crashing.";
+ }
+
+ auto proc = createRpcTestSocketServerProcess({.numSessions = 2});
+
+ // need a working transaction
+ EXPECT_EQ(OK, proc.rootBinder->pingBinder());
+
+ // see libbinder internal Constants.h
+ const size_t kTooLargeSize = 650 * 1024;
+ const std::vector<uint8_t> kTestValue(kTooLargeSize / sizeof(uint8_t), 42);
+
+ // TODO(b/392717039): Telling a server to allocate too much data currently causes the session to
+ // close since RpcServer treats any transaction error as a failure. We likely want to change
+ // this behavior to be a soft failure, since it isn't hard to keep track of this state.
+ sp<IBinderRpcTest> rootIface2 = interface_cast<IBinderRpcTest>(proc.proc->sessions.at(1).root);
+ std::vector<uint8_t> result;
+ status_t res = rootIface2->repeatBytes(kTestValue, &result).transactionError();
+
+ // TODO(b/392717039): consistent error results always
+ EXPECT_TRUE(res == -ECONNRESET || res == DEAD_OBJECT) << statusToString(res);
+
+ // died, so remove it for checks in destructor of proc
+ proc.proc->sessions.erase(proc.proc->sessions.begin() + 1);
+}
+
TEST_P(BinderRpc, SessionWithIncomingThreadpoolDoesntLeak) {
if (clientOrServerSingleThreaded()) {
GTEST_SKIP() << "This test requires multiple threads";
@@ -1328,6 +1357,109 @@
EXPECT_EQ(status, OK);
}
+class BinderRpcAccessorNoConnection : public ::testing::Test {};
+
+TEST_F(BinderRpcAccessorNoConnection, listServices) {
+ const String16 kInstanceName("super.cool.service/better_than_default");
+ const String16 kInstanceName2("super.cool.service/better_than_default2");
+
+ auto receipt =
+ addAccessorProvider({String8(kInstanceName).c_str(), String8(kInstanceName2).c_str()},
+ [&](const String16&) -> sp<IBinder> { return nullptr; });
+ EXPECT_FALSE(receipt.expired());
+ Vector<String16> list =
+ defaultServiceManager()->listServices(IServiceManager::DUMP_FLAG_PRIORITY_ALL);
+ bool name1 = false;
+ bool name2 = false;
+ for (auto name : list) {
+ if (name == kInstanceName) name1 = true;
+ if (name == kInstanceName2) name2 = true;
+ }
+ EXPECT_TRUE(name1);
+ EXPECT_TRUE(name2);
+ status_t status = removeAccessorProvider(receipt);
+ EXPECT_EQ(status, OK);
+}
+
+TEST_F(BinderRpcAccessorNoConnection, isDeclared) {
+ const String16 kInstanceName("super.cool.service/default");
+ const String16 kInstanceName2("still_counts_as_declared");
+
+ auto receipt =
+ addAccessorProvider({String8(kInstanceName).c_str(), String8(kInstanceName2).c_str()},
+ [&](const String16&) -> sp<IBinder> { return nullptr; });
+ EXPECT_FALSE(receipt.expired());
+ EXPECT_TRUE(defaultServiceManager()->isDeclared(kInstanceName));
+ EXPECT_TRUE(defaultServiceManager()->isDeclared(kInstanceName2));
+ EXPECT_FALSE(defaultServiceManager()->isDeclared(String16("doesnt_exist")));
+ status_t status = removeAccessorProvider(receipt);
+ EXPECT_EQ(status, OK);
+}
+
+TEST_F(BinderRpcAccessorNoConnection, getDeclaredInstances) {
+ const String16 kInstanceName("super.cool.service.IFoo/default");
+ const String16 kInstanceName2("super.cool.service.IFoo/extra/default");
+ const String16 kInstanceName3("super.cool.service.IFoo");
+
+ auto receipt =
+ addAccessorProvider({String8(kInstanceName).c_str(), String8(kInstanceName2).c_str(),
+ String8(kInstanceName3).c_str()},
+ [&](const String16&) -> sp<IBinder> { return nullptr; });
+ EXPECT_FALSE(receipt.expired());
+ Vector<String16> list =
+ defaultServiceManager()->getDeclaredInstances(String16("super.cool.service.IFoo"));
+ // We would prefer ASSERT_EQ here, but we must call removeAccessorProvider
+ EXPECT_EQ(list.size(), 3u);
+ if (list.size() == 3) {
+ bool name1 = false;
+ bool name2 = false;
+ bool name3 = false;
+ for (auto name : list) {
+ if (name == String16("default")) name1 = true;
+ if (name == String16("extra/default")) name2 = true;
+ if (name == String16()) name3 = true;
+ }
+ EXPECT_TRUE(name1) << String8(list[0]);
+ EXPECT_TRUE(name2) << String8(list[1]);
+ EXPECT_TRUE(name3) << String8(list[2]);
+ }
+
+ status_t status = removeAccessorProvider(receipt);
+ EXPECT_EQ(status, OK);
+}
+
+TEST_F(BinderRpcAccessorNoConnection, getDeclaredWrongInstances) {
+ const String16 kInstanceName("super.cool.service.IFoo");
+
+ auto receipt = addAccessorProvider({String8(kInstanceName).c_str()},
+ [&](const String16&) -> sp<IBinder> { return nullptr; });
+ EXPECT_FALSE(receipt.expired());
+ Vector<String16> list = defaultServiceManager()->getDeclaredInstances(String16("unknown"));
+ EXPECT_TRUE(list.empty());
+
+ status_t status = removeAccessorProvider(receipt);
+ EXPECT_EQ(status, OK);
+}
+
+TEST_F(BinderRpcAccessorNoConnection, getDeclaredInstancesSlash) {
+ // This is treated as if there were no '/' and the declared instance is ""
+ const String16 kInstanceName("super.cool.service.IFoo/");
+
+ auto receipt = addAccessorProvider({String8(kInstanceName).c_str()},
+ [&](const String16&) -> sp<IBinder> { return nullptr; });
+ EXPECT_FALSE(receipt.expired());
+ Vector<String16> list =
+ defaultServiceManager()->getDeclaredInstances(String16("super.cool.service.IFoo"));
+ bool name1 = false;
+ for (auto name : list) {
+ if (name == String16("")) name1 = true;
+ }
+ EXPECT_TRUE(name1);
+
+ status_t status = removeAccessorProvider(receipt);
+ EXPECT_EQ(status, OK);
+}
+
constexpr const char* kARpcInstance = "some.instance.name.IFoo/default";
const char* kARpcSupportedServices[] = {
kARpcInstance,
@@ -2639,7 +2771,9 @@
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
+#ifndef __ANDROID__
__android_log_set_logger(__android_log_stderr_logger);
+#endif
return RUN_ALL_TESTS();
}
diff --git a/libs/binder/tests/binderRpcTestCommon.h b/libs/binder/tests/binderRpcTestCommon.h
index dc22647..6e00246 100644
--- a/libs/binder/tests/binderRpcTestCommon.h
+++ b/libs/binder/tests/binderRpcTestCommon.h
@@ -348,6 +348,10 @@
*out = binder;
return Status::ok();
}
+ Status repeatBytes(const std::vector<uint8_t>& bytes, std::vector<uint8_t>* out) override {
+ *out = bytes;
+ return Status::ok();
+ }
static sp<IBinder> mHeldBinder;
Status holdBinder(const sp<IBinder>& binder) override {
mHeldBinder = binder;
diff --git a/libs/binder/tests/binderRpcTestService.cpp b/libs/binder/tests/binderRpcTestService.cpp
index aef9464..0084b9a 100644
--- a/libs/binder/tests/binderRpcTestService.cpp
+++ b/libs/binder/tests/binderRpcTestService.cpp
@@ -100,7 +100,9 @@
};
int main(int argc, char* argv[]) {
+#ifndef __ANDROID__
__android_log_set_logger(__android_log_stderr_logger);
+#endif
LOG_ALWAYS_FATAL_IF(argc != 3, "Invalid number of arguments: %d", argc);
unique_fd writeEnd(atoi(argv[1]));
diff --git a/libs/binder/tests/binderRpcUniversalTests.cpp b/libs/binder/tests/binderRpcUniversalTests.cpp
index c6fd487..d227e6e 100644
--- a/libs/binder/tests/binderRpcUniversalTests.cpp
+++ b/libs/binder/tests/binderRpcUniversalTests.cpp
@@ -209,6 +209,18 @@
EXPECT_EQ(0, MyBinderRpcSession::gNum);
}
+TEST_P(BinderRpc, SendLargeVector) {
+ auto proc = createRpcTestSocketServerProcess({});
+
+ // see libbinder internal Constants.h
+ const size_t kLargeSize = 550 * 1024;
+ const std::vector<uint8_t> kTestValue(kLargeSize / sizeof(uint8_t), 42);
+
+ std::vector<uint8_t> result;
+ EXPECT_OK(proc.rootIface->repeatBytes(kTestValue, &result));
+ EXPECT_EQ(result, kTestValue);
+}
+
TEST_P(BinderRpc, RepeatTheirBinder) {
auto proc = createRpcTestSocketServerProcess({});
@@ -498,9 +510,9 @@
// same thread, everything should have happened in a nested call. Otherwise,
// the callback will be processed on another thread.
if (callIsOneway || callbackIsOneway || delayed) {
- using std::literals::chrono_literals::operator""s;
+ using std::literals::chrono_literals::operator""ms;
RpcMutexUniqueLock _l(cb->mMutex);
- cb->mCv.wait_for(_l, 1s, [&] { return !cb->mValues.empty(); });
+ cb->mCv.wait_for(_l, 1500ms, [&] { return !cb->mValues.empty(); });
}
EXPECT_EQ(cb->mValues.size(), 1UL)
diff --git a/libs/binder/tests/binderSafeInterfaceTest.cpp b/libs/binder/tests/binderSafeInterfaceTest.cpp
index 849dc7c..45b2103 100644
--- a/libs/binder/tests/binderSafeInterfaceTest.cpp
+++ b/libs/binder/tests/binderSafeInterfaceTest.cpp
@@ -789,7 +789,7 @@
std::optional<int32_t> waitForCallback() {
std::unique_lock<decltype(mMutex)> lock(mMutex);
bool success =
- mCondition.wait_for(lock, 100ms, [&]() { return static_cast<bool>(mValue); });
+ mCondition.wait_for(lock, 1000ms, [&]() { return static_cast<bool>(mValue); });
return success ? mValue : std::nullopt;
}
@@ -858,7 +858,13 @@
ASSERT_EQ(b + 1, bPlusOne);
}
-extern "C" int main(int argc, char **argv) {
+} // namespace tests
+} // namespace android
+
+int main(int argc, char** argv) {
+ using namespace android;
+ using namespace android::tests;
+
testing::InitGoogleTest(&argc, argv);
if (fork() == 0) {
@@ -875,6 +881,3 @@
return RUN_ALL_TESTS();
}
-
-} // namespace tests
-} // namespace android
diff --git a/libs/binder/tests/binderStabilityIntegrationTest.cpp b/libs/binder/tests/binderStabilityIntegrationTest.cpp
new file mode 100644
index 0000000..cbc4180
--- /dev/null
+++ b/libs/binder/tests/binderStabilityIntegrationTest.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <binder/Binder.h>
+#include <binder/IServiceManager.h>
+#include <binder/Stability.h>
+#include <gtest/gtest.h>
+#include <procpartition/procpartition.h>
+
+using namespace android;
+using android::internal::Stability; // for testing only!
+using android::procpartition::getPartition;
+using android::procpartition::Partition;
+
+class BinderStabilityIntegrationTest : public testing::Test,
+ public ::testing::WithParamInterface<String16> {
+public:
+ virtual ~BinderStabilityIntegrationTest() {}
+};
+
+TEST_P(BinderStabilityIntegrationTest, ExpectedStabilityForItsPartition) {
+ const String16& serviceName = GetParam();
+
+ sp<IBinder> binder = defaultServiceManager()->checkService(serviceName);
+ if (!binder) GTEST_SKIP() << "Could not get service, may have gone away.";
+
+ pid_t pid;
+ status_t res = binder->getDebugPid(&pid);
+ if (res != OK) {
+ GTEST_SKIP() << "Could not talk to service to get PID, res: " << statusToString(res);
+ }
+
+ Partition partition = getPartition(pid);
+
+ Stability::Level level = Stability::Level::UNDECLARED;
+ switch (partition) {
+ case Partition::PRODUCT:
+ case Partition::SYSTEM:
+ case Partition::SYSTEM_EXT:
+ level = Stability::Level::SYSTEM;
+ break;
+ case Partition::VENDOR:
+ case Partition::ODM:
+ level = Stability::Level::VENDOR;
+ break;
+ case Partition::UNKNOWN:
+ GTEST_SKIP() << "Not sure of partition of process.";
+ return;
+ default:
+ ADD_FAILURE() << "Unrecognized partition for service: " << partition;
+ return;
+ }
+
+ ASSERT_TRUE(Stability::check(Stability::getRepr(binder.get()), level))
+ << "Binder hosted on partition " << partition
+ << " should have corresponding stability set.";
+}
+
+std::string PrintTestParam(
+ const testing::TestParamInfo<BinderStabilityIntegrationTest::ParamType>& info) {
+ std::string name = String8(info.param).c_str();
+ for (size_t i = 0; i < name.size(); i++) {
+ bool alnum = false;
+ alnum |= (name[i] >= 'a' && name[i] <= 'z');
+ alnum |= (name[i] >= 'A' && name[i] <= 'Z');
+ alnum |= (name[i] >= '0' && name[i] <= '9');
+ alnum |= (name[i] == '_');
+ if (!alnum) name[i] = '_';
+ }
+
+ // index for uniqueness
+ return std::to_string(info.index) + "__" + name;
+}
+
+INSTANTIATE_TEST_CASE_P(RegisteredServices, BinderStabilityIntegrationTest,
+ ::testing::ValuesIn(defaultServiceManager()->listServices()),
+ PrintTestParam);
diff --git a/libs/binder/tests/binderUtilsHostTest.cpp b/libs/binder/tests/binderUtilsHostTest.cpp
index a62ad96..ca70b66 100644
--- a/libs/binder/tests/binderUtilsHostTest.cpp
+++ b/libs/binder/tests/binderUtilsHostTest.cpp
@@ -89,8 +89,8 @@
}
// ~CommandResult() called, child process is killed.
- // Assert that the second sleep does not finish.
- EXPECT_LT(millisSince(start), 6000);
+ // Assert that the last sleep does not finish.
+ EXPECT_LT(millisSince(start), 8000);
}
TEST(UtilsHost, KillWithSigKill) {
diff --git a/libs/binder/tests/parcel_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/Android.bp
index cac054e..457eaa5 100644
--- a/libs/binder/tests/parcel_fuzzer/Android.bp
+++ b/libs/binder/tests/parcel_fuzzer/Android.bp
@@ -109,6 +109,9 @@
"libcutils",
"libutils",
],
+ header_libs: [
+ "libaidl_transactions",
+ ],
local_include_dirs: ["include_random_parcel"],
export_include_dirs: ["include_random_parcel"],
}
diff --git a/libs/binder/tests/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp
index 07f0143..b2ba1ae 100644
--- a/libs/binder/tests/parcel_fuzzer/binder.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder.cpp
@@ -121,6 +121,11 @@
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;
@@ -312,8 +317,6 @@
PARCEL_READ_NO_STATUS(int, readParcelFileDescriptor),
PARCEL_READ_WITH_STATUS(unique_fd, readUniqueFileDescriptor),
- PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<unique_fd>>,
- readUniqueFileDescriptorVector),
PARCEL_READ_WITH_STATUS(std::optional<std::vector<unique_fd>>, readUniqueFileDescriptorVector),
PARCEL_READ_WITH_STATUS(std::vector<unique_fd>, readUniqueFileDescriptorVector),
diff --git a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h
index 2812da7..11fcb06 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 @@
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/libbinder_driver.cpp b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
index 02e69cc..11aa768 100644
--- a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
+++ b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
@@ -13,6 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
+#include <aidl/transaction_ids.h>
#include <fuzzbinder/libbinder_driver.h>
#include <fuzzbinder/random_parcel.h>
@@ -31,6 +33,28 @@
fuzzService(std::vector<sp<IBinder>>{binder}, std::move(provider));
}
+uint32_t getCode(FuzzedDataProvider& provider) {
+ if (provider.ConsumeBool()) {
+ return provider.ConsumeIntegral<uint32_t>();
+ }
+
+ // Most of the AIDL services will have small set of transaction codes.
+ if (provider.ConsumeBool()) {
+ return provider.ConsumeIntegralInRange<uint32_t>(0, 100);
+ }
+
+ if (provider.ConsumeBool()) {
+ return provider.PickValueInArray<uint32_t>(
+ {IBinder::DUMP_TRANSACTION, IBinder::PING_TRANSACTION,
+ IBinder::SHELL_COMMAND_TRANSACTION, IBinder::INTERFACE_TRANSACTION,
+ IBinder::SYSPROPS_TRANSACTION, IBinder::EXTENSION_TRANSACTION,
+ IBinder::TWEET_TRANSACTION, IBinder::LIKE_TRANSACTION});
+ }
+
+ return provider.ConsumeIntegralInRange<uint32_t>(aidl::kLastMetaMethodId,
+ aidl::kFirstMetaMethodId);
+}
+
void fuzzService(const std::vector<sp<IBinder>>& binders, FuzzedDataProvider&& provider) {
RandomParcelOptions options{
.extraBinders = binders,
@@ -61,16 +85,7 @@
}
while (provider.remaining_bytes() > 0) {
- // Most of the AIDL services will have small set of transaction codes.
- // TODO(b/295942369) : Add remaining transact codes from IBinder.h
- uint32_t code = provider.ConsumeBool() ? provider.ConsumeIntegral<uint32_t>()
- : provider.ConsumeBool()
- ? provider.ConsumeIntegralInRange<uint32_t>(0, 100)
- : provider.PickValueInArray<uint32_t>(
- {IBinder::DUMP_TRANSACTION, IBinder::PING_TRANSACTION,
- IBinder::SHELL_COMMAND_TRANSACTION, IBinder::INTERFACE_TRANSACTION,
- IBinder::SYSPROPS_TRANSACTION, IBinder::EXTENSION_TRANSACTION,
- IBinder::TWEET_TRANSACTION, IBinder::LIKE_TRANSACTION});
+ uint32_t code = getCode(provider);
uint32_t flags = provider.ConsumeIntegral<uint32_t>();
Parcel data;
// for increased fuzz coverage
diff --git a/libs/binder/tests/parcel_fuzzer/main.cpp b/libs/binder/tests/parcel_fuzzer/main.cpp
index 192f9d5..d06b2d9 100644
--- a/libs/binder/tests/parcel_fuzzer/main.cpp
+++ b/libs/binder/tests/parcel_fuzzer/main.cpp
@@ -70,7 +70,7 @@
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 @@
// 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 @@
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 7c19614..61b9612 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 @@
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 0ed8a55..3cb6289 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 @@
// 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/binder/trusty/RpcTransportTipcTrusty.cpp b/libs/binder/trusty/RpcTransportTipcTrusty.cpp
index c74ba0a..65ad896 100644
--- a/libs/binder/trusty/RpcTransportTipcTrusty.cpp
+++ b/libs/binder/trusty/RpcTransportTipcTrusty.cpp
@@ -47,6 +47,71 @@
return mHaveMessage ? OK : WOULD_BLOCK;
}
+ void moveMsgStart(ipc_msg_t* msg, size_t msg_size, size_t offset) {
+ LOG_ALWAYS_FATAL_IF(offset > msg_size, "tried to move message past its end %zd>%zd", offset,
+ msg_size);
+ while (true) {
+ if (offset == 0) {
+ break;
+ }
+ if (offset >= msg->iov[0].iov_len) {
+ // Move to the next iov, this one was sent already
+ offset -= msg->iov[0].iov_len;
+ msg->iov++;
+ msg->num_iov -= 1;
+ } else {
+ // We need to move the base of the current iov
+ msg->iov[0].iov_len -= offset;
+ msg->iov[0].iov_base = static_cast<char*>(msg->iov[0].iov_base) + offset;
+ offset = 0;
+ }
+ }
+ // We only send handles on the first message. This can be changed in the future if we want
+ // to send more handles than the maximum per message limit (which would require sending
+ // multiple messages). The current code makes sure that we send less handles than the
+ // maximum trusty allows.
+ msg->num_handles = 0;
+ }
+
+ status_t sendTrustyMsg(ipc_msg_t* msg, size_t msg_size) {
+ do {
+ ssize_t rc = send_msg(mSocket.fd.get(), msg);
+ if (rc == ERR_NOT_ENOUGH_BUFFER) {
+ // Peer is blocked, wait until it unblocks.
+ // TODO: when tipc supports a send-unblocked handler,
+ // save the message here in a queue and retry it asynchronously
+ // when the handler gets called by the library
+ uevent uevt;
+ do {
+ rc = ::wait(mSocket.fd.get(), &uevt, INFINITE_TIME);
+ if (rc < 0) {
+ return statusFromTrusty(rc);
+ }
+ if (uevt.event & IPC_HANDLE_POLL_HUP) {
+ return DEAD_OBJECT;
+ }
+ } while (!(uevt.event & IPC_HANDLE_POLL_SEND_UNBLOCKED));
+
+ // Retry the send, it should go through this time because
+ // sending is now unblocked
+ rc = send_msg(mSocket.fd.get(), msg);
+ }
+ if (rc < 0) {
+ return statusFromTrusty(rc);
+ }
+ size_t sent_bytes = static_cast<size_t>(rc);
+ if (sent_bytes < msg_size) {
+ moveMsgStart(msg, msg_size, static_cast<size_t>(sent_bytes));
+ msg_size -= sent_bytes;
+ } else {
+ LOG_ALWAYS_FATAL_IF(static_cast<size_t>(rc) != msg_size,
+ "Sent the wrong number of bytes %zd!=%zu", rc, msg_size);
+ break;
+ }
+ } while (true);
+ return OK;
+ }
+
status_t interruptableWriteFully(
FdTrigger* /*fdTrigger*/, iovec* iovs, int niovs,
const std::optional<SmallFunction<status_t()>>& /*altPoll*/,
@@ -86,34 +151,7 @@
msg.handles = msgHandles;
}
- ssize_t rc = send_msg(mSocket.fd.get(), &msg);
- if (rc == ERR_NOT_ENOUGH_BUFFER) {
- // Peer is blocked, wait until it unblocks.
- // TODO: when tipc supports a send-unblocked handler,
- // save the message here in a queue and retry it asynchronously
- // when the handler gets called by the library
- uevent uevt;
- do {
- rc = ::wait(mSocket.fd.get(), &uevt, INFINITE_TIME);
- if (rc < 0) {
- return statusFromTrusty(rc);
- }
- if (uevt.event & IPC_HANDLE_POLL_HUP) {
- return DEAD_OBJECT;
- }
- } while (!(uevt.event & IPC_HANDLE_POLL_SEND_UNBLOCKED));
-
- // Retry the send, it should go through this time because
- // sending is now unblocked
- rc = send_msg(mSocket.fd.get(), &msg);
- }
- if (rc < 0) {
- return statusFromTrusty(rc);
- }
- LOG_ALWAYS_FATAL_IF(static_cast<size_t>(rc) != size,
- "Sent the wrong number of bytes %zd!=%zu", rc, size);
-
- return OK;
+ return sendTrustyMsg(&msg, size);
}
status_t interruptableReadFully(
diff --git a/libs/binder/trusty/binderRpcTest/manifest.json b/libs/binder/trusty/binderRpcTest/manifest.json
index 6e20b8a..da0f2ed 100644
--- a/libs/binder/trusty/binderRpcTest/manifest.json
+++ b/libs/binder/trusty/binderRpcTest/manifest.json
@@ -1,6 +1,6 @@
{
"uuid": "9dbe9fb8-60fd-4bdd-af86-03e95d7ad78b",
"app_name": "binderRpcTest",
- "min_heap": 262144,
+ "min_heap": 4194304,
"min_stack": 20480
}
diff --git a/libs/binder/trusty/binderRpcTest/service/manifest.json b/libs/binder/trusty/binderRpcTest/service/manifest.json
index d2a1fc0..55ff49c 100644
--- a/libs/binder/trusty/binderRpcTest/service/manifest.json
+++ b/libs/binder/trusty/binderRpcTest/service/manifest.json
@@ -1,7 +1,7 @@
{
"uuid": "87e424e5-69d7-4bbd-8b7c-7e24812cbc94",
"app_name": "binderRpcTestService",
- "min_heap": 65536,
+ "min_heap": 4194304,
"min_stack": 20480,
"mgmt_flags": {
"restart_on_exit": true,
diff --git a/libs/binder/trusty/include/binder/RpcServerTrusty.h b/libs/binder/trusty/include/binder/RpcServerTrusty.h
index 583ad01..1ac00ca 100644
--- a/libs/binder/trusty/include/binder/RpcServerTrusty.h
+++ b/libs/binder/trusty/include/binder/RpcServerTrusty.h
@@ -94,9 +94,17 @@
static sp<RpcServer> makeRpcServer(std::unique_ptr<RpcTransportCtx> ctx) {
auto rpcServer = sp<RpcServer>::make(std::move(ctx));
- // TODO(b/266741352): follow-up to prevent needing this in the future
- // Trusty needs to be set to the latest stable version that is in prebuilts there.
- LOG_ALWAYS_FATAL_IF(!rpcServer->setProtocolVersion(0));
+ // By default we use the latest stable version.
+ LOG_ALWAYS_FATAL_IF(!rpcServer->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION));
+
+ // The default behavior in trusty is to allow handles to be passed with tipc IPC.
+ // We add mode NONE so that servers do not reject connections from clients who do
+ // not change their default transport mode.
+ static const std::vector<RpcSession::FileDescriptorTransportMode>
+ TRUSTY_SERVER_SUPPORTED_FD_MODES = {RpcSession::FileDescriptorTransportMode::TRUSTY,
+ RpcSession::FileDescriptorTransportMode::NONE};
+
+ rpcServer->setSupportedFileDescriptorTransportModes(TRUSTY_SERVER_SUPPORTED_FD_MODES);
return rpcServer;
}
diff --git a/libs/binder/trusty/rules.mk b/libs/binder/trusty/rules.mk
index 5e38ad0..dd9d4d1 100644
--- a/libs/binder/trusty/rules.mk
+++ b/libs/binder/trusty/rules.mk
@@ -64,14 +64,24 @@
MODULE_EXPORT_INCLUDES += \
$(LIBBINDER_DIR)/ndk/include_cpp \
+ifeq (false,$(call TOBOOL,$(USE_SYSTEM_BINDER)))
+BINDER_EXTRA_COMPILE_FLAGS := \
+ -D__ANDROID_VENDOR__ \
+ -D__ANDROID_VNDK__ \
+
+else
+BINDER_EXTRA_COMPILE_FLAGS := \
+ -DANDROID_PLATFORM \
+
+endif
+
MODULE_EXPORT_COMPILEFLAGS += \
-DBINDER_RPC_SINGLE_THREADED \
-DBINDER_ENABLE_LIBLOG_ASSERT \
-DBINDER_DISABLE_NATIVE_HANDLE \
-DBINDER_DISABLE_BLOB \
-DBINDER_NO_LIBBASE \
- -D__ANDROID_VENDOR__ \
- -D__ANDROID_VNDK__ \
+ $(BINDER_EXTRA_COMPILE_FLAGS)
# libbinder has some deprecated declarations that we want to produce warnings
# not errors
diff --git a/libs/binder/trusty/rust/binder_ndk_sys/rules.mk b/libs/binder/trusty/rust/binder_ndk_sys/rules.mk
index 2aaa061..56d711e 100644
--- a/libs/binder/trusty/rust/binder_ndk_sys/rules.mk
+++ b/libs/binder/trusty/rust/binder_ndk_sys/rules.mk
@@ -30,9 +30,14 @@
trusty/user/base/lib/trusty-sys \
MODULE_RUSTFLAGS += \
- --cfg 'android_vendor' \
--cfg 'trusty' \
+ifeq (false,$(call TOBOOL,$(USE_SYSTEM_BINDER)))
+MODULE_RUSTFLAGS += \
+ --cfg 'android_vendor' \
+
+endif
+
MODULE_BINDGEN_SRC_HEADER := $(LIBBINDER_DIR)/rust/sys/BinderBindings.hpp
# Add the flags from the flag file
diff --git a/libs/binder/trusty/rust/binder_rpc_test/binder_rpc_test_session/lib.rs b/libs/binder/trusty/rust/binder_rpc_test/binder_rpc_test_session/lib.rs
index 22cba44..caf3117 100644
--- a/libs/binder/trusty/rust/binder_rpc_test/binder_rpc_test_session/lib.rs
+++ b/libs/binder/trusty/rust/binder_rpc_test/binder_rpc_test_session/lib.rs
@@ -82,6 +82,9 @@
fn repeatBinder(&self, _binder: Option<&SpIBinder>) -> Result<Option<SpIBinder>, Status> {
todo!()
}
+ fn repeatBytes(&self, _bytes: &[u8]) -> Result<Vec<u8>, Status> {
+ todo!()
+ }
fn holdBinder(&self, _binder: Option<&SpIBinder>) -> Result<(), Status> {
todo!()
}
diff --git a/libs/binder/trusty/rust/binder_rpc_test/service/main.rs b/libs/binder/trusty/rust/binder_rpc_test/service/main.rs
index c4a758a..6f454be 100644
--- a/libs/binder/trusty/rust/binder_rpc_test/service/main.rs
+++ b/libs/binder/trusty/rust/binder_rpc_test/service/main.rs
@@ -96,6 +96,9 @@
None => Err(Status::from(StatusCode::BAD_VALUE)),
}
}
+ fn repeatBytes(&self, _bytes: &[u8]) -> Result<Vec<u8>, Status> {
+ todo!()
+ }
fn holdBinder(&self, binder: Option<&SpIBinder>) -> Result<(), Status> {
*HOLD_BINDER.lock().unwrap() = binder.cloned();
Ok(())
diff --git a/libs/binder/trusty/rust/binder_rpc_unstable_bindgen/rules.mk b/libs/binder/trusty/rust/binder_rpc_unstable_bindgen/rules.mk
index ef1b7c3..40fc218 100644
--- a/libs/binder/trusty/rust/binder_rpc_unstable_bindgen/rules.mk
+++ b/libs/binder/trusty/rust/binder_rpc_unstable_bindgen/rules.mk
@@ -30,6 +30,8 @@
trusty/user/base/lib/libstdc++-trusty \
trusty/user/base/lib/trusty-sys \
+MODULE_SRCDEPS := $(LIBBINDER_DIR)/include_rpc_unstable/binder_rpc_unstable.hpp
+
MODULE_BINDGEN_SRC_HEADER := $(LOCAL_DIR)/BinderBindings.hpp
MODULE_BINDGEN_FLAGS += \
diff --git a/libs/binder/trusty/rust/rules.mk b/libs/binder/trusty/rust/rules.mk
index e622b22..acd74d2 100644
--- a/libs/binder/trusty/rust/rules.mk
+++ b/libs/binder/trusty/rust/rules.mk
@@ -32,9 +32,15 @@
trusty/user/base/lib/trusty-sys \
MODULE_RUSTFLAGS += \
- --cfg 'android_vendor' \
--cfg 'trusty' \
+ifeq (false,$(call TOBOOL,$(USE_SYSTEM_BINDER)))
+MODULE_RUSTFLAGS += \
+ --cfg 'android_vendor' \
+
+endif
+
+
# Trusty does not have `ProcessState`, so there are a few
# doc links in `IBinder` that are still broken.
MODULE_RUSTFLAGS += \
diff --git a/libs/binderdebug/stats.cpp b/libs/binderdebug/stats.cpp
index 9c26afa..972fbd5 100644
--- a/libs/binderdebug/stats.cpp
+++ b/libs/binderdebug/stats.cpp
@@ -22,9 +22,9 @@
#include <inttypes.h>
-namespace android {
+int main() {
+ using namespace android;
-extern "C" int main() {
// ignore args - we only print csv
// we should use a csv library here for escaping, because
@@ -58,5 +58,3 @@
}
return 0;
}
-
-} // namespace android
diff --git a/libs/binderdebug/tests/binderdebug_test.cpp b/libs/binderdebug/tests/binderdebug_test.cpp
index ea799c0..ad2b581 100644
--- a/libs/binderdebug/tests/binderdebug_test.cpp
+++ b/libs/binderdebug/tests/binderdebug_test.cpp
@@ -60,8 +60,15 @@
EXPECT_GE(pidInfo.threadCount, 1);
}
-extern "C" {
+} // namespace test
+} // namespace binderdebug
+} // namespace android
+
int main(int argc, char** argv) {
+ using namespace android;
+ using namespace android::binderdebug;
+ using namespace android::binderdebug::test;
+
::testing::InitGoogleTest(&argc, argv);
// Create a child/client process to call into the main process so we can ensure
@@ -84,7 +91,3 @@
return RUN_ALL_TESTS();
}
-} // extern "C"
-} // namespace test
-} // namespace binderdebug
-} // namespace android
diff --git a/libs/debugstore/rust/src/core.rs b/libs/debugstore/rust/src/core.rs
index 6bf79d4..16147dd 100644
--- a/libs/debugstore/rust/src/core.rs
+++ b/libs/debugstore/rust/src/core.rs
@@ -48,7 +48,7 @@
///
/// 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 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 2ad7f4e..47760f3 100644
--- a/libs/debugstore/rust/src/storage.rs
+++ b/libs/debugstore/rust/src/storage.rs
@@ -32,14 +32,18 @@
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 @@
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 @@
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 @@
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 @@
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 a9bd11e..2d18d80 100644
--- a/libs/dumputils/dump_utils.cpp
+++ b/libs/dumputils/dump_utils.cpp
@@ -50,7 +50,6 @@
// Native processes to dump on debuggable builds.
static const char* debuggable_native_processes_to_dump[] = {
- "/system/bin/keystore2",
"/system/bin/vold",
NULL,
};
@@ -101,7 +100,7 @@
"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 368f5e0..5244442 100644
--- a/libs/ftl/Android.bp
+++ b/libs/ftl/Android.bp
@@ -21,10 +21,12 @@
"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 0000000..4f5c225
--- /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 0000000..5d5c67b
--- /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 af50a29..dce7778 100644
--- a/libs/graphicsenv/Android.bp
+++ b/libs/graphicsenv/Android.bp
@@ -21,10 +21,27 @@
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 @@
"-Werror",
],
+ static_libs: [
+ "graphicsenv_flags_c_lib",
+ ],
+
shared_libs: [
"libbase",
"libbinder",
@@ -42,6 +63,7 @@
"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 0000000..9e7a4cf
--- /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 a8d5fe7..03e6456 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 @@
}
} // namespace
+namespace graphicsenv_flags = com::android::graphics::graphicsenv::flags;
+
namespace android {
enum NativeLibrary {
@@ -596,7 +599,7 @@
// If path is set to nonempty and shouldUseNativeDriver is true, ANGLE will be used regardless.
void GraphicsEnv::setAngleInfo(const std::string& path, const bool shouldUseNativeDriver,
const std::string& packageName,
- const std::vector<std::string> eglFeatures) {
+ const std::vector<std::string>& eglFeatures) {
if (mShouldUseAngle) {
// ANGLE is already set up for this application process, even if the application
// needs to switch from apk to system or vice versa, the application process must
@@ -606,11 +609,11 @@
return;
}
- mAngleEglFeatures = std::move(eglFeatures);
+ mAngleEglFeatures = eglFeatures;
ALOGV("setting ANGLE path to '%s'", path.c_str());
- mAnglePath = std::move(path);
+ mAnglePath = path;
ALOGV("setting app package name to '%s'", packageName.c_str());
- mPackageName = std::move(packageName);
+ mPackageName = packageName;
if (mAnglePath == "system") {
mShouldUseSystemAngle = true;
}
@@ -618,16 +621,62 @@
mShouldUseAngle = true;
}
mShouldUseNativeDriver = shouldUseNativeDriver;
+
+ if (mShouldUseAngle) {
+ updateAngleFeatureOverrides();
+ }
}
std::string& GraphicsEnv::getPackageName() {
return mPackageName;
}
+// List of ANGLE features to enable, specified in the Global.Settings value "angle_egl_features".
const std::vector<std::string>& GraphicsEnv::getAngleEglFeatures() {
return mAngleEglFeatures;
}
+// List of ANGLE features to override (enabled or disable).
+// The list of overrides is loaded and parsed by GpuService.
+void GraphicsEnv::updateAngleFeatureOverrides() {
+ if (!graphicsenv_flags::angle_feature_overrides()) {
+ return;
+ }
+
+ const sp<IGpuService> gpuService = getGpuService();
+ if (!gpuService) {
+ ALOGE("No GPU service");
+ return;
+ }
+
+ mFeatureOverrides = gpuService->getFeatureOverrides();
+}
+
+void GraphicsEnv::getAngleFeatureOverrides(std::vector<const char*>& enabled,
+ std::vector<const char*>& disabled) {
+ if (!graphicsenv_flags::angle_feature_overrides()) {
+ return;
+ }
+
+ for (const FeatureConfig& feature : mFeatureOverrides.mGlobalFeatures) {
+ if (feature.mEnabled) {
+ enabled.push_back(feature.mFeatureName.c_str());
+ } else {
+ disabled.push_back(feature.mFeatureName.c_str());
+ }
+ }
+
+ if (mFeatureOverrides.mPackageFeatures.count(mPackageName)) {
+ for (const FeatureConfig& feature : mFeatureOverrides.mPackageFeatures[mPackageName]) {
+ if (feature.mEnabled) {
+ enabled.push_back(feature.mFeatureName.c_str());
+ } else {
+ disabled.push_back(feature.mFeatureName.c_str());
+ }
+ }
+ }
+}
+
android_namespace_t* GraphicsEnv::getAngleNamespace() {
std::lock_guard<std::mutex> lock(mNamespaceMutex);
diff --git a/libs/graphicsenv/IGpuService.cpp b/libs/graphicsenv/IGpuService.cpp
index 42e7c37..9a34aff 100644
--- a/libs/graphicsenv/IGpuService.cpp
+++ b/libs/graphicsenv/IGpuService.cpp
@@ -119,6 +119,21 @@
}
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 @@
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 0000000..efa4bca
--- /dev/null
+++ b/libs/graphicsenv/graphicsenv_flags.aconfig
@@ -0,0 +1,9 @@
+package: "com.android.graphics.graphicsenv.flags"
+container: "system"
+
+flag {
+ name: "angle_feature_overrides"
+ namespace: "gpu"
+ description: "This flag controls the ANGLE Feature Overrides in GraphicsEnv."
+ bug: "372694741"
+}
diff --git a/libs/graphicsenv/include/graphicsenv/FeatureOverrides.h b/libs/graphicsenv/include/graphicsenv/FeatureOverrides.h
new file mode 100644
index 0000000..5dc613b
--- /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 b0ab0b9..6821900 100644
--- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
+++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_UI_GRAPHICS_ENV_H
#define ANDROID_UI_GRAPHICS_ENV_H 1
+#include <graphicsenv/FeatureOverrides.h>
#include <graphicsenv/GpuStatsInfo.h>
#include <mutex>
@@ -114,12 +115,15 @@
// If shouldUseNativeDriver is true, it means native GLES drivers must be used for the process.
// If path is set to nonempty and shouldUseNativeDriver is true, ANGLE will be used regardless.
void setAngleInfo(const std::string& path, const bool shouldUseNativeDriver,
- const std::string& packageName, const std::vector<std::string> eglFeatures);
+ const std::string& packageName, const std::vector<std::string>& eglFeatures);
// Get the ANGLE driver namespace.
android_namespace_t* getAngleNamespace();
// Get the app package name.
std::string& getPackageName();
const std::vector<std::string>& getAngleEglFeatures();
+ void updateAngleFeatureOverrides();
+ void getAngleFeatureOverrides(std::vector<const char*>& enabled,
+ std::vector<const char*>& disabled);
// Set the persist.graphics.egl system property value.
void nativeToggleAngleAsSystemDriver(bool enabled);
bool shouldUseSystemAngle();
@@ -177,6 +181,7 @@
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 a0d6e37..442683a 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 @@
// 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 @@
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 22ebfe0..bda2756 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -93,6 +93,7 @@
"android/gui/StalledTransactionInfo.aidl",
"android/**/TouchOcclusionMode.aidl",
"android/gui/TrustedOverlay.aidl",
+ "android/gui/BorderSettings.aidl",
],
}
@@ -266,8 +267,6 @@
"FenceMonitor.cpp",
"Flags.cpp",
"GLConsumer.cpp",
- "IConsumerListener.cpp",
- "IGraphicBufferConsumer.cpp",
"IGraphicBufferProducer.cpp",
"IProducerListener.cpp",
"ISurfaceComposer.cpp",
@@ -283,6 +282,7 @@
"SurfaceControl.cpp",
"SurfaceComposerClient.cpp",
"SyncFeatures.cpp",
+ "TransactionState.cpp",
"VsyncEventData.cpp",
"view/Surface.cpp",
"WindowInfosListenerReporter.cpp",
@@ -296,7 +296,6 @@
cc_defaults {
name: "libgui-defaults",
defaults: ["libgui_bufferqueue-defaults"],
- srcs: [":libgui-sources"],
static_libs: [
"libgui_aidl_static",
"libgui_window_info_static",
@@ -320,6 +319,10 @@
"libgui-defaults",
],
+ srcs: [
+ ":libgui-sources",
+ ],
+
export_static_lib_headers: [
"libgui_aidl_static",
"libgui_window_info_static",
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 7aee903..5b0f21d 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -197,15 +197,15 @@
mUpdateDestinationFrame(updateDestinationFrame) {
createBufferQueue(&mProducer, &mConsumer);
#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
- mBufferItemConsumer = new BLASTBufferItemConsumer(mProducer, mConsumer,
- GraphicBuffer::USAGE_HW_COMPOSER |
- GraphicBuffer::USAGE_HW_TEXTURE,
- 1, false, this);
+ mBufferItemConsumer = sp<BLASTBufferItemConsumer>::make(mProducer, mConsumer,
+ GraphicBuffer::USAGE_HW_COMPOSER |
+ GraphicBuffer::USAGE_HW_TEXTURE,
+ 1, false, this);
#else
- mBufferItemConsumer = new BLASTBufferItemConsumer(mConsumer,
- GraphicBuffer::USAGE_HW_COMPOSER |
- GraphicBuffer::USAGE_HW_TEXTURE,
- 1, false, this);
+ mBufferItemConsumer = sp<BLASTBufferItemConsumer>::make(mConsumer,
+ GraphicBuffer::USAGE_HW_COMPOSER |
+ GraphicBuffer::USAGE_HW_TEXTURE,
+ 1, false, this);
#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
// since the adapter is in the client process, set dequeue timeout
// explicitly so that dequeueBuffer will block
@@ -222,8 +222,6 @@
ComposerServiceAIDL::getComposerService()->getMaxAcquiredBufferCount(&mMaxAcquiredBuffers);
mBufferItemConsumer->setMaxAcquiredBufferCount(mMaxAcquiredBuffers);
mCurrentMaxAcquiredBufferCount = mMaxAcquiredBuffers;
- mNumAcquired = 0;
- mNumFrameAvailable = 0;
TransactionCompletedListener::getInstance()->addQueueStallListener(
[&](const std::string& reason) {
@@ -244,12 +242,6 @@
BQA_LOGV("BLASTBufferQueue created");
}
-BLASTBufferQueue::BLASTBufferQueue(const std::string& name, const sp<SurfaceControl>& surface,
- int width, int height, int32_t format)
- : BLASTBufferQueue(name) {
- update(surface, width, height, format);
-}
-
BLASTBufferQueue::~BLASTBufferQueue() {
TransactionCompletedListener::getInstance()->removeQueueStallListener(this);
if (mPendingTransactions.empty()) {
@@ -421,14 +413,12 @@
stat.frameEventStats.dequeueReadyTime);
}
auto currFrameNumber = stat.frameEventStats.frameNumber;
- std::vector<ReleaseCallbackId> staleReleases;
- for (const auto& [key, value]: mSubmitted) {
- if (currFrameNumber > key.framenumber) {
- staleReleases.push_back(key);
+ // Release stale buffers.
+ for (const auto& [key, _] : mSubmitted) {
+ if (currFrameNumber <= key.framenumber) {
+ continue; // not stale.
}
- }
- for (const auto& staleRelease : staleReleases) {
- releaseBufferCallbackLocked(staleRelease,
+ releaseBufferCallbackLocked(key,
stat.previousReleaseFence
? stat.previousReleaseFence
: Fence::NO_FENCE,
@@ -447,7 +437,7 @@
void BLASTBufferQueue::flushShadowQueue() {
BQA_LOGV("flushShadowQueue");
- int numFramesToFlush = mNumFrameAvailable;
+ int32_t numFramesToFlush = mNumFrameAvailable;
while (numFramesToFlush > 0) {
acquireNextBufferLocked(std::nullopt);
numFramesToFlush--;
@@ -624,7 +614,7 @@
mNumAcquired++;
mLastAcquiredFrameNumber = bufferItem.mFrameNumber;
ReleaseCallbackId releaseCallbackId(buffer->getId(), mLastAcquiredFrameNumber);
- mSubmitted[releaseCallbackId] = bufferItem;
+ mSubmitted.emplace_or_replace(releaseCallbackId, bufferItem);
bool needsDisconnect = false;
mBufferItemConsumer->getConnectionEvents(bufferItem.mFrameNumber, &needsDisconnect);
@@ -647,7 +637,8 @@
bufferItem.mScalingMode, crop);
auto releaseBufferCallback = makeReleaseBufferCallbackThunk();
- sp<Fence> fence = bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE;
+ sp<Fence> fence =
+ bufferItem.mFence ? sp<Fence>::make(bufferItem.mFence->dup()) : Fence::NO_FENCE;
nsecs_t dequeueTime = -1;
{
@@ -857,7 +848,7 @@
void BLASTBufferQueue::onFrameDequeued(const uint64_t bufferId) {
std::lock_guard _lock{mTimestampMutex};
- mDequeueTimestamps[bufferId] = systemTime();
+ mDequeueTimestamps.emplace_or_replace(bufferId, systemTime());
};
void BLASTBufferQueue::onFrameCancelled(const uint64_t bufferId) {
@@ -946,15 +937,22 @@
: Surface(igbp, controlledByApp, scHandle), mBbq(bbq) {}
void allocateBuffers() override {
+ ATRACE_CALL();
uint32_t reqWidth = mReqWidth ? mReqWidth : mUserWidth;
uint32_t reqHeight = mReqHeight ? mReqHeight : mUserHeight;
auto gbp = getIGraphicBufferProducer();
- std::thread ([reqWidth, reqHeight, gbp=getIGraphicBufferProducer(),
- reqFormat=mReqFormat, reqUsage=mReqUsage] () {
+ std::thread allocateThread([reqWidth, reqHeight, gbp = getIGraphicBufferProducer(),
+ reqFormat = mReqFormat, reqUsage = mReqUsage]() {
+ if (com_android_graphics_libgui_flags_allocate_buffer_priority()) {
+ androidSetThreadName("allocateBuffers");
+ pid_t tid = gettid();
+ androidSetThreadPriority(tid, ANDROID_PRIORITY_DISPLAY);
+ }
+
gbp->allocateBuffers(reqWidth, reqHeight,
reqFormat, reqUsage);
-
- }).detach();
+ });
+ allocateThread.detach();
}
status_t setFrameRate(float frameRate, int8_t compatibility,
@@ -1024,7 +1022,7 @@
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,
@@ -1032,9 +1030,9 @@
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);
+ mPendingTransactions.emplace_back(frameNumber, std::move(*t));
// Clear the transaction so it can't be applied elsewhere.
t->clear();
}
@@ -1052,8 +1050,8 @@
void BLASTBufferQueue::mergePendingTransactions(SurfaceComposerClient::Transaction* t,
uint64_t frameNumber) {
auto mergeTransaction =
- [&t, currentFrameNumber = frameNumber](
- std::tuple<uint64_t, SurfaceComposerClient::Transaction> pendingTransaction) {
+ [t, currentFrameNumber = frameNumber](
+ std::pair<uint64_t, SurfaceComposerClient::Transaction>& pendingTransaction) {
auto& [targetFrameNumber, transaction] = pendingTransaction;
if (currentFrameNumber < targetFrameNumber) {
return false;
@@ -1130,10 +1128,10 @@
class AsyncProducerListener : public BnProducerListener {
private:
const sp<IProducerListener> mListener;
+ AsyncProducerListener(const sp<IProducerListener>& listener) : mListener(listener) {}
+ friend class sp<AsyncProducerListener>;
public:
- AsyncProducerListener(const sp<IProducerListener>& listener) : mListener(listener) {}
-
void onBufferReleased() override {
AsyncWorker::getInstance().post([listener = mListener]() { listener->onBufferReleased(); });
}
@@ -1187,7 +1185,7 @@
return BufferQueueProducer::connect(listener, api, producerControlledByApp, output);
}
- return BufferQueueProducer::connect(new AsyncProducerListener(listener), api,
+ return BufferQueueProducer::connect(sp<AsyncProducerListener>::make(listener), api,
producerControlledByApp, output);
}
@@ -1227,6 +1225,7 @@
#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;
@@ -1253,6 +1252,14 @@
}
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
@@ -1344,6 +1351,17 @@
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 3b2d337..9dcd5dc 100644
--- a/libs/gui/BufferItem.cpp
+++ b/libs/gui/BufferItem.cpp
@@ -215,14 +215,14 @@
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 8566419..4926ceb 100644
--- a/libs/gui/BufferItemConsumer.cpp
+++ b/libs/gui/BufferItemConsumer.cpp
@@ -17,6 +17,7 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "BufferItemConsumer"
//#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <utils/Errors.h>
#include <utils/Log.h>
#include <inttypes.h>
@@ -24,6 +25,7 @@
#include <com_android_graphics_libgui_flags.h>
#include <gui/BufferItem.h>
#include <gui/BufferItemConsumer.h>
+#include <gui/Surface.h>
#include <ui/BufferQueueDefs.h>
#include <ui/GraphicBuffer.h>
@@ -35,6 +37,30 @@
namespace android {
+std::tuple<sp<BufferItemConsumer>, sp<Surface>> BufferItemConsumer::create(
+ uint64_t consumerUsage, int bufferCount, bool controlledByApp,
+ bool isConsumerSurfaceFlinger) {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ sp<BufferItemConsumer> bufferItemConsumer =
+ sp<BufferItemConsumer>::make(consumerUsage, bufferCount, controlledByApp,
+ isConsumerSurfaceFlinger);
+ return {bufferItemConsumer, bufferItemConsumer->getSurface()};
+#else
+ sp<IGraphicBufferProducer> igbp;
+ sp<IGraphicBufferConsumer> igbc;
+ BufferQueue::createBufferQueue(&igbp, &igbc, isConsumerSurfaceFlinger);
+ sp<BufferItemConsumer> bufferItemConsumer =
+ sp<BufferItemConsumer>::make(igbc, consumerUsage, bufferCount, controlledByApp);
+ return {bufferItemConsumer, sp<Surface>::make(igbp, controlledByApp)};
+#endif
+}
+
+sp<BufferItemConsumer> BufferItemConsumer::create(const sp<IGraphicBufferConsumer>& consumer,
+ uint64_t consumerUsage, int bufferCount,
+ bool controlledByApp) {
+ return sp<BufferItemConsumer>::make(consumer, consumerUsage, bufferCount, controlledByApp);
+}
+
#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
BufferItemConsumer::BufferItemConsumer(uint64_t consumerUsage, int bufferCount,
bool controlledByApp, bool isConsumerSurfaceFlinger)
@@ -107,13 +133,36 @@
return OK;
}
+status_t BufferItemConsumer::attachBuffer(const sp<GraphicBuffer>& buffer) {
+ if (!buffer) {
+ BI_LOGE("BufferItemConsumer::attachBuffer no input buffer specified.");
+ return BAD_VALUE;
+ }
+
+ Mutex::Autolock _l(mMutex);
+
+ int slot = INVALID_BUFFER_SLOT;
+ status_t status = mConsumer->attachBuffer(&slot, buffer);
+ if (status != OK) {
+ BI_LOGE("BufferItemConsumer::attachBuffer unable to attach buffer %d", status);
+ return status;
+ }
+
+ mSlots[slot] = {
+ .mGraphicBuffer = buffer,
+ .mFence = nullptr,
+ .mFrameNumber = 0,
+ };
+
+ return OK;
+}
+
status_t BufferItemConsumer::releaseBuffer(const BufferItem &item,
const sp<Fence>& releaseFence) {
Mutex::Autolock _l(mMutex);
return releaseBufferSlotLocked(item.mSlot, item.mGraphicBuffer, releaseFence);
}
-#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
status_t BufferItemConsumer::releaseBuffer(const sp<GraphicBuffer>& buffer,
const sp<Fence>& releaseFence) {
Mutex::Autolock _l(mMutex);
@@ -129,7 +178,6 @@
return releaseBufferSlotLocked(slotIndex, buffer, releaseFence);
}
-#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
status_t BufferItemConsumer::releaseBufferSlotLocked(int slotIndex, const sp<GraphicBuffer>& buffer,
const sp<Fence>& releaseFence) {
diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp
index b0f6e69..f21ac18 100644
--- a/libs/gui/BufferQueue.cpp
+++ b/libs/gui/BufferQueue.cpp
@@ -108,6 +108,15 @@
}
#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 @@
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 9855b5b..4681c9e 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -14,10 +14,6 @@
* limitations under the License.
*/
-#include <inttypes.h>
-#include <pwd.h>
-#include <sys/types.h>
-
#define LOG_TAG "BufferQueueConsumer"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
//#define LOG_NDEBUG 0
@@ -48,6 +44,11 @@
#include <com_android_graphics_libgui_flags.h>
+#include <inttypes.h>
+#include <pwd.h>
+#include <sys/types.h>
+#include <optional>
+
namespace android {
// Macros for include BufferQueueCore information in log messages
@@ -341,9 +342,9 @@
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 +478,38 @@
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 +656,43 @@
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 +711,28 @@
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();
@@ -714,20 +768,31 @@
return NO_ERROR;
}
+status_t BufferQueueConsumer::setMaxAcquiredBufferCount(int maxAcquiredBuffers) {
+ return setMaxAcquiredBufferCount(maxAcquiredBuffers, std::nullopt);
+}
+
status_t BufferQueueConsumer::setMaxAcquiredBufferCount(
- int maxAcquiredBuffers) {
+ int maxAcquiredBuffers, std::optional<OnBufferReleasedCallback> onBuffersReleasedCallback) {
ATRACE_FORMAT("%s(%d)", __func__, maxAcquiredBuffers);
- if (maxAcquiredBuffers < 1 ||
- maxAcquiredBuffers > BufferQueueCore::MAX_MAX_ACQUIRED_BUFFERS) {
- BQ_LOGE("setMaxAcquiredBufferCount: invalid count %d",
- maxAcquiredBuffers);
- return BAD_VALUE;
- }
-
- sp<IConsumerListener> listener;
+ std::optional<OnBufferReleasedCallback> callback;
{ // 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) {
@@ -773,13 +838,20 @@
BQ_LOGV("setMaxAcquiredBufferCount: %d", maxAcquiredBuffers);
mCore->mMaxAcquiredBufferCount = maxAcquiredBuffers;
VALIDATE_CONSISTENCY();
- if (delta < 0 && mCore->mBufferReleasedCbEnabled) {
- listener = mCore->mConsumerListener;
+ if (delta < 0) {
+ if (onBuffersReleasedCallback) {
+ callback = std::move(onBuffersReleasedCallback);
+ } else if (mCore->mBufferReleasedCbEnabled) {
+ callback = [listener = mCore->mConsumerListener]() {
+ listener->onBuffersReleased();
+ };
+ }
}
}
+
// Call back without lock held
- if (listener != nullptr) {
- listener->onBuffersReleased();
+ if (callback) {
+ (*callback)();
}
return NO_ERROR;
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index 5a09399..6c79904 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 @@
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 @@
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 @@
}
}
+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 @@
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::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 bc23f49..c08552e 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 @@
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 @@
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,11 +214,13 @@
int minUndequedBufferCount = mCore->getMinUndequeuedBufferCountLocked();
int bufferCount = minUndequedBufferCount + 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);
- bufferCount = BufferQueueDefs::NUM_BUFFER_SLOTS;
- maxDequeuedBuffers = bufferCount - minUndequedBufferCount;
+ "(max %d)",
+ bufferCount, mCore->getTotalSlotCountLocked());
+ bufferCount = mCore->getTotalSlotCountLocked();
+ maxDequeuedBuffers = bufferCount - minUndequedBufferCount;
+ return BAD_VALUE;
}
const int minBufferSlots = mCore->getMinMaxBufferCountLocked();
@@ -320,8 +366,10 @@
// Producers are not allowed to dequeue more than
// mMaxDequeuedBufferCount buffers.
// This check is only done if a buffer has already been queued
- if (mCore->mBufferHasBeenQueued &&
- dequeuedCount >= mCore->mMaxDequeuedBufferCount) {
+ using namespace com::android::graphics::libgui::flags;
+ bool flagGatedBufferHasBeenQueued =
+ bq_always_use_max_dequeued_buffer_count() || mCore->mBufferHasBeenQueued;
+ if (flagGatedBufferHasBeenQueued && dequeuedCount >= mCore->mMaxDequeuedBufferCount) {
// Supress error logs when timeout is non-negative.
if (mDequeueTimeout < 0) {
BQ_LOGE("%s: attempting to exceed the max dequeued buffer "
@@ -649,11 +697,11 @@
.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();
@@ -757,9 +805,9 @@
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
@@ -994,9 +1042,9 @@
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 "
@@ -1240,9 +1288,9 @@
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 "
@@ -1410,6 +1458,9 @@
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
@@ -1417,7 +1468,7 @@
#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);
@@ -1506,8 +1557,7 @@
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 =
@@ -1638,11 +1688,11 @@
#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 e9cb013..4f495d0 100644
--- a/libs/gui/BufferReleaseChannel.cpp
+++ b/libs/gui/BufferReleaseChannel.cpp
@@ -108,7 +108,7 @@
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 @@
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 ba50bf8..80a3543 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 @@
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 @@
"()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 @@
// 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 602bba8..1a975de 100644
--- a/libs/gui/ConsumerBase.cpp
+++ b/libs/gui/ConsumerBase.cpp
@@ -37,6 +37,8 @@
#include <private/gui/ComposerService.h>
+#include <ui/BufferQueueDefs.h>
+
#include <log/log.h>
#include <utils/Log.h>
#include <utils/String8.h>
@@ -59,7 +61,11 @@
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) {
@@ -68,7 +74,12 @@
#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) {
sp<IGraphicBufferProducer> producer;
BufferQueue::createBufferQueue(&producer, &mConsumer, consumerIsSurfaceFlinger);
mSurface = sp<Surface>::make(producer, controlledByApp);
@@ -77,7 +88,11 @@
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) {
@@ -101,9 +116,16 @@
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 +152,11 @@
}
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;
@@ -234,7 +260,10 @@
void ConsumerBase::onBuffersReleased() {
Mutex::Autolock lock(mMutex);
+ onBuffersReleasedLocked();
+}
+void ConsumerBase::onBuffersReleasedLocked() {
CB_LOGV("onBuffersReleased");
if (mAbandoned) {
@@ -242,6 +271,15 @@
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 +287,23 @@
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 +320,11 @@
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 +388,26 @@
}
#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 +461,15 @@
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)
@@ -397,10 +480,10 @@
CB_LOGE("setMaxAcquiredBufferCount: ConsumerBase is abandoned!");
return NO_INIT;
}
- return mConsumer->setMaxAcquiredBufferCount(maxAcquiredBuffers);
+ return mConsumer->setMaxAcquiredBufferCount(maxAcquiredBuffers,
+ {[this]() { onBuffersReleasedLocked(); }});
}
-#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
status_t ConsumerBase::setConsumerIsProtected(bool isProtected) {
Mutex::Autolock lock(mMutex);
if (mAbandoned) {
@@ -409,7 +492,6 @@
}
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 @@
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 @@
freeBufferLocked(i);
}
}
+#endif
+
return OK;
}
@@ -585,9 +678,13 @@
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 @@
// 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 @@
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 23b432e..ecbceb7 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 @@
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 68f10f4..6f23885 100644
--- a/libs/gui/DisplayEventDispatcher.cpp
+++ b/libs/gui/DisplayEventDispatcher.cpp
@@ -167,7 +167,7 @@
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 @@
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 @@
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 85ee2cd..ee2802f 100644
--- a/libs/gui/Flags.cpp
+++ b/libs/gui/Flags.cpp
@@ -29,6 +29,14 @@
#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 5c4879c..1b2354e 100644
--- a/libs/gui/FrameRateUtils.cpp
+++ b/libs/gui/FrameRateUtils.cpp
@@ -42,7 +42,7 @@
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 f2173cd..2c5770d 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 @@
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 @@
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 @@
}
#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 @@
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 @@
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 @@
}
if (mReleasedTexImage == nullptr) {
- mReleasedTexImage = new EglImage(getDebugTexImageBuffer());
+ mReleasedTexImage = sp<EglImage>::make(getDebugTexImageBuffer());
}
mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT;
@@ -354,10 +410,10 @@
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 @@
// 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 @@
// 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 @@
}
} 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 @@
"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 @@
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 @@
}
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 f3bd90c..0000000
--- 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 282957b..0000000
--- 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 0914480..1d1910e 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -81,6 +81,7 @@
GET_LAST_QUEUED_BUFFER2,
SET_FRAME_RATE,
SET_ADDITIONAL_OPTIONS,
+ SET_MAX_BUFER_COUNT_EXTENDED,
};
class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer>
@@ -103,7 +104,7 @@
}
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 @@
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 @@
}
*buf = reply.readInt32();
- *fence = new Fence();
+ *fence = sp<Fence>::make();
result = reply.read(**fence);
if (result != NO_ERROR) {
fence->clear();
@@ -278,7 +293,7 @@
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 @@
}
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 @@
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 @@
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 @@
}
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 @@
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 @@
// ----------------------------------------------------------------------
+#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 @@
}
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 @@
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 @@
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 @@
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 4e92a39..393e1c3 100644
--- a/libs/gui/IGraphicBufferProducerFlattenables.cpp
+++ b/libs/gui/IGraphicBufferProducerFlattenables.cpp
@@ -105,7 +105,7 @@
: 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
FlattenableUtils::read(buffer, size, slot);
- fence = new Fence();
+ fence = sp<Fence>::make();
return fence->unflatten(buffer, size, fds, count);
}
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 2699368..ae4b74e 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -26,6 +26,7 @@
#include <gui/ISurfaceComposer.h>
#include <gui/LayerState.h>
#include <gui/SchedulingPolicy.h>
+#include <gui/TransactionState.h>
#include <private/gui/ParcelUtils.h>
#include <stdint.h>
#include <sys/types.h>
@@ -60,54 +61,12 @@
virtual ~BpSurfaceComposer();
- status_t setTransactionState(
- const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& state,
- Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
- InputWindowCommands commands, int64_t desiredPresentTime, bool isAutoTimestamp,
- const std::vector<client_cache_t>& uncacheBuffers, bool hasListenerCallbacks,
- const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId,
- const std::vector<uint64_t>& mergedTransactionIds) override {
+ status_t setTransactionState(TransactionState&& state) override {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ SAFE_PARCEL(state.writeToParcel, &data);
- frameTimelineInfo.writeToParcel(&data);
-
- SAFE_PARCEL(data.writeUint32, static_cast<uint32_t>(state.size()));
- for (const auto& s : state) {
- SAFE_PARCEL(s.write, data);
- }
-
- SAFE_PARCEL(data.writeUint32, static_cast<uint32_t>(displays.size()));
- for (const auto& d : displays) {
- SAFE_PARCEL(d.write, data);
- }
-
- SAFE_PARCEL(data.writeUint32, flags);
- SAFE_PARCEL(data.writeStrongBinder, applyToken);
- SAFE_PARCEL(commands.write, data);
- SAFE_PARCEL(data.writeInt64, desiredPresentTime);
- SAFE_PARCEL(data.writeBool, isAutoTimestamp);
- SAFE_PARCEL(data.writeUint32, static_cast<uint32_t>(uncacheBuffers.size()));
- for (const client_cache_t& uncacheBuffer : uncacheBuffers) {
- SAFE_PARCEL(data.writeStrongBinder, uncacheBuffer.token.promote());
- SAFE_PARCEL(data.writeUint64, uncacheBuffer.id);
- }
- SAFE_PARCEL(data.writeBool, hasListenerCallbacks);
-
- SAFE_PARCEL(data.writeVectorSize, listenerCallbacks);
- for (const auto& [listener, callbackIds] : listenerCallbacks) {
- SAFE_PARCEL(data.writeStrongBinder, listener);
- SAFE_PARCEL(data.writeParcelableVector, callbackIds);
- }
-
- SAFE_PARCEL(data.writeUint64, transactionId);
-
- SAFE_PARCEL(data.writeUint32, static_cast<uint32_t>(mergedTransactionIds.size()));
- for (auto mergedTransactionId : mergedTransactionIds) {
- SAFE_PARCEL(data.writeUint64, mergedTransactionId);
- }
-
- if (flags & ISurfaceComposer::eOneWay) {
+ if (state.mFlags & ISurfaceComposer::eOneWay) {
return remote()->transact(BnSurfaceComposer::SET_TRANSACTION_STATE,
data, &reply, IBinder::FLAG_ONEWAY);
} else {
@@ -132,75 +91,9 @@
case SET_TRANSACTION_STATE: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
- FrameTimelineInfo frameTimelineInfo;
- frameTimelineInfo.readFromParcel(&data);
-
- uint32_t count = 0;
- SAFE_PARCEL_READ_SIZE(data.readUint32, &count, data.dataSize());
- Vector<ComposerState> state;
- state.setCapacity(count);
- for (size_t i = 0; i < count; i++) {
- ComposerState s;
- SAFE_PARCEL(s.read, data);
- state.add(s);
- }
-
- SAFE_PARCEL_READ_SIZE(data.readUint32, &count, data.dataSize());
- DisplayState d;
- Vector<DisplayState> displays;
- displays.setCapacity(count);
- for (size_t i = 0; i < count; i++) {
- SAFE_PARCEL(d.read, data);
- displays.add(d);
- }
-
- uint32_t stateFlags = 0;
- SAFE_PARCEL(data.readUint32, &stateFlags);
- sp<IBinder> applyToken;
- SAFE_PARCEL(data.readStrongBinder, &applyToken);
- InputWindowCommands inputWindowCommands;
- SAFE_PARCEL(inputWindowCommands.read, data);
-
- int64_t desiredPresentTime = 0;
- bool isAutoTimestamp = true;
- SAFE_PARCEL(data.readInt64, &desiredPresentTime);
- SAFE_PARCEL(data.readBool, &isAutoTimestamp);
-
- SAFE_PARCEL_READ_SIZE(data.readUint32, &count, data.dataSize());
- std::vector<client_cache_t> uncacheBuffers(count);
- sp<IBinder> tmpBinder;
- for (size_t i = 0; i < count; i++) {
- SAFE_PARCEL(data.readNullableStrongBinder, &tmpBinder);
- uncacheBuffers[i].token = tmpBinder;
- SAFE_PARCEL(data.readUint64, &uncacheBuffers[i].id);
- }
-
- bool hasListenerCallbacks = false;
- SAFE_PARCEL(data.readBool, &hasListenerCallbacks);
-
- std::vector<ListenerCallbacks> listenerCallbacks;
- int32_t listenersSize = 0;
- SAFE_PARCEL_READ_SIZE(data.readInt32, &listenersSize, data.dataSize());
- for (int32_t i = 0; i < listenersSize; i++) {
- SAFE_PARCEL(data.readStrongBinder, &tmpBinder);
- std::vector<CallbackId> callbackIds;
- SAFE_PARCEL(data.readParcelableVector, &callbackIds);
- listenerCallbacks.emplace_back(tmpBinder, callbackIds);
- }
-
- uint64_t transactionId = -1;
- SAFE_PARCEL(data.readUint64, &transactionId);
-
- SAFE_PARCEL_READ_SIZE(data.readUint32, &count, data.dataSize());
- std::vector<uint64_t> mergedTransactions(count);
- for (size_t i = 0; i < count; i++) {
- SAFE_PARCEL(data.readUint64, &mergedTransactions[i]);
- }
-
- return setTransactionState(frameTimelineInfo, state, displays, stateFlags, applyToken,
- std::move(inputWindowCommands), desiredPresentTime,
- isAutoTimestamp, uncacheBuffers, hasListenerCallbacks,
- listenerCallbacks, transactionId, mergedTransactions);
+ TransactionState state;
+ SAFE_PARCEL(state.readFromParcel, &data);
+ return setTransactionState(std::move(state));
}
case GET_SCHEDULING_POLICY: {
gui::SchedulingPolicy policy;
diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp
index 83fc827..ed28e79 100644
--- a/libs/gui/ITransactionCompletedListener.cpp
+++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -92,7 +92,7 @@
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 @@
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 @@
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 c1a03fc..86bc97e 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -55,6 +55,28 @@
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 @@
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 @@
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 @@
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);
@@ -155,6 +180,7 @@
SAFE_PARCEL(output.writeParcelableVector, listener.callbackIds);
}
SAFE_PARCEL(output.writeFloat, shadowRadius);
+ SAFE_PARCEL(output.writeParcelable, borderSettings);
SAFE_PARCEL(output.writeInt32, frameRateSelectionPriority);
SAFE_PARCEL(output.writeFloat, frameRate);
SAFE_PARCEL(output.writeByte, frameRateCompatibility);
@@ -239,8 +265,10 @@
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 +280,9 @@
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 +291,7 @@
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 +302,7 @@
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);
@@ -300,6 +329,8 @@
listeners.emplace_back(listener, callbackIds);
}
SAFE_PARCEL(input.readFloat, &shadowRadius);
+ SAFE_PARCEL(input.readParcelable, &borderSettings);
+
SAFE_PARCEL(input.readInt32, &frameRateSelectionPriority);
SAFE_PARCEL(input.readFloat, &frameRate);
SAFE_PARCEL(input.readByte, &frameRateCompatibility);
@@ -580,7 +611,7 @@
}
if (other.what & eTransparentRegionChanged) {
what |= eTransparentRegionChanged;
- transparentRegion = other.transparentRegion;
+ mNotDefCmpState.transparentRegion = other.mNotDefCmpState.transparentRegion;
}
if (other.what & eFlagsChanged) {
what |= eFlagsChanged;
@@ -596,6 +627,10 @@
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 +643,13 @@
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 +695,7 @@
}
if (other.what & eSurfaceDamageRegionChanged) {
what |= eSurfaceDamageRegionChanged;
- surfaceDamageRegion = other.surfaceDamageRegion;
+ mNotDefCmpState.surfaceDamageRegion = other.mNotDefCmpState.surfaceDamageRegion;
}
if (other.what & eApiChanged) {
what |= eApiChanged;
@@ -677,7 +714,8 @@
}
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;
@@ -692,6 +730,10 @@
what |= eShadowRadiusChanged;
shadowRadius = other.shadowRadius;
}
+ if (other.what & eBorderSettingsChanged) {
+ what |= eBorderSettingsChanged;
+ borderSettings = other.borderSettings;
+ }
if (other.what & eLutsChanged) {
what |= eLutsChanged;
luts = other.luts;
@@ -800,7 +842,8 @@
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 +852,7 @@
}
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 +860,8 @@
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 +875,8 @@
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);
@@ -843,6 +888,7 @@
CHECK_DIFF2(diff, eBackgroundColorChanged, other, bgColor, bgColorDataspace);
if (other.what & eMetadataChanged) diff |= eMetadataChanged;
CHECK_DIFF(diff, eShadowRadiusChanged, other, shadowRadius);
+ CHECK_DIFF(diff, eBorderSettingsChanged, other, borderSettings);
CHECK_DIFF(diff, eDefaultFrameRateCompatibilityChanged, other, defaultFrameRateCompatibility);
CHECK_DIFF(diff, eFrameRateSelectionPriority, other, frameRateSelectionPriority);
CHECK_DIFF3(diff, eFrameRateChanged, other, frameRate, frameRateCompatibility,
@@ -893,6 +939,38 @@
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 +1071,13 @@
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 +1104,8 @@
}
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 +1113,9 @@
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 2de023e..30b5785 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 @@
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 @@
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 653b91b..848ae7a 100644
--- a/libs/gui/StreamSplitter.cpp
+++ b/libs/gui/StreamSplitter.cpp
@@ -47,7 +47,7 @@
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 @@
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 @@
// 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 e41f9bb..83c6b57 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 @@
: 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
}
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 @@
mReportRemovedBuffers = reportBufferRemoval;
if (listener != nullptr) {
- mListenerProxy = new ProducerListenerProxy(this, listener);
+ mListenerProxy = sp<ProducerListenerProxy>::make(this, listener);
}
int err =
@@ -2094,6 +2114,9 @@
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 @@
*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) {
@@ -2203,17 +2230,47 @@
return NO_ERROR;
}
+int Surface::isBufferOwned(const sp<GraphicBuffer>& buffer, bool* outIsOwned) const {
+ ATRACE_CALL();
+
+ if (buffer == nullptr) {
+ ALOGE("%s: Bad input, buffer was null", __FUNCTION__);
+ return BAD_VALUE;
+ }
+ if (outIsOwned == nullptr) {
+ ALOGE("%s: Bad input, output was null", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ Mutex::Autolock lock(mMutex);
+
+ int slot = this->getSlotFromBufferLocked(buffer->getNativeBuffer());
+ if (slot == BAD_VALUE) {
+ ALOGV("%s: Buffer %" PRIu64 " is not owned", __FUNCTION__, buffer->getId());
+ *outIsOwned = false;
+ return NO_ERROR;
+ } else if (slot < 0) {
+ ALOGV("%s: Buffer %" PRIu64 " look up failed (%d)", __FUNCTION__, buffer->getId(), slot);
+ *outIsOwned = false;
+ return slot;
+ }
+
+ *outIsOwned = true;
+ return NO_ERROR;
+}
+
int Surface::attachBuffer(ANativeWindowBuffer* buffer)
{
ATRACE_CALL();
- ALOGV("Surface::attachBuffer");
+ sp<GraphicBuffer> graphicBuffer(static_cast<GraphicBuffer*>(buffer));
+
+ ALOGV("Surface::attachBuffer bufferId=%" PRIu64, graphicBuffer->getId());
Mutex::Autolock lock(mMutex);
if (mReportRemovedBuffers) {
mRemovedBuffers.clear();
}
- sp<GraphicBuffer> graphicBuffer(static_cast<GraphicBuffer*>(buffer));
uint32_t priorGeneration = graphicBuffer->mGenerationNumber;
graphicBuffer->mGenerationNumber = mGenerationNumber;
int32_t attachedSlot = -1;
@@ -2292,8 +2349,35 @@
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 +2585,11 @@
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 +2598,11 @@
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 +2762,11 @@
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 be88b11..786bc06 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
*/
class BufferCache : public Singleton<BufferCache> {
public:
- BufferCache() : token(new BBinder()) {}
+ BufferCache() : token(sp<BBinder>::make()) {}
sp<IBinder> getToken() {
return IInterface::asBinder(TransactionCompletedListener::getIInstance());
@@ -823,36 +824,24 @@
// ---------------------------------------------------------------------------
SurfaceComposerClient::Transaction::Transaction() {
- mId = generateId();
+ mState.mId = generateId();
mTransactionCompletedListener = TransactionCompletedListener::getInstance();
}
-SurfaceComposerClient::Transaction::Transaction(const Transaction& other)
- : mId(other.mId),
- mAnimation(other.mAnimation),
- mEarlyWakeupStart(other.mEarlyWakeupStart),
- mEarlyWakeupEnd(other.mEarlyWakeupEnd),
- mMayContainBuffer(other.mMayContainBuffer),
- mDesiredPresentTime(other.mDesiredPresentTime),
- mIsAutoTimestamp(other.mIsAutoTimestamp),
- mFrameTimelineInfo(other.mFrameTimelineInfo),
- mApplyToken(other.mApplyToken) {
- mDisplayStates = other.mDisplayStates;
- mComposerStates = other.mComposerStates;
- mInputWindowCommands = other.mInputWindowCommands;
- mListenerCallbacks = other.mListenerCallbacks;
- mTransactionCompletedListener = TransactionCompletedListener::getInstance();
-}
+SurfaceComposerClient::Transaction::Transaction(Transaction&& other)
+ : mTransactionCompletedListener(TransactionCompletedListener::getInstance()),
+ mState(std::move(other.mState)),
+ mListenerCallbacks(std::move(other.mListenerCallbacks)) {}
void SurfaceComposerClient::Transaction::sanitize(int pid, int uid) {
uint32_t permissions = LayerStatePermissions::getTransactionPermissions(pid, uid);
- for (auto & [handle, composerState] : mComposerStates) {
+ for (auto& composerState : mState.mComposerStates) {
composerState.state.sanitize(permissions);
}
- if (!mInputWindowCommands.empty() &&
+ if (!mState.mInputWindowCommands.empty() &&
(permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER) == 0) {
ALOGE("Only privileged callers are allowed to send input commands.");
- mInputWindowCommands.clear();
+ mState.mInputWindowCommands.clear();
}
}
@@ -867,36 +856,13 @@
status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel) {
- const uint64_t transactionId = parcel->readUint64();
- const bool animation = parcel->readBool();
- const bool earlyWakeupStart = parcel->readBool();
- const bool earlyWakeupEnd = parcel->readBool();
- const int64_t desiredPresentTime = parcel->readInt64();
- const bool isAutoTimestamp = parcel->readBool();
- const bool logCallPoints = parcel->readBool();
- FrameTimelineInfo frameTimelineInfo;
- frameTimelineInfo.readFromParcel(parcel);
+ TransactionState tmpState;
+ SAFE_PARCEL(tmpState.readFromParcel, parcel);
- sp<IBinder> applyToken;
- parcel->readNullableStrongBinder(&applyToken);
size_t count = static_cast<size_t>(parcel->readUint32());
if (count > parcel->dataSize()) {
return BAD_VALUE;
}
- SortedVector<DisplayState> displayStates;
- displayStates.setCapacity(count);
- for (size_t i = 0; i < count; i++) {
- DisplayState displayState;
- if (displayState.read(*parcel) == BAD_VALUE) {
- return BAD_VALUE;
- }
- displayStates.add(displayState);
- }
-
- count = static_cast<size_t>(parcel->readUint32());
- if (count > parcel->dataSize()) {
- return BAD_VALUE;
- }
std::unordered_map<sp<ITransactionCompletedListener>, CallbackInfo, TCLHash> listenerCallbacks;
listenerCallbacks.reserve(count);
for (size_t i = 0; i < count; i++) {
@@ -922,62 +888,8 @@
}
}
- count = static_cast<size_t>(parcel->readUint32());
- if (count > parcel->dataSize()) {
- return BAD_VALUE;
- }
- std::unordered_map<sp<IBinder>, ComposerState, IBinderHash> composerStates;
- composerStates.reserve(count);
- for (size_t i = 0; i < count; i++) {
- sp<IBinder> surfaceControlHandle;
- SAFE_PARCEL(parcel->readStrongBinder, &surfaceControlHandle);
-
- ComposerState composerState;
- if (composerState.read(*parcel) == BAD_VALUE) {
- return BAD_VALUE;
- }
- composerStates[surfaceControlHandle] = composerState;
- }
-
- InputWindowCommands inputWindowCommands;
- inputWindowCommands.read(*parcel);
-
- count = static_cast<size_t>(parcel->readUint32());
- if (count > parcel->dataSize()) {
- return BAD_VALUE;
- }
- std::vector<client_cache_t> uncacheBuffers(count);
- for (size_t i = 0; i < count; i++) {
- sp<IBinder> tmpBinder;
- SAFE_PARCEL(parcel->readStrongBinder, &tmpBinder);
- uncacheBuffers[i].token = tmpBinder;
- SAFE_PARCEL(parcel->readUint64, &uncacheBuffers[i].id);
- }
-
- count = static_cast<size_t>(parcel->readUint32());
- if (count > parcel->dataSize()) {
- return BAD_VALUE;
- }
- std::vector<uint64_t> mergedTransactionIds(count);
- for (size_t i = 0; i < count; i++) {
- SAFE_PARCEL(parcel->readUint64, &mergedTransactionIds[i]);
- }
-
- // Parsing was successful. Update the object.
- mId = transactionId;
- mAnimation = animation;
- mEarlyWakeupStart = earlyWakeupStart;
- mEarlyWakeupEnd = earlyWakeupEnd;
- mDesiredPresentTime = desiredPresentTime;
- mIsAutoTimestamp = isAutoTimestamp;
- mFrameTimelineInfo = frameTimelineInfo;
- mDisplayStates = displayStates;
- mListenerCallbacks = listenerCallbacks;
- mComposerStates = composerStates;
- mInputWindowCommands = inputWindowCommands;
- mApplyToken = applyToken;
- mUncacheBuffers = std::move(uncacheBuffers);
- mMergedTransactionIds = std::move(mergedTransactionIds);
+ mState = std::move(tmpState);
+ mListenerCallbacks = std::move(listenerCallbacks);
return NO_ERROR;
}
@@ -995,19 +907,7 @@
const_cast<SurfaceComposerClient::Transaction*>(this)->cacheBuffers();
- parcel->writeUint64(mId);
- parcel->writeBool(mAnimation);
- parcel->writeBool(mEarlyWakeupStart);
- parcel->writeBool(mEarlyWakeupEnd);
- parcel->writeInt64(mDesiredPresentTime);
- parcel->writeBool(mIsAutoTimestamp);
- parcel->writeBool(mLogCallPoints);
- mFrameTimelineInfo.writeToParcel(parcel);
- parcel->writeStrongBinder(mApplyToken);
- parcel->writeUint32(static_cast<uint32_t>(mDisplayStates.size()));
- for (auto const& displayState : mDisplayStates) {
- displayState.write(*parcel);
- }
+ SAFE_PARCEL(mState.writeToParcel, parcel);
parcel->writeUint32(static_cast<uint32_t>(mListenerCallbacks.size()));
for (auto const& [listener, callbackInfo] : mListenerCallbacks) {
@@ -1022,25 +922,6 @@
}
}
- parcel->writeUint32(static_cast<uint32_t>(mComposerStates.size()));
- for (auto const& [handle, composerState] : mComposerStates) {
- SAFE_PARCEL(parcel->writeStrongBinder, handle);
- composerState.write(*parcel);
- }
-
- mInputWindowCommands.write(*parcel);
-
- SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(mUncacheBuffers.size()));
- for (const client_cache_t& uncacheBuffer : mUncacheBuffers) {
- SAFE_PARCEL(parcel->writeStrongBinder, uncacheBuffer.token.promote());
- SAFE_PARCEL(parcel->writeUint64, uncacheBuffer.id);
- }
-
- SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(mMergedTransactionIds.size()));
- for (auto mergedTransactionId : mMergedTransactionIds) {
- SAFE_PARCEL(parcel->writeUint64, mergedTransactionId);
- }
-
return NO_ERROR;
}
@@ -1065,42 +946,8 @@
}
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::merge(Transaction&& other) {
- while (mMergedTransactionIds.size() + other.mMergedTransactionIds.size() >
- MAX_MERGE_HISTORY_LENGTH - 1 &&
- mMergedTransactionIds.size() > 0) {
- mMergedTransactionIds.pop_back();
- }
- if (other.mMergedTransactionIds.size() == MAX_MERGE_HISTORY_LENGTH) {
- mMergedTransactionIds.insert(mMergedTransactionIds.begin(),
- other.mMergedTransactionIds.begin(),
- other.mMergedTransactionIds.end() - 1);
- } else if (other.mMergedTransactionIds.size() > 0u) {
- mMergedTransactionIds.insert(mMergedTransactionIds.begin(),
- other.mMergedTransactionIds.begin(),
- other.mMergedTransactionIds.end());
- }
- mMergedTransactionIds.insert(mMergedTransactionIds.begin(), other.mId);
-
- for (auto const& [handle, composerState] : other.mComposerStates) {
- if (mComposerStates.count(handle) == 0) {
- mComposerStates[handle] = composerState;
- } else {
- if (composerState.state.what & layer_state_t::eBufferChanged) {
- releaseBufferIfOverwriting(mComposerStates[handle].state);
- }
- mComposerStates[handle].state.merge(composerState.state);
- }
- }
-
- for (auto const& state : other.mDisplayStates) {
- ssize_t index = mDisplayStates.indexOf(state);
- if (index < 0) {
- mDisplayStates.add(state);
- } else {
- mDisplayStates.editItemAt(static_cast<size_t>(index)).merge(state);
- }
- }
-
+ mState.merge(std::move(other.mState),
+ std::bind(&Transaction::releaseBufferIfOverwriting, this, std::placeholders::_1));
for (const auto& [listener, callbackInfo] : other.mListenerCallbacks) {
auto& [callbackIds, surfaceControls] = callbackInfo;
mListenerCallbacks[listener].callbackIds.insert(std::make_move_iterator(
@@ -1124,53 +971,21 @@
}
}
- for (const auto& cacheId : other.mUncacheBuffers) {
- mUncacheBuffers.push_back(cacheId);
- }
-
- mInputWindowCommands.merge(other.mInputWindowCommands);
-
- mMayContainBuffer |= other.mMayContainBuffer;
- mEarlyWakeupStart = mEarlyWakeupStart || other.mEarlyWakeupStart;
- mEarlyWakeupEnd = mEarlyWakeupEnd || other.mEarlyWakeupEnd;
- mApplyToken = other.mApplyToken;
-
- mergeFrameTimelineInfo(mFrameTimelineInfo, other.mFrameTimelineInfo);
-
- mLogCallPoints |= other.mLogCallPoints;
- if (mLogCallPoints) {
- ALOG(LOG_DEBUG, LOG_SURFACE_CONTROL_REGISTRY,
- "Transaction %" PRIu64 " merged with transaction %" PRIu64, other.getId(), mId);
- }
-
other.clear();
return *this;
}
void SurfaceComposerClient::Transaction::clear() {
- mComposerStates.clear();
- mDisplayStates.clear();
+ mState.clear();
mListenerCallbacks.clear();
- mInputWindowCommands.clear();
- mUncacheBuffers.clear();
- mMayContainBuffer = false;
- mAnimation = false;
- mEarlyWakeupStart = false;
- mEarlyWakeupEnd = false;
- mDesiredPresentTime = 0;
- mIsAutoTimestamp = true;
- mFrameTimelineInfo = {};
- mApplyToken = nullptr;
- mMergedTransactionIds.clear();
- mLogCallPoints = false;
}
-uint64_t SurfaceComposerClient::Transaction::getId() {
- return mId;
+uint64_t SurfaceComposerClient::Transaction::getId() const {
+ return mState.mId;
}
std::vector<uint64_t> SurfaceComposerClient::Transaction::getMergedTransactionIds() {
- return mMergedTransactionIds;
+ return mState.mMergedTransactionIds;
}
void SurfaceComposerClient::doUncacheBufferTransaction(uint64_t cacheId) {
@@ -1179,12 +994,13 @@
client_cache_t uncacheBuffer;
uncacheBuffer.token = BufferCache::getInstance().getToken();
uncacheBuffer.id = cacheId;
- Vector<ComposerState> composerStates;
- Vector<DisplayState> displayStates;
- status_t status = sf->setTransactionState(FrameTimelineInfo{}, composerStates, displayStates,
- ISurfaceComposer::eOneWay,
- Transaction::getDefaultApplyToken(), {}, systemTime(),
- true, {uncacheBuffer}, false, {}, generateId(), {});
+ TransactionState state;
+ state.mId = generateId();
+ state.mApplyToken = Transaction::getDefaultApplyToken();
+ state.mUncacheBuffers.emplace_back(std::move(uncacheBuffer));
+ state.mFlags = ISurfaceComposer::eOneWay;
+ state.mDesiredPresentTime = systemTime();
+ status_t status = sf->setTransactionState(std::move(state));
if (status != NO_ERROR) {
ALOGE_AND_TRACE("SurfaceComposerClient::doUncacheBufferTransaction - %s",
strerror(-status));
@@ -1192,13 +1008,13 @@
}
void SurfaceComposerClient::Transaction::cacheBuffers() {
- if (!mMayContainBuffer) {
+ if (!mState.mMayContainBuffer) {
return;
}
size_t count = 0;
- for (auto& [handle, cs] : mComposerStates) {
- layer_state_t* s = &(mComposerStates[handle].state);
+ for (auto& cs : mState.mComposerStates) {
+ layer_state_t* s = &cs.state;
if (!(s->what & layer_state_t::eBufferChanged)) {
continue;
} else if (s->bufferData &&
@@ -1225,7 +1041,7 @@
std::optional<client_cache_t> uncacheBuffer;
cacheId = BufferCache::getInstance().cache(s->bufferData->buffer, uncacheBuffer);
if (uncacheBuffer) {
- mUncacheBuffers.push_back(*uncacheBuffer);
+ mState.mUncacheBuffers.emplace_back(*uncacheBuffer);
}
}
s->bufferData->flags |= BufferData::BufferDataChange::cachedBufferChanged;
@@ -1294,8 +1110,7 @@
/*callbackContext=*/nullptr);
}
- bool hasListenerCallbacks = !mListenerCallbacks.empty();
- std::vector<ListenerCallbacks> listenerCallbacks;
+ mState.mHasListenerCallbacks = !mListenerCallbacks.empty();
// For every listener with registered callbacks
for (const auto& [listener, callbackInfo] : mListenerCallbacks) {
auto& [callbackIds, surfaceControls] = callbackInfo;
@@ -1304,7 +1119,8 @@
}
if (surfaceControls.empty()) {
- listenerCallbacks.emplace_back(IInterface::asBinder(listener), std::move(callbackIds));
+ mState.mListenerCallbacks.emplace_back(IInterface::asBinder(listener),
+ std::move(callbackIds));
} else {
// If the listener has any SurfaceControls set on this Transaction update the surface
// state
@@ -1316,53 +1132,33 @@
}
std::vector<CallbackId> callbacks(callbackIds.begin(), callbackIds.end());
s->what |= layer_state_t::eHasListenerCallbacksChanged;
- s->listeners.emplace_back(IInterface::asBinder(listener), callbacks);
+ s->listeners.emplace_back(IInterface::asBinder(listener), std::move(callbacks));
}
}
}
cacheBuffers();
- Vector<ComposerState> composerStates;
- Vector<DisplayState> displayStates;
- uint32_t flags = 0;
-
- for (auto const& kv : mComposerStates) {
- composerStates.add(kv.second);
- }
-
- displayStates = std::move(mDisplayStates);
-
- if (mAnimation) {
- flags |= ISurfaceComposer::eAnimation;
- }
if (oneWay) {
if (synchronous) {
ALOGE("Transaction attempted to set synchronous and one way at the same time"
" this is an invalid request. Synchronous will win for safety");
} else {
- flags |= ISurfaceComposer::eOneWay;
+ mState.mFlags |= ISurfaceComposer::eOneWay;
}
}
- // If both mEarlyWakeupStart and mEarlyWakeupEnd are set
+ // If both ISurfaceComposer::eEarlyWakeupStart and ISurfaceComposer::eEarlyWakeupEnd are set
// it is equivalent for none
- if (mEarlyWakeupStart && !mEarlyWakeupEnd) {
- flags |= ISurfaceComposer::eEarlyWakeupStart;
+ uint32_t wakeupFlags = ISurfaceComposer::eEarlyWakeupStart | ISurfaceComposer::eEarlyWakeupEnd;
+ if ((mState.mFlags & wakeupFlags) == wakeupFlags) {
+ mState.mFlags &= ~(wakeupFlags);
}
- if (mEarlyWakeupEnd && !mEarlyWakeupStart) {
- flags |= ISurfaceComposer::eEarlyWakeupEnd;
- }
-
- sp<IBinder> applyToken = mApplyToken ? mApplyToken : getDefaultApplyToken();
+ if (!mState.mApplyToken) mState.mApplyToken = getDefaultApplyToken();
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
- status_t binderStatus =
- sf->setTransactionState(mFrameTimelineInfo, composerStates, displayStates, flags,
- applyToken, mInputWindowCommands, mDesiredPresentTime,
- mIsAutoTimestamp, mUncacheBuffers, hasListenerCallbacks,
- listenerCallbacks, mId, mMergedTransactionIds);
- mId = generateId();
+ status_t binderStatus = sf->setTransactionState(std::move(mState));
+ mState.mId = generateId();
// Clear the current states and flags
clear();
@@ -1371,15 +1167,15 @@
syncCallback->wait();
}
- if (mLogCallPoints) {
- ALOG(LOG_DEBUG, LOG_SURFACE_CONTROL_REGISTRY, "Transaction %" PRIu64 " applied", mId);
+ if (mState.mLogCallPoints) {
+ ALOG(LOG_DEBUG, LOG_SURFACE_CONTROL_REGISTRY, "Transaction %" PRIu64 " applied", getId());
}
mStatus = NO_ERROR;
return binderStatus;
}
-sp<IBinder> SurfaceComposerClient::Transaction::sApplyToken = new BBinder();
+sp<IBinder> SurfaceComposerClient::Transaction::sApplyToken = sp<BBinder>::make();
std::mutex SurfaceComposerClient::Transaction::sApplyTokenMutex;
@@ -1407,17 +1203,22 @@
}
void SurfaceComposerClient::Transaction::enableDebugLogCallPoints() {
- mLogCallPoints = true;
+ mState.mLogCallPoints = true;
}
// ---------------------------------------------------------------------------
sp<IBinder> SurfaceComposerClient::createVirtualDisplay(const std::string& displayName,
- bool isSecure, const std::string& uniqueId,
+ bool isSecure, bool optimizeForPower,
+ const std::string& uniqueId,
float requestedRefreshRate) {
+ const gui::ISurfaceComposer::OptimizationPolicy optimizationPolicy = optimizeForPower
+ ? gui::ISurfaceComposer::OptimizationPolicy::optimizeForPower
+ : gui::ISurfaceComposer::OptimizationPolicy::optimizeForPerformance;
sp<IBinder> display = nullptr;
binder::Status status =
ComposerServiceAIDL::getComposerService()->createVirtualDisplay(displayName, isSecure,
+ optimizationPolicy,
uniqueId,
requestedRefreshRate,
&display);
@@ -1437,9 +1238,8 @@
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 +1261,19 @@
}
void SurfaceComposerClient::Transaction::setAnimationTransaction() {
- mAnimation = true;
+ mState.mFlags |= ISurfaceComposer::eAnimation;
}
void SurfaceComposerClient::Transaction::setEarlyWakeupStart() {
- mEarlyWakeupStart = true;
+ mState.mFlags |= ISurfaceComposer::eEarlyWakeupStart;
}
void SurfaceComposerClient::Transaction::setEarlyWakeupEnd() {
- mEarlyWakeupEnd = true;
+ mState.mFlags |= ISurfaceComposer::eEarlyWakeupEnd;
}
layer_state_t* SurfaceComposerClient::Transaction::getLayerState(const sp<SurfaceControl>& sc) {
- auto handle = sc->getLayerStateHandle();
-
- if (mComposerStates.count(handle) == 0) {
- // we don't have it, add an initialized layer_state to our list
- ComposerState s;
-
- s.state.surface = handle;
- s.state.layerId = sc->getLayerId();
-
- mComposerStates[handle] = s;
- }
-
- return &(mComposerStates[handle].state);
+ return mState.getLayerState(sc);
}
void SurfaceComposerClient::Transaction::registerSurfaceControlForCallback(
@@ -1543,11 +1331,7 @@
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 +1361,7 @@
mStatus = BAD_INDEX;
return *this;
}
- s->what |= layer_state_t::eTransparentRegionChanged;
- s->transparentRegion = transparentRegion;
-
+ s->updateTransparentRegion(transparentRegion);
registerSurfaceControlForCallback(sc);
return *this;
}
@@ -1695,6 +1477,18 @@
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 +1523,7 @@
if (SurfaceControl::isSameSurface(sc, newParent)) {
return *this;
}
- s->what |= layer_state_t::eReparent;
- s->parentSurfaceControlForChild = newParent ? newParent->getParentingLayer() : nullptr;
-
+ s->updateParentLayer(newParent);
registerSurfaceControlForCallback(sc);
return *this;
}
@@ -1857,8 +1649,8 @@
setReleaseBufferCallback(bufferData.get(), callback);
}
- if (mIsAutoTimestamp) {
- mDesiredPresentTime = systemTime();
+ if (mState.mIsAutoTimestamp) {
+ mState.mDesiredPresentTime = systemTime();
}
s->what |= layer_state_t::eBufferChanged;
s->bufferData = std::move(bufferData);
@@ -1876,7 +1668,7 @@
const std::vector<SurfaceControlStats>&) {},
nullptr);
- mMayContainBuffer = true;
+ mState.mMayContainBuffer = true;
return *this;
}
@@ -1961,9 +1753,9 @@
}
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 +1764,8 @@
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 +1809,7 @@
mStatus = BAD_INDEX;
return *this;
}
- s->what |= layer_state_t::eSurfaceDamageRegionChanged;
- s->surfaceDamageRegion = surfaceDamageRegion;
-
+ s->updateSurfaceDamageRegion(surfaceDamageRegion);
registerSurfaceControlForCallback(sc);
return *this;
}
@@ -2054,8 +1844,8 @@
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setDesiredPresentTime(
nsecs_t desiredPresentTime) {
- mDesiredPresentTime = desiredPresentTime;
- mIsAutoTimestamp = false;
+ mState.mDesiredPresentTime = desiredPresentTime;
+ mState.mIsAutoTimestamp = false;
return *this;
}
@@ -2138,21 +1928,20 @@
mStatus = BAD_INDEX;
return *this;
}
- s->windowInfoHandle = std::move(info);
- s->what |= layer_state_t::eInputInfoChanged;
+ s->updateInputWindowInfo(std::move(info));
return *this;
}
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFocusedWindow(
const FocusRequest& request) {
- mInputWindowCommands.focusRequests.push_back(request);
+ mState.mInputWindowCommands.addFocusRequest(request);
return *this;
}
SurfaceComposerClient::Transaction&
SurfaceComposerClient::Transaction::addWindowInfosReportedListener(
sp<gui::IWindowInfosReportedListener> windowInfosReportedListener) {
- mInputWindowCommands.windowInfosReportedListeners.insert(windowInfosReportedListener);
+ mState.mInputWindowCommands.addWindowInfosReportedListener(windowInfosReportedListener);
return *this;
}
@@ -2236,6 +2025,19 @@
return *this;
}
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBorderSettings(
+ const sp<SurfaceControl>& sc, gui::BorderSettings settings) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+
+ s->what |= layer_state_t::eBorderSettingsChanged;
+ s->borderSettings = settings;
+ return *this;
+}
+
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrameRate(
const sp<SurfaceControl>& sc, float frameRate, int8_t compatibility,
int8_t changeFrameRateStrategy) {
@@ -2316,7 +2118,7 @@
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrameTimelineInfo(
const FrameTimelineInfo& frameTimelineInfo) {
- mergeFrameTimelineInfo(mFrameTimelineInfo, frameTimelineInfo);
+ mState.mergeFrameTimelineInfo(frameTimelineInfo);
return *this;
}
@@ -2355,7 +2157,7 @@
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setApplyToken(
const sp<IBinder>& applyToken) {
- mApplyToken = applyToken;
+ mState.mApplyToken = applyToken;
return *this;
}
@@ -2372,10 +2174,6 @@
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 +2285,7 @@
// ---------------------------------------------------------------------------
DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp<IBinder>& token) {
- DisplayState s;
- s.token = token;
- ssize_t index = mDisplayStates.indexOf(s);
- if (index < 0) {
- // we don't have it, add an initialized layer_state to our list
- s.what = 0;
- index = mDisplayStates.add(s);
- }
- return mDisplayStates.editItemAt(static_cast<size_t>(index));
+ return mState.getDisplayState(token);
}
status_t SurfaceComposerClient::Transaction::setDisplaySurface(const sp<IBinder>& token,
@@ -2548,20 +2338,6 @@
s.what |= DisplayState::eDisplaySizeChanged;
}
-// copied from FrameTimelineInfo::merge()
-void SurfaceComposerClient::Transaction::mergeFrameTimelineInfo(FrameTimelineInfo& t,
- const FrameTimelineInfo& other) {
- // When merging vsync Ids we take the oldest valid one
- if (t.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID &&
- other.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID) {
- if (other.vsyncId > t.vsyncId) {
- t = other;
- }
- } else if (t.vsyncId == FrameTimelineInfo::INVALID_VSYNC_ID) {
- t = other;
- }
-}
-
SurfaceComposerClient::Transaction&
SurfaceComposerClient::Transaction::setTrustedPresentationCallback(
const sp<SurfaceControl>& sc, TrustedPresentationCallback cb,
@@ -2578,8 +2354,9 @@
}
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 +2372,7 @@
}
s->what |= layer_state_t::eTrustedPresentationInfoChanged;
s->trustedPresentationThresholds = TrustedPresentationThresholds();
- s->trustedPresentationListener.callbackInterface = nullptr;
- s->trustedPresentationListener.callbackId = -1;
+ s->trustedPresentationListener.clear();
return *this;
}
@@ -2689,9 +2465,9 @@
}
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 +2483,8 @@
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 +2494,8 @@
const binder::Status status = mClient->mirrorDisplay(displayId.value, &result);
const status_t err = statusTFromBinderStatus(status);
if (err == NO_ERROR) {
- return new SurfaceControl(this, result.handle, result.layerId, toString(result.layerName));
+ return sp<SurfaceControl>::make(this, result.handle, result.layerId,
+ toString(result.layerName));
}
return nullptr;
}
@@ -2780,6 +2558,7 @@
if (status.isOk()) {
// convert gui::StaticDisplayInfo to ui::StaticDisplayInfo
outInfo->connectionType = static_cast<ui::DisplayConnectionType>(ginfo.connectionType);
+ outInfo->port = ginfo.port;
outInfo->density = ginfo.density;
outInfo->secure = ginfo.secure;
outInfo->installOrientation = static_cast<ui::Rotation>(ginfo.installOrientation);
@@ -3293,10 +3072,17 @@
return statusTFromBinderStatus(status);
}
-status_t SurfaceComposerClient::setActivePictureListener(
+status_t SurfaceComposerClient::addActivePictureListener(
const sp<gui::IActivePictureListener>& listener) {
binder::Status status =
- ComposerServiceAIDL::getComposerService()->setActivePictureListener(listener);
+ ComposerServiceAIDL::getComposerService()->addActivePictureListener(listener);
+ return statusTFromBinderStatus(status);
+}
+
+status_t SurfaceComposerClient::removeActivePictureListener(
+ const sp<gui::IActivePictureListener>& listener) {
+ binder::Status status =
+ ComposerServiceAIDL::getComposerService()->removeActivePictureListener(listener);
return statusTFromBinderStatus(status);
}
diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp
index f126c0b..1eb9b87 100644
--- a/libs/gui/SurfaceControl.cpp
+++ b/libs/gui/SurfaceControl.cpp
@@ -141,7 +141,8 @@
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 @@
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 a590c86..14d6df6 100644
--- a/libs/gui/TEST_MAPPING
+++ b/libs/gui/TEST_MAPPING
@@ -60,5 +60,34 @@
}
]
}
+ ],
+ "postsubmit": [
+ {
+ "name": "libgui_test",
+ "keywords": [ "primary-device" ],
+ "options": [
+ // TODO(b/397776630): Failing on real devices.
+ {
+ "exclude-filter": "InputSurfacesTest#input_respects_scaled_touchable_region_overflow"
+ },
+ // TODO(b/233363648): Failing on real devices.
+ {
+ "exclude-filter": "SurfaceTextureGLTest#TexturingFromCpuFilledYV12BufferNpot"
+ },
+ {
+ "exclude-filter": "SurfaceTextureGLTest#TexturingFromCpuFilledYV12BufferPow2"
+ },
+ {
+ "exclude-filter": "SurfaceTextureGLTest#TexturingFromCpuFilledYV12BufferWithCrop"
+ },
+ // TODO(b/233363648): Flaky on real devices.
+ {
+ "exclude-filter": "SurfaceTextureGLToGLTest#EglMakeCurrentBeforeConsumerDeathUnrefsBuffers"
+ },
+ {
+ "exclude-filter": "SurfaceTextureGLToGLTest#EglMakeCurrentAfterConsumerDeathUnrefsBuffers"
+ }
+ ]
+ }
]
}
diff --git a/libs/gui/TransactionState.cpp b/libs/gui/TransactionState.cpp
new file mode 100644
index 0000000..9e09bc2
--- /dev/null
+++ b/libs/gui/TransactionState.cpp
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "TransactionState"
+#include <gui/LayerState.h>
+#include <gui/SurfaceComposerClient.h>
+#include <gui/TransactionState.h>
+#include <private/gui/ParcelUtils.h>
+#include <algorithm>
+
+namespace android {
+
+status_t TransactionState::writeToParcel(Parcel* parcel) const {
+ SAFE_PARCEL(parcel->writeUint64, mId);
+ SAFE_PARCEL(parcel->writeUint32, mFlags);
+ SAFE_PARCEL(parcel->writeInt64, mDesiredPresentTime);
+ SAFE_PARCEL(parcel->writeBool, mIsAutoTimestamp);
+ SAFE_PARCEL(parcel->writeParcelable, mFrameTimelineInfo);
+ SAFE_PARCEL(parcel->writeStrongBinder, mApplyToken);
+ SAFE_PARCEL(parcel->writeBool, mMayContainBuffer);
+ SAFE_PARCEL(parcel->writeBool, mLogCallPoints);
+
+ SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(mDisplayStates.size()));
+ for (auto const& displayState : mDisplayStates) {
+ displayState.write(*parcel);
+ }
+ SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(mComposerStates.size()));
+ for (auto const& composerState : mComposerStates) {
+ composerState.write(*parcel);
+ }
+
+ mInputWindowCommands.write(*parcel);
+ SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(mUncacheBuffers.size()));
+ for (const client_cache_t& uncacheBuffer : mUncacheBuffers) {
+ SAFE_PARCEL(parcel->writeStrongBinder, uncacheBuffer.token.promote());
+ SAFE_PARCEL(parcel->writeUint64, uncacheBuffer.id);
+ }
+
+ SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(mMergedTransactionIds.size()));
+ for (auto mergedTransactionId : mMergedTransactionIds) {
+ SAFE_PARCEL(parcel->writeUint64, mergedTransactionId);
+ }
+
+ SAFE_PARCEL(parcel->writeBool, mHasListenerCallbacks);
+ SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(mListenerCallbacks.size()));
+ for (const auto& [listener, callbackIds] : mListenerCallbacks) {
+ SAFE_PARCEL(parcel->writeStrongBinder, listener);
+ SAFE_PARCEL(parcel->writeParcelableVector, callbackIds);
+ }
+
+ return NO_ERROR;
+}
+
+status_t TransactionState::readFromParcel(const Parcel* parcel) {
+ SAFE_PARCEL(parcel->readUint64, &mId);
+ SAFE_PARCEL(parcel->readUint32, &mFlags);
+ SAFE_PARCEL(parcel->readInt64, &mDesiredPresentTime);
+ SAFE_PARCEL(parcel->readBool, &mIsAutoTimestamp);
+ SAFE_PARCEL(parcel->readParcelable, &mFrameTimelineInfo);
+ SAFE_PARCEL(parcel->readNullableStrongBinder, &mApplyToken);
+ SAFE_PARCEL(parcel->readBool, &mMayContainBuffer);
+ SAFE_PARCEL(parcel->readBool, &mLogCallPoints);
+
+ uint32_t count;
+ SAFE_PARCEL_READ_SIZE(parcel->readUint32, &count, parcel->dataSize())
+ mDisplayStates.clear();
+ mDisplayStates.reserve(count);
+ for (size_t i = 0; i < count; i++) {
+ DisplayState displayState;
+ if (displayState.read(*parcel) == BAD_VALUE) {
+ return BAD_VALUE;
+ }
+ mDisplayStates.emplace_back(std::move(displayState));
+ }
+
+ SAFE_PARCEL_READ_SIZE(parcel->readUint32, &count, parcel->dataSize())
+ mComposerStates.clear();
+ mComposerStates.reserve(count);
+ for (size_t i = 0; i < count; i++) {
+ ComposerState composerState;
+ if (composerState.read(*parcel) == BAD_VALUE) {
+ return BAD_VALUE;
+ }
+ mComposerStates.emplace_back(std::move(composerState));
+ }
+
+ if (status_t status = mInputWindowCommands.read(*parcel) != NO_ERROR) {
+ return status;
+ }
+
+ SAFE_PARCEL_READ_SIZE(parcel->readUint32, &count, parcel->dataSize())
+ mUncacheBuffers.clear();
+ mUncacheBuffers.reserve(count);
+ for (size_t i = 0; i < count; i++) {
+ client_cache_t client_cache;
+ sp<IBinder> tmpBinder;
+ SAFE_PARCEL(parcel->readStrongBinder, &tmpBinder);
+ client_cache.token = tmpBinder;
+ SAFE_PARCEL(parcel->readUint64, &client_cache.id);
+ mUncacheBuffers.emplace_back(std::move(client_cache));
+ }
+
+ SAFE_PARCEL_READ_SIZE(parcel->readUint32, &count, parcel->dataSize())
+ mMergedTransactionIds.clear();
+ mMergedTransactionIds.resize(count);
+ for (size_t i = 0; i < count; i++) {
+ SAFE_PARCEL(parcel->readUint64, &mMergedTransactionIds[i]);
+ }
+
+ SAFE_PARCEL(parcel->readBool, &mHasListenerCallbacks);
+ SAFE_PARCEL_READ_SIZE(parcel->readUint32, &count, parcel->dataSize());
+ mListenerCallbacks.clear();
+ mListenerCallbacks.reserve(count);
+ for (uint32_t i = 0; i < count; i++) {
+ sp<IBinder> tmpBinder;
+ SAFE_PARCEL(parcel->readStrongBinder, &tmpBinder);
+ std::vector<CallbackId> callbackIds;
+ SAFE_PARCEL(parcel->readParcelableVector, &callbackIds);
+ mListenerCallbacks.emplace_back(tmpBinder, callbackIds);
+ }
+
+ return NO_ERROR;
+}
+
+void TransactionState::merge(TransactionState&& other,
+ const std::function<void(layer_state_t&)>& onBufferOverwrite) {
+ while (mMergedTransactionIds.size() + other.mMergedTransactionIds.size() >
+ MAX_MERGE_HISTORY_LENGTH - 1 &&
+ mMergedTransactionIds.size() > 0) {
+ mMergedTransactionIds.pop_back();
+ }
+ if (other.mMergedTransactionIds.size() == MAX_MERGE_HISTORY_LENGTH) {
+ mMergedTransactionIds.insert(mMergedTransactionIds.begin(),
+ other.mMergedTransactionIds.begin(),
+ other.mMergedTransactionIds.end() - 1);
+ } else if (other.mMergedTransactionIds.size() > 0u) {
+ mMergedTransactionIds.insert(mMergedTransactionIds.begin(),
+ other.mMergedTransactionIds.begin(),
+ other.mMergedTransactionIds.end());
+ }
+ mMergedTransactionIds.insert(mMergedTransactionIds.begin(), other.mId);
+
+ for (auto const& otherState : other.mComposerStates) {
+ if (auto it = std::find_if(mComposerStates.begin(), mComposerStates.end(),
+ [&otherState](const auto& composerState) {
+ return composerState.state.surface ==
+ otherState.state.surface;
+ });
+ it != mComposerStates.end()) {
+ if (otherState.state.what & layer_state_t::eBufferChanged) {
+ onBufferOverwrite(it->state);
+ }
+ it->state.merge(otherState.state);
+ } else {
+ mComposerStates.push_back(otherState);
+ }
+ }
+
+ for (auto const& state : other.mDisplayStates) {
+ if (auto it = std::find_if(mDisplayStates.begin(), mDisplayStates.end(),
+ [&state](const auto& displayState) {
+ return displayState.token == state.token;
+ });
+ it != mDisplayStates.end()) {
+ it->merge(state);
+ } else {
+ mDisplayStates.push_back(state);
+ }
+ }
+
+ for (const auto& cacheId : other.mUncacheBuffers) {
+ mUncacheBuffers.push_back(cacheId);
+ }
+
+ mInputWindowCommands.merge(other.mInputWindowCommands);
+ // TODO(b/385156191) Consider merging desired present time.
+ mFlags |= other.mFlags;
+ mMayContainBuffer |= other.mMayContainBuffer;
+ mLogCallPoints |= other.mLogCallPoints;
+
+ // mApplyToken is explicitly not merged. Token should be set before applying the transactions to
+ // make synchronization decisions a bit simpler.
+ mergeFrameTimelineInfo(other.mFrameTimelineInfo);
+ other.clear();
+}
+
+// copied from FrameTimelineInfo::merge()
+void TransactionState::mergeFrameTimelineInfo(const FrameTimelineInfo& other) {
+ // When merging vsync Ids we take the oldest valid one
+ if (mFrameTimelineInfo.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID &&
+ other.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID) {
+ if (other.vsyncId > mFrameTimelineInfo.vsyncId) {
+ mFrameTimelineInfo = other;
+ }
+ } else if (mFrameTimelineInfo.vsyncId == FrameTimelineInfo::INVALID_VSYNC_ID) {
+ mFrameTimelineInfo = other;
+ }
+}
+
+void TransactionState::clear() {
+ mComposerStates.clear();
+ mDisplayStates.clear();
+ mListenerCallbacks.clear();
+ mHasListenerCallbacks = false;
+ mInputWindowCommands.clear();
+ mUncacheBuffers.clear();
+ mDesiredPresentTime = 0;
+ mIsAutoTimestamp = true;
+ mApplyToken = nullptr;
+ mFrameTimelineInfo = {};
+ mMergedTransactionIds.clear();
+ mFlags = 0;
+ mMayContainBuffer = false;
+ mLogCallPoints = false;
+}
+
+layer_state_t* TransactionState::getLayerState(const sp<SurfaceControl>& sc) {
+ auto handle = sc->getLayerStateHandle();
+ if (auto it = std::find_if(mComposerStates.begin(), mComposerStates.end(),
+ [&handle](const auto& composerState) {
+ return composerState.state.surface == handle;
+ });
+ it != mComposerStates.end()) {
+ return &it->state;
+ }
+
+ // we don't have it, add an initialized layer_state to our list
+ ComposerState s;
+ s.state.surface = handle;
+ s.state.layerId = sc->getLayerId();
+ mComposerStates.push_back(s);
+
+ return &mComposerStates.back().state;
+}
+
+DisplayState& TransactionState::getDisplayState(const sp<IBinder>& token) {
+ if (auto it = std::find_if(mDisplayStates.begin(), mDisplayStates.end(),
+ [token](const auto& display) { return display.token == token; });
+ it != mDisplayStates.end()) {
+ return *it;
+ }
+
+ // If display state doesn't exist, add a new one.
+ DisplayState s;
+ s.token = token;
+ mDisplayStates.push_back(s);
+ return mDisplayStates.back();
+}
+
+}; // namespace android
diff --git a/libs/gui/WindowInfo.cpp b/libs/gui/WindowInfo.cpp
index 82d2554..3fb66d1 100644
--- a/libs/gui/WindowInfo.cpp
+++ b/libs/gui/WindowInfo.cpp
@@ -59,6 +59,32 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 91c9a85..d633f9f 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::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 @@
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 4920344..2bbed2b 100644
--- a/libs/gui/aidl/android/gui/CaptureArgs.aidl
+++ b/libs/gui/aidl/android/gui/CaptureArgs.aidl
@@ -69,10 +69,5 @@
// 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 8c19bbb..9b2f089 100644
--- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
+++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
@@ -67,6 +67,11 @@
frameRateOverride = 1 << 1,
}
+ enum OptimizationPolicy {
+ optimizeForPower = 0,
+ optimizeForPerformance = 1,
+ }
+
/**
* Signal that we're done booting.
* Requires ACCESS_SURFACE_FLINGER permission
@@ -97,6 +102,10 @@
* 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 @@
* 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 @@
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 setActivePictureListener(IActivePictureListener listener);
+ oneway void addActivePictureListener(IActivePictureListener listener);
+
+ /**
+ * Removes a listener used to monitor visible content that is being processed with picture
+ * profiles.
+ */
+ oneway void removeActivePictureListener(IActivePictureListener listener);
}
diff --git a/libs/gui/aidl/android/gui/StaticDisplayInfo.aidl b/libs/gui/aidl/android/gui/StaticDisplayInfo.aidl
index 0ccda56..7ff332c 100644
--- a/libs/gui/aidl/android/gui/StaticDisplayInfo.aidl
+++ b/libs/gui/aidl/android/gui/StaticDisplayInfo.aidl
@@ -23,6 +23,7 @@
/** @hide */
parcelable StaticDisplayInfo {
DisplayConnectionType connectionType = DisplayConnectionType.Internal;
+ int port = -1;
float density;
boolean secure;
@nullable DeviceProductInfo deviceProductInfo;
diff --git a/libs/gui/android/gui/BorderSettings.aidl b/libs/gui/android/gui/BorderSettings.aidl
new file mode 100644
index 0000000..547f57f
--- /dev/null
+++ b/libs/gui/android/gui/BorderSettings.aidl
@@ -0,0 +1,24 @@
+/**
+ * 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 android.gui;
+
+/** @hide */
+parcelable BorderSettings {
+ float strokeWidth;
+ // Space is sRGB, not premultiplied, bit pattern is 0xAARRGGBB.
+ int color;
+}
diff --git a/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp b/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp
index fd8ffe1..b1a23b3 100644
--- a/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp
+++ b/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp
@@ -971,7 +971,7 @@
// 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 @@
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::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 @@
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 @@
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 c76d771..4384bd5 100644
--- a/libs/gui/bufferqueue/2.0/B2HGraphicBufferProducer.cpp
+++ b/libs/gui/bufferqueue/2.0/B2HGraphicBufferProducer.cpp
@@ -272,7 +272,7 @@
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 ae00a26..7121bb7 100644
--- a/libs/gui/bufferqueue/2.0/H2BGraphicBufferProducer.cpp
+++ b/libs/gui/bufferqueue/2.0/H2BGraphicBufferProducer.cpp
@@ -325,7 +325,7 @@
}
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 cbd6cad..c245766 100644
--- a/libs/gui/bufferqueue/2.0/types.cpp
+++ b/libs/gui/bufferqueue/2.0/types.cpp
@@ -147,13 +147,13 @@
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 07558aa..c69b0a7 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 @@
#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 @@
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 @@
*/
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 @@
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 @@
// 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
@@ -269,7 +284,7 @@
std::function<void(SurfaceComposerClient::Transaction*)> mTransactionReadyCallback
GUARDED_BY(mMutex);
SurfaceComposerClient::Transaction* mSyncTransaction GUARDED_BY(mMutex);
- std::vector<std::tuple<uint64_t /* framenumber */, SurfaceComposerClient::Transaction>>
+ std::vector<std::pair<uint64_t /* framenumber */, SurfaceComposerClient::Transaction>>
mPendingTransactions GUARDED_BY(mMutex);
std::queue<std::pair<uint64_t, FrameTimelineInfo>> mPendingFrameTimelines GUARDED_BY(mMutex);
@@ -286,8 +301,8 @@
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 @@
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 6810eda..fc31f46 100644
--- a/libs/gui/include/gui/BufferItemConsumer.h
+++ b/libs/gui/include/gui/BufferItemConsumer.h
@@ -47,6 +47,16 @@
enum { INVALID_BUFFER_SLOT = BufferQueue::INVALID_BUFFER_SLOT };
enum { NO_BUFFER_AVAILABLE = BufferQueue::NO_BUFFER_AVAILABLE };
+ static std::tuple<sp<BufferItemConsumer>, sp<Surface>> create(
+ uint64_t consumerUsage, int bufferCount = DEFAULT_MAX_BUFFERS,
+ bool controlledByApp = false, bool isConsumerSurfaceFlinger = false);
+
+ static sp<BufferItemConsumer> create(const sp<IGraphicBufferConsumer>& consumer,
+ uint64_t consumerUsage,
+ int bufferCount = DEFAULT_MAX_BUFFERS,
+ bool controlledByApp = false)
+ __attribute((deprecated("Prefer ctors that create their own surface and consumer.")));
+
// Create a new buffer item consumer. The consumerUsage parameter determines
// the consumer usage flags passed to the graphics allocator. The
// bufferCount parameter specifies how many buffers can be locked for user
@@ -86,6 +96,14 @@
status_t acquireBuffer(BufferItem* item, nsecs_t presentWhen,
bool waitForFence = true);
+ // Transfer ownership of a buffer to the BufferQueue. On NO_ERROR, the buffer
+ // is considered as if it were acquired. Buffer must not be null.
+ //
+ // Returns
+ // - BAD_VALUE if buffer is null
+ // - INVALID_OPERATION if too many buffers have already been acquired
+ status_t attachBuffer(const sp<GraphicBuffer>& buffer);
+
// Returns an acquired buffer to the queue, allowing it to be reused. Since
// only a fixed number of buffers may be acquired at a time, old buffers
// must be released by calling releaseBuffer to ensure new buffers can be
@@ -95,10 +113,8 @@
status_t releaseBuffer(const BufferItem &item,
const sp<Fence>& releaseFence = Fence::NO_FENCE);
-#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
status_t releaseBuffer(const sp<GraphicBuffer>& buffer,
const sp<Fence>& releaseFence = Fence::NO_FENCE);
-#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
protected:
#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
diff --git a/libs/gui/include/gui/BufferQueue.h b/libs/gui/include/gui/BufferQueue.h
index 0948c4d0..7b97e13 100644
--- a/libs/gui/include/gui/BufferQueue.h
+++ b/libs/gui/include/gui/BufferQueue.h
@@ -57,7 +57,7 @@
// 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 @@
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 6aa801a..ba6a6a7 100644
--- a/libs/gui/include/gui/BufferQueueConsumer.h
+++ b/libs/gui/include/gui/BufferQueueConsumer.h
@@ -28,8 +28,7 @@
class BufferQueueCore;
-class BufferQueueConsumer : public BnGraphicBufferConsumer {
-
+class BufferQueueConsumer : public IGraphicBufferConsumer {
public:
explicit BufferQueueConsumer(const sp<BufferQueueCore>& core);
~BufferQueueConsumer() override;
@@ -65,13 +64,14 @@
// 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,18 +96,36 @@
// 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);
// setMaxAcquiredBufferCount sets the maximum number of buffers that can
// be acquired by the consumer at one time (default 1). This call will
// fail if a producer is connected to the BufferQueue.
- virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers);
+ virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) override;
+ virtual status_t setMaxAcquiredBufferCount(
+ int maxAcquiredBuffers,
+ std::optional<OnBufferReleasedCallback> onBuffersReleasedCallback) override;
// setConsumerName sets the name used in logging
status_t setConsumerName(const String8& name) override;
@@ -152,6 +170,7 @@
// 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 +180,7 @@
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 77cdf2c..7f92a46 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 <list>
-#include <set>
-#include <mutex>
#include <condition_variable>
+#include <list>
+#include <mutex>
+#include <set>
+#include <vector>
#define ATRACE_BUFFER_INDEX(index) \
do { \
@@ -91,6 +92,10 @@
// 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 @@
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 @@
// 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 @@
// 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 ffafb49..42cf439 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 @@
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 086ce7c..6a1e9f6 100644
--- a/libs/gui/include/gui/BufferQueueProducer.h
+++ b/libs/gui/include/gui/BufferQueueProducer.h
@@ -36,8 +36,6 @@
public:
friend class BufferQueue; // Needed to access binderDied
- explicit BufferQueueProducer(const sp<BufferQueueCore>& core,
- bool consumerIsSurfaceFlinger = false);
~BufferQueueProducer() override;
// requestBuffer returns the GraphicBuffer for slot N.
@@ -47,6 +45,11 @@
// 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);
@@ -214,6 +217,9 @@
#endif
protected:
+ explicit BufferQueueProducer(const sp<BufferQueueCore>& core,
+ bool consumerIsSurfaceFlinger = false);
+ friend class sp<BufferQueueProducer>;
// see IGraphicsBufferProducer::setMaxDequeuedBufferCount, but with the ability to retrieve the
// total maximum buffer count for the buffer queue (dequeued AND acquired)
status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers, int* maxBufferCount);
diff --git a/libs/gui/include/gui/Choreographer.h b/libs/gui/include/gui/Choreographer.h
index a93ba14..c632098 100644
--- a/libs/gui/include/gui/Choreographer.h
+++ b/libs/gui/include/gui/Choreographer.h
@@ -81,6 +81,7 @@
explicit Choreographer(const sp<Looper>& looper, const sp<IBinder>& layerHandle = nullptr)
EXCLUDES(gChoreographers.lock);
+
void postFrameCallbackDelayed(AChoreographer_frameCallback cb,
AChoreographer_frameCallback64 cb64,
AChoreographer_vsyncCallback vsyncCallback, void* data,
@@ -103,7 +104,7 @@
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 e976aa4..63c1ef3 100644
--- a/libs/gui/include/gui/ConsumerBase.h
+++ b/libs/gui/include/gui/ConsumerBase.h
@@ -98,6 +98,8 @@
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 @@
// 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;
@@ -185,9 +185,13 @@
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 void onBuffersReleasedLocked();
+
virtual status_t detachBufferLocked(int slotIndex);
// freeBufferLocked frees up the given buffer slot. If the slot has been
@@ -243,10 +247,13 @@
// 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
// returns true iff the slot still has the graphicBuffer in it.
bool stillTracking(int slot, const sp<GraphicBuffer> graphicBuffer);
@@ -284,7 +291,11 @@
// 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
diff --git a/libs/gui/include/gui/CpuConsumer.h b/libs/gui/include/gui/CpuConsumer.h
index 2bba61b..995cdfb 100644
--- a/libs/gui/include/gui/CpuConsumer.h
+++ b/libs/gui/include/gui/CpuConsumer.h
@@ -31,6 +31,7 @@
class BufferQueue;
class GraphicBuffer;
class String8;
+class Surface;
/**
* CpuConsumer is a BufferQueue consumer endpoint that allows direct CPU
@@ -92,6 +93,13 @@
// 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 @@
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/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h
index b06ad07..cdf216c 100644
--- a/libs/gui/include/gui/DisplayEventDispatcher.h
+++ b/libs/gui/include/gui/DisplayEventDispatcher.h
@@ -23,12 +23,6 @@
class DisplayEventDispatcher : public LooperCallback {
public:
- explicit DisplayEventDispatcher(const sp<Looper>& looper,
- gui::ISurfaceComposer::VsyncSource vsyncSource =
- gui::ISurfaceComposer::VsyncSource::eVsyncSourceApp,
- EventRegistrationFlags eventRegistration = {},
- const sp<IBinder>& layerHandle = nullptr);
-
status_t initialize();
void dispose();
status_t scheduleVsync();
@@ -38,6 +32,14 @@
status_t getLatestVsyncEventData(ParcelableVsyncEventData* outVsyncEventData) const;
protected:
+ explicit DisplayEventDispatcher(const sp<Looper>& looper,
+ gui::ISurfaceComposer::VsyncSource vsyncSource =
+ gui::ISurfaceComposer::VsyncSource::eVsyncSourceApp,
+ EventRegistrationFlags eventRegistration = {},
+ const sp<IBinder>& layerHandle = nullptr);
+
+ friend class sp<DisplayEventDispatcher>;
+
virtual ~DisplayEventDispatcher() = default;
private:
diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h
index ab6a6b7..f51390a 100644
--- a/libs/gui/include/gui/DisplayEventReceiver.h
+++ b/libs/gui/include/gui/DisplayEventReceiver.h
@@ -55,20 +55,20 @@
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 @@
// 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 ab86ac4..187381c 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 @@
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 845bc54..446841b 100644
--- a/libs/gui/include/gui/Flags.h
+++ b/libs/gui/include/gui/Flags.h
@@ -46,6 +46,7 @@
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 8a66dc0..254d8ac 100644
--- a/libs/gui/include/gui/GLConsumer.h
+++ b/libs/gui/include/gui/GLConsumer.h
@@ -83,6 +83,20 @@
// 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 @@
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 @@
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 @@
// 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 @@
// 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 51d3959..95e66c7 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 @@
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;
-};
-
-#else
-class IConsumerListener : public ConsumerListener {
-};
-class BnConsumerListener : public IConsumerListener {
-};
+#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
+};
+
+class IConsumerListener : public ConsumerListener {};
} // namespace android
diff --git a/libs/gui/include/gui/IGraphicBufferConsumer.h b/libs/gui/include/gui/IGraphicBufferConsumer.h
index 18f5488..8066b07 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 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 @@
//
// 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 @@
// * 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 @@
// * 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 @@
// * 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
@@ -207,6 +243,9 @@
// maxAcquiredBuffers must be (inclusive) between 1 and MAX_MAX_ACQUIRED_BUFFERS. It also cannot
// cause the maxBufferCount value to be exceeded.
//
+ // If called with onBuffersReleasedCallback, that call back will be called in lieu of
+ // IConsumerListener::onBuffersReleased.
+ //
// Return of a value other than NO_ERROR means an error has occurred:
// * NO_INIT - the BufferQueue has been abandoned
// * BAD_VALUE - one of the below conditions occurred:
@@ -217,6 +256,11 @@
// * INVALID_OPERATION - attempting to call this after a producer connected.
virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) = 0;
+ using OnBufferReleasedCallback = std::function<void(void)>;
+ virtual status_t setMaxAcquiredBufferCount(
+ int maxAcquiredBuffers,
+ std::optional<OnBufferReleasedCallback> onBuffersReleasedCallback) = 0;
+
// setConsumerName sets the name used in logging
virtual status_t setConsumerName(const String8& name) = 0;
@@ -279,18 +323,4 @@
}
};
-#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 a42ddc4..7accca6 100644
--- a/libs/gui/include/gui/IGraphicBufferProducer.h
+++ b/libs/gui/include/gui/IGraphicBufferProducer.h
@@ -72,6 +72,14 @@
* 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 @@
// 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 @@
// * 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 @@
// 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 @@
// * 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 @@
FrameEventHistoryDelta frameTimestamps;
bool bufferReplaced{false};
int maxBufferCount{BufferQueueDefs::NUM_BUFFER_SLOTS};
+ bool isSlotExpansionAllowed{false};
status_t result{NO_ERROR};
};
@@ -430,7 +463,7 @@
// 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 @@
//
// 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/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 9a422fd..de553ae 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -65,6 +65,7 @@
struct InputWindowCommands;
class HdrCapabilities;
class Rect;
+class TransactionState;
using gui::FrameTimelineInfo;
using gui::IDisplayEventConnection;
@@ -105,13 +106,7 @@
};
/* open/close transactions. requires ACCESS_SURFACE_FLINGER permission */
- virtual status_t setTransactionState(
- const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& state,
- Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
- InputWindowCommands inputWindowCommands, int64_t desiredPresentTime,
- bool isAutoTimestamp, const std::vector<client_cache_t>& uncacheBuffer,
- bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks,
- uint64_t transactionId, const std::vector<uint64_t>& mergedTransactionIds) = 0;
+ virtual status_t setTransactionState(TransactionState&& state) = 0;
};
// ----------------------------------------------------------------------------
diff --git a/libs/gui/include/gui/InputTransferToken.h b/libs/gui/include/gui/InputTransferToken.h
index 6530b50..b83f245 100644
--- a/libs/gui/include/gui/InputTransferToken.h
+++ b/libs/gui/include/gui/InputTransferToken.h
@@ -25,7 +25,7 @@
namespace android {
struct InputTransferToken : public RefBase, Parcelable {
public:
- InputTransferToken() { mToken = new BBinder(); }
+ InputTransferToken() { mToken = sp<BBinder>::make(); }
InputTransferToken(const sp<IBinder>& token) { mToken = token; }
@@ -39,15 +39,9 @@
return NO_ERROR;
};
+ bool operator==(const InputTransferToken& other) const { return mToken == other.mToken; }
+
sp<IBinder> mToken;
};
-static inline bool operator==(const sp<InputTransferToken>& token1,
- const sp<InputTransferToken>& token2) {
- if (token1.get() == token2.get()) {
- return true;
- }
- return token1->mToken == token2->mToken;
-}
-
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/libs/gui/include/gui/LayerMetadata.h b/libs/gui/include/gui/LayerMetadata.h
index 7ee291d..6381db2 100644
--- a/libs/gui/include/gui/LayerMetadata.h
+++ b/libs/gui/include/gui/LayerMetadata.h
@@ -44,6 +44,10 @@
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 1c31e46..e2d27ac 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -17,10 +17,11 @@
#ifndef ANDROID_SF_LAYER_STATE_H
#define ANDROID_SF_LAYER_STATE_H
-
#include <stdint.h>
#include <sys/types.h>
+#include <span>
+#include <android/gui/BorderSettings.h>
#include <android/gui/DisplayCaptureArgs.h>
#include <android/gui/IWindowInfosReportedListener.h>
#include <android/gui/LayerCaptureArgs.h>
@@ -69,21 +70,39 @@
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 +250,8 @@
eBufferReleaseChannelChanged = 0x40000'00000000,
ePictureProfileHandleChanged = 0x80000'00000000,
eAppContentPriorityChanged = 0x100000'00000000,
+ eClientDrawnCornerRadiusChanged = 0x200000'00000000,
+ eBorderSettingsChanged = 0x400000'00000000,
};
layer_state_t();
@@ -251,9 +272,9 @@
// 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.
@@ -275,7 +296,7 @@
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::eAppContentPriorityChanged | layer_state_t::eBorderSettingsChanged;
// Changes which invalidates the layer's visible region in CE.
static constexpr uint64_t CONTENT_DIRTY = layer_state_t::CONTENT_CHANGES |
@@ -300,9 +321,41 @@
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 |
+ layer_state_t::eBorderSettingsChanged;
+
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 +381,23 @@
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
@@ -368,6 +414,9 @@
// Draws a shadow around the surface.
float shadowRadius;
+ // Draws an outline around the layer.
+ gui::BorderSettings borderSettings;
+
// Priority of the layer assigned by Window Manager.
int32_t frameRateSelectionPriority;
@@ -425,7 +474,7 @@
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 +486,18 @@
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 +505,9 @@
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 +559,49 @@
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 b4eef29..8176f75 100644
--- a/libs/gui/include/gui/StreamSplitter.h
+++ b/libs/gui/include/gui/StreamSplitter.h
@@ -37,7 +37,7 @@
// 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 @@
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 14a3513..3cfbed1 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -442,6 +442,9 @@
status_t detachBuffer(const sp<GraphicBuffer>& buffer);
#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+ // Sets outIsOwned to true if the given buffer is currently known to be owned by this Surface.
+ status_t isBufferOwned(const sp<GraphicBuffer>& buffer, bool* outIsOwned) const;
+
// Batch version of dequeueBuffer, cancelBuffer and queueBuffer
// Note that these batched operations are not supported when shared buffer mode is being used.
struct BatchBuffer {
@@ -558,7 +561,11 @@
// 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 +739,10 @@
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 0ce0c0a..5c348cb 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -52,6 +52,7 @@
#include <gui/ITransactionCompletedListener.h>
#include <gui/LayerState.h>
#include <gui/SurfaceControl.h>
+#include <gui/TransactionState.h>
#include <gui/WindowInfosListenerReporter.h>
#include <math/vec3.h>
@@ -298,7 +299,9 @@
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 +346,6 @@
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 +395,7 @@
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);
@@ -441,64 +443,16 @@
virtual ~PresentationCallbackRAII();
};
- class Transaction : public Parcelable {
+ class Transaction {
private:
static sp<IBinder> sApplyToken;
static std::mutex sApplyTokenMutex;
void releaseBufferIfOverwriting(const layer_state_t& state);
- static void mergeFrameTimelineInfo(FrameTimelineInfo& t, const FrameTimelineInfo& other);
// Tracks registered callbacks
sp<TransactionCompletedListener> mTransactionCompletedListener = nullptr;
- // Prints debug logs when enabled.
- bool mLogCallPoints = false;
- protected:
- std::unordered_map<sp<IBinder>, ComposerState, IBinderHash> mComposerStates;
- SortedVector<DisplayState> mDisplayStates;
- std::unordered_map<sp<ITransactionCompletedListener>, CallbackInfo, TCLHash>
- mListenerCallbacks;
- std::vector<client_cache_t> mUncacheBuffers;
+ TransactionState mState;
- // We keep track of the last MAX_MERGE_HISTORY_LENGTH merged transaction ids.
- // Ordered most recently merged to least recently merged.
- static const size_t MAX_MERGE_HISTORY_LENGTH = 10u;
- std::vector<uint64_t> mMergedTransactionIds;
-
- uint64_t mId;
-
- bool mAnimation = false;
- bool mEarlyWakeupStart = false;
- bool mEarlyWakeupEnd = false;
-
- // Indicates that the Transaction may contain buffers that should be cached. The reason this
- // is only a guess is that buffers can be removed before cache is called. This is only a
- // hint that at some point a buffer was added to this transaction before apply was called.
- bool mMayContainBuffer = false;
-
- // mDesiredPresentTime is the time in nanoseconds that the client would like the transaction
- // to be presented. When it is not possible to present at exactly that time, it will be
- // presented after the time has passed.
- //
- // If the client didn't pass a desired presentation time, mDesiredPresentTime will be
- // populated to the time setBuffer was called, and mIsAutoTimestamp will be set to true.
- //
- // Desired present times that are more than 1 second in the future may be ignored.
- // When a desired present time has already passed, the transaction will be presented as soon
- // as possible.
- //
- // Transactions from the same process are presented in the same order that they are applied.
- // The desired present time does not affect this ordering.
- int64_t mDesiredPresentTime = 0;
- bool mIsAutoTimestamp = true;
-
- // The vsync id provided by Choreographer.getVsyncId and the input event id
- FrameTimelineInfo mFrameTimelineInfo;
-
- // If not null, transactions will be queued up using this token otherwise a common token
- // per process will be used.
- sp<IBinder> mApplyToken = nullptr;
-
- InputWindowCommands mInputWindowCommands;
int mStatus = NO_ERROR;
layer_state_t* getLayerState(const sp<SurfaceControl>& sc);
@@ -508,23 +462,29 @@
void registerSurfaceControlForCallback(const sp<SurfaceControl>& sc);
void setReleaseBufferCallback(BufferData*, ReleaseBufferCallback);
+ protected:
+ // Accessed in tests.
+ explicit Transaction(Transaction const& other) = default;
+ std::unordered_map<sp<ITransactionCompletedListener>, CallbackInfo, TCLHash>
+ mListenerCallbacks;
+
public:
Transaction();
- virtual ~Transaction() = default;
- Transaction(Transaction const& other);
+ Transaction(Transaction&& other);
+ Transaction& operator=(Transaction&& other) = default;
// Factory method that creates a new Transaction instance from the parcel.
static std::unique_ptr<Transaction> createFromParcel(const Parcel* parcel);
- status_t writeToParcel(Parcel* parcel) const override;
- status_t readFromParcel(const Parcel* parcel) override;
+ status_t writeToParcel(Parcel* parcel) const;
+ status_t readFromParcel(const Parcel* parcel);
// Clears the contents of the transaction without applying it.
void clear();
// Returns the current id of the transaction.
// The id is updated every time the transaction is applied.
- uint64_t getId();
+ uint64_t getId() const;
std::vector<uint64_t> getMergedTransactionIds();
@@ -565,6 +525,11 @@
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 +581,7 @@
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,
@@ -714,6 +679,8 @@
const Rect& source, const Rect& dst, int transform);
Transaction& setShadowRadius(const sp<SurfaceControl>& sc, float cornerRadius);
+ Transaction& setBorderSettings(const sp<SurfaceControl>& sc, gui::BorderSettings settings);
+
Transaction& setFrameRate(const sp<SurfaceControl>& sc, float frameRate,
int8_t compatibility, int8_t changeFrameRateStrategy);
diff --git a/libs/gui/include/gui/TransactionState.h b/libs/gui/include/gui/TransactionState.h
new file mode 100644
index 0000000..79124f3
--- /dev/null
+++ b/libs/gui/include/gui/TransactionState.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/gui/FrameTimelineInfo.h>
+#include <binder/Parcelable.h>
+#include <gui/LayerState.h>
+
+namespace android {
+
+// Class to store all the transaction data and the parcelling logic
+class TransactionState {
+public:
+ explicit TransactionState() = default;
+ TransactionState(TransactionState&& other) = default;
+ TransactionState& operator=(TransactionState&& other) = default;
+ status_t writeToParcel(Parcel* parcel) const;
+ status_t readFromParcel(const Parcel* parcel);
+ layer_state_t* getLayerState(const sp<SurfaceControl>& sc);
+ DisplayState& getDisplayState(const sp<IBinder>& token);
+
+ // Returns the current id of the transaction.
+ // The id is updated every time the transaction is applied.
+ uint64_t getId() const { return mId; }
+ std::vector<uint64_t> getMergedTransactionIds() const { return mMergedTransactionIds; }
+ void enableDebugLogCallPoints() { mLogCallPoints = true; }
+ void merge(TransactionState&& other,
+ const std::function<void(layer_state_t&)>& onBufferOverwrite);
+
+ // copied from FrameTimelineInfo::merge()
+ void mergeFrameTimelineInfo(const FrameTimelineInfo& other);
+ void clear();
+ bool operator==(const TransactionState& rhs) const = default;
+ bool operator!=(const TransactionState& rhs) const = default;
+
+ uint64_t mId = 0;
+ std::vector<uint64_t> mMergedTransactionIds;
+ uint32_t mFlags = 0;
+ // The vsync id provided by Choreographer.getVsyncId and the input event id
+ gui::FrameTimelineInfo mFrameTimelineInfo;
+ // mDesiredPresentTime is the time in nanoseconds that the client would like the transaction
+ // to be presented. When it is not possible to present at exactly that time, it will be
+ // presented after the time has passed.
+ //
+ // If the client didn't pass a desired presentation time, mDesiredPresentTime will be
+ // populated to the time setBuffer was called, and mIsAutoTimestamp will be set to true.
+ //
+ // Desired present times that are more than 1 second in the future may be ignored.
+ // When a desired present time has already passed, the transaction will be presented as soon
+ // as possible.
+ //
+ // Transactions from the same process are presented in the same order that they are applied.
+ // The desired present time does not affect this ordering.
+ int64_t mDesiredPresentTime = 0;
+ bool mIsAutoTimestamp = true;
+ // If not null, transactions will be queued up using this token otherwise a common token
+ // per process will be used.
+ sp<IBinder> mApplyToken;
+ // Indicates that the Transaction may contain buffers that should be cached. The reason this
+ // is only a guess is that buffers can be removed before cache is called. This is only a
+ // hint that at some point a buffer was added to this transaction before apply was called.
+ bool mMayContainBuffer = false;
+ // Prints debug logs when enabled.
+ bool mLogCallPoints = false;
+
+ std::vector<DisplayState> mDisplayStates;
+ std::vector<ComposerState> mComposerStates;
+ InputWindowCommands mInputWindowCommands;
+ std::vector<client_cache_t> mUncacheBuffers;
+ // Note: mHasListenerCallbacks can be true even if mListenerCallbacks is
+ // empty.
+ bool mHasListenerCallbacks = false;
+ std::vector<ListenerCallbacks> mListenerCallbacks;
+
+private:
+ explicit TransactionState(TransactionState const& other) = default;
+ friend class TransactionApplicationTest;
+ friend class SurfaceComposerClient;
+ // We keep track of the last MAX_MERGE_HISTORY_LENGTH merged transaction ids.
+ // Ordered most recently merged to least recently merged.
+ static constexpr size_t MAX_MERGE_HISTORY_LENGTH = 10u;
+};
+
+}; // namespace android
diff --git a/libs/gui/include/gui/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h
index eb3be55..9ac49c0 100644
--- a/libs/gui/include/gui/WindowInfo.h
+++ b/libs/gui/include/gui/WindowInfo.h
@@ -150,8 +150,6 @@
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 @@
// 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 @@
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 @@
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/WindowInfosListenerReporter.h b/libs/gui/include/gui/WindowInfosListenerReporter.h
index 684e21a..f9a3ace 100644
--- a/libs/gui/include/gui/WindowInfosListenerReporter.h
+++ b/libs/gui/include/gui/WindowInfosListenerReporter.h
@@ -40,6 +40,9 @@
void reconnect(const sp<gui::ISurfaceComposer>&);
private:
+ WindowInfosListenerReporter() = default;
+ friend class sp<WindowInfosListenerReporter>;
+
std::mutex mListenersMutex;
std::unordered_set<sp<gui::WindowInfosListener>, gui::SpHash<gui::WindowInfosListener>>
mWindowInfosListeners GUARDED_BY(mListenersMutex);
diff --git a/libs/gui/include/gui/mock/GraphicBufferConsumer.h b/libs/gui/include/gui/mock/GraphicBufferConsumer.h
index 98f24c2..18a7e12 100644
--- a/libs/gui/include/gui/mock/GraphicBufferConsumer.h
+++ b/libs/gui/include/gui/mock/GraphicBufferConsumer.h
@@ -18,6 +18,7 @@
#include <gmock/gmock.h>
+#include <com_android_graphics_libgui_flags.h>
#include <gui/IGraphicBufferConsumer.h>
#include <utils/RefBase.h>
@@ -25,21 +26,28 @@
namespace android {
namespace mock {
-class GraphicBufferConsumer : public BnGraphicBufferConsumer, public virtual android::RefBase {
+class GraphicBufferConsumer : public IGraphicBufferConsumer {
public:
GraphicBufferConsumer();
- ~GraphicBufferConsumer() override;
+ ~GraphicBufferConsumer();
MOCK_METHOD3(acquireBuffer, status_t(BufferItem*, nsecs_t, uint64_t));
MOCK_METHOD1(detachBuffer, status_t(int));
MOCK_METHOD2(attachBuffer, status_t(int*, const sp<GraphicBuffer>&));
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP)
+ MOCK_METHOD3(releaseBuffer, status_t(int, uint64_t, const sp<Fence>&));
+#else
MOCK_METHOD5(releaseBuffer, status_t(int, uint64_t, EGLDisplay, EGLSyncKHR, const sp<Fence>&));
+#endif
MOCK_METHOD2(consumerConnect, status_t(const sp<IConsumerListener>&, bool));
MOCK_METHOD0(consumerDisconnect, status_t());
+ MOCK_METHOD1(allowUnlimitedSlots, status_t(bool));
MOCK_METHOD1(getReleasedBuffers, status_t(uint64_t*));
+ MOCK_METHOD1(getReleasedBuffersExtended, status_t(std::vector<bool>*));
MOCK_METHOD2(setDefaultBufferSize, status_t(uint32_t, uint32_t));
MOCK_METHOD1(setMaxBufferCount, status_t(int));
MOCK_METHOD1(setMaxAcquiredBufferCount, status_t(int));
+ MOCK_METHOD2(setMaxAcquiredBufferCount, status_t(int, std::optional<OnBufferReleasedCallback>));
MOCK_METHOD1(setConsumerName, status_t(const String8&));
MOCK_METHOD1(setDefaultBufferFormat, status_t(PixelFormat));
MOCK_METHOD1(setDefaultBufferDataSpace, status_t(android_dataspace));
diff --git a/libs/gui/libgui_flags.aconfig b/libs/gui/libgui_flags.aconfig
index 6bf38c0..ce1bc95 100644
--- a/libs/gui/libgui_flags.aconfig
+++ b/libs/gui/libgui_flags.aconfig
@@ -77,14 +77,6 @@
} # wb_stream_splitter
flag {
- name: "edge_extension_shader"
- namespace: "windowing_frontend"
- description: "Enable edge extension via shader"
- bug: "322036393"
- is_fixed_read_only: true
-} # edge_extension_shader
-
-flag {
name: "buffer_release_channel"
namespace: "window_surfaces"
description: "Enable BufferReleaseChannel to optimize buffer releases"
@@ -139,3 +131,33 @@
bug: "339705065"
is_fixed_read_only: true
} # bq_gl_fence_cleanup
+
+flag {
+ name: "wb_media_migration"
+ namespace: "core_graphics"
+ description: "Main flag for the warren buffers media migration."
+ bug: "340934031"
+ is_fixed_read_only: true
+} # wb_media_migration
+
+flag {
+ name: "allocate_buffer_priority"
+ namespace: "wear_system_health"
+ description: "Boost priority for buffer allocation"
+ bug: "399701430"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+ is_fixed_read_only: true
+} # allocate_buffer_priority
+
+flag {
+ name: "bq_always_use_max_dequeued_buffer_count"
+ namespace: "core_graphics"
+ description: "BufferQueueProducer::dequeue's respects setMaxDequeuedBufferCount even before a buffer is dequeued."
+ bug: "399328309"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+ is_fixed_read_only: true
+} # bq_always_use_max_dequeued_buffer_count
diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp
index f07747f..bd53031 100644
--- a/libs/gui/tests/Android.bp
+++ b/libs/gui/tests/Android.bp
@@ -51,10 +51,6 @@
"-Werror",
"-Wextra",
"-Wthread-safety",
- "-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_BQ_SETFRAMERATE=true",
- "-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",
],
srcs: [
@@ -89,6 +85,7 @@
"testserver/TestServerClient.cpp",
"testserver/TestServerHost.cpp",
"TextureRenderer.cpp",
+ "TransactionState_test.cpp",
"VsyncEventData_test.cpp",
"WindowInfo_test.cpp",
],
@@ -98,6 +95,7 @@
"android.hardware.configstore-utils",
"libSurfaceFlingerProp",
"libGLESv1_CM",
+ "libgui",
"libgui_test_server_aidl-cpp",
"libinput",
"libnativedisplay",
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index 53f4a36..4e4c8a2 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -81,7 +81,9 @@
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 @@
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) {
@@ -199,7 +201,7 @@
protected:
void SetUp() {
mComposer = ComposerService::getComposerService();
- mClient = new SurfaceComposerClient();
+ mClient = sp<SurfaceComposerClient>::make();
const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
ASSERT_FALSE(ids.empty());
// display 0 is picked as this test is not much display depedent
diff --git a/libs/gui/tests/BufferItemConsumer_test.cpp b/libs/gui/tests/BufferItemConsumer_test.cpp
index 3b6a66e..80eea26 100644
--- a/libs/gui/tests/BufferItemConsumer_test.cpp
+++ b/libs/gui/tests/BufferItemConsumer_test.cpp
@@ -22,7 +22,11 @@
#include <gui/BufferItemConsumer.h>
#include <gui/IProducerListener.h>
#include <gui/Surface.h>
+#include <ui/BufferQueueDefs.h>
#include <ui/GraphicBuffer.h>
+#include <utils/Errors.h>
+
+#include <unordered_set>
namespace android {
@@ -57,14 +61,17 @@
};
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 +144,11 @@
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 +158,7 @@
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.
@@ -224,6 +236,38 @@
ASSERT_EQ(1, GetFreedBufferCount());
}
+TEST_F(BufferItemConsumerTest, ResizeAcquireCount) {
+ EXPECT_EQ(OK, mBIC->setMaxAcquiredBufferCount(kMaxLockedBuffers + 1));
+ EXPECT_EQ(OK, mBIC->setMaxAcquiredBufferCount(kMaxLockedBuffers + 2));
+ EXPECT_EQ(OK, mBIC->setMaxAcquiredBufferCount(kMaxLockedBuffers - 1));
+ EXPECT_EQ(OK, mBIC->setMaxAcquiredBufferCount(kMaxLockedBuffers - 2));
+ EXPECT_EQ(OK, mBIC->setMaxAcquiredBufferCount(kMaxLockedBuffers + 1));
+ EXPECT_EQ(OK, mBIC->setMaxAcquiredBufferCount(kMaxLockedBuffers - 1));
+}
+
+TEST_F(BufferItemConsumerTest, AttachBuffer) {
+ ASSERT_EQ(OK, mBIC->setMaxAcquiredBufferCount(1));
+
+ int slot;
+ DequeueBuffer(&slot);
+ QueueBuffer(slot);
+ AcquireBuffer(&slot);
+
+ sp<GraphicBuffer> newBuffer1 = sp<GraphicBuffer>::make(kWidth, kHeight, kFormat, kUsage);
+ sp<GraphicBuffer> newBuffer2 = sp<GraphicBuffer>::make(kWidth, kHeight, kFormat, kUsage);
+
+ // For some reason, you can attach an extra buffer?
+ // b/400973991 to investigate
+ EXPECT_EQ(OK, mBIC->attachBuffer(newBuffer1));
+ EXPECT_EQ(INVALID_OPERATION, mBIC->attachBuffer(newBuffer2));
+
+ ReleaseBuffer(slot);
+
+ EXPECT_EQ(OK, mBIC->attachBuffer(newBuffer2));
+ EXPECT_EQ(OK, mBIC->releaseBuffer(newBuffer1, Fence::NO_FENCE));
+ EXPECT_EQ(OK, mBIC->releaseBuffer(newBuffer2, Fence::NO_FENCE));
+}
+
#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
// Test that delete BufferItemConsumer triggers onBufferFreed.
TEST_F(BufferItemConsumerTest, DetachBufferWithBuffer) {
@@ -239,4 +283,52 @@
}
#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 1606099..e22f57e 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 @@
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 @@
sp<IGraphicBufferProducer> mProducer;
sp<IGraphicBufferConsumer> mConsumer;
+ std::vector<std::function<void()>> mTeardownFns;
};
static const uint32_t TEST_DATA = 0x12345678u;
@@ -102,12 +118,14 @@
// 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 @@
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 @@
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) {
@@ -1411,10 +1452,6 @@
ASSERT_EQ(NO_INIT, mProducer->disconnect(NATIVE_WINDOW_API_CPU));
}
-TEST_F(BufferQueueTest, TestBqSetFrameRateFlagBuildTimeIsSet) {
- ASSERT_EQ(flags::bq_setframerate(), COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_SETFRAMERATE));
-}
-
struct BufferItemConsumerSetFrameRateListener : public BufferItemConsumer {
BufferItemConsumerSetFrameRateListener() : BufferItemConsumer(GRALLOC_USAGE_SW_READ_OFTEN, 1) {}
@@ -1520,9 +1557,14 @@
{.name = "android.hardware.graphics.common.Dataspace", ADATASPACE_DISPLAY_P3},
}};
- ASSERT_EQ(NO_INIT,
- native_window_set_buffers_additional_options(surface.get(), extras.data(),
- extras.size()));
+ auto status = native_window_set_buffers_additional_options(surface.get(), extras.data(),
+ extras.size());
+ if (flags::bq_extendedallocate()) {
+ ASSERT_EQ(NO_INIT, status);
+ } else {
+ ASSERT_EQ(INVALID_OPERATION, status);
+ GTEST_SKIP() << "Flag bq_extendedallocate not enabled";
+ }
if (!IsCuttlefish()) {
GTEST_SKIP() << "Not cuttlefish";
@@ -1612,4 +1654,221 @@
}
}
+#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 8db48d2..314dea6 100644
--- a/libs/gui/tests/Choreographer_test.cpp
+++ b/libs/gui/tests/Choreographer_test.cpp
@@ -50,7 +50,7 @@
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 @@
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 f4239cb..482cfde 100644
--- a/libs/gui/tests/CpuConsumer_test.cpp
+++ b/libs/gui/tests/CpuConsumer_test.cpp
@@ -66,10 +66,9 @@
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 @@
::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 6e6915b..3d5f633 100644
--- a/libs/gui/tests/DisconnectWaiter.h
+++ b/libs/gui/tests/DisconnectWaiter.h
@@ -29,7 +29,7 @@
// 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/DisplayedContentSampling_test.cpp b/libs/gui/tests/DisplayedContentSampling_test.cpp
index bffb3f0..62d73ca 100644
--- a/libs/gui/tests/DisplayedContentSampling_test.cpp
+++ b/libs/gui/tests/DisplayedContentSampling_test.cpp
@@ -30,7 +30,7 @@
class DisplayedContentSamplingTest : public ::testing::Test {
protected:
void SetUp() {
- mComposerClient = new SurfaceComposerClient;
+ mComposerClient = sp<SurfaceComposerClient>::make();
ASSERT_EQ(OK, mComposerClient->initCheck());
const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
ASSERT_FALSE(ids.empty());
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index 06f00a4..5a5067b 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -85,6 +85,7 @@
// 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 @@
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();
@@ -221,8 +222,8 @@
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(flags, mev->getFlags() & flags);
ev = consumeEvent();
@@ -240,8 +241,8 @@
MotionEvent* mev = static_cast<MotionEvent*>(ev);
EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, mev->getAction());
const PointerCoords& coords = *mev->getRawPointerCoords(0 /*pointerIndex*/);
- EXPECT_EQ(displayX, coords.getX());
- EXPECT_EQ(displayY, coords.getY());
+ EXPECT_NEAR(displayX, coords.getX(), EPSILON);
+ EXPECT_NEAR(displayY, coords.getY(), EPSILON);
EXPECT_EQ(0, mev->getFlags() & VERIFIED_MOTION_EVENT_FLAGS);
ev = consumeEvent();
@@ -397,7 +398,7 @@
InputSurfacesTest() { ProcessState::self()->startThreadPool(); }
void SetUp() {
- mComposerClient = new SurfaceComposerClient;
+ mComposerClient = sp<SurfaceComposerClient>::make();
ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
ASSERT_FALSE(ids.empty());
@@ -1230,7 +1231,7 @@
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 b60995a..11383d9 100644
--- a/libs/gui/tests/FillBuffer.cpp
+++ b/libs/gui/tests/FillBuffer.cpp
@@ -76,7 +76,7 @@
}
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, 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 b584179..f5d6b8b 100644
--- a/libs/gui/tests/FillBuffer.h
+++ b/libs/gui/tests/FillBuffer.h
@@ -30,6 +30,8 @@
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 9ffe91f..c502533 100644
--- a/libs/gui/tests/FrameRateUtilsTest.cpp
+++ b/libs/gui/tests/FrameRateUtilsTest.cpp
@@ -34,7 +34,7 @@
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/GLTest.cpp b/libs/gui/tests/GLTest.cpp
index 40af8e8..407c18e 100644
--- a/libs/gui/tests/GLTest.cpp
+++ b/libs/gui/tests/GLTest.cpp
@@ -56,7 +56,7 @@
}
if (mDisplaySecs > 0) {
- mComposerClient = new SurfaceComposerClient;
+ mComposerClient = sp<SurfaceComposerClient>::make();
ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
mSurfaceControl = mComposerClient->createSurface(
diff --git a/libs/gui/tests/Malicious.cpp b/libs/gui/tests/Malicious.cpp
index 376420c..cc16383 100644
--- a/libs/gui/tests/Malicious.cpp
+++ b/libs/gui/tests/Malicious.cpp
@@ -129,7 +129,7 @@
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 4a6c51c..2e8bc63 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 2428bb3..7ae6f40 100644
--- a/libs/gui/tests/MultiTextureConsumer_test.cpp
+++ b/libs/gui/tests/MultiTextureConsumer_test.cpp
@@ -34,8 +34,8 @@
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 a0d8c53..d80d223 100644
--- a/libs/gui/tests/RegionSampling_test.cpp
+++ b/libs/gui/tests/RegionSampling_test.cpp
@@ -40,7 +40,7 @@
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);
@@ -180,7 +180,7 @@
}
void SetUp() override {
- mSurfaceComposerClient = new SurfaceComposerClient;
+ mSurfaceComposerClient = sp<SurfaceComposerClient>::make();
ASSERT_EQ(NO_ERROR, mSurfaceComposerClient->initCheck());
mBackgroundLayer =
diff --git a/libs/gui/tests/SamplingDemo.cpp b/libs/gui/tests/SamplingDemo.cpp
index 8fea689..a2fe8fd 100644
--- a/libs/gui/tests/SamplingDemo.cpp
+++ b/libs/gui/tests/SamplingDemo.cpp
@@ -36,7 +36,7 @@
class Button : public gui::BnRegionSamplingListener {
public:
Button(const char* name, const Rect& samplingArea) {
- sp<SurfaceComposerClient> client = new SurfaceComposerClient;
+ sp<SurfaceComposerClient> client = sp<SurfaceComposerClient>::make();
mButton = client->createSurface(String8(name), 0, 0, PIXEL_FORMAT_RGBA_8888,
ISurfaceComposerClient::eFXSurfaceEffect);
diff --git a/libs/gui/tests/StreamSplitter_test.cpp b/libs/gui/tests/StreamSplitter_test.cpp
index 1c439cd..9570a36 100644
--- a/libs/gui/tests/StreamSplitter_test.cpp
+++ b/libs/gui/tests/StreamSplitter_test.cpp
@@ -32,7 +32,7 @@
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 59d05b6..d3434ea 100644
--- a/libs/gui/tests/SurfaceTextureClient_test.cpp
+++ b/libs/gui/tests/SurfaceTextureClient_test.cpp
@@ -40,8 +40,7 @@
}
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 @@
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 1309635..eb31cd1 100644
--- a/libs/gui/tests/SurfaceTextureGL.h
+++ b/libs/gui/tests/SurfaceTextureGL.h
@@ -38,8 +38,7 @@
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 449533a..b22b853 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 @@
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 76362ff..4fee11c 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 {
@@ -131,7 +134,7 @@
}
virtual void SetUp() {
- mComposerClient = new SurfaceComposerClient;
+ mComposerClient = sp<SurfaceComposerClient>::make();
ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
// TODO(brianderson): The following sometimes fails and is a source of
@@ -291,9 +294,7 @@
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, 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, 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 @@
ASSERT_GE(after, lastDequeueTime);
}
-class FakeConsumer : public BnConsumerListener {
+class FakeConsumer : public IConsumerListener {
public:
void onFrameAvailable(const BufferItem& /*item*/) override {}
void onBuffersReleased() override {}
@@ -650,16 +648,7 @@
mSupportsPresent = supportsPresent;
}
- status_t setTransactionState(
- const FrameTimelineInfo& /*frameTimelineInfo*/, Vector<ComposerState>& /*state*/,
- Vector<DisplayState>& /*displays*/, uint32_t /*flags*/,
- const sp<IBinder>& /*applyToken*/, InputWindowCommands /*inputWindowCommands*/,
- int64_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/,
- const std::vector<client_cache_t>& /*cachedBuffer*/, bool /*hasListenerCallbacks*/,
- const std::vector<ListenerCallbacks>& /*listenerCallbacks*/, uint64_t /*transactionId*/,
- const std::vector<uint64_t>& /*mergedTransactionIds*/) override {
- return NO_ERROR;
- }
+ status_t setTransactionState(TransactionState&&) override { return NO_ERROR; }
protected:
IBinder* onAsBinder() override { return nullptr; }
@@ -689,10 +678,11 @@
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 +1006,11 @@
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 +2167,7 @@
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 +2215,7 @@
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();
@@ -2245,6 +2237,52 @@
ASSERT_EQ(NO_ERROR, surface->disconnect(NATIVE_WINDOW_API_CPU));
}
+TEST_F(SurfaceTest, setMaxDequeuedBufferCount_setMaxAcquiredBufferCount_allocations) {
+ //
+ // Set up the consumer and producer--nothing fancy.
+ //
+ auto [consumer, surface] =
+ BufferItemConsumer::create(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER);
+ sp<SurfaceListener> surfaceListener = sp<StubSurfaceListener>::make();
+ surface->connect(NATIVE_WINDOW_API_CPU, surfaceListener);
+ sp<GraphicBuffer> buffer;
+ sp<Fence> fence;
+
+ //
+ // These values are independent. The consumer can dequeue 3 and the consumer can acquire 3 at
+ // the same time.
+ //
+ ASSERT_EQ(OK, consumer->setMaxAcquiredBufferCount(3));
+ ASSERT_EQ(OK, surface->setMaxDequeuedBufferCount(3));
+
+ //
+ // Take all three buffers out of the queue--a fourth can't be retrieved. Then queue them.
+ //
+ std::vector<Surface::BatchBuffer> dequeuedBuffers(3);
+ EXPECT_EQ(OK, surface->dequeueBuffers(&dequeuedBuffers));
+ if (::com::android::graphics::libgui::flags::bq_always_use_max_dequeued_buffer_count()) {
+ EXPECT_EQ(INVALID_OPERATION, surface->dequeueBuffer(&buffer, &fence));
+ }
+
+ for (auto& batchBuffer : dequeuedBuffers) {
+ EXPECT_EQ(OK,
+ surface->queueBuffer(GraphicBuffer::from(batchBuffer.buffer),
+ sp<Fence>::make(batchBuffer.fenceFd)));
+ }
+ dequeuedBuffers.assign(3, {});
+
+ //
+ // Acquire all three, then we should be able to dequeue 3 more.
+ //
+ std::vector<BufferItem> acquiredBuffers(3);
+ for (auto& bufferItem : acquiredBuffers) {
+ EXPECT_EQ(OK, consumer->acquireBuffer(&bufferItem, 0));
+ }
+
+ EXPECT_EQ(OK, surface->dequeueBuffers(&dequeuedBuffers));
+ EXPECT_EQ(INVALID_OPERATION, surface->dequeueBuffer(&buffer, &fence));
+}
+
#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
TEST_F(SurfaceTest, PlatformBufferMethods) {
@@ -2370,8 +2408,7 @@
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 +2566,181 @@
}
#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)
+
+TEST_F(SurfaceTest, isBufferOwned) {
+ const int TEST_USAGE_FLAGS = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER;
+ auto [bufferItemConsumer, surface] = BufferItemConsumer::create(TEST_USAGE_FLAGS);
+
+ sp<SurfaceListener> listener = sp<StubSurfaceListener>::make();
+ ASSERT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, listener));
+
+ sp<GraphicBuffer> surfaceAttachableBuffer =
+ sp<GraphicBuffer>::make(10, 10, PIXEL_FORMAT_RGBA_8888, 1, TEST_USAGE_FLAGS);
+
+ //
+ // Attaching a buffer makes it owned.
+ //
+
+ bool isOwned;
+ EXPECT_EQ(OK, surface->isBufferOwned(surfaceAttachableBuffer, &isOwned));
+ EXPECT_FALSE(isOwned);
+
+ EXPECT_EQ(OK, surface->attachBuffer(surfaceAttachableBuffer.get()));
+ EXPECT_EQ(OK, surface->isBufferOwned(surfaceAttachableBuffer, &isOwned));
+ EXPECT_TRUE(isOwned);
+
+ //
+ // A dequeued buffer is always owned.
+ //
+
+ sp<GraphicBuffer> buffer;
+ sp<Fence> fence;
+ EXPECT_EQ(OK, surface->dequeueBuffer(&buffer, &fence));
+ EXPECT_EQ(OK, surface->isBufferOwned(buffer, &isOwned));
+ EXPECT_TRUE(isOwned);
+
+ //
+ // A detached buffer is no longer owned.
+ //
+
+ EXPECT_EQ(OK, surface->detachBuffer(buffer));
+ EXPECT_EQ(OK, surface->isBufferOwned(buffer, &isOwned));
+ EXPECT_FALSE(isOwned);
+
+ //
+ // It's not currently possible to verify whether or not a consumer has attached a buffer until
+ // it shows up on the Surface.
+ //
+
+ sp<GraphicBuffer> consumerAttachableBuffer =
+ sp<GraphicBuffer>::make(10, 10, PIXEL_FORMAT_RGBA_8888, 1, TEST_USAGE_FLAGS);
+
+ ASSERT_EQ(OK, bufferItemConsumer->attachBuffer(consumerAttachableBuffer));
+ EXPECT_EQ(OK, surface->isBufferOwned(consumerAttachableBuffer, &isOwned));
+ EXPECT_FALSE(isOwned);
+}
} // namespace android
diff --git a/libs/gui/tests/TransactionState_test.cpp b/libs/gui/tests/TransactionState_test.cpp
new file mode 100644
index 0000000..179b264
--- /dev/null
+++ b/libs/gui/tests/TransactionState_test.cpp
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gmock/gmock.h>
+
+#include <gtest/gtest.h>
+#include <unordered_map>
+#include "android/gui/FocusRequest.h"
+#include "binder/Binder.h"
+#include "binder/Parcel.h"
+#include "gtest/gtest.h"
+#include "gui/LayerState.h"
+#include "gui/WindowInfo.h"
+
+#include "gui/TransactionState.h"
+
+namespace android {
+
+void sprintf(std::string& out, const char* format, ...) {
+ va_list arg_list;
+ va_start(arg_list, format);
+
+ int len = vsnprintf(nullptr, 0, format, arg_list);
+ if (len < 0) {
+ va_end(arg_list);
+ }
+ std::string line(len, '\0');
+ int written = vsnprintf(line.data(), len + 1, format, arg_list);
+ if (written != len) {
+ va_end(arg_list);
+ }
+ line.pop_back();
+ out += line;
+ va_end(arg_list);
+}
+
+constexpr std::string dump_struct(auto& x) {
+ std::string s;
+#if __has_builtin(__builtin_dump_struct)
+ __builtin_dump_struct(&x, sprintf, s);
+#else
+ (void)x;
+#endif
+ return s;
+}
+
+void PrintTo(const TransactionState& state, ::std::ostream* os) {
+ *os << dump_struct(state);
+ *os << state.mFrameTimelineInfo.toString();
+ for (auto mergedId : state.mMergedTransactionIds) {
+ *os << mergedId << ",";
+ }
+}
+
+void PrintTo(const ComposerState& state, ::std::ostream* os) {
+ *os << dump_struct(state.state);
+ *os << state.state.getWindowInfo();
+}
+
+// In case EXPECT_EQ fails, this function is useful to pinpoint exactly which
+// field did not compare ==.
+void Compare(const TransactionState& s1, const TransactionState& s2) {
+ EXPECT_EQ(s1.mId, s2.mId);
+ EXPECT_EQ(s1.mMergedTransactionIds, s2.mMergedTransactionIds);
+ EXPECT_EQ(s1.mFlags, s2.mFlags);
+ EXPECT_EQ(s1.mFrameTimelineInfo, s2.mFrameTimelineInfo);
+ EXPECT_EQ(s1.mDesiredPresentTime, s2.mDesiredPresentTime);
+ EXPECT_EQ(s1.mIsAutoTimestamp, s2.mIsAutoTimestamp);
+ EXPECT_EQ(s1.mApplyToken, s2.mApplyToken);
+ EXPECT_EQ(s1.mMayContainBuffer, s2.mMayContainBuffer);
+ EXPECT_EQ(s1.mLogCallPoints, s2.mLogCallPoints);
+ EXPECT_EQ(s1.mDisplayStates.size(), s2.mDisplayStates.size());
+ EXPECT_THAT(s1.mDisplayStates, ::testing::ContainerEq(s2.mDisplayStates));
+ EXPECT_EQ(s1.mComposerStates.size(), s2.mComposerStates.size());
+ EXPECT_EQ(s1.mComposerStates, s2.mComposerStates);
+ EXPECT_EQ(s1.mInputWindowCommands, s2.mInputWindowCommands);
+ EXPECT_EQ(s1.mUncacheBuffers, s2.mUncacheBuffers);
+ EXPECT_EQ(s1.mHasListenerCallbacks, s2.mHasListenerCallbacks);
+ EXPECT_EQ(s1.mListenerCallbacks.size(), s2.mListenerCallbacks.size());
+ EXPECT_EQ(s1.mListenerCallbacks, s2.mListenerCallbacks);
+}
+
+std::unique_ptr<std::unordered_map<int, sp<BBinder>>> createTokenMap(size_t maxSize) {
+ auto result = std::make_unique<std::unordered_map<int, sp<BBinder>>>();
+ for (size_t i = 0; i < maxSize; ++i) {
+ result->emplace(i, sp<BBinder>::make());
+ }
+ return result;
+}
+
+constexpr size_t kMaxComposerStates = 2;
+ComposerState createComposerStateForTest(size_t i) {
+ static const auto* const sLayerHandle = createTokenMap(kMaxComposerStates).release();
+
+ ComposerState state;
+ state.state.what = layer_state_t::eFlagsChanged;
+ state.state.surface = sLayerHandle->at(i);
+ state.state.layerId = i;
+ state.state.flags = 20 * i;
+ return state;
+}
+
+constexpr size_t kMaxDisplayStates = 5;
+DisplayState createDisplayStateForTest(size_t i) {
+ static const auto* const sDisplayTokens = createTokenMap(kMaxDisplayStates).release();
+
+ DisplayState displayState;
+ displayState.what = DisplayState::eFlagsChanged;
+ displayState.token = sDisplayTokens->at(i);
+ displayState.flags = 20 * i;
+ return displayState;
+}
+
+TransactionState createTransactionStateForTest() {
+ static sp<BBinder> sApplyToken = sp<BBinder>::make();
+
+ TransactionState state;
+ state.mId = 123;
+ state.mMergedTransactionIds.push_back(15);
+ state.mMergedTransactionIds.push_back(0);
+ state.mFrameTimelineInfo.vsyncId = 14;
+ state.mDesiredPresentTime = 11;
+ state.mIsAutoTimestamp = true;
+ state.mApplyToken = sApplyToken;
+ for (size_t i = 0; i < kMaxDisplayStates; i++) {
+ state.mDisplayStates.push_back(createDisplayStateForTest(i));
+ }
+ for (size_t i = 0; i < kMaxComposerStates; i++) {
+ state.mComposerStates.push_back(createComposerStateForTest(i));
+ }
+ static const auto* const sFocusRequestTokens = createTokenMap(5).release();
+ for (int i = 0; i < 5; i++) {
+ gui::FocusRequest request;
+ request.token = sFocusRequestTokens->at(i);
+ request.timestamp = i;
+ state.mInputWindowCommands.addFocusRequest(request);
+ }
+ static const auto* const sCacheToken = createTokenMap(5).release();
+ for (int i = 0; i < 5; i++) {
+ client_cache_t cache;
+ cache.token = sCacheToken->at(i);
+ cache.id = i;
+ state.mUncacheBuffers.emplace_back(std::move(cache));
+ }
+ static const auto* const sListenerCallbacks = []() {
+ auto* callbacks = new std::vector<ListenerCallbacks>();
+ for (int i = 0; i < 5; i++) {
+ callbacks->emplace_back(sp<BBinder>::make(),
+ std::unordered_set<CallbackId, CallbackIdHash>{});
+ }
+ return callbacks;
+ }();
+ state.mHasListenerCallbacks = true;
+ state.mListenerCallbacks = *sListenerCallbacks;
+ return state;
+}
+
+TransactionState createEmptyTransaction(uint64_t id) {
+ TransactionState state;
+ state.mId = id;
+ return state;
+}
+
+TEST(TransactionStateTest, parcel) {
+ TransactionState state = createTransactionStateForTest();
+ Parcel p;
+ state.writeToParcel(&p);
+ p.setDataPosition(0);
+ TransactionState parcelledState;
+ parcelledState.readFromParcel(&p);
+ EXPECT_EQ(state, parcelledState);
+};
+
+TEST(TransactionStateTest, parcelDisplayState) {
+ DisplayState state = createDisplayStateForTest(0);
+ Parcel p;
+ state.write(p);
+ p.setDataPosition(0);
+ DisplayState parcelledState;
+ parcelledState.read(p);
+ EXPECT_EQ(state, parcelledState);
+};
+
+TEST(TransactionStateTest, parcelLayerState) {
+ ComposerState state = createComposerStateForTest(0);
+ Parcel p;
+ state.write(p);
+ p.setDataPosition(0);
+ ComposerState parcelledState;
+ parcelledState.read(p);
+ EXPECT_EQ(state, parcelledState);
+};
+
+TEST(TransactionStateTest, parcelEmptyState) {
+ TransactionState state;
+ Parcel p;
+ state.writeToParcel(&p);
+ p.setDataPosition(0);
+ TransactionState parcelledState;
+ state.readFromParcel(&p);
+ EXPECT_EQ(state, parcelledState);
+};
+
+TEST(TransactionStateTest, mergeLayerState) {
+ ComposerState composerState = createComposerStateForTest(0);
+ ComposerState update;
+ update.state.surface = composerState.state.surface;
+ update.state.layerId = 0;
+ update.state.what = layer_state_t::eAlphaChanged;
+ update.state.color.a = .42;
+ composerState.state.merge(update.state);
+
+ ComposerState expectedMergedState = createComposerStateForTest(0);
+ expectedMergedState.state.what |= layer_state_t::eAlphaChanged;
+ expectedMergedState.state.color.a = .42;
+ EXPECT_EQ(composerState, expectedMergedState);
+};
+
+TEST(TransactionStateTest, merge) {
+ // Setup.
+ static constexpr uint64_t kUpdateTransactionId = 200;
+
+ TransactionState state = createTransactionStateForTest();
+
+ TransactionState update;
+ update.mId = kUpdateTransactionId;
+ {
+ ComposerState composerState;
+ composerState.state.surface = state.mComposerStates[0].state.surface;
+ composerState.state.what = layer_state_t::eAlphaChanged;
+ composerState.state.color.a = .42;
+ update.mComposerStates.push_back(composerState);
+ }
+ {
+ ComposerState composerState;
+ composerState.state.surface = state.mComposerStates[1].state.surface;
+ composerState.state.what = layer_state_t::eBufferChanged;
+ update.mComposerStates.push_back(composerState);
+ }
+ int32_t overrwiteLayerId = -1;
+ // Mutation.
+ state.merge(std::move(update),
+ [&overrwiteLayerId](layer_state_t ls) { overrwiteLayerId = ls.layerId; });
+ // Assertions.
+ EXPECT_EQ(1, overrwiteLayerId);
+ EXPECT_EQ(update, createEmptyTransaction(update.getId()));
+
+ TransactionState expectedMergedState = createTransactionStateForTest();
+ expectedMergedState.mMergedTransactionIds
+ .insert(expectedMergedState.mMergedTransactionIds.begin(), kUpdateTransactionId);
+ expectedMergedState.mComposerStates.at(0).state.what |= layer_state_t::eAlphaChanged;
+ expectedMergedState.mComposerStates.at(0).state.color.a = .42;
+ expectedMergedState.mComposerStates.at(1).state.what |= layer_state_t::eBufferChanged;
+ auto inputCommands = expectedMergedState.mInputWindowCommands;
+
+ // desired present time is not merged.
+ expectedMergedState.mDesiredPresentTime = state.mDesiredPresentTime;
+
+ EXPECT_EQ(state.mComposerStates[0], expectedMergedState.mComposerStates[0]);
+ EXPECT_EQ(state.mInputWindowCommands, expectedMergedState.mInputWindowCommands);
+ EXPECT_EQ(state, expectedMergedState);
+};
+
+TEST(TransactionStateTest, clear) {
+ TransactionState state = createTransactionStateForTest();
+ state.clear();
+ TransactionState emptyState = createEmptyTransaction(state.getId());
+ EXPECT_EQ(state, emptyState);
+};
+
+} // namespace android
diff --git a/libs/gui/tests/WindowInfo_test.cpp b/libs/gui/tests/WindowInfo_test.cpp
index ce22082..e3f9a07 100644
--- a/libs/gui/tests/WindowInfo_test.cpp
+++ b/libs/gui/tests/WindowInfo_test.cpp
@@ -40,7 +40,18 @@
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 @@
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 @@
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 0000000..a728bef
--- /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 0000000..0a51895
--- /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 0000000..685c7c6
--- /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 cd8824e..17d1b4a 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 0a92a71..1ed9794 100644
--- a/libs/input/AccelerationCurve.cpp
+++ b/libs/input/AccelerationCurve.cpp
@@ -40,6 +40,18 @@
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 @@
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 a4ae54b..52e0276 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -27,9 +27,9 @@
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 @@
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_SOURCE_CLASS_BUTTON",
- "--allowlist-var=AINPUT_SOURCE_CLASS_POINTER",
- "--allowlist-var=AINPUT_SOURCE_CLASS_NAVIGATION",
- "--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_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_TOUCHPAD",
- "--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_ALPHABETIC",
"--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_BLUETOOTH_STYLUS",
+ "--allowlist-var=AINPUT_SOURCE_CLASS_BUTTON",
+ "--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_DPAD",
+ "--allowlist-var=AINPUT_SOURCE_GAMEPAD",
+ "--allowlist-var=AINPUT_SOURCE_HDMI",
+ "--allowlist-var=AINPUT_SOURCE_JOYSTICK",
+ "--allowlist-var=AINPUT_SOURCE_KEYBOARD",
+ "--allowlist-var=AINPUT_SOURCE_MOUSE",
+ "--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_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_CTRL_LEFT_ON",
- "--allowlist-var=AMETA_CTRL_RIGHT_ON",
- "--allowlist-var=AMETA_META_ON",
- "--allowlist-var=AMETA_META_LEFT_ON",
- "--allowlist-var=AMETA_META_RIGHT_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_FUNCTION_ON",
+ "--allowlist-var=AMETA_META_LEFT_ON",
+ "--allowlist-var=AMETA_META_ON",
+ "--allowlist-var=AMETA_META_RIGHT_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 @@
],
shared_libs: ["libc++"],
header_libs: [
- "native_headers",
- "jni_headers",
"flatbuffer_headers",
+ "jni_headers",
+ "native_headers",
],
}
@@ -179,8 +186,8 @@
host_supported: true,
cflags: [
"-Wall",
- "-Wextra",
"-Werror",
+ "-Wextra",
],
srcs: [
"FromRustToCpp.cpp",
@@ -205,30 +212,32 @@
cpp_std: "c++20",
host_supported: true,
cflags: [
+ "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
"-Wall",
- "-Wextra",
"-Werror",
+ "-Wextra",
"-Wno-unused-parameter",
- "-Wthread-safety",
"-Wshadow",
"-Wshadow-field-in-constructor-modified",
"-Wshadow-uncaptured-local",
- "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
+ "-Wthread-safety",
],
srcs: [
"AccelerationCurve.cpp",
"CoordinateFilter.cpp",
+ "DisplayTopologyGraph.cpp",
"Input.cpp",
"InputConsumer.cpp",
"InputConsumerNoResampling.cpp",
"InputDevice.cpp",
"InputEventLabels.cpp",
+ "InputFlags.cpp",
"InputTransport.cpp",
"InputVerifier.cpp",
- "Keyboard.cpp",
"KeyCharacterMap.cpp",
- "KeyboardClassifier.cpp",
"KeyLayoutMap.cpp",
+ "Keyboard.cpp",
+ "KeyboardClassifier.cpp",
"MotionPredictor.cpp",
"MotionPredictorMetricsManager.cpp",
"OneEuroFilter.cpp",
@@ -262,12 +271,14 @@
shared_libs: [
"android.companion.virtualdevice.flags-aconfig-cc",
+ "com.android.window.flags.window-aconfig_flags_c_lib",
+ "libPlatformProperties",
+ "libaconfig_storage_read_api_cc",
"libbase",
"libbinder",
"libbinder_ndk",
"libcutils",
"liblog",
- "libPlatformProperties",
"libtinyxml2",
"libutils",
"libz", // needed by libkernelconfigs
@@ -286,15 +297,15 @@
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: [
@@ -309,8 +320,8 @@
target: {
android: {
required: [
- "motion_predictor_model_prebuilt",
"motion_predictor_model_config",
+ "motion_predictor_model_prebuilt",
],
static_libs: [
"libstatslog_libinput",
@@ -371,9 +382,9 @@
cpp_std: "c++20",
host_supported: true,
shared_libs: [
- "libutils",
"libbase",
"liblog",
+ "libutils",
],
}
diff --git a/libs/input/DisplayTopologyGraph.cpp b/libs/input/DisplayTopologyGraph.cpp
new file mode 100644
index 0000000..934f2e8
--- /dev/null
+++ b/libs/input/DisplayTopologyGraph.cpp
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "DisplayTopologyValidator"
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <ftl/enum.h>
+#include <input/DisplayTopologyGraph.h>
+#include <input/PrintTools.h>
+#include <ui/LogicalDisplayId.h>
+
+#include <algorithm>
+
+#define INDENT " "
+
+namespace android {
+
+namespace {
+
+DisplayTopologyPosition getOppositePosition(DisplayTopologyPosition position) {
+ switch (position) {
+ case DisplayTopologyPosition::LEFT:
+ return DisplayTopologyPosition::RIGHT;
+ case DisplayTopologyPosition::TOP:
+ return DisplayTopologyPosition::BOTTOM;
+ case DisplayTopologyPosition::RIGHT:
+ return DisplayTopologyPosition::LEFT;
+ case DisplayTopologyPosition::BOTTOM:
+ return DisplayTopologyPosition::TOP;
+ }
+}
+
+bool validatePrimaryDisplay(const android::DisplayTopologyGraph& displayTopologyGraph) {
+ return displayTopologyGraph.primaryDisplayId != ui::LogicalDisplayId::INVALID &&
+ displayTopologyGraph.graph.contains(displayTopologyGraph.primaryDisplayId);
+}
+
+bool validateTopologyGraph(const android::DisplayTopologyGraph& displayTopologyGraph) {
+ for (const auto& [sourceDisplay, adjacentDisplays] : displayTopologyGraph.graph) {
+ for (const DisplayTopologyAdjacentDisplay& adjacentDisplay : adjacentDisplays) {
+ const auto adjacentGraphIt = displayTopologyGraph.graph.find(adjacentDisplay.displayId);
+ if (adjacentGraphIt == displayTopologyGraph.graph.end()) {
+ LOG(ERROR) << "Missing adjacent display in topology graph: "
+ << adjacentDisplay.displayId << " for source " << sourceDisplay;
+ return false;
+ }
+ const auto reverseEdgeIt =
+ std::find_if(adjacentGraphIt->second.begin(), adjacentGraphIt->second.end(),
+ [sourceDisplay](const DisplayTopologyAdjacentDisplay&
+ reverseAdjacentDisplay) {
+ return sourceDisplay == reverseAdjacentDisplay.displayId;
+ });
+ if (reverseEdgeIt == adjacentGraphIt->second.end()) {
+ LOG(ERROR) << "Missing reverse edge in topology graph for: " << sourceDisplay
+ << " -> " << adjacentDisplay.displayId;
+ return false;
+ }
+ DisplayTopologyPosition expectedPosition =
+ getOppositePosition(adjacentDisplay.position);
+ if (reverseEdgeIt->position != expectedPosition) {
+ LOG(ERROR) << "Unexpected reverse edge for: " << sourceDisplay << " -> "
+ << adjacentDisplay.displayId
+ << " expected position: " << ftl::enum_string(expectedPosition)
+ << " actual " << ftl::enum_string(reverseEdgeIt->position);
+ return false;
+ }
+ if (reverseEdgeIt->offsetDp != -adjacentDisplay.offsetDp) {
+ LOG(ERROR) << "Unexpected reverse edge offset: " << sourceDisplay << " -> "
+ << adjacentDisplay.displayId
+ << " expected offset: " << -adjacentDisplay.offsetDp << " actual "
+ << reverseEdgeIt->offsetDp;
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+bool validateDensities(const android::DisplayTopologyGraph& displayTopologyGraph) {
+ for (const auto& [sourceDisplay, adjacentDisplays] : displayTopologyGraph.graph) {
+ if (!displayTopologyGraph.displaysDensity.contains(sourceDisplay)) {
+ LOG(ERROR) << "Missing density value in topology graph for display: " << sourceDisplay;
+ return false;
+ }
+ }
+ return true;
+}
+
+std::string logicalDisplayIdToString(const ui::LogicalDisplayId& displayId) {
+ return base::StringPrintf("displayId(%d)", displayId.val());
+}
+
+std::string adjacentDisplayToString(const DisplayTopologyAdjacentDisplay& adjacentDisplay) {
+ return adjacentDisplay.dump();
+}
+
+std::string adjacentDisplayVectorToString(
+ const std::vector<DisplayTopologyAdjacentDisplay>& adjacentDisplays) {
+ return dumpVector(adjacentDisplays, adjacentDisplayToString);
+}
+
+} // namespace
+
+std::string DisplayTopologyAdjacentDisplay::dump() const {
+ std::string dump;
+ dump += base::StringPrintf("DisplayTopologyAdjacentDisplay: {displayId: %d, position: %s, "
+ "offsetDp: %f}",
+ displayId.val(), ftl::enum_string(position).c_str(), offsetDp);
+ return dump;
+}
+
+bool DisplayTopologyGraph::isValid() const {
+ return validatePrimaryDisplay(*this) && validateTopologyGraph(*this) &&
+ validateDensities(*this);
+}
+
+std::string DisplayTopologyGraph::dump() const {
+ std::string dump;
+ dump += base::StringPrintf("PrimaryDisplayId: %d\n", primaryDisplayId.val());
+ dump += base::StringPrintf("TopologyGraph:\n");
+ dump += addLinePrefix(dumpMap(graph, logicalDisplayIdToString, adjacentDisplayVectorToString),
+ INDENT);
+ dump += "\n";
+ dump += base::StringPrintf("DisplaysDensity:\n");
+ dump += addLinePrefix(dumpMap(displaysDensity, logicalDisplayIdToString), INDENT);
+ dump += "\n";
+ return dump;
+}
+
+} // namespace android
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 65a088e..155ea00 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -284,6 +284,36 @@
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 cd85821..9578639 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>
@@ -170,11 +171,6 @@
return msg;
}
-std::ostream& operator<<(std::ostream& out, const InputMessage& msg) {
- out << ftl::enum_string(msg.header.type);
- return out;
-}
-
} // namespace
// --- InputConsumerNoResampling ---
diff --git a/libs/input/InputFlags.cpp b/libs/input/InputFlags.cpp
new file mode 100644
index 0000000..6aa9ae6
--- /dev/null
+++ b/libs/input/InputFlags.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <input/InputFlags.h>
+
+#include <android-base/logging.h>
+#include <com_android_input_flags.h>
+#include <com_android_window_flags.h>
+#include <cutils/properties.h>
+
+#include <string>
+
+namespace android {
+
+bool InputFlags::connectedDisplaysCursorEnabled() {
+ if (!com::android::window::flags::enable_desktop_mode_through_dev_option()) {
+ return com::android::input::flags::connected_displays_cursor();
+ }
+ static std::optional<bool> cachedDevOption;
+ if (!cachedDevOption.has_value()) {
+ char value[PROPERTY_VALUE_MAX];
+ constexpr static auto sysprop_name = "persist.wm.debug.desktop_experience_devopts";
+ const int devOptionEnabled =
+ property_get(sysprop_name, value, nullptr) > 0 ? std::atoi(value) : 0;
+ cachedDevOption = devOptionEnabled == 1;
+ }
+ if (cachedDevOption.value_or(false)) {
+ return true;
+ }
+ return com::android::input::flags::connected_displays_cursor();
+}
+
+bool InputFlags::connectedDisplaysCursorAndAssociatedDisplayCursorBugfixEnabled() {
+ return connectedDisplaysCursorEnabled() &&
+ com::android::input::flags::connected_displays_associated_display_cursor_bugfix();
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index 6a55726..cb6f7f0 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -327,8 +327,8 @@
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
@@ -436,16 +436,29 @@
if (error == EAGAIN || error == EWOULDBLOCK) {
return android::base::Error(WOULD_BLOCK);
}
- if (error == EPIPE || error == ENOTCONN || error == ECONNREFUSED) {
- return android::base::Error(DEAD_OBJECT);
+ if (error == EPIPE) {
+ return android::base::ResultError("Got EPIPE", DEAD_OBJECT);
+ }
+ if (error == ENOTCONN) {
+ return android::base::ResultError("Got ENOTCONN", DEAD_OBJECT);
+ }
+ if (error == ECONNREFUSED) {
+ return android::base::ResultError("Got ECONNREFUSED", DEAD_OBJECT);
+ }
+ if (error == ECONNRESET) {
+ // This means that the client has closed the channel while there was
+ // still some data in the buffer. In most cases, subsequent reads
+ // would result in more data. However, that is not guaranteed, so we
+ // should not return WOULD_BLOCK here to try again.
+ return android::base::ResultError("Got ECONNRESET", DEAD_OBJECT);
}
return android::base::Error(-error);
}
if (nRead == 0) { // check for EOF
- ALOGD_IF(DEBUG_CHANNEL_MESSAGES,
- "channel '%s' ~ receive message failed because peer was closed", name.c_str());
- return android::base::Error(DEAD_OBJECT);
+ LOG_IF(INFO, DEBUG_CHANNEL_MESSAGES)
+ << "channel '" << name << "' ~ receive message failed because peer was closed";
+ return android::base::ResultError("::recv returned 0", DEAD_OBJECT);
}
if (!msg.isValid(nRead)) {
@@ -651,9 +664,9 @@
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;
@@ -766,4 +779,9 @@
return android::base::Error(UNKNOWN_ERROR);
}
+std::ostream& operator<<(std::ostream& out, const InputMessage& msg) {
+ out << ftl::enum_string(msg.header.type);
+ return out;
+}
+
} // namespace android
diff --git a/libs/input/InputVerifier.cpp b/libs/input/InputVerifier.cpp
index cec2445..7811ace 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 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 @@
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 90d29dd..d2c49b1 100644
--- a/libs/input/KeyCharacterMap.cpp
+++ b/libs/input/KeyCharacterMap.cpp
@@ -615,7 +615,7 @@
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 @@
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 31592cd..4b87dab 100644
--- a/libs/input/android/os/IInputConstants.aidl
+++ b/libs/input/android/os/IInputConstants.aidl
@@ -43,8 +43,9 @@
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 @@
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 @@
/* 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 da62e03..e5f7b56 100644
--- a/libs/input/android/os/InputConfig.aidl
+++ b/libs/input/android/os/InputConfig.aidl
@@ -57,16 +57,9 @@
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.
+ * This flag is now deprecated and should not be used.
*/
- PREVENT_SPLITTING = 1 << 4,
+ 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 2093b06..6c9ccfb 100644
--- a/libs/input/android/os/MotionEventFlag.aidl
+++ b/libs/input/android/os/MotionEventFlag.aidl
@@ -118,13 +118,24 @@
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 fd77048..a2de009 100644
--- a/libs/input/input_flags.aconfig
+++ b/libs/input/input_flags.aconfig
@@ -16,10 +16,13 @@
}
flag {
- name: "remove_input_channel_from_windowstate"
+ name: "enable_button_state_verification"
namespace: "input"
- description: "Do not store a copy of input channel inside WindowState."
- bug: "323450804"
+ description: "Set to true to enable crashing whenever bad inbound events are going into InputDispatcher"
+ bug: "392870542"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
}
flag {
@@ -37,9 +40,9 @@
}
flag {
- name: "split_all_touches"
+ name: "deprecate_split_touch_apis"
namespace: "input"
- description: "Set FLAG_SPLIT_TOUCHES to true for all windows, regardless of what they specify. This is essentially deprecating this flag by forcefully enabling the split functionality"
+ description: "Deprecate all public APIs related to split touch because now all windows behave as if split touch is permanently enabled and there's no way for a window to disable split touch."
bug: "239934827"
}
@@ -51,20 +54,6 @@
}
flag {
- name: "report_palms_to_gestures_library"
- namespace: "input"
- description: "Report touches marked as palm by firmware to gestures library"
- bug: "302505955"
-}
-
-flag {
- name: "enable_touchpad_typing_palm_rejection"
- namespace: "input"
- description: "Enabling additional touchpad palm rejection will disable the tap to click while the user is typing on a physical keyboard"
- bug: "301055381"
-}
-
-flag {
name: "enable_v2_touchpad_typing_palm_rejection"
namespace: "input"
description: "In addition to touchpad palm rejection v1, v2 will also cancel ongoing move gestures while typing and add delay in re-enabling the tap to click."
@@ -79,13 +68,6 @@
}
flag {
- 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 +91,6 @@
}
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."
@@ -138,10 +113,13 @@
}
flag {
- name: "hide_pointer_indicators_for_secure_windows"
+ name: "allow_transfer_of_entire_gesture"
namespace: "input"
- description: "Hide touch and pointer indicators if a secure window is present on display"
- bug: "325252005"
+ description: "When calling 'transferTouchGesture', the entire gesture (including new POINTER_DOWN events from the same device) will be automatically transferred to the destination window"
+ bug: "397979572"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
}
flag {
@@ -159,13 +137,6 @@
}
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 +166,16 @@
}
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 +212,43 @@
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: "prevent_merging_input_pointer_devices"
+ namespace: "desktop_input"
+ description: "Prevent merging input sub-devices that provide pointer input streams"
+ bug: "389689566"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "enable_display_topology_validation"
+ namespace: "input"
+ description: "Set to true to enable display topology validation"
+ bug: "401219231"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/libs/input/rust/Android.bp b/libs/input/rust/Android.bp
index 63853f7..fae9074 100644
--- a/libs/input/rust/Android.bp
+++ b/libs/input/rust/Android.bp
@@ -18,12 +18,12 @@
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 90f509d..35ba04f 100644
--- a/libs/input/rust/input.rs
+++ b/libs/input/rust/input.rs
@@ -50,7 +50,7 @@
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 @@
/// 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 @@
/// 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 @@
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 @@
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 @@
}
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 @@
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 b1d7760..f87dd41 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 @@
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 @@
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 @@
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)?;
- match action.into() {
+ if self.verify_buttons {
+ self.button_verifier_by_device
+ .entry(event.device_id)
+ .or_default()
+ .process_event(event)?;
+ }
+
+ 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 @@
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 @@
}
}
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 @@
* 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 @@
#[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(),
- )
+ .process_movement(NotifyMotionArgs { action: MotionAction::Down, ..BASE_EVENT })
.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::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 @@
// 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 ab74efb..26a8d8f 100644
--- a/libs/input/rust/keyboard_classification_config.rs
+++ b/libs/input/rust/keyboard_classification_config.rs
@@ -20,6 +20,15 @@
// 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 @@
(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 @@
(0x1050, 0x0010, KeyboardType::NonAlphabetic, true),
// Yubico.com Yubikey 4 OTP+U2F+CCID
(0x1050, 0x0407, KeyboardType::NonAlphabetic, true),
+ // Razer DeathAdder Essential
+ (0x1532, 0x0098, KeyboardType::NonAlphabetic, true),
// Lenovo USB-C Wired Compact Mouse
(0x17ef, 0x6123, KeyboardType::NonAlphabetic, true),
// Corsair Katar Pro Wireless (USB dongle)
diff --git a/libs/input/rust/keyboard_classifier.rs b/libs/input/rust/keyboard_classifier.rs
index 3c789b4..1b89a5c 100644
--- a/libs/input/rust/keyboard_classifier.rs
+++ b/libs/input/rust/keyboard_classifier.rs
@@ -66,11 +66,11 @@
/// Get keyboard type for a tracked keyboard in KeyboardClassifier
pub fn get_keyboard_type(&self, device_id: DeviceId) -> KeyboardType {
- return if let Some(keyboard) = self.device_map.get(&device_id) {
+ if let Some(keyboard) = self.device_map.get(&device_id) {
keyboard.keyboard_type
} else {
KeyboardType::None
- };
+ }
}
/// Tells if keyboard type classification is finalized. Once finalized the classification can't
@@ -79,11 +79,11 @@
/// Finalized devices are either "alphabetic" keyboards or keyboards in blocklist or
/// allowlist that are explicitly categorized and won't change with future key events
pub fn is_finalized(&self, device_id: DeviceId) -> bool {
- return if let Some(keyboard) = self.device_map.get(&device_id) {
+ if let Some(keyboard) = self.device_map.get(&device_id) {
keyboard.is_finalized
} else {
false
- };
+ }
}
/// Process a key event and change keyboard type if required.
diff --git a/libs/input/rust/lib.rs b/libs/input/rust/lib.rs
index 4f4ea85..ee999f7 100644
--- a/libs/input/rust/lib.rs
+++ b/libs/input/rust/lib.rs
@@ -24,10 +24,10 @@
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 @@
/// ```
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 @@
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 @@
}
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 d1c564d..968fa5f 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -16,16 +16,16 @@
"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,32 +53,41 @@
],
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,
@@ -91,11 +100,6 @@
"libstatssocket_lazy",
],
},
- host: {
- sanitize: {
- address: true,
- },
- },
},
native_coverage: false,
}
@@ -113,10 +117,10 @@
"-Wextra",
],
shared_libs: [
- "libinput",
- "libcutils",
- "libutils",
- "libbinder",
"libbase",
+ "libbinder",
+ "libcutils",
+ "libinput",
+ "libutils",
],
}
diff --git a/libs/input/tests/InputChannel_test.cpp b/libs/input/tests/InputChannel_test.cpp
index 25356cf..9b582d9 100644
--- a/libs/input/tests/InputChannel_test.cpp
+++ b/libs/input/tests/InputChannel_test.cpp
@@ -20,6 +20,7 @@
#include <time.h>
#include <errno.h>
+#include <android-base/logging.h>
#include <binder/Binder.h>
#include <binder/Parcel.h>
#include <gtest/gtest.h>
@@ -43,6 +44,39 @@
return left.getName() == right.getName() &&
left.getConnectionToken() == right.getConnectionToken() && lhs.st_ino == rhs.st_ino;
}
+
+/**
+ * Read a message from the provided channel. Read will continue until there's data, so only call
+ * this if there's data in the channel, or it's closed. If there's no data, this will loop forever.
+ */
+android::base::Result<InputMessage> readMessage(InputChannel& channel) {
+ while (true) {
+ // Keep reading until we get something other than 'WOULD_BLOCK'
+ android::base::Result<InputMessage> result = channel.receiveMessage();
+ if (!result.ok() && result.error().code() == WOULD_BLOCK) {
+ // The data is not available yet.
+ continue; // try again
+ }
+ return result;
+ }
+}
+
+InputMessage createFinishedMessage(uint32_t seq) {
+ InputMessage finish{};
+ finish.header.type = InputMessage::Type::FINISHED;
+ finish.header.seq = seq;
+ finish.body.finished.handled = true;
+ return finish;
+}
+
+InputMessage createKeyMessage(uint32_t seq) {
+ InputMessage key{};
+ key.header.type = InputMessage::Type::KEY;
+ key.header.seq = seq;
+ key.body.key.action = AKEY_EVENT_ACTION_DOWN;
+ return key;
+}
+
} // namespace
class InputChannelTest : public testing::Test {
@@ -227,6 +261,120 @@
}
}
+/**
+ * In this test, server writes 3 key events to the client. The client, upon receiving the first key,
+ * sends a "finished" signal back to server, and then closes the fd.
+ *
+ * Next, we check what the server receives.
+ *
+ * In most cases, the server will receive the finish event, and then an 'fd closed' event.
+ *
+ * However, sometimes, the 'finish' event will not be delivered to the server. This is communicated
+ * to the server via 'ECONNRESET', which the InputChannel converts into DEAD_OBJECT.
+ *
+ * The server needs to be aware of this behaviour and correctly clean up any state associated with
+ * the client, even if the client did not end up finishing some of the messages.
+ *
+ * This test is written to expose a behaviour on the linux side - occasionally, the
+ * last events written to the fd by the consumer are not delivered to the server.
+ *
+ * When tested on 2025 hardware, ECONNRESET was received approximately 1 out of 40 tries.
+ * In vast majority (~ 29999 / 30000) of cases, after receiving ECONNRESET, the server could still
+ * read the client data after receiving ECONNRESET.
+ */
+TEST_F(InputChannelTest, ReceiveAfterCloseMultiThreaded) {
+ std::unique_ptr<InputChannel> serverChannel, clientChannel;
+ status_t result =
+ InputChannel::openInputChannelPair("channel name", serverChannel, clientChannel);
+ ASSERT_EQ(OK, result) << "should have successfully opened a channel pair";
+
+ // Sender / publisher: publish 3 keys
+ InputMessage key1 = createKeyMessage(/*seq=*/1);
+ serverChannel->sendMessage(&key1);
+ // The client should close the fd after it reads this one, but we will send 2 more here.
+ InputMessage key2 = createKeyMessage(/*seq=*/2);
+ serverChannel->sendMessage(&key2);
+ InputMessage key3 = createKeyMessage(/*seq=*/3);
+ serverChannel->sendMessage(&key3);
+
+ std::thread consumer = std::thread([clientChannel = std::move(clientChannel)]() mutable {
+ // Read the first key
+ android::base::Result<InputMessage> firstKey = readMessage(*clientChannel);
+ if (!firstKey.ok()) {
+ FAIL() << "Did not receive the first key";
+ }
+
+ // Send finish
+ const InputMessage finish = createFinishedMessage(firstKey->header.seq);
+ clientChannel->sendMessage(&finish);
+ // Now close the fd
+ clientChannel.reset();
+ });
+
+ // Now try to read the finish message, even though client closed the fd
+ android::base::Result<InputMessage> response = readMessage(*serverChannel);
+ consumer.join();
+ if (response.ok()) {
+ ASSERT_EQ(response->header.type, InputMessage::Type::FINISHED);
+ } else {
+ // It's possible that after the client closes the fd, server will receive ECONNRESET.
+ // In those situations, this error code will be translated into DEAD_OBJECT by the
+ // InputChannel.
+ ASSERT_EQ(response.error().code(), DEAD_OBJECT);
+ // In most cases, subsequent attempts to read the client channel at this
+ // point would succeed. However, for simplicity, we exit here (since
+ // it's not guaranteed).
+ return;
+ }
+
+ // There should not be any more events from the client, since the client closed fd after the
+ // first key.
+ android::base::Result<InputMessage> noEvent = serverChannel->receiveMessage();
+ ASSERT_FALSE(noEvent.ok()) << "Got event " << *noEvent;
+}
+
+/**
+ * Similar test as above, but single-threaded.
+ */
+TEST_F(InputChannelTest, ReceiveAfterCloseSingleThreaded) {
+ std::unique_ptr<InputChannel> serverChannel, clientChannel;
+ status_t result =
+ InputChannel::openInputChannelPair("channel name", serverChannel, clientChannel);
+ ASSERT_EQ(OK, result) << "should have successfully opened a channel pair";
+
+ // Sender / publisher: publish 3 keys
+ InputMessage key1 = createKeyMessage(/*seq=*/1);
+ serverChannel->sendMessage(&key1);
+ // The client should close the fd after it reads this one, but we will send 2 more here.
+ InputMessage key2 = createKeyMessage(/*seq=*/2);
+ serverChannel->sendMessage(&key2);
+ InputMessage key3 = createKeyMessage(/*seq=*/3);
+ serverChannel->sendMessage(&key3);
+
+ // Read the first key
+ android::base::Result<InputMessage> firstKey = readMessage(*clientChannel);
+ if (!firstKey.ok()) {
+ FAIL() << "Did not receive the first key";
+ }
+
+ // Send finish
+ const InputMessage finish = createFinishedMessage(firstKey->header.seq);
+ clientChannel->sendMessage(&finish);
+ // Now close the fd
+ clientChannel.reset();
+
+ // Now try to read the finish message, even though client closed the fd
+ android::base::Result<InputMessage> response = readMessage(*serverChannel);
+ ASSERT_FALSE(response.ok());
+ ASSERT_EQ(response.error().code(), DEAD_OBJECT);
+
+ // We can still read the finish event (but in practice, the expectation is that the server will
+ // not be doing this after getting DEAD_OBJECT).
+ android::base::Result<InputMessage> finishEvent = serverChannel->receiveMessage();
+ ASSERT_TRUE(finishEvent.ok());
+ ASSERT_EQ(finishEvent->header.type, InputMessage::Type::FINISHED);
+}
+
TEST_F(InputChannelTest, DuplicateChannelAndAssertEqual) {
std::unique_ptr<InputChannel> serverChannel, clientChannel;
diff --git a/libs/input/tests/InputVerifier_test.cpp b/libs/input/tests/InputVerifier_test.cpp
index e2eb080..8e0d906 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 @@
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 56eaefd..8dbdcb3 100644
--- a/libs/input/tests/TestEventMatchers.h
+++ b/libs/input/tests/TestEventMatchers.h
@@ -27,12 +27,8 @@
namespace android {
-namespace {
-
using ::testing::Matcher;
-} // namespace
-
/**
* This file contains a copy of Matchers from .../inputflinger/tests/TestEventMatchers.h. Ideally,
* implementations must not be duplicated.
diff --git a/libs/math/OWNERS b/libs/math/OWNERS
index 82ae422..08f0c5f 100644
--- a/libs/math/OWNERS
+++ b/libs/math/OWNERS
@@ -1,5 +1,4 @@
mathias@google.com
-randolphs@google.com
romainguy@google.com
sumir@google.com
jreck@google.com
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
index bed31e2..89a97de 100644
--- a/libs/nativedisplay/AChoreographer.cpp
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -142,7 +142,7 @@
}
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 @@
}
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 @@
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 444722b..226a8a6 100644
--- a/libs/nativedisplay/include/surfacetexture/EGLConsumer.h
+++ b/libs/nativedisplay/include/surfacetexture/EGLConsumer.h
@@ -113,18 +113,11 @@
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 @@
* 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 @@
* 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 006a785..253aa18 100644
--- a/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h
+++ b/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h
@@ -343,9 +343,13 @@
* 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 3959fce..fad0f6c 100644
--- a/libs/nativedisplay/surfacetexture/EGLConsumer.cpp
+++ b/libs/nativedisplay/surfacetexture/EGLConsumer.cpp
@@ -221,7 +221,11 @@
}
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 @@
// 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 @@
} 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 @@
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 @@
}
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 60e87b5..1ffd382 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 @@
}
// 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 ce232cc..c0a1cc5 100644
--- a/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp
+++ b/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp
@@ -178,13 +178,21 @@
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 ed3e8c1..10abb7c 100644
--- a/libs/nativewindow/include/android/native_window.h
+++ b/libs/nativewindow/include/android/native_window.h
@@ -258,11 +258,11 @@
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/rust/src/lib.rs b/libs/nativewindow/rust/src/lib.rs
index d760285..aeb2603 100644
--- a/libs/nativewindow/rust/src/lib.rs
+++ b/libs/nativewindow/rust/src/lib.rs
@@ -541,7 +541,7 @@
pub address: NonNull<c_void>,
}
-impl<'a> Drop for HardwareBufferGuard<'a> {
+impl Drop for HardwareBufferGuard<'_> {
fn drop(&mut self) {
self.buffer
.unlock()
diff --git a/libs/nativewindow/tests/ANativeWindowTest.cpp b/libs/nativewindow/tests/ANativeWindowTest.cpp
index 937ff02..51d0c81 100644
--- a/libs/nativewindow/tests/ANativeWindowTest.cpp
+++ b/libs/nativewindow/tests/ANativeWindowTest.cpp
@@ -50,14 +50,9 @@
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 0eeca54..929f067 100644
--- a/libs/permission/Android.bp
+++ b/libs/permission/Android.bp
@@ -16,6 +16,7 @@
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 @@
],
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 2b3f462..0000000
--- 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 0000000..36b19df
--- /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 243532b..a22c975 100644
--- a/libs/permission/include/binder/AppOpsManager.h
+++ b/libs/permission/include/binder/AppOpsManager.h
@@ -148,7 +148,10 @@
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 @@
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 eb76f57..0000000
--- 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 918fcdb..1468fd9 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/Android.bp b/libs/renderengine/Android.bp
index 7f207f0..39182aa 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -25,6 +25,7 @@
defaults: [
"android.hardware.graphics.composer3-ndk_shared",
"renderengine_defaults",
+ "libsurfaceflinger_common_deps",
],
cflags: [
"-DGL_GLEXT_PROTOTYPES",
@@ -51,6 +52,7 @@
"libtonemap",
"libsurfaceflinger_common",
"libsurfaceflingerflags",
+ "libgui_window_info_static",
],
local_include_dirs: ["include"],
export_include_dirs: ["include"],
@@ -117,8 +119,17 @@
// possible if libskia_renderengine is just pulled into librenderengine via whole_static_libs.
cc_defaults {
name: "librenderengine_deps",
- defaults: ["skia_renderengine_deps"],
- static_libs: ["libskia_renderengine"],
+ defaults: [
+ "skia_renderengine_deps",
+ "libsurfaceflinger_common_deps",
+ ],
+ static_libs: [
+ "libgui_window_info_static",
+ "libskia_renderengine",
+ ],
+ shared_libs: [
+ "libbinder",
+ ],
}
// Note: if compilation fails when adding librenderengine as a dependency, try adding
diff --git a/libs/renderengine/OWNERS b/libs/renderengine/OWNERS
index 17ab29f..e296283 100644
--- a/libs/renderengine/OWNERS
+++ b/libs/renderengine/OWNERS
@@ -7,4 +7,3 @@
lpy@google.com
nscobie@google.com
sallyqi@google.com
-scroggo@google.com
diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp
index 907590a..873fc67 100644
--- a/libs/renderengine/RenderEngine.cpp
+++ b/libs/renderengine/RenderEngine.cpp
@@ -107,16 +107,15 @@
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/benchmark/Android.bp b/libs/renderengine/benchmark/Android.bp
index f84db0b..2d18ddb 100644
--- a/libs/renderengine/benchmark/Android.bp
+++ b/libs/renderengine/benchmark/Android.bp
@@ -28,6 +28,7 @@
"android.hardware.graphics.composer3-ndk_shared",
"librenderengine_deps",
"surfaceflinger_defaults",
+ "libsurfaceflinger_common_deps",
],
srcs: [
"main.cpp",
@@ -38,7 +39,6 @@
static_libs: [
"librenderengine",
"libshaders",
- "libsurfaceflinger_common",
"libtonemap",
],
cflags: [
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index ac43da8..3523497 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -16,6 +16,7 @@
#pragma once
+#include <android/gui/BorderSettings.h>
#include <gui/DisplayLuts.h>
#include <math/mat4.h>
#include <math/vec3.h>
@@ -71,6 +72,10 @@
// Boundaries of the layer.
FloatRect boundaries = FloatRect();
+ // Boundaries of the layer before transparent region hint is subtracted.
+ // Effects like shadows and outline ignore the transparent region hint.
+ FloatRect originalBounds = FloatRect();
+
// Transform matrix to apply to mesh coordinates.
mat4 positionTransform = mat4();
@@ -127,6 +132,8 @@
ShadowSettings shadow;
+ gui::BorderSettings borderSettings;
+
int backgroundBlurRadius = 0;
std::vector<BlurRegion> blurRegions;
@@ -301,6 +308,10 @@
*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 95c4d03..c2dd4ae 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -217,12 +217,17 @@
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 @@
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 fb8331d..c42e403 100644
--- a/libs/renderengine/include/renderengine/mock/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -46,17 +46,16 @@
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 57041ee..f43694e 100644
--- a/libs/renderengine/skia/Cache.cpp
+++ b/libs/renderengine/skia/Cache.cpp
@@ -14,6 +14,9 @@
* limitations under the License.
*/
#include "Cache.h"
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
#include "AutoBackendTexture.h"
#include "SkiaRenderEngine.h"
#include "android-base/unique_fd.h"
@@ -28,6 +31,7 @@
#include "utils/Timers.h"
#include <com_android_graphics_libgui_flags.h>
+#include <common/trace.h>
namespace android::renderengine::skia {
@@ -337,17 +341,17 @@
LayerSettings layer{
.geometry =
Geometry{
+ .boundaries = rect,
// The position transform doesn't matter when the reduced shader mode
// in in effect. A matrix transform stage is always included.
.positionTransform = mat4(),
- .boundaries = rect,
- .roundedCornersCrop = rect,
.roundedCornersRadius = {0.f, 0.f},
+ .roundedCornersCrop = rect,
},
.source = PixelSource{.buffer = Buffer{.buffer = srcTexture,
- .maxLuminanceNits = 1000.f,
.usePremultipliedAlpha = true,
- .isOpaque = true}},
+ .isOpaque = true,
+ .maxLuminanceNits = 1000.f}},
.alpha = 1.f,
.sourceDataspace = kDestDataSpace,
};
@@ -370,16 +374,16 @@
LayerSettings layer{
.geometry =
Geometry{
- .positionTransform = mat4(),
.boundaries = rect,
+ .positionTransform = mat4(),
.roundedCornersCrop = rect,
},
.source = PixelSource{.buffer =
Buffer{
.buffer = srcTexture,
- .maxLuminanceNits = 1000.f,
.usePremultipliedAlpha = true,
.isOpaque = false,
+ .maxLuminanceNits = 1000.f,
}},
.sourceDataspace = kDestDataSpace,
};
@@ -421,17 +425,17 @@
LayerSettings layer{
.geometry =
Geometry{
- .positionTransform = mat4(),
.boundaries = boundary,
- .roundedCornersCrop = rect,
+ .positionTransform = mat4(),
.roundedCornersRadius = {27.f, 27.f},
+ .roundedCornersCrop = rect,
},
.source = PixelSource{.buffer =
Buffer{
.buffer = srcTexture,
- .maxLuminanceNits = 1000.f,
.usePremultipliedAlpha = true,
.isOpaque = false,
+ .maxLuminanceNits = 1000.f,
}},
.alpha = 1.f,
.sourceDataspace = kDestDataSpace,
@@ -489,17 +493,17 @@
LayerSettings layer{
.geometry =
Geometry{
+ .boundaries = rect,
// The position transform doesn't matter when the reduced shader mode
// in in effect. A matrix transform stage is always included.
.positionTransform = mat4(),
- .boundaries = rect,
- .roundedCornersCrop = rect,
.roundedCornersRadius = {0.f, 0.f},
+ .roundedCornersCrop = rect,
},
.source = PixelSource{.buffer = Buffer{.buffer = srcTexture,
- .maxLuminanceNits = 1000.f,
.usePremultipliedAlpha = true,
- .isOpaque = true}},
+ .isOpaque = true,
+ .maxLuminanceNits = 1000.f}},
.alpha = 1.f,
.sourceDataspace = kBT2020DataSpace,
};
@@ -527,17 +531,17 @@
LayerSettings layer{
.geometry =
Geometry{
- .positionTransform = kScaleAsymmetric,
.boundaries = boundary,
- .roundedCornersCrop = rect,
+ .positionTransform = kScaleAsymmetric,
.roundedCornersRadius = {64.1f, 64.1f},
+ .roundedCornersCrop = rect,
},
.source = PixelSource{.buffer =
Buffer{
.buffer = srcTexture,
- .maxLuminanceNits = 1000.f,
.usePremultipliedAlpha = true,
.isOpaque = true,
+ .maxLuminanceNits = 1000.f,
}},
.alpha = 0.5f,
.sourceDataspace = kBT2020DataSpace,
@@ -556,17 +560,17 @@
LayerSettings layer{
.geometry =
Geometry{
+ .boundaries = rect,
// The position transform doesn't matter when the reduced shader mode
// in in effect. A matrix transform stage is always included.
.positionTransform = mat4(),
- .boundaries = rect,
- .roundedCornersCrop = rect,
.roundedCornersRadius = {50.f, 50.f},
+ .roundedCornersCrop = rect,
},
.source = PixelSource{.buffer = Buffer{.buffer = srcTexture,
- .maxLuminanceNits = 1000.f,
.usePremultipliedAlpha = true,
- .isOpaque = true}},
+ .isOpaque = true,
+ .maxLuminanceNits = 1000.f}},
.alpha = 0.5f,
.sourceDataspace = kExtendedHdrDataSpce,
};
@@ -594,17 +598,17 @@
LayerSettings layer{
.geometry =
Geometry{
+ .boundaries = rect,
// The position transform doesn't matter when the reduced shader mode
// in in effect. A matrix transform stage is always included.
.positionTransform = mat4(),
- .boundaries = rect,
- .roundedCornersCrop = rect,
.roundedCornersRadius = {50.f, 50.f},
+ .roundedCornersCrop = rect,
},
.source = PixelSource{.buffer = Buffer{.buffer = srcTexture,
- .maxLuminanceNits = 1000.f,
.usePremultipliedAlpha = true,
- .isOpaque = false}},
+ .isOpaque = false,
+ .maxLuminanceNits = 1000.f}},
.alpha = 0.5f,
.sourceDataspace = kOtherDataSpace,
};
@@ -659,6 +663,7 @@
// 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 @@
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 @@
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 @@
}
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 @@
};
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 b986967..5b6edb4 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -347,6 +347,7 @@
if (useProtectedContextImpl(
useProtectedContext ? GrProtected::kYes : GrProtected::kNo)) {
mInProtectedContext = useProtectedContext;
+ SFTRACE_INT("RE inProtectedContext", mInProtectedContext);
// given that we are sharing the same thread between two contexts we need to
// make sure that the thread state is reset when switching between the two.
if (getActiveContext()) {
@@ -544,9 +545,18 @@
}
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 +577,7 @@
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 +618,12 @@
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 +839,7 @@
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 +853,9 @@
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 +888,6 @@
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 +911,29 @@
// 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,
@@ -959,6 +986,30 @@
drawShadow(canvas, rrect, layer.shadow);
}
+ // Similar to shadows, do the rendering before the clip is applied because even when the
+ // layer is occluded it should have an outline.
+ if (layer.borderSettings.strokeWidth > 0) {
+ // TODO(b/367464660): Move this code to the parent scope and
+ // update shadow rendering above to use these bounds since they should be
+ // identical.
+ SkRRect originalBounds, originalClip;
+ std::tie(originalBounds, originalClip) =
+ getBoundsAndClip(layer.geometry.boundaries, layer.geometry.roundedCornersCrop,
+ layer.geometry.roundedCornersRadius);
+ const SkRRect& preferredOriginalBounds =
+ originalBounds.isRect() && !originalClip.isEmpty() ? originalClip
+ : originalBounds;
+
+ SkRRect outlineRect = preferredOriginalBounds;
+ outlineRect.outset(layer.borderSettings.strokeWidth, layer.borderSettings.strokeWidth);
+
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setColor(layer.borderSettings.color);
+ paint.setStyle(SkPaint::kFill_Style);
+ canvas->drawDRRect(outlineRect, preferredOriginalBounds, paint);
+ }
+
const float layerDimmingRatio = layer.whitePointNits <= 0.f
? displayDimmingRatio
: (layer.whitePointNits / maxLayerWhitePoint) * displayDimmingRatio;
@@ -1209,47 +1260,71 @@
LOG_ALWAYS_FATAL_IF(activeSurface != dstSurface);
auto drawFence = sp<Fence>::make(flushAndSubmit(context, dstSurface));
trace(drawFence);
+ FenceTimePtr fenceTime = FenceTime::makeValid(drawFence);
+ for (const auto& layer : layers) {
+ if (FlagManager::getInstance().monitor_buffer_fences()) {
+ if (layer.source.buffer.buffer) {
+ layer.source.buffer.buffer->getBuffer()
+ ->getDependencyMonitor()
+ .addAccessCompletion(fenceTime, "RE");
+ }
+ }
+ }
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);
+ auto gainmapTextureRef = getOrCreateBackendTexture(gainmap->getBuffer(), true);
+ sk_sp<SkSurface> gainmapSurface =
+ gainmapTextureRef->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 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 7be4c25..92b7af9 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.h
+++ b/libs/renderengine/skia/SkiaRenderEngine.h
@@ -143,13 +143,11 @@
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 @@
};
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 37b69f6..7331bbc 100644
--- a/libs/renderengine/skia/VulkanInterface.cpp
+++ b/libs/renderengine/skia/VulkanInterface.cpp
@@ -204,10 +204,10 @@
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 69f5832..7a72d09 100644
--- a/libs/renderengine/skia/compat/GraphiteGpuContext.cpp
+++ b/libs/renderengine/skia/compat/GraphiteGpuContext.cpp
@@ -110,8 +110,40 @@
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 413817f..57da796 100644
--- a/libs/renderengine/skia/compat/GraphiteGpuContext.h
+++ b/libs/renderengine/skia/compat/GraphiteGpuContext.h
@@ -39,16 +39,10 @@
size_t getMaxRenderTargetSize() const override;
size_t getMaxTextureSize() const override;
bool isAbandonedOrDeviceLost() override;
- // No-op (large resources like textures, surfaces, images, etc. created by clients don't count
- // towards Graphite's internal caching budgets, so adjusting its limits based on display change
- // events should be unnecessary. Additionally, Graphite doesn't expose many cache tweaking
- // functions yet, as its design may evolve.)
- void setResourceCacheLimit(size_t maxResourceBytes) override{};
- // TODO: b/293371537 - Triple-check and validate that no cleanup is necessary when switching
- // contexts.
- // No-op (unnecessary during context switch for Graphite's client-budgeted memory model).
- void purgeUnlockedScratchResources() override{};
+ void setResourceCacheLimit(size_t maxResourceBytes) override;
+ void purgeUnlockedScratchResources() override;
+
// No-op (only applicable to GL).
void resetContextIfApplicable() override{};
diff --git a/libs/renderengine/skia/debug/CaptureTimer.cpp b/libs/renderengine/skia/debug/CaptureTimer.cpp
index 11bcdb8..1c1ee0a 100644
--- a/libs/renderengine/skia/debug/CaptureTimer.cpp
+++ b/libs/renderengine/skia/debug/CaptureTimer.cpp
@@ -30,7 +30,7 @@
void CaptureTimer::setTimeout(TimeoutCallback function, std::chrono::milliseconds delay) {
this->clear = false;
- CommonPool::post([=]() {
+ CommonPool::post([=,this]() {
if (this->clear) return;
std::this_thread::sleep_for(delay);
if (this->clear) return;
diff --git a/libs/renderengine/skia/filters/BlurFilter.cpp b/libs/renderengine/skia/filters/BlurFilter.cpp
index cd1bd71..8edf98e 100644
--- a/libs/renderengine/skia/filters/BlurFilter.cpp
+++ b/libs/renderengine/skia/filters/BlurFilter.cpp
@@ -90,6 +90,8 @@
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 4164c4b..f007427 100644
--- a/libs/renderengine/skia/filters/EdgeExtensionShaderFactory.cpp
+++ b/libs/renderengine/skia/filters/EdgeExtensionShaderFactory.cpp
@@ -58,9 +58,6 @@
)");
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 8c52c57..b03ebe3 100644
--- a/libs/renderengine/skia/filters/GaussianBlurFilter.cpp
+++ b/libs/renderengine/skia/filters/GaussianBlurFilter.cpp
@@ -64,7 +64,7 @@
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 da47aae..ff96b08 100644
--- a/libs/renderengine/skia/filters/KawaseBlurDualFilter.cpp
+++ b/libs/renderengine/skia/filters/KawaseBlurDualFilter.cpp
@@ -39,12 +39,36 @@
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 @@
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 @@
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 @@
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 6f4adbf..5efda35 100644
--- a/libs/renderengine/skia/filters/KawaseBlurDualFilter.h
+++ b/libs/renderengine/skia/filters/KawaseBlurDualFilter.h
@@ -41,13 +41,14 @@
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 defaf6e..f71a63d 100644
--- a/libs/renderengine/skia/filters/KawaseBlurFilter.cpp
+++ b/libs/renderengine/skia/filters/KawaseBlurFilter.cpp
@@ -70,7 +70,7 @@
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 5e9dfbb..6a577ff 100644
--- a/libs/renderengine/skia/filters/LutShader.cpp
+++ b/libs/renderengine/skia/filters/LutShader.cpp
@@ -24,7 +24,6 @@
#include <ui/ColorSpace.h>
#include "include/core/SkColorSpace.h"
-#include "src/core/SkColorFilterPriv.h"
using aidl::android::hardware::graphics::composer3::LutProperties;
@@ -39,10 +38,13 @@
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 +54,19 @@
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 +112,10 @@
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(fromLinearSrgb(linear), rgba.a);
})");
// same as shader::toColorSpace function
@@ -181,15 +181,21 @@
* (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 +203,22 @@
? 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 +230,7 @@
mBuilder->uniform("size") = uSize;
mBuilder->uniform("key") = uKey;
mBuilder->uniform("dimension") = uDimension;
+ mBuilder->uniform("normalizeScalar") = uNormalizeScalar;
return mBuilder->makeShader();
}
@@ -268,9 +288,7 @@
lutProperties[i].samplingKey, srcDataspace);
}
- auto colorXformLutToDst =
- SkColorFilterPriv::MakeColorSpaceXform(lutMathColorSpace, outColorSpace);
- input = input->makeWithColorFilter(colorXformLutToDst);
+ input = input->makeWithWorkingColorSpace(outColorSpace);
}
return input;
}
diff --git a/libs/renderengine/skia/filters/MouriMap.cpp b/libs/renderengine/skia/filters/MouriMap.cpp
index b099bcf..5dc36e6 100644
--- a/libs/renderengine/skia/filters/MouriMap.cpp
+++ b/libs/renderengine/skia/filters/MouriMap.cpp
@@ -30,12 +30,12 @@
}
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 @@
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 @@
paint.setShader(std::move(shader));
paint.setBlendMode(SkBlendMode::kSrc);
surface->getCanvas()->drawPaint(paint);
- return surface->makeImageSnapshot();
+ return surface->makeTemporaryImage();
}
} // namespace
@@ -116,19 +116,19 @@
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 @@
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 @@
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 9ba2b6f..f4bfa15 100644
--- a/libs/renderengine/skia/filters/MouriMap.h
+++ b/libs/renderengine/skia/filters/MouriMap.h
@@ -62,10 +62,13 @@
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 c187f93..67f4aa1 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 @@
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 @@
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 @@
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 @@
{
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 cb6e924..8554b55 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.h
+++ b/libs/renderengine/threaded/RenderEngineThreaded.h
@@ -55,12 +55,10 @@
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 @@
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/sensor/OWNERS b/libs/sensor/OWNERS
index 7347ac7..4929b3f 100644
--- a/libs/sensor/OWNERS
+++ b/libs/sensor/OWNERS
@@ -1 +1 @@
-bduddie@google.com
+include platform/frameworks/native:/services/sensorservice/OWNERS
\ No newline at end of file
diff --git a/libs/shaders/OWNERS b/libs/shaders/OWNERS
index 6d91da3..6977a49 100644
--- a/libs/shaders/OWNERS
+++ b/libs/shaders/OWNERS
@@ -1,4 +1,3 @@
alecmouri@google.com
jreck@google.com
sallyqi@google.com
-scroggo@google.com
\ No newline at end of file
diff --git a/libs/tonemap/OWNERS b/libs/tonemap/OWNERS
index 6d91da3..6977a49 100644
--- a/libs/tonemap/OWNERS
+++ b/libs/tonemap/OWNERS
@@ -1,4 +1,3 @@
alecmouri@google.com
jreck@google.com
sallyqi@google.com
-scroggo@google.com
\ No newline at end of file
diff --git a/libs/tracing_perfetto/Android.bp b/libs/tracing_perfetto/Android.bp
index 9a2d4f7..1ef83a4 100644
--- a/libs/tracing_perfetto/Android.bp
+++ b/libs/tracing_perfetto/Android.bp
@@ -21,7 +21,7 @@
default_applicable_licenses: ["frameworks_native_license"],
}
-cc_library_shared {
+cc_library {
name: "libtracing_perfetto",
export_include_dirs: [
"include",
@@ -37,13 +37,17 @@
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 0000000..271d7c8
--- /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 d203467..79fb704 100644
--- a/libs/tracing_perfetto/tests/Android.bp
+++ b/libs/tracing_perfetto/tests/Android.bp
@@ -36,7 +36,6 @@
"android.os.flags-aconfig-cc-host",
"libbase",
"libperfetto_c",
- "liblog",
"libprotobuf-cpp-lite",
"libtracing_perfetto",
],
@@ -44,5 +43,8 @@
"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 0000000..b2630e1
--- /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 e9fee2e..b21a090 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 8c4d4a8..af61bc2 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 @@
}
{
- 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 @@
}
}
- 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 @@
} // 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 8edb414..0000000
--- 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 c35e078..4b70213 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 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 @@
vsnprintf(buf, BUFFER_SIZE, fmt, ap);
va_end(ap);
-
if (preferAtrace) {
atrace_begin(category, buf);
} else if (preferPerfetto) {
@@ -99,26 +101,28 @@
}
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 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 traceInstantForTrack(uint64_t category, const char* trackName,
- const char* name) {
+ const char* name) {
struct PerfettoTeCategory* perfettoTeCategory =
internal::toPerfettoCategory(category);
@@ -181,20 +187,21 @@
}
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 a58bc77..4478732 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 @@
}
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 3e1ac2a..0e3fb07 100644
--- a/libs/tracing_perfetto/tracing_perfetto_internal.h
+++ b/libs/tracing_perfetto/tracing_perfetto_internal.h
@@ -25,8 +25,6 @@
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 0000000..70b8be9
--- /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/Android.bp b/libs/ui/Android.bp
index 9082b17..92fa932 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -122,6 +122,7 @@
srcs: [
"DebugUtils.cpp",
+ "DependencyMonitor.cpp",
"DeviceProductInfo.cpp",
"DisplayIdentification.cpp",
"DynamicDisplayInfo.cpp",
diff --git a/libs/ui/DependencyMonitor.cpp b/libs/ui/DependencyMonitor.cpp
new file mode 100644
index 0000000..b7e490e
--- /dev/null
+++ b/libs/ui/DependencyMonitor.cpp
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// #define LOG_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "DependencyMonitor"
+
+#include <ui/DependencyMonitor.h>
+#include <ui/Fence.h>
+#include <utils/Timers.h>
+
+#include <inttypes.h>
+
+namespace android {
+
+void DependencyMonitor::addIngress(FenceTimePtr fence, std::string annotation) {
+ std::lock_guard lock(mMutex);
+ resolveLocked();
+ if (mDependencies.isFull() && !mDependencies.front().updateSignalTimes(true)) {
+ ALOGD("%s: Clobbering unresolved dependencies -- make me bigger!", mToken.c_str());
+ }
+
+ auto& entry = mDependencies.next();
+ entry.reset(mToken.c_str());
+ ALOGV("%" PRId64 "/%s: addIngress at CPU time %" PRId64 " (%s)", mDependencies.back().id,
+ mToken.c_str(), systemTime(), annotation.c_str());
+
+ mDependencies.back().ingress = {std::move(fence), std::move(annotation)};
+}
+
+void DependencyMonitor::addAccessCompletion(FenceTimePtr fence, std::string annotation) {
+ std::lock_guard lock(mMutex);
+ if (mDependencies.size() == 0) {
+ return;
+ }
+ ALOGV("%" PRId64 "/%s: addAccessCompletion at CPU time %" PRId64 " (%s)",
+ mDependencies.back().id, mToken.c_str(), systemTime(), annotation.c_str());
+ mDependencies.back().accessCompletions.emplace_back(std::move(fence), std::move(annotation));
+}
+
+void DependencyMonitor::addEgress(FenceTimePtr fence, std::string annotation) {
+ std::lock_guard lock(mMutex);
+ if (mDependencies.size() == 0) {
+ return;
+ }
+ ALOGV("%" PRId64 "/%s: addEgress at CPU time %" PRId64 " (%s)", mDependencies.back().id,
+ mToken.c_str(), systemTime(), annotation.c_str());
+ mDependencies.back().egress = {std::move(fence), std::move(annotation)};
+}
+
+void DependencyMonitor::resolveLocked() {
+ if (mDependencies.size() == 0) {
+ return;
+ }
+
+ for (size_t i = mDependencies.size(); i > 0; i--) {
+ auto& dependencyBlock = mDependencies[i - 1];
+
+ if (dependencyBlock.validated) {
+ continue;
+ }
+
+ if (!dependencyBlock.updateSignalTimes(false)) {
+ break;
+ }
+
+ dependencyBlock.validated = true;
+ dependencyBlock.checkUnsafeAccess();
+ }
+}
+
+bool DependencyMonitor::DependencyBlock::updateSignalTimes(bool excludeIngress) {
+ if (egress.fence->getSignalTime() == Fence::SIGNAL_TIME_PENDING) {
+ return false;
+ }
+
+ if (!excludeIngress && ingress.fence->getSignalTime() == Fence::SIGNAL_TIME_PENDING) {
+ return false;
+ }
+
+ for (auto& accessCompletion : accessCompletions) {
+ if (accessCompletion.fence->getSignalTime() == Fence::SIGNAL_TIME_PENDING) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void DependencyMonitor::DependencyBlock::checkUnsafeAccess() const {
+ const nsecs_t egressTime = egress.fence->getCachedSignalTime();
+ const nsecs_t ingressTime = ingress.fence->getCachedSignalTime();
+
+ ALOGV_IF(egressTime != Fence::SIGNAL_TIME_INVALID,
+ "%" PRId64 "/%s: Egress time: %" PRId64 " (%s)", token, id, egressTime,
+ egress.annotation.c_str());
+ ALOGV_IF(Fence::isValidTimestamp(egressTime) && Fence::isValidTimestamp(ingressTime) &&
+ egressTime < ingressTime,
+ "%" PRId64 "/%s: Detected egress before ingress!: %" PRId64 " (%s) < %" PRId64 " (%s)",
+ id, token, egressTime, egress.annotation, ingressTime, ingress.annotation.c_str());
+
+ for (auto& accessCompletion : accessCompletions) {
+ const nsecs_t accessCompletionTime = accessCompletion.fence->getCachedSignalTime();
+ if (!Fence::isValidTimestamp(accessCompletionTime)) {
+ ALOGI("%" PRId64 "/%s: Detected invalid access completion! <%s>", id, token,
+ accessCompletion.annotation.c_str());
+ continue;
+ } else {
+ ALOGV("%" PRId64 "/%s: Access completion time: %" PRId64 " <%s>", id, token,
+ accessCompletionTime, accessCompletion.annotation.c_str());
+ }
+
+ ALOGI_IF(Fence::isValidTimestamp(egressTime) && accessCompletionTime > egressTime,
+ "%" PRId64 "/%s: Detected access completion after egress!: %" PRId64
+ " (%s) > %" PRId64 " (%s)",
+ id, token, accessCompletionTime, accessCompletion.annotation.c_str(), egressTime,
+ egress.annotation.c_str());
+
+ ALOGI_IF(Fence::isValidTimestamp(ingressTime) && accessCompletionTime < ingressTime,
+ "%" PRId64 "/%s: Detected access completion prior to ingress!: %" PRId64
+ " (%s) < %" PRId64 " (%s)",
+ id, token, accessCompletionTime, accessCompletion.annotation.c_str(), ingressTime,
+ ingress.annotation.c_str());
+ }
+
+ ALOGV_IF(ingressTime != Fence::SIGNAL_TIME_INVALID,
+ "%" PRId64 "/%s: Ingress time: %" PRId64 " (%s)", id, token, ingressTime,
+ ingress.annotation.c_str());
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/ui/DisplayIdentification.cpp b/libs/ui/DisplayIdentification.cpp
index 8d6f74b..78e84fc 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 @@
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 @@
return DisplayIdentificationInfo{
.id = displayId,
.name = std::string(edid->displayName),
+ .port = port,
.deviceProductInfo = buildDeviceProductInfo(*edid),
.preferredDetailedTimingDescriptor = edid->preferredDetailedTimingDescriptor,
};
@@ -426,4 +424,27 @@
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/FenceTime.cpp b/libs/ui/FenceTime.cpp
index 4246c40..81afe9e 100644
--- a/libs/ui/FenceTime.cpp
+++ b/libs/ui/FenceTime.cpp
@@ -59,6 +59,14 @@
}
}
+FenceTimePtr FenceTime::makeValid(const sp<Fence>& fence) {
+ if (fence && fence->isValid()) {
+ return std::make_shared<FenceTime>(fence);
+ } else {
+ return std::make_shared<FenceTime>(systemTime());
+ }
+}
+
void FenceTime::applyTrustedSnapshot(const Snapshot& src) {
if (CC_UNLIKELY(src.state != Snapshot::State::SIGNAL_TIME)) {
// Applying Snapshot::State::FENCE, could change the valid state of the
@@ -289,9 +297,10 @@
// ============================================================================
void FenceTimeline::push(const std::shared_ptr<FenceTime>& fence) {
std::lock_guard<std::mutex> lock(mMutex);
- while (mQueue.size() >= MAX_ENTRIES) {
+ static constexpr size_t MAX_QUEUE_SIZE = 64;
+ while (mQueue.size() >= MAX_QUEUE_SIZE) {
// This is a sanity check to make sure the queue doesn't grow unbounded.
- // MAX_ENTRIES should be big enough not to trigger this path.
+ // MAX_QUEUE_SIZE should be big enough not to trigger this path.
// In case this path is taken though, users of FenceTime must make sure
// not to rely solely on FenceTimeline to get the final timestamp and
// should eventually call Fence::getSignalTime on their own.
diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp
index b0c6e44..f7c9400 100644
--- a/libs/ui/GraphicBuffer.cpp
+++ b/libs/ui/GraphicBuffer.cpp
@@ -27,6 +27,8 @@
#include <ui/GraphicBufferMapper.h>
#include <utils/Trace.h>
+#include <string>
+
namespace android {
// ===========================================================================
@@ -104,6 +106,7 @@
usage = 0;
layerCount = 0;
handle = nullptr;
+ mDependencyMonitor.setToken(std::to_string(mId));
}
// deprecated
@@ -155,6 +158,8 @@
layerCount = request.layerCount;
usage = request.usage;
usage_deprecated = int(usage);
+ std::string name = request.requestorName;
+ mDependencyMonitor.setToken(name.append(":").append(std::to_string(mId)));
}
}
@@ -252,6 +257,7 @@
usage = inUsage;
usage_deprecated = int(usage);
stride = static_cast<int>(outStride);
+ mDependencyMonitor.setToken(requestorName.append(":").append(std::to_string(mId)));
}
return err;
}
@@ -596,6 +602,8 @@
width = height = stride = format = usage_deprecated = 0;
layerCount = 0;
usage = 0;
+ native_handle_close(handle);
+ native_handle_delete(const_cast<native_handle_t*>(handle));
handle = nullptr;
ALOGE("unflatten: registerBuffer failed: %s (%d)", strerror(-err), err);
return err;
@@ -607,6 +615,14 @@
mBufferMapper.getTransportSize(handle, &mTransportNumFds, &mTransportNumInts);
}
+ std::string name;
+ status_t err = mBufferMapper.getName(handle, &name);
+ if (err != NO_ERROR) {
+ name = "<Unknown>";
+ }
+
+ mDependencyMonitor.setToken(name.append(":").append(std::to_string(mId)));
+
buffer = static_cast<void const*>(static_cast<uint8_t const*>(buffer) + sizeNeeded);
size -= sizeNeeded;
fds += numFds;
diff --git a/libs/ui/OWNERS b/libs/ui/OWNERS
index a0b5fe7..2a85a4b 100644
--- a/libs/ui/OWNERS
+++ b/libs/ui/OWNERS
@@ -2,6 +2,5 @@
alecmouri@google.com
chrisforbes@google.com
jreck@google.com
-lpy@google.com
mathias@google.com
romainguy@google.com
diff --git a/libs/ui/include/ui/DependencyMonitor.h b/libs/ui/include/ui/DependencyMonitor.h
new file mode 100644
index 0000000..5ad1fd9
--- /dev/null
+++ b/libs/ui/include/ui/DependencyMonitor.h
@@ -0,0 +1,104 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <ui/FatVector.h>
+#include <ui/FenceTime.h>
+#include <ui/RingBuffer.h>
+
+namespace android {
+
+// Debugging class for that tries to add userspace logging for fence depencencies.
+// The model that a DependencyMonitor tries to follow is, for each access of some resource:
+// 1. There is a single ingress fence, that guards whether a resource is now safe to read from
+// another system.
+// 2. There are multiple access fences, that are fired when a resource is read.
+// 3. There is a single egress fence, that is fired when a resource is released and sent to another
+// system.
+//
+// Note that there can be repeated ingress and egress of a resource, but the assumption is that
+// there is exactly one egress for every ingress, unless the resource is destroyed rather than
+// released.
+//
+// The DependencyMonitor will log if there is an anomaly in the fences tracked for some resource.
+// This includes:
+// * If (2) happens before (1)
+// * If (2) happens after (3)
+//
+// Note that this class has no knowledge of the "other system". I.e., if the other system ignores
+// the fence reported in (3), but still takes a long time to write to the resource and produce (1),
+// then nothing will be logged. That other system must have its own DependencyMonitor. Conversely,
+// this class has imperfect knowledge of the system it is monitoring. For example, this class does
+// not know the precise start times of reading from a resource, the exact time that a read might
+// occur from a hardware unit is not known to userspace.
+//
+// In other words, this class logs specific classes of fence violations, but is not sensitive to
+// *all* violations. One property of this is that unless the system tracked by a DependencyMonitor
+// is feeding in literally incorrect fences, then there is no chance of a false positive.
+//
+// This class is thread safe.
+class DependencyMonitor {
+public:
+ // Sets a debug token identifying the resource this monitor is tracking.
+ void setToken(std::string token) { mToken = std::move(token); }
+
+ // Adds a fence that is fired when the resource ready to be ingested by the system using the
+ // DependencyMonitor.
+ void addIngress(FenceTimePtr fence, std::string annotation);
+ // Adds a fence that is fired when the resource is accessed.
+ void addAccessCompletion(FenceTimePtr fence, std::string annotation);
+ // Adds a fence that is fired when the resource is released to another system.
+ void addEgress(FenceTimePtr fence, std::string annotation);
+
+private:
+ struct AnnotatedFenceTime {
+ FenceTimePtr fence;
+ std::string annotation;
+ };
+
+ struct DependencyBlock {
+ int64_t id = -1;
+ AnnotatedFenceTime ingress = {FenceTime::NO_FENCE, ""};
+ FatVector<AnnotatedFenceTime> accessCompletions;
+ AnnotatedFenceTime egress = {FenceTime::NO_FENCE, ""};
+ bool validated = false;
+ const char* token = nullptr;
+
+ void reset(const char* newToken) {
+ static std::atomic<int64_t> counter = 0;
+ id = counter++;
+ ingress = {FenceTime::NO_FENCE, ""};
+ accessCompletions.clear();
+ egress = {FenceTime::NO_FENCE, ""};
+ validated = false;
+ token = newToken;
+ }
+
+ // Returns true if all fences in this block have valid signal times.
+ bool updateSignalTimes(bool excludeIngress);
+
+ void checkUnsafeAccess() const;
+ };
+
+ void resolveLocked() REQUIRES(mMutex);
+
+ std::string mToken;
+ std::mutex mMutex;
+ ui::RingBuffer<DependencyBlock, 10> mDependencies GUARDED_BY(mMutex);
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/ui/include/ui/DisplayId.h b/libs/ui/include/ui/DisplayId.h
index 8a14db8..1e1c77b 100644
--- a/libs/ui/include/ui/DisplayId.h
+++ b/libs/ui/include/ui/DisplayId.h
@@ -20,7 +20,7 @@
#include <ostream>
#include <string>
-#include <ftl/hash.h>
+#include <ftl/match.h>
#include <ftl/optional.h>
namespace android {
@@ -31,31 +31,15 @@
// Flag indicating that the display is virtual.
static constexpr uint64_t FLAG_VIRTUAL = 1ULL << 63;
- // Flag indicating that the ID is stable across reboots.
- static constexpr uint64_t FLAG_STABLE = 1ULL << 62;
-
- // TODO(b/162612135) Remove default constructor
+ // TODO: b/162612135 - Remove default constructor.
DisplayId() = default;
constexpr DisplayId(const DisplayId&) = default;
DisplayId& operator=(const DisplayId&) = default;
- constexpr bool isVirtual() const { return value & FLAG_VIRTUAL; }
- constexpr bool isStable() const { return value & FLAG_STABLE; }
+ static constexpr DisplayId fromValue(uint64_t value) { return DisplayId(value); }
uint64_t value;
- // For deserialization.
- static constexpr std::optional<DisplayId> fromValue(uint64_t);
-
- // As above, but also upcast to Id.
- template <typename Id>
- static constexpr std::optional<Id> fromValue(uint64_t value) {
- if (const auto id = Id::tryCast(DisplayId(value))) {
- return id;
- }
- return {};
- }
-
protected:
explicit constexpr DisplayId(uint64_t id) : value(id) {}
};
@@ -79,14 +63,10 @@
// DisplayId of a physical display, such as the internal display or externally connected display.
struct PhysicalDisplayId : DisplayId {
- static constexpr ftl::Optional<PhysicalDisplayId> tryCast(DisplayId id) {
- if (id.isVirtual()) {
- return std::nullopt;
- }
- return PhysicalDisplayId(id);
- }
+ // TODO: b/162612135 - Remove default constructor.
+ PhysicalDisplayId() = default;
- // Returns a stable ID based on EDID information.
+ // Returns a stable ID based on EDID and port information.
static constexpr PhysicalDisplayId fromEdid(uint8_t port, uint16_t manufacturerId,
uint32_t modelHash) {
return PhysicalDisplayId(FLAG_STABLE, port, manufacturerId, modelHash);
@@ -99,13 +79,16 @@
return PhysicalDisplayId(0, port, kManufacturerId, kModelHash);
}
- // TODO(b/162612135) Remove default constructor
- PhysicalDisplayId() = default;
-
- constexpr uint16_t getManufacturerId() const { return static_cast<uint16_t>(value >> 40); }
- constexpr uint8_t getPort() const { return static_cast<uint8_t>(value); }
+ static constexpr PhysicalDisplayId fromValue(uint64_t value) {
+ return PhysicalDisplayId(value);
+ }
private:
+ // Flag indicating that the ID is stable across reboots.
+ static constexpr uint64_t FLAG_STABLE = 1ULL << 62;
+
+ using DisplayId::DisplayId;
+
constexpr PhysicalDisplayId(uint64_t flags, uint8_t port, uint16_t manufacturerId,
uint32_t modelHash)
: DisplayId(flags | (static_cast<uint64_t>(manufacturerId) << 40) |
@@ -120,55 +103,38 @@
// Flag indicating that this virtual display is backed by the GPU.
static constexpr uint64_t FLAG_GPU = 1ULL << 61;
- static constexpr std::optional<VirtualDisplayId> tryCast(DisplayId id) {
- if (id.isVirtual()) {
- return VirtualDisplayId(id);
- }
- return std::nullopt;
+ static constexpr VirtualDisplayId fromValue(uint64_t value) {
+ return VirtualDisplayId(SkipVirtualFlag{}, value);
}
protected:
+ struct SkipVirtualFlag {};
+ constexpr VirtualDisplayId(SkipVirtualFlag, uint64_t value) : DisplayId(value) {}
explicit constexpr VirtualDisplayId(uint64_t value) : DisplayId(FLAG_VIRTUAL | value) {}
+
explicit constexpr VirtualDisplayId(DisplayId other) : DisplayId(other) {}
};
struct HalVirtualDisplayId : VirtualDisplayId {
explicit constexpr HalVirtualDisplayId(BaseId baseId) : VirtualDisplayId(baseId) {}
- static constexpr std::optional<HalVirtualDisplayId> tryCast(DisplayId id) {
- if (id.isVirtual() && !(id.value & FLAG_GPU)) {
- return HalVirtualDisplayId(id);
- }
- return std::nullopt;
+ static constexpr HalVirtualDisplayId fromValue(uint64_t value) {
+ return HalVirtualDisplayId(SkipVirtualFlag{}, value);
}
private:
- explicit constexpr HalVirtualDisplayId(DisplayId other) : VirtualDisplayId(other) {}
+ using VirtualDisplayId::VirtualDisplayId;
};
struct GpuVirtualDisplayId : VirtualDisplayId {
explicit constexpr GpuVirtualDisplayId(BaseId baseId) : VirtualDisplayId(FLAG_GPU | baseId) {}
- static constexpr std::optional<GpuVirtualDisplayId> fromUniqueId(const std::string& uniqueId) {
- if (const auto hashOpt = ftl::stable_hash(uniqueId)) {
- return GpuVirtualDisplayId(HashTag{}, *hashOpt);
- }
- return {};
- }
-
- static constexpr std::optional<GpuVirtualDisplayId> tryCast(DisplayId id) {
- if (id.isVirtual() && (id.value & FLAG_GPU)) {
- return GpuVirtualDisplayId(id);
- }
- return std::nullopt;
+ static constexpr GpuVirtualDisplayId fromValue(uint64_t value) {
+ return GpuVirtualDisplayId(SkipVirtualFlag{}, value);
}
private:
- struct HashTag {}; // Disambiguate with BaseId constructor.
- constexpr GpuVirtualDisplayId(HashTag, uint64_t hash)
- : VirtualDisplayId(FLAG_STABLE | FLAG_GPU | hash) {}
-
- explicit constexpr GpuVirtualDisplayId(DisplayId other) : VirtualDisplayId(other) {}
+ using VirtualDisplayId::VirtualDisplayId;
};
// HalDisplayId is the ID of a display which is managed by HWC.
@@ -176,26 +142,52 @@
struct HalDisplayId : DisplayId {
constexpr HalDisplayId(HalVirtualDisplayId other) : DisplayId(other) {}
constexpr HalDisplayId(PhysicalDisplayId other) : DisplayId(other) {}
-
- static constexpr std::optional<HalDisplayId> tryCast(DisplayId id) {
- if (GpuVirtualDisplayId::tryCast(id)) {
- return std::nullopt;
- }
- return HalDisplayId(id);
- }
+ static constexpr HalDisplayId fromValue(uint64_t value) { return HalDisplayId(value); }
private:
+ using DisplayId::DisplayId;
explicit constexpr HalDisplayId(DisplayId other) : DisplayId(other) {}
};
-constexpr std::optional<DisplayId> DisplayId::fromValue(uint64_t value) {
- if (const auto id = fromValue<PhysicalDisplayId>(value)) {
- return id;
- }
- if (const auto id = fromValue<VirtualDisplayId>(value)) {
- return id;
- }
- return {};
+using DisplayIdVariant = std::variant<PhysicalDisplayId, GpuVirtualDisplayId, HalVirtualDisplayId>;
+using VirtualDisplayIdVariant = std::variant<GpuVirtualDisplayId, HalVirtualDisplayId>;
+
+template <typename DisplayIdType>
+inline auto asDisplayIdOfType(DisplayIdVariant variant) -> ftl::Optional<DisplayIdType> {
+ return ftl::match(
+ variant,
+ [](DisplayIdType id) -> ftl::Optional<DisplayIdType> { return ftl::Optional(id); },
+ [](auto) -> ftl::Optional<DisplayIdType> { return std::nullopt; });
+}
+
+template <typename Variant>
+inline auto asHalDisplayId(Variant variant) -> ftl::Optional<HalDisplayId> {
+ return ftl::match(
+ variant,
+ [](GpuVirtualDisplayId) -> ftl::Optional<HalDisplayId> { return std::nullopt; },
+ [](auto id) -> ftl::Optional<HalDisplayId> {
+ return ftl::Optional(static_cast<HalDisplayId>(id));
+ });
+}
+
+inline auto asPhysicalDisplayId(DisplayIdVariant variant) -> ftl::Optional<PhysicalDisplayId> {
+ return asDisplayIdOfType<PhysicalDisplayId>(variant);
+}
+
+inline auto asVirtualDisplayId(DisplayIdVariant variant) -> ftl::Optional<VirtualDisplayId> {
+ return ftl::match(
+ variant,
+ [](GpuVirtualDisplayId id) -> ftl::Optional<VirtualDisplayId> {
+ return ftl::Optional(static_cast<VirtualDisplayId>(id));
+ },
+ [](HalVirtualDisplayId id) -> ftl::Optional<VirtualDisplayId> {
+ return ftl::Optional(static_cast<VirtualDisplayId>(id));
+ },
+ [](auto) -> ftl::Optional<VirtualDisplayId> { return std::nullopt; });
+}
+
+inline auto asDisplayId(DisplayIdVariant variant) -> DisplayId {
+ return ftl::match(variant, [](auto id) -> DisplayId { return static_cast<DisplayId>(id); });
}
static_assert(sizeof(DisplayId) == sizeof(uint64_t));
diff --git a/libs/ui/include/ui/DisplayIdentification.h b/libs/ui/include/ui/DisplayIdentification.h
index cf67d7b..201d5e9 100644
--- a/libs/ui/include/ui/DisplayIdentification.h
+++ b/libs/ui/include/ui/DisplayIdentification.h
@@ -39,11 +39,29 @@
ui::Size physicalSizeInMm;
};
+// These values must match the ones in ScreenPartStatus.aidl file in the composer HAL
+enum class ScreenPartStatus : uint8_t {
+ /**
+ * Device cannot differentiate an original screen from a replaced screen.
+ */
+ UNSUPPORTED = 0,
+ /**
+ * Device has the original screen it was manufactured with.
+ */
+ ORIGINAL = 1,
+ /**
+ * Device has a replaced screen.
+ */
+ REPLACED = 2,
+};
+
struct DisplayIdentificationInfo {
PhysicalDisplayId id;
std::string name;
+ uint8_t port;
std::optional<DeviceProductInfo> deviceProductInfo;
std::optional<DetailedTimingDescriptor> preferredDetailedTimingDescriptor;
+ ScreenPartStatus screenPartStatus;
};
struct ExtensionBlock {
@@ -73,6 +91,7 @@
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 +103,14 @@
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 65d2b8f..834a304 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 @@
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/FenceTime.h b/libs/ui/include/ui/FenceTime.h
index 334106f..3560d57 100644
--- a/libs/ui/include/ui/FenceTime.h
+++ b/libs/ui/include/ui/FenceTime.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_FENCE_TIME_H
#define ANDROID_FENCE_TIME_H
+#include <stddef.h>
#include <ui/Fence.h>
#include <utils/Flattenable.h>
#include <utils/Mutex.h>
@@ -30,6 +31,8 @@
namespace android {
class FenceToFenceTimeMap;
+class FenceTime;
+using FenceTimePtr = std::shared_ptr<FenceTime>;
// A wrapper around fence that only implements isValid and getSignalTime.
// It automatically closes the fence in a thread-safe manner once the signal
@@ -95,6 +98,10 @@
FenceTime& operator=(const FenceTime&) = delete;
FenceTime& operator=(FenceTime&&) = delete;
+ // Constructs a FenceTime, falling back to a timestamp if the fence is
+ // invalid.
+ static FenceTimePtr makeValid(const sp<Fence>& fence);
+
// This method should only be called when replacing the fence with
// a signalTime. Since this is an indirect way of setting the signal time
// of a fence, the snapshot should come from a trusted source.
@@ -142,8 +149,6 @@
std::atomic<nsecs_t> mSignalTime{Fence::SIGNAL_TIME_INVALID};
};
-using FenceTimePtr = std::shared_ptr<FenceTime>;
-
// A queue of FenceTimes that are expected to signal in FIFO order.
// Only maintains a queue of weak pointers so it doesn't keep references
// to Fences on its own.
@@ -162,8 +167,6 @@
// different threads.
class FenceTimeline {
public:
- static constexpr size_t MAX_ENTRIES = 64;
-
void push(const std::shared_ptr<FenceTime>& fence);
void updateSignalTimes();
diff --git a/libs/ui/include/ui/GraphicBuffer.h b/libs/ui/include/ui/GraphicBuffer.h
index 936bf8f..9305180 100644
--- a/libs/ui/include/ui/GraphicBuffer.h
+++ b/libs/ui/include/ui/GraphicBuffer.h
@@ -23,6 +23,7 @@
#include <string>
#include <utility>
#include <vector>
+#include "ui/DependencyMonitor.h"
#include <android/hardware_buffer.h>
#include <ui/ANativeObjectBase.h>
@@ -229,6 +230,8 @@
void addDeathCallback(GraphicBufferDeathCallback deathCallback, void* context);
+ DependencyMonitor& getDependencyMonitor() { return mDependencyMonitor; }
+
private:
~GraphicBuffer();
@@ -295,6 +298,8 @@
// and informs SurfaceFlinger that it should drop its strong pointer reference to the buffer.
std::vector<std::pair<GraphicBufferDeathCallback, void* /*mDeathCallbackContext*/>>
mDeathCallbacks;
+
+ DependencyMonitor mDependencyMonitor;
};
} // namespace android
diff --git a/libs/ui/include/ui/RingBuffer.h b/libs/ui/include/ui/RingBuffer.h
new file mode 100644
index 0000000..31d5a95
--- /dev/null
+++ b/libs/ui/include/ui/RingBuffer.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stddef.h>
+#include <array>
+
+namespace android::ui {
+
+template <class T, size_t SIZE>
+class RingBuffer {
+ RingBuffer(const RingBuffer&) = delete;
+ void operator=(const RingBuffer&) = delete;
+
+public:
+ RingBuffer() = default;
+ ~RingBuffer() = default;
+
+ constexpr size_t capacity() const { return SIZE; }
+ size_t size() const { return mCount; }
+ bool isFull() const { return size() == capacity(); }
+
+ T& next() {
+ mHead = static_cast<size_t>(mHead + 1) % SIZE;
+ if (mCount < SIZE) {
+ mCount++;
+ }
+ return mBuffer[static_cast<size_t>(mHead)];
+ }
+
+ T& front() { return (*this)[0]; }
+ const T& front() const { return (*this)[0]; }
+
+ T& back() { return (*this)[size() - 1]; }
+ const T& back() const { return (*this)[size() - 1]; }
+
+ T& operator[](size_t index) {
+ return mBuffer[(static_cast<size_t>(mHead + 1) + index) % mCount];
+ }
+
+ const T& operator[](size_t index) const {
+ return mBuffer[(static_cast<size_t>(mHead + 1) + index) % mCount];
+ }
+
+ void clear() {
+ mCount = 0;
+ mHead = -1;
+ }
+
+private:
+ std::array<T, SIZE> mBuffer;
+ int mHead = -1;
+ size_t mCount = 0;
+};
+
+} // namespace android::ui
diff --git a/libs/ui/include/ui/StaticDisplayInfo.h b/libs/ui/include/ui/StaticDisplayInfo.h
index 83da821..5316448 100644
--- a/libs/ui/include/ui/StaticDisplayInfo.h
+++ b/libs/ui/include/ui/StaticDisplayInfo.h
@@ -28,6 +28,7 @@
// Immutable information about physical display.
struct StaticDisplayInfo {
DisplayConnectionType connectionType = DisplayConnectionType::Internal;
+ uint8_t port;
float density = 0.f;
bool secure = false;
std::optional<DeviceProductInfo> deviceProductInfo;
diff --git a/libs/ui/include_types/ui/HdrRenderTypeUtils.h b/libs/ui/include_types/ui/HdrRenderTypeUtils.h
index 70c50f0..98018d9 100644
--- a/libs/ui/include_types/ui/HdrRenderTypeUtils.h
+++ b/libs/ui/include_types/ui/HdrRenderTypeUtils.h
@@ -36,7 +36,7 @@
*/
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 @@
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 2d8a1e3..d950f2a 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -45,16 +45,6 @@
}
cc_test {
- name: "DisplayId_test",
- shared_libs: ["libui"],
- srcs: ["DisplayId_test.cpp"],
- cflags: [
- "-Wall",
- "-Werror",
- ],
-}
-
-cc_test {
name: "DisplayIdentification_test",
shared_libs: ["libui"],
static_libs: ["libgmock"],
@@ -144,6 +134,17 @@
}
cc_test {
+ name: "RingBuffer_test",
+ test_suites: ["device-tests"],
+ shared_libs: ["libui"],
+ srcs: ["RingBuffer_test.cpp"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+}
+
+cc_test {
name: "Size_test",
test_suites: ["device-tests"],
shared_libs: ["libui"],
diff --git a/libs/ui/tests/DisplayId_test.cpp b/libs/ui/tests/DisplayId_test.cpp
deleted file mode 100644
index ef686df..0000000
--- a/libs/ui/tests/DisplayId_test.cpp
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <ui/DisplayId.h>
-
-#include <gtest/gtest.h>
-
-namespace android::ui {
-
-TEST(DisplayIdTest, createPhysicalIdFromEdid) {
- constexpr uint8_t port = 1;
- constexpr uint16_t manufacturerId = 13;
- constexpr uint32_t modelHash = 42;
- const PhysicalDisplayId id = PhysicalDisplayId::fromEdid(port, manufacturerId, modelHash);
- EXPECT_EQ(port, id.getPort());
- EXPECT_EQ(manufacturerId, id.getManufacturerId());
- EXPECT_FALSE(VirtualDisplayId::tryCast(id));
- EXPECT_FALSE(HalVirtualDisplayId::tryCast(id));
- EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id));
- EXPECT_TRUE(PhysicalDisplayId::tryCast(id));
- EXPECT_TRUE(HalDisplayId::tryCast(id));
-
- EXPECT_EQ(id, DisplayId::fromValue(id.value));
- EXPECT_EQ(id, DisplayId::fromValue<PhysicalDisplayId>(id.value));
-}
-
-TEST(DisplayIdTest, createPhysicalIdFromPort) {
- constexpr uint8_t port = 3;
- const PhysicalDisplayId id = PhysicalDisplayId::fromPort(port);
- EXPECT_EQ(port, id.getPort());
- EXPECT_FALSE(VirtualDisplayId::tryCast(id));
- EXPECT_FALSE(HalVirtualDisplayId::tryCast(id));
- EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id));
- EXPECT_TRUE(PhysicalDisplayId::tryCast(id));
- EXPECT_TRUE(HalDisplayId::tryCast(id));
-
- EXPECT_EQ(id, DisplayId::fromValue(id.value));
- EXPECT_EQ(id, DisplayId::fromValue<PhysicalDisplayId>(id.value));
-}
-
-TEST(DisplayIdTest, createGpuVirtualId) {
- const GpuVirtualDisplayId id(42);
- EXPECT_TRUE(VirtualDisplayId::tryCast(id));
- EXPECT_TRUE(GpuVirtualDisplayId::tryCast(id));
- EXPECT_FALSE(HalVirtualDisplayId::tryCast(id));
- EXPECT_FALSE(PhysicalDisplayId::tryCast(id));
- EXPECT_FALSE(HalDisplayId::tryCast(id));
-
- EXPECT_EQ(id, DisplayId::fromValue(id.value));
- EXPECT_EQ(id, DisplayId::fromValue<GpuVirtualDisplayId>(id.value));
-}
-
-TEST(DisplayIdTest, createVirtualIdFromGpuVirtualId) {
- const VirtualDisplayId id(GpuVirtualDisplayId(42));
- EXPECT_TRUE(VirtualDisplayId::tryCast(id));
- EXPECT_TRUE(GpuVirtualDisplayId::tryCast(id));
- EXPECT_FALSE(HalVirtualDisplayId::tryCast(id));
- EXPECT_FALSE(PhysicalDisplayId::tryCast(id));
- EXPECT_FALSE(HalDisplayId::tryCast(id));
-
- const bool isGpuVirtualId = (id.value & VirtualDisplayId::FLAG_GPU);
- EXPECT_EQ((id.isVirtual() && isGpuVirtualId), GpuVirtualDisplayId::tryCast(id).has_value());
-}
-
-TEST(DisplayIdTest, createGpuVirtualIdFromUniqueId) {
- static const std::string kUniqueId("virtual:ui:DisplayId_test");
- const auto idOpt = GpuVirtualDisplayId::fromUniqueId(kUniqueId);
- ASSERT_TRUE(idOpt.has_value());
- const GpuVirtualDisplayId id = idOpt.value();
- EXPECT_TRUE(VirtualDisplayId::tryCast(id));
- EXPECT_TRUE(GpuVirtualDisplayId::tryCast(id));
- EXPECT_FALSE(HalVirtualDisplayId::tryCast(id));
- EXPECT_FALSE(PhysicalDisplayId::tryCast(id));
- EXPECT_FALSE(HalDisplayId::tryCast(id));
-
- EXPECT_EQ(id, DisplayId::fromValue(id.value));
- EXPECT_EQ(id, DisplayId::fromValue<GpuVirtualDisplayId>(id.value));
-}
-
-TEST(DisplayIdTest, createHalVirtualId) {
- const HalVirtualDisplayId id(42);
- EXPECT_TRUE(VirtualDisplayId::tryCast(id));
- EXPECT_TRUE(HalVirtualDisplayId::tryCast(id));
- EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id));
- EXPECT_FALSE(PhysicalDisplayId::tryCast(id));
- EXPECT_TRUE(HalDisplayId::tryCast(id));
-
- EXPECT_EQ(id, DisplayId::fromValue(id.value));
- EXPECT_EQ(id, DisplayId::fromValue<HalVirtualDisplayId>(id.value));
-}
-
-TEST(DisplayIdTest, createVirtualIdFromHalVirtualId) {
- const VirtualDisplayId id(HalVirtualDisplayId(42));
- EXPECT_TRUE(VirtualDisplayId::tryCast(id));
- EXPECT_TRUE(HalVirtualDisplayId::tryCast(id));
- EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id));
- EXPECT_FALSE(PhysicalDisplayId::tryCast(id));
- EXPECT_TRUE(HalDisplayId::tryCast(id));
-
- const bool isGpuVirtualId = (id.value & VirtualDisplayId::FLAG_GPU);
- EXPECT_EQ((id.isVirtual() && !isGpuVirtualId), HalVirtualDisplayId::tryCast(id).has_value());
-}
-
-} // namespace android::ui
diff --git a/libs/ui/tests/DisplayIdentification_test.cpp b/libs/ui/tests/DisplayIdentification_test.cpp
index d1699e7..75c71a5 100644
--- a/libs/ui/tests/DisplayIdentification_test.cpp
+++ b/libs/ui/tests/DisplayIdentification_test.cpp
@@ -376,6 +376,22 @@
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, 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 0000000..9839492
--- /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