Merge Android R (rvc-dev-plus-aosp-without-vendor@6692709)
Bug: 166295507
Merged-In: I70ea776b8589ac3a7982c710c5c8b2941d86e55b
Change-Id: Ic1d535e9d2d6f80d95215240dbdb024995b045f8
diff --git a/libs/adbd_auth/adbd_auth.cpp b/libs/adbd_auth/adbd_auth.cpp
index 5a0d3f6..dae6eeb 100644
--- a/libs/adbd_auth/adbd_auth.cpp
+++ b/libs/adbd_auth/adbd_auth.cpp
@@ -83,28 +83,28 @@
InitFrameworkHandlers();
epoll_fd_.reset(epoll_create1(EPOLL_CLOEXEC));
if (epoll_fd_ == -1) {
- PLOG(FATAL) << "failed to create epoll fd";
+ PLOG(FATAL) << "adbd_auth: failed to create epoll fd";
}
event_fd_.reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
if (event_fd_ == -1) {
- PLOG(FATAL) << "failed to create eventfd";
+ PLOG(FATAL) << "adbd_auth: failed to create eventfd";
}
sock_fd_.reset(android_get_control_socket("adbd"));
if (sock_fd_ == -1) {
- PLOG(ERROR) << "failed to get adbd authentication socket";
+ PLOG(ERROR) << "adbd_auth: failed to get adbd authentication socket";
} else {
if (fcntl(sock_fd_.get(), F_SETFD, FD_CLOEXEC) != 0) {
- PLOG(FATAL) << "failed to make adbd authentication socket cloexec";
+ PLOG(FATAL) << "adbd_auth: failed to make adbd authentication socket cloexec";
}
if (fcntl(sock_fd_.get(), F_SETFL, O_NONBLOCK) != 0) {
- PLOG(FATAL) << "failed to make adbd authentication socket nonblocking";
+ PLOG(FATAL) << "adbd_auth: failed to make adbd authentication socket nonblocking";
}
if (listen(sock_fd_.get(), 4) != 0) {
- PLOG(FATAL) << "failed to listen on adbd authentication socket";
+ PLOG(FATAL) << "adbd_auth: failed to listen on adbd authentication socket";
}
}
}
@@ -146,7 +146,7 @@
struct epoll_event event;
event.events = EPOLLIN;
if (!output_queue_.empty()) {
- LOG(INFO) << "marking framework writable";
+ LOG(INFO) << "adbd_auth: marking framework writable";
event.events |= EPOLLOUT;
}
event.data.u64 = kEpollConstFramework;
@@ -155,7 +155,7 @@
}
void ReplaceFrameworkFd(unique_fd new_fd) REQUIRES(mutex_) {
- LOG(INFO) << "received new framework fd " << new_fd.get()
+ LOG(INFO) << "adbd_auth: received new framework fd " << new_fd.get()
<< " (current = " << framework_fd_.get() << ")";
// If we already had a framework fd, clean up after ourselves.
@@ -170,7 +170,7 @@
struct epoll_event event;
event.events = EPOLLIN;
if (!output_queue_.empty()) {
- LOG(INFO) << "marking framework writable";
+ LOG(INFO) << "adbd_auth: marking framework writable";
event.events |= EPOLLOUT;
}
event.data.u64 = kEpollConstFramework;
@@ -180,10 +180,10 @@
}
void HandlePacket(std::string_view packet) EXCLUDES(mutex_) {
- LOG(INFO) << "received packet: " << packet;
+ LOG(INFO) << "adbd_auth: received packet: " << packet;
if (packet.size() < 2) {
- LOG(ERROR) << "received packet of invalid length";
+ LOG(ERROR) << "adbd_auth: received packet of invalid length";
std::lock_guard<std::mutex> lock(mutex_);
ReplaceFrameworkFd(unique_fd());
}
@@ -197,7 +197,7 @@
}
}
if (!handled_packet) {
- LOG(ERROR) << "unhandled packet: " << packet;
+ LOG(ERROR) << "adbd_auth: unhandled packet: " << packet;
std::lock_guard<std::mutex> lock(mutex_);
ReplaceFrameworkFd(unique_fd());
}
@@ -206,12 +206,18 @@
void AllowUsbDevice(std::string_view buf) EXCLUDES(mutex_) {
std::lock_guard<std::mutex> lock(mutex_);
CHECK(buf.empty());
- CHECK(dispatched_prompt_.has_value());
- auto& [id, key, arg] = *dispatched_prompt_;
- keys_.emplace(id, std::move(key));
- callbacks_.key_authorized(arg, id);
- dispatched_prompt_ = std::nullopt;
+ if (dispatched_prompt_.has_value()) {
+ // It's possible for the framework to send us a response without our having sent a
+ // request to it: e.g. if adbd restarts while we have a pending request.
+ auto& [id, key, arg] = *dispatched_prompt_;
+ keys_.emplace(id, std::move(key));
+
+ callbacks_.key_authorized(arg, id);
+ dispatched_prompt_ = std::nullopt;
+ } else {
+ LOG(WARNING) << "adbd_auth: received authorization for unknown prompt, ignoring";
+ }
// We need to dispatch pending prompts here upon success as well,
// since we might have multiple queued prompts.
@@ -273,14 +279,14 @@
iovs[2].iov_base = p->public_key.data();
iovs[2].iov_len = p->public_key.size();
} else {
- LOG(FATAL) << "unhandled packet type?";
+ LOG(FATAL) << "adbd_auth: unhandled packet type?";
}
output_queue_.pop_front();
ssize_t rc = writev(framework_fd_.get(), iovs, iovcnt);
if (rc == -1 && errno != EAGAIN && errno != EWOULDBLOCK) {
- PLOG(ERROR) << "failed to write to framework fd";
+ PLOG(ERROR) << "adbd_auth: failed to write to framework fd";
ReplaceFrameworkFd(unique_fd());
return false;
}
@@ -290,7 +296,7 @@
void Run() {
if (sock_fd_ == -1) {
- LOG(ERROR) << "adbd authentication socket unavailable, disabling user prompts";
+ LOG(ERROR) << "adbd_auth: socket unavailable, disabling user prompts";
} else {
struct epoll_event event;
event.events = EPOLLIN;
@@ -309,9 +315,9 @@
struct epoll_event events[3];
int rc = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd_.get(), events, 3, -1));
if (rc == -1) {
- PLOG(FATAL) << "epoll_wait failed";
+ PLOG(FATAL) << "adbd_auth: epoll_wait failed";
} else if (rc == 0) {
- LOG(FATAL) << "epoll_wait returned 0";
+ LOG(FATAL) << "adbd_auth: epoll_wait returned 0";
}
bool restart = false;
@@ -326,7 +332,7 @@
unique_fd new_framework_fd(accept4(sock_fd_.get(), nullptr, nullptr,
SOCK_CLOEXEC | SOCK_NONBLOCK));
if (new_framework_fd == -1) {
- PLOG(FATAL) << "failed to accept framework fd";
+ PLOG(FATAL) << "adbd_auth: failed to accept framework fd";
}
LOG(INFO) << "adbd_auth: received a new framework connection";
@@ -344,7 +350,8 @@
uint64_t dummy;
int rc = TEMP_FAILURE_RETRY(read(event_fd_.get(), &dummy, sizeof(dummy)));
if (rc != 8) {
- PLOG(FATAL) << "failed to read from eventfd (rc = " << rc << ")";
+ PLOG(FATAL)
+ << "adbd_auth: failed to read from eventfd (rc = " << rc << ")";
}
std::lock_guard<std::mutex> lock(mutex_);
@@ -357,9 +364,9 @@
if (event.events & EPOLLIN) {
int rc = TEMP_FAILURE_RETRY(read(framework_fd_.get(), buf, sizeof(buf)));
if (rc == -1) {
- LOG(FATAL) << "failed to read from framework fd";
+ LOG(FATAL) << "adbd_auth: failed to read from framework fd";
} else if (rc == 0) {
- LOG(INFO) << "hit EOF on framework fd";
+ LOG(INFO) << "adbd_auth: hit EOF on framework fd";
std::lock_guard<std::mutex> lock(mutex_);
ReplaceFrameworkFd(unique_fd());
} else {
@@ -386,10 +393,10 @@
void IteratePublicKeys(bool (*callback)(void*, const char*, size_t), void* opaque) {
for (const auto& path : key_paths) {
if (access(path, R_OK) == 0) {
- LOG(INFO) << "Loading keys from " << path;
+ LOG(INFO) << "adbd_auth: loading keys from " << path;
std::string content;
if (!android::base::ReadFileToString(path, &content)) {
- PLOG(ERROR) << "Couldn't read " << path;
+ PLOG(ERROR) << "adbd_auth: couldn't read " << path;
continue;
}
for (const auto& line : android::base::Split(content, "\n")) {
@@ -405,6 +412,7 @@
uint64_t id = NextId();
std::lock_guard<std::mutex> lock(mutex_);
+ LOG(INFO) << "adbd_auth: sending prompt with id " << id;
pending_prompts_.emplace_back(id, public_key, arg);
DispatchPendingPrompt();
return id;
@@ -423,7 +431,7 @@
std::lock_guard<std::mutex> lock(mutex_);
auto it = keys_.find(id);
if (it == keys_.end()) {
- LOG(DEBUG) << "couldn't find public key to notify disconnection, skipping";
+ LOG(DEBUG) << "adbd_auth: couldn't find public key to notify disconnection, skipping";
return;
}
output_queue_.emplace_back(AdbdAuthPacketDisconnected{.public_key = std::move(it->second)});
@@ -446,7 +454,8 @@
std::lock_guard<std::mutex> lock(mutex_);
auto it = keys_.find(id);
if (it == keys_.end()) {
- LOG(DEBUG) << "couldn't find public key to notify disconnection of tls device, skipping";
+ LOG(DEBUG) << "adbd_auth: couldn't find public key to notify disconnection of tls "
+ "device, skipping";
return;
}
output_queue_.emplace_back(AdbdPacketTlsDeviceDisconnected{
@@ -461,9 +470,9 @@
uint64_t value = 1;
ssize_t rc = write(event_fd_.get(), &value, sizeof(value));
if (rc == -1) {
- PLOG(FATAL) << "write to eventfd failed";
+ PLOG(FATAL) << "adbd_auth: write to eventfd failed";
} else if (rc != sizeof(value)) {
- LOG(FATAL) << "write to eventfd returned short (" << rc << ")";
+ LOG(FATAL) << "adbd_auth: write to eventfd returned short (" << rc << ")";
}
}
@@ -516,8 +525,9 @@
if (callbacks->version == 1) {
return new AdbdAuthContext(reinterpret_cast<AdbdAuthCallbacksV1*>(callbacks));
} else {
- LOG(ERROR) << "received unknown AdbdAuthCallbacks version " << callbacks->version;
- return nullptr;
+ LOG(ERROR) << "adbd_auth: received unknown AdbdAuthCallbacks version "
+ << callbacks->version;
+ return nullptr;
}
}
@@ -545,7 +555,12 @@
void adbd_auth_prompt_user(AdbdAuthContext* ctx, const char* public_key, size_t len,
void* opaque) {
- ctx->PromptUser(std::string_view(public_key, len), opaque);
+ adbd_auth_prompt_user_with_id(ctx, public_key, len, opaque);
+}
+
+uint64_t adbd_auth_prompt_user_with_id(AdbdAuthContext* ctx, const char* public_key, size_t len,
+ void* opaque) {
+ return ctx->PromptUser(std::string_view(public_key, len), opaque);
}
uint64_t adbd_auth_tls_device_connected(AdbdAuthContext* ctx,
diff --git a/libs/adbd_auth/include/adbd_auth.h b/libs/adbd_auth/include/adbd_auth.h
index 6ee3166..8f834df 100644
--- a/libs/adbd_auth/include/adbd_auth.h
+++ b/libs/adbd_auth/include/adbd_auth.h
@@ -122,9 +122,23 @@
* @param len the length of the public_key argument
* @param arg an opaque userdata argument
*/
-void adbd_auth_prompt_user(AdbdAuthContext* ctx,
- const char* public_key,
- size_t len, void* opaque) __INTRODUCED_IN(30);
+void adbd_auth_prompt_user(AdbdAuthContext* ctx, const char* public_key, size_t len, void* opaque)
+ __INTRODUCED_IN(30);
+
+/**
+ * Prompt the user to authorize a public key.
+ *
+ * When this happens, a callback will be run on the auth thread with the result.
+ *
+ * @param ctx the AdbdAuthContext
+ * @param public_key the RSA public key to prompt user with
+ * @param len the length of the public_key argument
+ * @param arg an opaque userdata argument
+ * @return a unique id which will be returned via callback
+ */
+__attribute__((weak)) uint64_t adbd_auth_prompt_user_with_id(AdbdAuthContext* ctx,
+ const char* public_key, size_t len,
+ void* opaque) __INTRODUCED_IN(30);
/**
* Let system_server know that a TLS device has connected.
diff --git a/libs/adbd_auth/libadbd_auth.map.txt b/libs/adbd_auth/libadbd_auth.map.txt
index 5857ecb..7584ca3 100644
--- a/libs/adbd_auth/libadbd_auth.map.txt
+++ b/libs/adbd_auth/libadbd_auth.map.txt
@@ -7,6 +7,7 @@
adbd_auth_notify_auth; # apex introduced=30
adbd_auth_notify_disconnect; # apex introduced=30
adbd_auth_prompt_user; # apex introduced=30
+ adbd_auth_prompt_user_with_id; # apex introduced=30
adbd_auth_tls_device_connected; # apex introduced=30
adbd_auth_tls_device_disconnected; # apex introduced=30
adbd_auth_get_max_version; # apex introduced=30
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 75eb7a3..9675a53 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -168,7 +168,9 @@
filegroup {
name: "libbinder_aidl",
srcs: [
+ "aidl/android/content/pm/IPackageChangeObserver.aidl",
"aidl/android/content/pm/IPackageManagerNative.aidl",
+ "aidl/android/content/pm/PackageChangeEvent.aidl",
"aidl/android/os/IClientCallback.aidl",
"aidl/android/os/IServiceCallback.aidl",
"aidl/android/os/IServiceManager.aidl",
diff --git a/libs/binder/AppOpsManager.cpp b/libs/binder/AppOpsManager.cpp
index 0a6685e..1c6b491 100644
--- a/libs/binder/AppOpsManager.cpp
+++ b/libs/binder/AppOpsManager.cpp
@@ -21,44 +21,34 @@
#include <utils/SystemClock.h>
+#include <sys/types.h>
+
+#ifdef LOG_TAG
+#undef LOG_TAG
+#endif
+#define LOG_TAG "AppOpsManager"
+
namespace android {
-namespace {
+static const sp<IBinder>& getClientId() {
+ static pthread_mutex_t gClientIdMutex = PTHREAD_MUTEX_INITIALIZER;
+ static sp<IBinder> gClientId;
-#if defined(__BRILLO__)
-// Because Brillo has no application model, security policy is managed
-// statically (at build time) with SELinux controls.
-// As a consequence, it also never runs the AppOpsManager service.
-const int APP_OPS_MANAGER_UNAVAILABLE_MODE = AppOpsManager::MODE_ALLOWED;
-#else
-const int APP_OPS_MANAGER_UNAVAILABLE_MODE = AppOpsManager::MODE_IGNORED;
-#endif // defined(__BRILLO__)
-
-} // namespace
-
-static String16 _appops("appops");
-static pthread_mutex_t gTokenMutex = PTHREAD_MUTEX_INITIALIZER;
-static sp<IBinder> gToken;
-
-static const sp<IBinder>& getToken(const sp<IAppOpsService>& service) {
- pthread_mutex_lock(&gTokenMutex);
- if (gToken == nullptr || gToken->pingBinder() != NO_ERROR) {
- gToken = service->getToken(new BBinder());
+ pthread_mutex_lock(&gClientIdMutex);
+ if (gClientId == nullptr) {
+ gClientId = new BBinder();
}
- pthread_mutex_unlock(&gTokenMutex);
- return gToken;
+ pthread_mutex_unlock(&gClientIdMutex);
+ return gClientId;
}
AppOpsManager::AppOpsManager()
{
}
-#if defined(__BRILLO__)
-// There is no AppOpsService on Brillo
-sp<IAppOpsService> AppOpsManager::getService() { return NULL; }
-#else
sp<IAppOpsService> AppOpsManager::getService()
{
+ static String16 _appops("appops");
std::lock_guard<Mutex> scoped_lock(mLock);
int64_t startTime = 0;
@@ -83,14 +73,13 @@
}
return service;
}
-#endif // defined(__BRILLO__)
int32_t AppOpsManager::checkOp(int32_t op, int32_t uid, const String16& callingPackage)
{
sp<IAppOpsService> service = getService();
return service != nullptr
? service->checkOperation(op, uid, callingPackage)
- : APP_OPS_MANAGER_UNAVAILABLE_MODE;
+ : AppOpsManager::MODE_IGNORED;
}
int32_t AppOpsManager::checkAudioOpNoThrow(int32_t op, int32_t usage, int32_t uid,
@@ -98,28 +87,52 @@
sp<IAppOpsService> service = getService();
return service != nullptr
? service->checkAudioOperation(op, usage, uid, callingPackage)
- : APP_OPS_MANAGER_UNAVAILABLE_MODE;
+ : AppOpsManager::MODE_IGNORED;
}
int32_t AppOpsManager::noteOp(int32_t op, int32_t uid, const String16& callingPackage) {
+ return noteOp(op, uid, callingPackage, {},
+ String16("Legacy AppOpsManager.noteOp call"));
+}
+
+int32_t AppOpsManager::noteOp(int32_t op, int32_t uid, const String16& callingPackage,
+ const std::optional<String16>& attributionTag, const String16& message) {
sp<IAppOpsService> service = getService();
- return service != nullptr
- ? service->noteOperation(op, uid, callingPackage)
- : APP_OPS_MANAGER_UNAVAILABLE_MODE;
+ int32_t mode = service != nullptr
+ ? service->noteOperation(op, uid, callingPackage, attributionTag,
+ shouldCollectNotes(op), message)
+ : AppOpsManager::MODE_IGNORED;
+
+ return mode;
}
int32_t AppOpsManager::startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage,
bool startIfModeDefault) {
+ return startOpNoThrow(op, uid, callingPackage, startIfModeDefault, {},
+ String16("Legacy AppOpsManager.startOpNoThrow call"));
+}
+
+int32_t AppOpsManager::startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage,
+ bool startIfModeDefault, const std::optional<String16>& attributionTag,
+ const String16& message) {
sp<IAppOpsService> service = getService();
- return service != nullptr
- ? service->startOperation(getToken(service), op, uid, callingPackage,
- startIfModeDefault) : APP_OPS_MANAGER_UNAVAILABLE_MODE;
+ int32_t mode = service != nullptr
+ ? service->startOperation(getClientId(), op, uid, callingPackage,
+ attributionTag, startIfModeDefault, shouldCollectNotes(op), message)
+ : AppOpsManager::MODE_IGNORED;
+
+ return mode;
}
void AppOpsManager::finishOp(int32_t op, int32_t uid, const String16& callingPackage) {
+ finishOp(op, uid, callingPackage, {});
+}
+
+void AppOpsManager::finishOp(int32_t op, int32_t uid, const String16& callingPackage,
+ const std::optional<String16>& attributionTag) {
sp<IAppOpsService> service = getService();
if (service != nullptr) {
- service->finishOperation(getToken(service), op, uid, callingPackage);
+ service->finishOperation(getClientId(), op, uid, callingPackage, attributionTag);
}
}
@@ -146,5 +159,27 @@
return -1;
}
+void AppOpsManager::setCameraAudioRestriction(int32_t mode) {
+ sp<IAppOpsService> service = getService();
+ if (service != nullptr) {
+ service->setCameraAudioRestriction(mode);
+ }
+}
+
+// check it the appops needs to be collected and cache result
+bool AppOpsManager::shouldCollectNotes(int32_t opcode) {
+ // Whether an appop should be collected: 0 == not initialized, 1 == don't note, 2 == note
+ static uint8_t appOpsToNote[AppOpsManager::_NUM_OP] = {0};
+
+ if (appOpsToNote[opcode] == 0) {
+ if (getService()->shouldCollectNotes(opcode)) {
+ appOpsToNote[opcode] = 2;
+ } else {
+ appOpsToNote[opcode] = 1;
+ }
+ }
+
+ return appOpsToNote[opcode] == 2;
+}
} // namespace android
diff --git a/libs/binder/IAppOpsCallback.cpp b/libs/binder/IAppOpsCallback.cpp
index 0ce1dd5..b9eb281 100644
--- a/libs/binder/IAppOpsCallback.cpp
+++ b/libs/binder/IAppOpsCallback.cpp
@@ -39,7 +39,7 @@
data.writeInterfaceToken(IAppOpsCallback::getInterfaceDescriptor());
data.writeInt32(op);
data.writeString16(packageName);
- remote()->transact(OP_CHANGED_TRANSACTION, data, &reply);
+ remote()->transact(OP_CHANGED_TRANSACTION, data, &reply, IBinder::FLAG_ONEWAY);
}
};
@@ -58,7 +58,6 @@
String16 packageName;
(void)data.readString16(&packageName);
opChanged(op, packageName);
- reply->writeNoException();
return NO_ERROR;
} break;
default:
diff --git a/libs/binder/IAppOpsService.cpp b/libs/binder/IAppOpsService.cpp
index e5f0a11..cd78866 100644
--- a/libs/binder/IAppOpsService.cpp
+++ b/libs/binder/IAppOpsService.cpp
@@ -48,12 +48,17 @@
return reply.readInt32();
}
- virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName) {
+ virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName,
+ const std::optional<String16>& attributionTag, bool shouldCollectAsyncNotedOp,
+ const String16& message) {
Parcel data, reply;
data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
data.writeInt32(code);
data.writeInt32(uid);
data.writeString16(packageName);
+ data.writeString16(attributionTag);
+ data.writeInt32(shouldCollectAsyncNotedOp ? 1 : 0);
+ data.writeString16(message);
remote()->transact(NOTE_OPERATION_TRANSACTION, data, &reply);
// fail on exception
if (reply.readExceptionCode() != 0) return MODE_ERRORED;
@@ -61,14 +66,18 @@
}
virtual int32_t startOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
- const String16& packageName, bool startIfModeDefault) {
+ const String16& packageName, const std::optional<String16>& attributionTag,
+ bool startIfModeDefault, bool shouldCollectAsyncNotedOp, const String16& message) {
Parcel data, reply;
data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
data.writeStrongBinder(token);
data.writeInt32(code);
data.writeInt32(uid);
data.writeString16(packageName);
+ data.writeString16(attributionTag);
data.writeInt32(startIfModeDefault ? 1 : 0);
+ data.writeInt32(shouldCollectAsyncNotedOp ? 1 : 0);
+ data.writeString16(message);
remote()->transact(START_OPERATION_TRANSACTION, data, &reply);
// fail on exception
if (reply.readExceptionCode() != 0) return MODE_ERRORED;
@@ -76,13 +85,14 @@
}
virtual void finishOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
- const String16& packageName) {
+ const String16& packageName, const std::optional<String16>& attributionTag) {
Parcel data, reply;
data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
data.writeStrongBinder(token);
data.writeInt32(code);
data.writeInt32(uid);
data.writeString16(packageName);
+ data.writeString16(attributionTag);
remote()->transact(FINISH_OPERATION_TRANSACTION, data, &reply);
}
@@ -103,17 +113,6 @@
remote()->transact(STOP_WATCHING_MODE_TRANSACTION, data, &reply);
}
- virtual sp<IBinder> getToken(const sp<IBinder>& clientToken) {
- Parcel data, reply;
- data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
- data.writeStrongBinder(clientToken);
- remote()->transact(GET_TOKEN_TRANSACTION, data, &reply);
- // fail on exception
- if (reply.readExceptionCode() != 0) return nullptr;
- return reply.readStrongBinder();
- }
-
-
virtual int32_t permissionToOpCode(const String16& permission) {
Parcel data, reply;
data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
@@ -139,6 +138,25 @@
}
return reply.readInt32();
}
+
+ virtual void setCameraAudioRestriction(int32_t mode) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
+ data.writeInt32(mode);
+ remote()->transact(SET_CAMERA_AUDIO_RESTRICTION_TRANSACTION, data, &reply);
+ }
+
+ virtual bool shouldCollectNotes(int32_t opCode) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
+ data.writeInt32(opCode);
+ remote()->transact(SHOULD_COLLECT_NOTES_TRANSACTION, data, &reply);
+ // fail on exception
+ if (reply.readExceptionCode() != 0) {
+ return false;
+ }
+ return reply.readBool();
+ }
};
IMPLEMENT_META_INTERFACE(AppOpsService, "com.android.internal.app.IAppOpsService");
@@ -166,7 +184,12 @@
int32_t code = data.readInt32();
int32_t uid = data.readInt32();
String16 packageName = data.readString16();
- int32_t res = noteOperation(code, uid, packageName);
+ std::optional<String16> attributionTag;
+ data.readString16(&attributionTag);
+ bool shouldCollectAsyncNotedOp = data.readInt32() == 1;
+ String16 message = data.readString16();
+ int32_t res = noteOperation(code, uid, packageName, attributionTag,
+ shouldCollectAsyncNotedOp, message);
reply->writeNoException();
reply->writeInt32(res);
return NO_ERROR;
@@ -177,8 +200,13 @@
int32_t code = data.readInt32();
int32_t uid = data.readInt32();
String16 packageName = data.readString16();
+ std::optional<String16> attributionTag;
+ data.readString16(&attributionTag);
bool startIfModeDefault = data.readInt32() == 1;
- int32_t res = startOperation(token, code, uid, packageName, startIfModeDefault);
+ bool shouldCollectAsyncNotedOp = data.readInt32() == 1;
+ String16 message = data.readString16();
+ int32_t res = startOperation(token, code, uid, packageName, attributionTag,
+ startIfModeDefault, shouldCollectAsyncNotedOp, message);
reply->writeNoException();
reply->writeInt32(res);
return NO_ERROR;
@@ -189,7 +217,9 @@
int32_t code = data.readInt32();
int32_t uid = data.readInt32();
String16 packageName = data.readString16();
- finishOperation(token, code, uid, packageName);
+ std::optional<String16> attributionTag;
+ data.readString16(&attributionTag);
+ finishOperation(token, code, uid, packageName, attributionTag);
reply->writeNoException();
return NO_ERROR;
} break;
@@ -209,14 +239,6 @@
reply->writeNoException();
return NO_ERROR;
} break;
- case GET_TOKEN_TRANSACTION: {
- CHECK_INTERFACE(IAppOpsService, data, reply);
- sp<IBinder> clientToken = data.readStrongBinder();
- sp<IBinder> token = getToken(clientToken);
- reply->writeNoException();
- reply->writeStrongBinder(token);
- return NO_ERROR;
- } break;
case PERMISSION_TO_OP_CODE_TRANSACTION: {
CHECK_INTERFACE(IAppOpsService, data, reply);
String16 permission = data.readString16();
@@ -236,6 +258,21 @@
reply->writeInt32(res);
return NO_ERROR;
} break;
+ case SET_CAMERA_AUDIO_RESTRICTION_TRANSACTION: {
+ CHECK_INTERFACE(IAppOpsService, data, reply);
+ const int32_t mode = data.readInt32();
+ setCameraAudioRestriction(mode);
+ reply->writeNoException();
+ return NO_ERROR;
+ } break;
+ case SHOULD_COLLECT_NOTES_TRANSACTION: {
+ CHECK_INTERFACE(IAppOpsService, data, reply);
+ int32_t opCode = data.readInt32();
+ bool shouldCollect = shouldCollectNotes(opCode);
+ reply->writeNoException();
+ reply->writeBool(shouldCollect);
+ return NO_ERROR;
+ } break;
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/binder/IMemory.cpp b/libs/binder/IMemory.cpp
index 84805ff..d8b44f9 100644
--- a/libs/binder/IMemory.cpp
+++ b/libs/binder/IMemory.cpp
@@ -150,10 +150,6 @@
}
void* IMemory::unsecurePointer() const {
- return pointer();
-}
-
-void* IMemory::pointer() const {
ssize_t offset;
sp<IMemoryHeap> heap = getMemory(&offset);
void* const base = heap!=nullptr ? heap->base() : MAP_FAILED;
@@ -162,6 +158,8 @@
return static_cast<char*>(base) + offset;
}
+void* IMemory::pointer() const { return unsecurePointer(); }
+
size_t IMemory::size() const {
size_t size;
getMemory(nullptr, &size);
diff --git a/libs/binder/IUidObserver.cpp b/libs/binder/IUidObserver.cpp
index 038e6bf..b21af96 100644
--- a/libs/binder/IUidObserver.cpp
+++ b/libs/binder/IUidObserver.cpp
@@ -56,13 +56,15 @@
remote()->transact(ON_UID_IDLE_TRANSACTION, data, &reply, IBinder::FLAG_ONEWAY);
}
- virtual void onUidStateChanged(uid_t uid, int32_t procState, int64_t procStateSeq)
+ virtual void onUidStateChanged(uid_t uid, int32_t procState, int64_t procStateSeq,
+ int32_t capability)
{
Parcel data, reply;
data.writeInterfaceToken(IUidObserver::getInterfaceDescriptor());
data.writeInt32((int32_t) uid);
data.writeInt32(procState);
data.writeInt64(procStateSeq);
+ data.writeInt32(capability);
remote()->transact(ON_UID_STATE_CHANGED_TRANSACTION, data, &reply, IBinder::FLAG_ONEWAY);
}
};
@@ -104,7 +106,8 @@
uid_t uid = data.readInt32();
int32_t procState = data.readInt32();
int64_t procStateSeq = data.readInt64();
- onUidStateChanged(uid, procState, procStateSeq);
+ int32_t capability = data.readInt32();
+ onUidStateChanged(uid, procState, procStateSeq, capability);
return NO_ERROR;
} break;
default:
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 333b95b..19f3606 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -1063,12 +1063,22 @@
status_t Parcel::writeString8(const String8& str)
{
- status_t err = writeInt32(str.bytes());
- // only write string if its length is more than zero characters,
- // as readString8 will only read if the length field is non-zero.
- // this is slightly different from how writeString16 works.
- if (str.bytes() > 0 && err == NO_ERROR) {
- err = write(str.string(), str.bytes()+1);
+ return writeString8(str.string(), str.size());
+}
+
+status_t Parcel::writeString8(const char* str, size_t len)
+{
+ if (str == nullptr) return writeInt32(-1);
+
+ status_t err = writeInt32(len);
+ if (err == NO_ERROR) {
+ uint8_t* data = (uint8_t*)writeInplace(len+sizeof(char));
+ if (data) {
+ memcpy(data, str, len);
+ *reinterpret_cast<char*>(data+len) = 0;
+ return NO_ERROR;
+ }
+ err = mError;
}
return err;
}
@@ -2023,37 +2033,39 @@
String8 Parcel::readString8() const
{
- String8 retString;
- status_t status = readString8(&retString);
- if (status != OK) {
- // We don't care about errors here, so just return an empty string.
- return String8();
- }
- return retString;
+ size_t len;
+ const char* str = readString8Inplace(&len);
+ if (str) return String8(str, len);
+ ALOGE("Reading a NULL string not supported here.");
+ return String8();
}
status_t Parcel::readString8(String8* pArg) const
{
- int32_t size;
- status_t status = readInt32(&size);
- if (status != OK) {
- return status;
- }
- // watch for potential int overflow from size+1
- if (size < 0 || size >= INT32_MAX) {
- return BAD_VALUE;
- }
- // |writeString8| writes nothing for empty string.
- if (size == 0) {
+ size_t len;
+ const char* str = readString8Inplace(&len);
+ if (str) {
+ pArg->setTo(str, len);
+ return 0;
+ } else {
*pArg = String8();
- return OK;
+ return UNEXPECTED_NULL;
}
- const char* str = (const char*)readInplace(size + 1);
- if (str == nullptr) {
- return BAD_VALUE;
+}
+
+const char* Parcel::readString8Inplace(size_t* outLen) const
+{
+ int32_t size = readInt32();
+ // watch for potential int overflow from size+1
+ if (size >= 0 && size < INT32_MAX) {
+ *outLen = size;
+ const char* str = (const char*)readInplace(size+1);
+ if (str != nullptr) {
+ return str;
+ }
}
- pArg->setTo(str, size);
- return OK;
+ *outLen = 0;
+ return nullptr;
}
String16 Parcel::readString16() const
@@ -2515,6 +2527,22 @@
mObjectsSize = 0;
break;
}
+ const flat_binder_object* flat
+ = reinterpret_cast<const flat_binder_object*>(mData + offset);
+ uint32_t type = flat->hdr.type;
+ if (!(type == BINDER_TYPE_BINDER || type == BINDER_TYPE_HANDLE ||
+ type == BINDER_TYPE_FD)) {
+ // We should never receive other types (eg BINDER_TYPE_FDA) as long as we don't support
+ // them in libbinder. If we do receive them, it probably means a kernel bug; try to
+ // recover gracefully by clearing out the objects, and releasing the objects we do
+ // know about.
+ android_errorWriteLog(0x534e4554, "135930648");
+ ALOGE("%s: unsupported type object (%" PRIu32 ") at offset %" PRIu64 "\n",
+ __func__, type, (uint64_t)offset);
+ releaseObjects();
+ mObjectsSize = 0;
+ break;
+ }
minOffset = offset + sizeof(flat_binder_object);
}
scanForFds();
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 4f81836..ef93ba1 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -421,7 +421,9 @@
}
}
+#ifdef __ANDROID__
LOG_ALWAYS_FATAL_IF(mDriverFD < 0, "Binder driver '%s' could not be opened. Terminating.", driver);
+#endif
}
ProcessState::~ProcessState()
diff --git a/libs/gui/tests/DummyConsumer.h b/libs/binder/aidl/android/content/pm/IPackageChangeObserver.aidl
similarity index 62%
copy from libs/gui/tests/DummyConsumer.h
copy to libs/binder/aidl/android/content/pm/IPackageChangeObserver.aidl
index 502bdf9..6929a6c 100644
--- a/libs/gui/tests/DummyConsumer.h
+++ b/libs/binder/aidl/android/content/pm/IPackageChangeObserver.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 The Android Open Source Project
+ * Copyright (C) 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.
@@ -14,14 +14,15 @@
* limitations under the License.
*/
-#include <gui/IConsumerListener.h>
+package android.content.pm;
-namespace android {
+import android.content.pm.PackageChangeEvent;
-struct DummyConsumer : public BnConsumerListener {
- void onFrameAvailable(const BufferItem& /* item */) override {}
- void onBuffersReleased() override {}
- void onSidebandStreamChanged() override {}
-};
-
-} // namespace android
+/**
+ * This is a non-blocking notification when a package has changed.
+ *
+ * @hide
+ */
+oneway interface IPackageChangeObserver {
+ void onPackageChanged(in PackageChangeEvent event);
+}
diff --git a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
index a7a7292..dc8d74c 100644
--- a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
+++ b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
@@ -17,6 +17,8 @@
package android.content.pm;
+import android.content.pm.IPackageChangeObserver;
+
/**
* Parallel implementation of certain {@link PackageManager} APIs that need to
* be exposed to native code.
@@ -87,4 +89,16 @@
* package.
*/
@utf8InCpp String getModuleMetadataPackageName();
+
+ /* Returns the names of all packages. */
+ @utf8InCpp String[] getAllPackages();
+
+ /** Register an extra package change observer to receive the multi-cast. */
+ void registerPackageChangeObserver(in IPackageChangeObserver observer);
+
+ /**
+ * Unregister an existing package change observer.
+ * This does nothing if this observer was not already registered.
+ */
+ void unregisterPackageChangeObserver(in IPackageChangeObserver observer);
}
diff --git a/libs/binder/aidl/android/content/pm/PackageChangeEvent.aidl b/libs/binder/aidl/android/content/pm/PackageChangeEvent.aidl
new file mode 100644
index 0000000..e30e907
--- /dev/null
+++ b/libs/binder/aidl/android/content/pm/PackageChangeEvent.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.content.pm;
+
+/**
+ * This event is designed for notification to native code listener about
+ * any changes on a package including update, deletion and etc.
+ *
+ * @hide
+ */
+parcelable PackageChangeEvent {
+ @utf8InCpp String packageName;
+ long version;
+ long lastUpdateTimeMillis;
+ boolean newInstalled;
+ boolean dataRemoved;
+ boolean isDeleted;
+}
diff --git a/libs/binder/fuzzer/Android.bp b/libs/binder/fuzzer/Android.bp
new file mode 100644
index 0000000..d2b4d52
--- /dev/null
+++ b/libs/binder/fuzzer/Android.bp
@@ -0,0 +1,47 @@
+cc_fuzz {
+ name: "binder_parcel_fuzzer",
+ defaults: ["libbinder_ndk_host_user"],
+ host_supported: true,
+
+ fuzz_config: {
+ cc: ["smoreland@google.com"],
+ },
+
+ srcs: [
+ "binder.cpp",
+ "binder_ndk.cpp",
+ "hwbinder.cpp",
+ "main.cpp",
+ "util.cpp",
+ ],
+ static_libs: [
+ "libbase",
+ "libcgrouprc",
+ "libcgrouprc_format",
+ "libcutils",
+ "libhidlbase",
+ "liblog",
+ "libprocessgroup",
+ "libjsoncpp",
+ "libutils",
+ ],
+
+ target: {
+ android: {
+ shared_libs: [
+ "libbinder_ndk",
+ "libbinder",
+ ],
+ },
+ host: {
+ static_libs: [
+ "libbinder_ndk",
+ "libbinder",
+ ],
+ },
+ },
+ // This flag enables verbose output in the fuzz target, and is very useful
+ // for debugging a failure. If you are trying to diagnose how a crash was
+ // produced, you may find uncommenting the below line very useful.
+ // cflags: ["-DENABLE_LOG_FUZZ"],
+}
diff --git a/libs/binder/fuzzer/binder.cpp b/libs/binder/fuzzer/binder.cpp
new file mode 100644
index 0000000..52c730c
--- /dev/null
+++ b/libs/binder/fuzzer/binder.cpp
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2019 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 FUZZ_LOG_TAG "binder"
+
+#include "binder.h"
+#include "util.h"
+
+#include <android/os/IServiceManager.h>
+
+using ::android::status_t;
+
+enum ByteEnum : int8_t {};
+enum IntEnum : int32_t {};
+enum LongEnum : int64_t {};
+
+class ExampleParcelable : public android::Parcelable {
+public:
+ status_t writeToParcel(android::Parcel* /*parcel*/) const override {
+ FUZZ_LOG() << "should not reach";
+ abort();
+ }
+ status_t readFromParcel(const android::Parcel* parcel) override {
+ mExampleExtraField++;
+ return parcel->readInt64(&(this->mExampleUsedData));
+ }
+private:
+ int64_t mExampleExtraField = 0;
+ int64_t mExampleUsedData = 0;
+};
+
+struct ExampleFlattenable : public android::Flattenable<ExampleFlattenable> {
+public:
+ size_t getFlattenedSize() const { return sizeof(mValue); }
+ size_t getFdCount() const { return 0; }
+ status_t flatten(void*& /*buffer*/, size_t& /*size*/, int*& /*fds*/, size_t& /*count*/) const {
+ FUZZ_LOG() << "should not reach";
+ abort();
+ }
+ status_t unflatten(void const*& buffer, size_t& size, int const*& /*fds*/, size_t& /*count*/) {
+ if (size < sizeof(mValue)) {
+ return android::NO_MEMORY;
+ }
+ android::FlattenableUtils::read(buffer, size, mValue);
+ return android::OK;
+ }
+private:
+ int32_t mValue = 0xFEEDBEEF;
+};
+
+struct ExampleLightFlattenable : public android::LightFlattenablePod<ExampleLightFlattenable> {
+ int32_t mValue = 0;
+};
+
+#define PARCEL_READ_WITH_STATUS(T, FUN) \
+ [] (const ::android::Parcel& p, uint8_t /*data*/) {\
+ FUZZ_LOG() << "about to read " #T " using " #FUN " with status";\
+ T t{};\
+ status_t status = p.FUN(&t);\
+ FUZZ_LOG() << #T " status: " << status /* << " value: " << t*/;\
+ }
+
+#define PARCEL_READ_NO_STATUS(T, FUN) \
+ [] (const ::android::Parcel& p, uint8_t /*data*/) {\
+ FUZZ_LOG() << "about to read " #T " using " #FUN " with no status";\
+ T t = p.FUN();\
+ (void) t;\
+ FUZZ_LOG() << #T " done " /* << " value: " << t*/;\
+ }
+
+#define PARCEL_READ_OPT_STATUS(T, FUN) \
+ PARCEL_READ_WITH_STATUS(T, FUN), \
+ PARCEL_READ_NO_STATUS(T, FUN)
+
+// clang-format off
+std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS {
+ PARCEL_READ_NO_STATUS(size_t, dataSize),
+ PARCEL_READ_NO_STATUS(size_t, dataAvail),
+ PARCEL_READ_NO_STATUS(size_t, dataPosition),
+ PARCEL_READ_NO_STATUS(size_t, dataCapacity),
+ [] (const ::android::Parcel& p, uint8_t pos) {
+ FUZZ_LOG() << "about to setDataPosition: " << pos;
+ p.setDataPosition(pos);
+ FUZZ_LOG() << "setDataPosition done";
+ },
+ PARCEL_READ_NO_STATUS(size_t, allowFds),
+ PARCEL_READ_NO_STATUS(size_t, hasFileDescriptors),
+ [] (const ::android::Parcel& p, uint8_t len) {
+ std::string interface(len, 'a');
+ FUZZ_LOG() << "about to enforceInterface: " << interface;
+ bool b = p.enforceInterface(::android::String16(interface.c_str()));
+ FUZZ_LOG() << "enforced interface: " << b;
+ },
+ [] (const ::android::Parcel& p, uint8_t /*len*/) {
+ FUZZ_LOG() << "about to checkInterface";
+ android::sp<android::IBinder> aBinder = new android::BBinder();
+ bool b = p.checkInterface(aBinder.get());
+ FUZZ_LOG() << "checked interface: " << b;
+ },
+ PARCEL_READ_NO_STATUS(size_t, objectsCount),
+ PARCEL_READ_NO_STATUS(status_t, errorCheck),
+ [] (const ::android::Parcel& p, uint8_t len) {
+ FUZZ_LOG() << "about to read void*";
+ std::vector<uint8_t> data(len);
+ status_t status = p.read(data.data(), len);
+ FUZZ_LOG() << "read status: " << status;
+ },
+ [] (const ::android::Parcel& p, uint8_t len) {
+ FUZZ_LOG() << "about to readInplace";
+ const void* r = p.readInplace(len);
+ FUZZ_LOG() << "readInplace done. pointer: " << r << " bytes: " << hexString(r, len);
+ },
+ PARCEL_READ_OPT_STATUS(int32_t, readInt32),
+ PARCEL_READ_OPT_STATUS(uint32_t, readUint32),
+ PARCEL_READ_OPT_STATUS(int64_t, readInt64),
+ PARCEL_READ_OPT_STATUS(uint64_t, readUint64),
+ PARCEL_READ_OPT_STATUS(float, readFloat),
+ PARCEL_READ_OPT_STATUS(double, readDouble),
+ PARCEL_READ_OPT_STATUS(intptr_t, readIntPtr),
+ PARCEL_READ_OPT_STATUS(bool, readBool),
+ PARCEL_READ_OPT_STATUS(char16_t, readChar),
+ PARCEL_READ_OPT_STATUS(int8_t, readByte),
+
+ PARCEL_READ_WITH_STATUS(std::string, readUtf8FromUtf16),
+ PARCEL_READ_WITH_STATUS(std::unique_ptr<std::string>, readUtf8FromUtf16),
+ [] (const ::android::Parcel& p, uint8_t /*data*/) {
+ FUZZ_LOG() << "about to read c-str";
+ const char* str = p.readCString();
+ FUZZ_LOG() << "read c-str: " << (str ? str : "<empty string>");
+ },
+ PARCEL_READ_OPT_STATUS(android::String8, readString8),
+ PARCEL_READ_OPT_STATUS(android::String16, readString16),
+ PARCEL_READ_WITH_STATUS(std::unique_ptr<android::String16>, readString16),
+ [] (const ::android::Parcel& p, uint8_t /*data*/) {
+ FUZZ_LOG() << "about to readString16Inplace";
+ size_t outLen = 0;
+ const char16_t* str = p.readString16Inplace(&outLen);
+ FUZZ_LOG() << "readString16Inplace: " << hexString(str, sizeof(char16_t) * outLen)
+ << " size: " << outLen;
+ },
+ PARCEL_READ_WITH_STATUS(android::sp<android::IBinder>, readStrongBinder),
+ PARCEL_READ_WITH_STATUS(android::sp<android::IBinder>, readNullableStrongBinder),
+
+ // TODO(b/131868573): can force read of arbitrarily sized vector
+ // PARCEL_READ_WITH_STATUS(std::vector<ByteEnum>, readEnumVector),
+ // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<ByteEnum>>, readEnumVector),
+ // PARCEL_READ_WITH_STATUS(std::vector<IntEnum>, readEnumVector),
+ // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<IntEnum>>, readEnumVector),
+ // PARCEL_READ_WITH_STATUS(std::vector<LongEnum>, readEnumVector),
+ // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<LongEnum>>, readEnumVector),
+
+ // only reading one parcelable type for now
+ // TODO(b/131868573): can force read of arbitrarily sized vector
+ // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<std::unique_ptr<ExampleParcelable>>>, readParcelableVector),
+ // PARCEL_READ_WITH_STATUS(std::vector<ExampleParcelable>, readParcelableVector),
+ PARCEL_READ_WITH_STATUS(ExampleParcelable, readParcelable),
+ PARCEL_READ_WITH_STATUS(std::unique_ptr<ExampleParcelable>, readParcelable),
+
+ // only reading one binder type for now
+ PARCEL_READ_WITH_STATUS(android::sp<android::os::IServiceManager>, readStrongBinder),
+ PARCEL_READ_WITH_STATUS(android::sp<android::os::IServiceManager>, readNullableStrongBinder),
+
+ // TODO(b/131868573): can force read of arbitrarily sized vector
+ // PARCEL_READ_WITH_STATUS(::std::unique_ptr<std::vector<android::sp<android::IBinder>>>, readStrongBinderVector),
+ // PARCEL_READ_WITH_STATUS(std::vector<android::sp<android::IBinder>>, readStrongBinderVector),
+
+ // TODO(b/131868573): can force read of arbitrarily sized vector
+ // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<int8_t>>, readByteVector),
+ // PARCEL_READ_WITH_STATUS(std::vector<int8_t>, readByteVector),
+ // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<uint8_t>>, readByteVector),
+ // PARCEL_READ_WITH_STATUS(std::vector<uint8_t>, readByteVector),
+ // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<int32_t>>, readInt32Vector),
+ // PARCEL_READ_WITH_STATUS(std::vector<int32_t>, readInt32Vector),
+ // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<int64_t>>, readInt64Vector),
+ // PARCEL_READ_WITH_STATUS(std::vector<int64_t>, readInt64Vector),
+ // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<uint64_t>>, readUint64Vector),
+ // PARCEL_READ_WITH_STATUS(std::vector<uint64_t>, readUint64Vector),
+ // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<float>>, readFloatVector),
+ // PARCEL_READ_WITH_STATUS(std::vector<float>, readFloatVector),
+ // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<double>>, readDoubleVector),
+ // PARCEL_READ_WITH_STATUS(std::vector<double>, readDoubleVector),
+ // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<bool>>, readBoolVector),
+ // PARCEL_READ_WITH_STATUS(std::vector<bool>, readBoolVector),
+ // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<char16_t>>, readCharVector),
+ // PARCEL_READ_WITH_STATUS(std::vector<char16_t>, readCharVector),
+ // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<std::unique_ptr<android::String16>>>, readString16Vector),
+ // PARCEL_READ_WITH_STATUS(std::vector<android::String16>, readString16Vector),
+ // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<std::unique_ptr<std::string>>>, readUtf8VectorFromUtf16Vector),
+ // PARCEL_READ_WITH_STATUS(std::vector<std::string>, readUtf8VectorFromUtf16Vector),
+
+ [] (const android::Parcel& p, uint8_t /*len*/) {
+ FUZZ_LOG() << "about to read flattenable";
+ ExampleFlattenable f;
+ status_t status = p.read(f);
+ FUZZ_LOG() << "read flattenable: " << status;
+ },
+ [] (const android::Parcel& p, uint8_t /*len*/) {
+ FUZZ_LOG() << "about to read lite flattenable";
+ ExampleLightFlattenable f;
+ status_t status = p.read(f);
+ FUZZ_LOG() << "read lite flattenable: " << status;
+ },
+
+ // TODO(b/131868573): can force read of arbitrarily sized vector
+ // TODO: resizeOutVector
+
+ PARCEL_READ_NO_STATUS(int32_t, readExceptionCode),
+ [] (const android::Parcel& p, uint8_t /*len*/) {
+ FUZZ_LOG() << "about to readNativeHandle";
+ native_handle_t* t = p.readNativeHandle();
+ FUZZ_LOG() << "readNativeHandle: " << t;
+ if (t != nullptr) {
+ FUZZ_LOG() << "about to free readNativeHandle";
+ native_handle_close(t);
+ native_handle_delete(t);
+ FUZZ_LOG() << "readNativeHandle freed";
+ }
+ },
+ PARCEL_READ_NO_STATUS(int, readFileDescriptor),
+ PARCEL_READ_NO_STATUS(int, readParcelFileDescriptor),
+ PARCEL_READ_WITH_STATUS(android::base::unique_fd, readUniqueFileDescriptor),
+
+ // TODO(b/131868573): can force read of arbitrarily sized vector
+ // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<android::base::unique_fd>>, readUniqueFileDescriptorVector),
+ // PARCEL_READ_WITH_STATUS(std::vector<android::base::unique_fd>, readUniqueFileDescriptorVector),
+
+ [] (const android::Parcel& p, uint8_t len) {
+ FUZZ_LOG() << "about to readBlob";
+ ::android::Parcel::ReadableBlob blob;
+ status_t status = p.readBlob(len, &blob);
+ FUZZ_LOG() << "readBlob status: " << status;
+ },
+ [] (const android::Parcel& p, uint8_t options) {
+ FUZZ_LOG() << "about to readObject";
+ bool nullMetaData = options & 0x1;
+ const void* obj = static_cast<const void*>(p.readObject(nullMetaData));
+ FUZZ_LOG() << "readObject: " << obj;
+ },
+ PARCEL_READ_NO_STATUS(uid_t, readCallingWorkSourceUid),
+ PARCEL_READ_NO_STATUS(size_t, getBlobAshmemSize),
+ PARCEL_READ_NO_STATUS(size_t, getOpenAshmemSize),
+};
+// clang-format on
diff --git a/libs/gui/tests/DummyConsumer.h b/libs/binder/fuzzer/binder.h
similarity index 62%
copy from libs/gui/tests/DummyConsumer.h
copy to libs/binder/fuzzer/binder.h
index 502bdf9..b224ef4 100644
--- a/libs/gui/tests/DummyConsumer.h
+++ b/libs/binder/fuzzer/binder.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 The Android Open Source Project
+ * Copyright (C) 2019 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.
@@ -14,14 +14,9 @@
* limitations under the License.
*/
-#include <gui/IConsumerListener.h>
+#include <binder/Parcel.h>
+#include <vector>
-namespace android {
+#include "parcel_fuzzer.h"
-struct DummyConsumer : public BnConsumerListener {
- void onFrameAvailable(const BufferItem& /* item */) override {}
- void onBuffersReleased() override {}
- void onSidebandStreamChanged() override {}
-};
-
-} // namespace android
+extern std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS;
diff --git a/libs/binder/fuzzer/binder_ndk.cpp b/libs/binder/fuzzer/binder_ndk.cpp
new file mode 100644
index 0000000..29da8f7
--- /dev/null
+++ b/libs/binder/fuzzer/binder_ndk.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2019 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 FUZZ_LOG_TAG "binder_ndk"
+
+#include "binder_ndk.h"
+
+#include <android/binder_parcel_utils.h>
+
+#include "util.h"
+
+// TODO(b/142061461): parent class
+class SomeParcelable {
+public:
+ binder_status_t readFromParcel(const AParcel* parcel) {
+ return AParcel_readInt32(parcel, &mValue);
+ }
+
+private:
+ int32_t mValue = 0;
+};
+
+#define PARCEL_READ(T, FUN) \
+ [](const NdkParcelAdapter& p, uint8_t /*data*/) { \
+ FUZZ_LOG() << "about to read " #T " using " #FUN " with status"; \
+ T t{}; \
+ binder_status_t status = FUN(p.aParcel(), &t); \
+ FUZZ_LOG() << #T " status: " << status /* << " value: " << t*/; \
+ }
+
+// clang-format off
+std::vector<ParcelRead<NdkParcelAdapter>> BINDER_NDK_PARCEL_READ_FUNCTIONS{
+ // methods from binder_parcel.h
+ [](const NdkParcelAdapter& p, uint8_t pos) {
+ FUZZ_LOG() << "about to set data position to " << pos;
+ binder_status_t status = AParcel_setDataPosition(p.aParcel(), pos);
+ FUZZ_LOG() << "set data position: " << status;
+ },
+ [](const NdkParcelAdapter& p, uint8_t /*data*/) {
+ FUZZ_LOG() << "about to read status header";
+ ndk::ScopedAStatus t;
+ binder_status_t status = AParcel_readStatusHeader(p.aParcel(), t.getR());
+ FUZZ_LOG() << "read status header: " << status;
+ },
+ PARCEL_READ(int32_t, AParcel_readInt32),
+ PARCEL_READ(uint32_t, AParcel_readUint32),
+ PARCEL_READ(int64_t, AParcel_readInt64),
+ PARCEL_READ(uint64_t, AParcel_readUint64),
+ PARCEL_READ(float, AParcel_readFloat),
+ PARCEL_READ(double, AParcel_readDouble),
+ PARCEL_READ(bool, AParcel_readBool),
+ PARCEL_READ(char16_t, AParcel_readChar),
+ PARCEL_READ(int8_t, AParcel_readByte),
+
+ // methods from binder_parcel_utils.h
+ PARCEL_READ(ndk::SpAIBinder, ndk::AParcel_readNullableStrongBinder),
+ PARCEL_READ(ndk::SpAIBinder, ndk::AParcel_readRequiredStrongBinder),
+ PARCEL_READ(ndk::ScopedFileDescriptor, ndk::AParcel_readNullableParcelFileDescriptor),
+ PARCEL_READ(ndk::ScopedFileDescriptor, ndk::AParcel_readRequiredParcelFileDescriptor),
+ PARCEL_READ(std::string, ndk::AParcel_readString),
+ PARCEL_READ(std::optional<std::string>, ndk::AParcel_readString),
+ // TODO(b/131868573): can force process to allocate arbitrary amount of
+ // memory
+ // PARCEL_READ(std::vector<std::string>, ndk::AParcel_readVector),
+ // PARCEL_READ(std::optional<std::vector<std::optional<std::string>>>,
+ // ndk::AParcel_readVector), PARCEL_READ(std::vector<SomeParcelable>,
+ // ndk::AParcel_readVector), PARCEL_READ(std::vector<int32_t>, ndk::AParcel_readVector),
+ // PARCEL_READ(std::optional<std::vector<int32_t>>, ndk::AParcel_readVector),
+ // PARCEL_READ(std::vector<uint32_t>, ndk::AParcel_readVector),
+ // PARCEL_READ(std::optional<std::vector<uint32_t>>, ndk::AParcel_readVector),
+ // PARCEL_READ(std::vector<int64_t>, ndk::AParcel_readVector),
+ // PARCEL_READ(std::optional<std::vector<int64_t>>, ndk::AParcel_readVector),
+ // PARCEL_READ(std::vector<uint64_t>, ndk::AParcel_readVector),
+ // PARCEL_READ(std::optional<std::vector<uint64_t>>, ndk::AParcel_readVector),
+ // PARCEL_READ(std::vector<float>, ndk::AParcel_readVector),
+ // PARCEL_READ(std::optional<std::vector<float>>, ndk::AParcel_readVector),
+ // PARCEL_READ(std::vector<double>, ndk::AParcel_readVector),
+ // PARCEL_READ(std::optional<std::vector<double>>, ndk::AParcel_readVector),
+ // PARCEL_READ(std::vector<bool>, ndk::AParcel_readVector),
+ // PARCEL_READ(std::optional<std::vector<bool>>, ndk::AParcel_readVector),
+ // PARCEL_READ(std::vector<char16_t>, ndk::AParcel_readVector),
+ // PARCEL_READ(std::optional<std::vector<char16_t>>, ndk::AParcel_readVector),
+ // PARCEL_READ(std::vector<int32_t>, ndk::AParcel_resizeVector),
+ // PARCEL_READ(std::optional<std::vector<int32_t>>, ndk::AParcel_resizeVector),
+};
+// clang-format on
diff --git a/libs/binder/fuzzer/binder_ndk.h b/libs/binder/fuzzer/binder_ndk.h
new file mode 100644
index 0000000..622cafc
--- /dev/null
+++ b/libs/binder/fuzzer/binder_ndk.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2019 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 <android/binder_auto_utils.h>
+#include <vector>
+
+#include <android/binder_parcel.h>
+#include "parcel_fuzzer.h"
+
+// libbinder_ndk doesn't export this header which breaks down its API for NDK
+// and APEX users, but we need access to it to fuzz.
+#include "../ndk/parcel_internal.h"
+
+class NdkParcelAdapter {
+public:
+ NdkParcelAdapter() : mParcel(new AParcel(nullptr /*binder*/)) {}
+
+ const AParcel* aParcel() const { return mParcel.get(); }
+ AParcel* aParcel() { return mParcel.get(); }
+
+ size_t dataSize() const { return aParcel()->get()->dataSize(); }
+ size_t dataAvail() const { return aParcel()->get()->dataAvail(); }
+ size_t dataPosition() const { return aParcel()->get()->dataPosition(); }
+ size_t dataCapacity() const { return aParcel()->get()->dataCapacity(); }
+ android::status_t setData(const uint8_t* buffer, size_t len) {
+ return aParcel()->get()->setData(buffer, len);
+ }
+
+private:
+ ndk::ScopedAParcel mParcel;
+};
+
+extern std::vector<ParcelRead<NdkParcelAdapter>> BINDER_NDK_PARCEL_READ_FUNCTIONS;
diff --git a/libs/binder/fuzzer/hwbinder.cpp b/libs/binder/fuzzer/hwbinder.cpp
new file mode 100644
index 0000000..0fec393
--- /dev/null
+++ b/libs/binder/fuzzer/hwbinder.cpp
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2019 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 FUZZ_LOG_TAG "hwbinder"
+
+#include "hwbinder.h"
+#include "util.h"
+
+#include <android-base/logging.h>
+#include <hwbinder/Parcel.h>
+
+using ::android::status_t;
+
+// TODO: support scatter-gather types
+
+std::ostream& operator<<(std::ostream& os, const ::android::sp<::android::hardware::IBinder>& binder) {
+ os << binder.get();
+ return os;
+}
+
+#define PARCEL_READ_OPT_STATUS(T, FUN) \
+ PARCEL_READ_NO_STATUS(T, FUN), PARCEL_READ_WITH_STATUS(T, FUN)
+
+#define PARCEL_READ_NO_STATUS(T, FUN) \
+ [] (const ::android::hardware::Parcel& p, uint8_t /*data*/) {\
+ FUZZ_LOG() << "about to read " #T " using " #FUN " with no status";\
+ T t = p.FUN();\
+ FUZZ_LOG() << #T " value: " << t;\
+ }
+
+#define PARCEL_READ_WITH_STATUS(T, FUN) \
+ [] (const ::android::hardware::Parcel& p, uint8_t /*data*/) {\
+ FUZZ_LOG() << "about to read " #T " using " #FUN " with status";\
+ T t;\
+ status_t status = p.FUN(&t);\
+ FUZZ_LOG() << #T " status: " << status << " value: " << t;\
+ }
+
+// clang-format off
+std::vector<ParcelRead<::android::hardware::Parcel>> HWBINDER_PARCEL_READ_FUNCTIONS {
+ PARCEL_READ_NO_STATUS(size_t, dataSize),
+ PARCEL_READ_NO_STATUS(size_t, dataAvail),
+ PARCEL_READ_NO_STATUS(size_t, dataPosition),
+ PARCEL_READ_NO_STATUS(size_t, dataCapacity),
+ [] (const ::android::hardware::Parcel& p, uint8_t pos) {
+ FUZZ_LOG() << "about to setDataPosition: " << pos;
+ p.setDataPosition(pos);
+ FUZZ_LOG() << "setDataPosition done";
+ },
+ [] (const ::android::hardware::Parcel& p, uint8_t length) {
+ FUZZ_LOG() << "about to enforceInterface";
+ std::string interfaceName(length, 'a');
+ bool okay = p.enforceInterface(interfaceName.c_str());
+ FUZZ_LOG() << "enforceInterface status: " << okay;
+ },
+ PARCEL_READ_NO_STATUS(size_t, objectsCount),
+ [] (const ::android::hardware::Parcel& p, uint8_t length) {
+ FUZZ_LOG() << "about to read";
+ std::vector<uint8_t> data (length);
+ status_t status = p.read(data.data(), length);
+ FUZZ_LOG() << "read status: " << status << " data: " << hexString(data.data(), data.size());
+ },
+ [] (const ::android::hardware::Parcel& p, uint8_t length) {
+ FUZZ_LOG() << "about to read";
+ std::vector<uint8_t> data (length);
+ const void* inplace = p.readInplace(length);
+ FUZZ_LOG() << "read status: " << hexString(inplace, length);
+ },
+ PARCEL_READ_WITH_STATUS(int8_t, readInt8),
+ PARCEL_READ_WITH_STATUS(uint8_t, readUint8),
+ PARCEL_READ_WITH_STATUS(int16_t, readInt16),
+ PARCEL_READ_WITH_STATUS(uint16_t, readUint16),
+ PARCEL_READ_OPT_STATUS(int32_t, readInt32),
+ PARCEL_READ_OPT_STATUS(uint32_t, readUint32),
+ PARCEL_READ_OPT_STATUS(int64_t, readInt64),
+ PARCEL_READ_OPT_STATUS(uint64_t, readUint64),
+ PARCEL_READ_OPT_STATUS(float, readFloat),
+ PARCEL_READ_OPT_STATUS(double, readDouble),
+ PARCEL_READ_OPT_STATUS(bool, readBool),
+ [] (const ::android::hardware::Parcel& p, uint8_t /*data*/) {
+ FUZZ_LOG() << "about to readCString";
+ const char* str = p.readCString();
+ FUZZ_LOG() << "readCString " << (str ? str : "<null>");
+ },
+ PARCEL_READ_OPT_STATUS(::android::String16, readString16),
+ PARCEL_READ_WITH_STATUS(std::unique_ptr<::android::String16>, readString16),
+ [] (const ::android::hardware::Parcel& p, uint8_t /*data*/) {
+ FUZZ_LOG() << "about to readString16Inplace";
+ size_t outSize = 0;
+ const char16_t* str = p.readString16Inplace(&outSize);
+ FUZZ_LOG() << "readString16Inplace: " << hexString(str, sizeof(char16_t) * outSize);
+ },
+ PARCEL_READ_OPT_STATUS(::android::sp<::android::hardware::IBinder>, readStrongBinder),
+ PARCEL_READ_WITH_STATUS(::android::sp<::android::hardware::IBinder>, readNullableStrongBinder),
+ [] (const ::android::hardware::Parcel& p, uint8_t size) {
+ FUZZ_LOG() << "about to readBuffer";
+ size_t handle = 0;
+ const void* data = nullptr;
+ status_t status = p.readBuffer(size, &handle, &data);
+ FUZZ_LOG() << "readBuffer status: " << status << " handle: " << handle << " data: " << data;
+
+ // should be null since we don't create any IPC objects
+ CHECK(data == nullptr) << data;
+ },
+ [] (const ::android::hardware::Parcel& p, uint8_t size) {
+ FUZZ_LOG() << "about to readNullableBuffer";
+ size_t handle = 0;
+ const void* data = nullptr;
+ status_t status = p.readNullableBuffer(size, &handle, &data);
+ FUZZ_LOG() << "readNullableBuffer status: " << status << " handle: " << handle << " data: " << data;
+
+ // should be null since we don't create any IPC objects
+ CHECK(data == nullptr) << data;
+ },
+ [] (const ::android::hardware::Parcel& p, uint8_t size) {
+ FUZZ_LOG() << "about to readEmbeddedBuffer";
+ size_t handle = 0;
+ size_t parent_buffer_handle = 0;
+ size_t parent_offset = 3;
+ const void* data = nullptr;
+ status_t status = p.readEmbeddedBuffer(size, &handle, parent_buffer_handle, parent_offset, &data);
+ FUZZ_LOG() << "readEmbeddedBuffer status: " << status << " handle: " << handle << " data: " << data;
+
+ // should be null since we don't create any IPC objects
+ CHECK(data == nullptr) << data;
+ },
+ [] (const ::android::hardware::Parcel& p, uint8_t size) {
+ FUZZ_LOG() << "about to readNullableEmbeddedBuffer";
+ size_t handle = 0;
+ size_t parent_buffer_handle = 0;
+ size_t parent_offset = 3;
+ const void* data = nullptr;
+ status_t status = p.readNullableEmbeddedBuffer(size, &handle, parent_buffer_handle, parent_offset, &data);
+ FUZZ_LOG() << "readNullableEmbeddedBuffer status: " << status << " handle: " << handle << " data: " << data;
+
+ // should be null since we don't create any IPC objects
+ CHECK(data == nullptr) << data;
+ },
+ [] (const ::android::hardware::Parcel& p, uint8_t size) {
+ FUZZ_LOG() << "about to readEmbeddedNativeHandle";
+ size_t parent_buffer_handle = size & 0xf;
+ size_t parent_offset = size >> 4;
+ const native_handle_t* handle = nullptr;
+ status_t status = p.readEmbeddedNativeHandle(parent_buffer_handle, parent_offset, &handle);
+ FUZZ_LOG() << "readEmbeddedNativeHandle status: " << status << " handle: " << handle << " handle: " << handle;
+
+ // should be null since we don't create any IPC objects
+ CHECK(handle == nullptr) << handle;
+ },
+ [] (const ::android::hardware::Parcel& p, uint8_t size) {
+ FUZZ_LOG() << "about to readNullableEmbeddedNativeHandle";
+ size_t parent_buffer_handle = size & 0xf;
+ size_t parent_offset = size >> 4;
+ const native_handle_t* handle = nullptr;
+ status_t status = p.readNullableEmbeddedNativeHandle(parent_buffer_handle, parent_offset, &handle);
+ FUZZ_LOG() << "readNullableEmbeddedNativeHandle status: " << status << " handle: " << handle << " handle: " << handle;
+
+ // should be null since we don't create any IPC objects
+ CHECK(handle == nullptr) << handle;
+ },
+ [] (const ::android::hardware::Parcel& p, uint8_t /*data*/) {
+ FUZZ_LOG() << "about to readNativeHandleNoDup";
+ const native_handle_t* handle = nullptr;
+ status_t status = p.readNativeHandleNoDup(&handle);
+ FUZZ_LOG() << "readNativeHandleNoDup status: " << status << " handle: " << handle;
+
+ // should be null since we don't create any IPC objects
+ CHECK(handle == nullptr) << handle;
+ CHECK(status != ::android::OK);
+ },
+ [] (const ::android::hardware::Parcel& p, uint8_t /*data*/) {
+ FUZZ_LOG() << "about to readNullableNativeHandleNoDup";
+ const native_handle_t* handle = nullptr;
+ status_t status = p.readNullableNativeHandleNoDup(&handle);
+ FUZZ_LOG() << "readNullableNativeHandleNoDup status: " << status << " handle: " << handle;
+
+ // should be null since we don't create any IPC objects
+ CHECK(handle == nullptr) << handle;
+ },
+};
+// clang-format on
diff --git a/libs/gui/tests/DummyConsumer.h b/libs/binder/fuzzer/hwbinder.h
similarity index 62%
copy from libs/gui/tests/DummyConsumer.h
copy to libs/binder/fuzzer/hwbinder.h
index 502bdf9..a6c66be 100644
--- a/libs/gui/tests/DummyConsumer.h
+++ b/libs/binder/fuzzer/hwbinder.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 The Android Open Source Project
+ * Copyright (C) 2019 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.
@@ -14,14 +14,9 @@
* limitations under the License.
*/
-#include <gui/IConsumerListener.h>
+#include <hwbinder/Parcel.h>
+#include <vector>
-namespace android {
+#include "parcel_fuzzer.h"
-struct DummyConsumer : public BnConsumerListener {
- void onFrameAvailable(const BufferItem& /* item */) override {}
- void onBuffersReleased() override {}
- void onSidebandStreamChanged() override {}
-};
-
-} // namespace android
+extern std::vector<ParcelRead<::android::hardware::Parcel>> HWBINDER_PARCEL_READ_FUNCTIONS;
diff --git a/libs/binder/fuzzer/main.cpp b/libs/binder/fuzzer/main.cpp
new file mode 100644
index 0000000..6657edb
--- /dev/null
+++ b/libs/binder/fuzzer/main.cpp
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2019 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 FUZZ_LOG_TAG "main"
+
+#include "binder.h"
+#include "binder_ndk.h"
+#include "hwbinder.h"
+#include "util.h"
+
+#include <android-base/logging.h>
+
+#include <cstdlib>
+#include <ctime>
+
+template <typename P>
+void doFuzz(
+ const std::vector<ParcelRead<P>>& reads,
+ const std::vector<uint8_t>& input,
+ const std::vector<uint8_t>& instructions) {
+
+ P p;
+ p.setData(input.data(), input.size());
+
+ // since we are only using a byte to index
+ CHECK(reads.size() <= 255) << reads.size();
+
+ for (size_t i = 0; i < instructions.size() - 1; i += 2) {
+ uint8_t a = instructions[i];
+ uint8_t readIdx = a % reads.size();
+
+ uint8_t b = instructions[i + 1];
+
+ FUZZ_LOG() << "Instruction: " << (i / 2) + 1 << "/" << instructions.size() / 2
+ << " cmd: " << static_cast<size_t>(a) << " (" << static_cast<size_t>(readIdx)
+ << ") arg: " << static_cast<size_t>(b) << " size: " << p.dataSize()
+ << " avail: " << p.dataAvail() << " pos: " << p.dataPosition()
+ << " cap: " << p.dataCapacity();
+
+ reads[readIdx](p, b);
+ }
+}
+
+void fuzz(uint8_t options, const std::vector<uint8_t>& input, const std::vector<uint8_t>& instructions) {
+ uint8_t parcelType = options & 0x3;
+
+ switch (parcelType) {
+ case 0x0:
+ doFuzz<::android::hardware::Parcel>(HWBINDER_PARCEL_READ_FUNCTIONS, input,
+ instructions);
+ break;
+ case 0x1:
+ doFuzz<::android::Parcel>(BINDER_PARCEL_READ_FUNCTIONS, input, instructions);
+ break;
+ case 0x2:
+ doFuzz<NdkParcelAdapter>(BINDER_NDK_PARCEL_READ_FUNCTIONS, input, instructions);
+ break;
+ case 0x3:
+ /*reserved for future use*/
+ break;
+ default:
+ LOG_ALWAYS_FATAL("unknown parcel type %d", static_cast<int>(parcelType));
+ }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ if (size <= 1) return 0; // no use
+
+ // avoid timeouts, see b/142617274, b/142473153
+ if (size > 50000) return 0;
+
+ uint8_t options = *data;
+ data++;
+ size--;
+
+ // TODO: generate 'objects' data
+
+ // data to fill out parcel
+ size_t inputLen = size / 2;
+ std::vector<uint8_t> input(data, data + inputLen);
+ data += inputLen;
+ size -= inputLen;
+
+ // data to use to determine what to do
+ size_t instructionLen = size;
+ std::vector<uint8_t> instructions(data, data + instructionLen);
+ data += instructionLen;
+ size -= instructionLen;
+
+ CHECK(size == 0) << "size: " << size;
+
+ FUZZ_LOG() << "options: " << (int)options << " inputLen: " << inputLen << " instructionLen: " << instructionLen;
+ FUZZ_LOG() << "input: " << hexString(input);
+ FUZZ_LOG() << "instructions: " << hexString(instructions);
+
+ fuzz(options, input, instructions);
+ return 0;
+}
diff --git a/libs/gui/tests/DummyConsumer.h b/libs/binder/fuzzer/parcel_fuzzer.h
similarity index 62%
copy from libs/gui/tests/DummyConsumer.h
copy to libs/binder/fuzzer/parcel_fuzzer.h
index 502bdf9..10cf17c 100644
--- a/libs/gui/tests/DummyConsumer.h
+++ b/libs/binder/fuzzer/parcel_fuzzer.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 The Android Open Source Project
+ * Copyright (C) 2019 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.
@@ -14,14 +14,5 @@
* limitations under the License.
*/
-#include <gui/IConsumerListener.h>
-
-namespace android {
-
-struct DummyConsumer : public BnConsumerListener {
- void onFrameAvailable(const BufferItem& /* item */) override {}
- void onBuffersReleased() override {}
- void onSidebandStreamChanged() override {}
-};
-
-} // namespace android
+template <typename P>
+using ParcelRead = std::function<void(const P& p, uint8_t data)>;
diff --git a/libs/binder/fuzzer/util.cpp b/libs/binder/fuzzer/util.cpp
new file mode 100644
index 0000000..479f406
--- /dev/null
+++ b/libs/binder/fuzzer/util.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2019 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 FUZZ_LOG_TAG "util"
+#include "util.h"
+
+#include <android-base/logging.h>
+
+#include <iomanip>
+#include <sstream>
+
+std::string hexString(const void* bytes, size_t len) {
+ if (bytes == nullptr) return "<null>";
+
+ const uint8_t* bytes8 = static_cast<const uint8_t*>(bytes);
+ char chars[] = "0123456789abcdef";
+ std::string result;
+ result.resize(len * 2);
+
+ for (size_t i = 0; i < len; i++) {
+ result[2 * i] = chars[bytes8[i] >> 4];
+ result[2 * i + 1] = chars[bytes8[i] & 0xf];
+ }
+
+ return result;
+}
+std::string hexString(const std::vector<uint8_t>& bytes) {
+ return hexString(bytes.data(), bytes.size());
+}
diff --git a/libs/binder/fuzzer/util.h b/libs/binder/fuzzer/util.h
new file mode 100644
index 0000000..aa504d2
--- /dev/null
+++ b/libs/binder/fuzzer/util.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2019 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 <iostream>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#ifndef FUZZ_LOG_TAG
+#error "Must define FUZZ_LOG_TAG"
+#endif
+
+#define FUZZ_LOG() FuzzLog(FUZZ_LOG_TAG).log()
+
+#ifdef ENABLE_LOG_FUZZ
+class FuzzLog {
+public:
+ FuzzLog(const char* tag) : mTag(tag) {}
+ ~FuzzLog() { std::cout << mTag << ": " << mOs.str() << std::endl; }
+
+ std::stringstream& log() { return mOs; }
+
+private:
+ const char* mTag = nullptr;
+ std::stringstream mOs;
+};
+#else
+class FuzzLog {
+public:
+ FuzzLog(const char* /*tag*/) {}
+ template <typename T>
+ FuzzLog& operator<<(const T& /*t*/) {
+ return *this;
+ }
+ FuzzLog& log() { return *this; }
+};
+#endif
+
+std::string hexString(const void* bytes, size_t len);
+std::string hexString(const std::vector<uint8_t>& bytes);
diff --git a/libs/binder/include/binder/AppOpsManager.h b/libs/binder/include/binder/AppOpsManager.h
index b19cde7..6d04f13 100644
--- a/libs/binder/include/binder/AppOpsManager.h
+++ b/libs/binder/include/binder/AppOpsManager.h
@@ -17,12 +17,16 @@
#ifndef ANDROID_APP_OPS_MANAGER_H
#define ANDROID_APP_OPS_MANAGER_H
-#ifndef __ANDROID_VNDK__
-
#include <binder/IAppOpsService.h>
#include <utils/threads.h>
+#include <optional>
+
+#ifdef __ANDROID_VNDK__
+#error "This header is not visible to vendors"
+#endif
+
// ---------------------------------------------------------------------------
namespace android {
@@ -109,6 +113,27 @@
OP_START_FOREGROUND = 76,
OP_BLUETOOTH_SCAN = 77,
OP_USE_BIOMETRIC = 78,
+ OP_ACTIVITY_RECOGNITION = 79,
+ OP_SMS_FINANCIAL_TRANSACTIONS = 80,
+ OP_READ_MEDIA_AUDIO = 81,
+ OP_WRITE_MEDIA_AUDIO = 82,
+ OP_READ_MEDIA_VIDEO = 83,
+ OP_WRITE_MEDIA_VIDEO = 84,
+ OP_READ_MEDIA_IMAGES = 85,
+ OP_WRITE_MEDIA_IMAGES = 86,
+ OP_LEGACY_STORAGE = 87,
+ OP_ACCESS_ACCESSIBILITY = 88,
+ OP_READ_DEVICE_IDENTIFIERS = 89,
+ OP_ACCESS_MEDIA_LOCATION = 90,
+ OP_QUERY_ALL_PACKAGES = 91,
+ OP_MANAGE_EXTERNAL_STORAGE = 92,
+ OP_INTERACT_ACROSS_PROFILES = 93,
+ OP_ACTIVATE_PLATFORM_VPN = 94,
+ OP_LOADER_USAGE_STATS = 95,
+ OP_DEPRECATED_1 = 96,
+ OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED = 97,
+ OP_AUTO_REVOKE_MANAGED_BY_INSTALLER = 98,
+ _NUM_OP = 99
};
AppOpsManager();
@@ -116,27 +141,39 @@
int32_t checkOp(int32_t op, int32_t uid, const String16& callingPackage);
int32_t checkAudioOpNoThrow(int32_t op, int32_t usage, int32_t uid,
const String16& callingPackage);
+ // @Deprecated, use noteOp(int32_t, int32_t uid, const String16&, const String16&,
+ // const String16&) instead
int32_t noteOp(int32_t op, int32_t uid, const String16& callingPackage);
+ int32_t noteOp(int32_t op, int32_t uid, const String16& callingPackage,
+ const std::optional<String16>& attributionTag, const String16& message);
+ // @Deprecated, use startOpNoThrow(int32_t, int32_t, const String16&, bool, const String16&,
+ // const String16&) instead
int32_t startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage,
bool startIfModeDefault);
+ int32_t startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage,
+ bool startIfModeDefault, const std::optional<String16>& attributionTag,
+ const String16& message);
+ // @Deprecated, use finishOp(int32_t, int32_t, const String16&, bool, const String16&) instead
void finishOp(int32_t op, int32_t uid, const String16& callingPackage);
+ 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);
void stopWatchingMode(const sp<IAppOpsCallback>& callback);
int32_t permissionToOpCode(const String16& permission);
+ void setCameraAudioRestriction(int32_t mode);
private:
Mutex mLock;
sp<IAppOpsService> mService;
sp<IAppOpsService> getService();
+ bool shouldCollectNotes(int32_t opCode);
};
} // namespace android
+
// ---------------------------------------------------------------------------
-#else // __ANDROID_VNDK__
-#error "This header is not visible to vendors"
-#endif // __ANDROID_VNDK__
#endif // ANDROID_APP_OPS_MANAGER_H
diff --git a/libs/binder/include/binder/IAppOpsService.h b/libs/binder/include/binder/IAppOpsService.h
index b74c623..a4a20c8 100644
--- a/libs/binder/include/binder/IAppOpsService.h
+++ b/libs/binder/include/binder/IAppOpsService.h
@@ -18,11 +18,15 @@
#ifndef ANDROID_IAPP_OPS_SERVICE_H
#define ANDROID_IAPP_OPS_SERVICE_H
-#ifndef __ANDROID_VNDK__
-
#include <binder/IAppOpsCallback.h>
#include <binder/IInterface.h>
+#include <optional>
+
+#ifdef __ANDROID_VNDK__
+#error "This header is not visible to vendors"
+#endif
+
namespace android {
// ----------------------------------------------------------------------
@@ -33,18 +37,22 @@
DECLARE_META_INTERFACE(AppOpsService)
virtual int32_t checkOperation(int32_t code, int32_t uid, const String16& packageName) = 0;
- virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName) = 0;
+ virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName,
+ const std::optional<String16>& attributionTag, bool shouldCollectAsyncNotedOp,
+ const String16& message) = 0;
virtual int32_t startOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
- const String16& packageName, bool startIfModeDefault) = 0;
+ const String16& packageName, const std::optional<String16>& attributionTag,
+ bool startIfModeDefault, bool shouldCollectAsyncNotedOp, const String16& message) = 0;
virtual void finishOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
- const String16& packageName) = 0;
+ const String16& packageName, const std::optional<String16>& attributionTag) = 0;
virtual void startWatchingMode(int32_t op, const String16& packageName,
const sp<IAppOpsCallback>& callback) = 0;
virtual void stopWatchingMode(const sp<IAppOpsCallback>& callback) = 0;
- virtual sp<IBinder> getToken(const sp<IBinder>& clientToken) = 0;
virtual int32_t permissionToOpCode(const String16& permission) = 0;
virtual int32_t checkAudioOperation(int32_t code, int32_t usage,int32_t uid,
const String16& packageName) = 0;
+ virtual void setCameraAudioRestriction(int32_t mode) = 0;
+ virtual bool shouldCollectNotes(int32_t opCode) = 0;
enum {
CHECK_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
@@ -53,9 +61,10 @@
FINISH_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+3,
START_WATCHING_MODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+4,
STOP_WATCHING_MODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+5,
- GET_TOKEN_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+6,
- PERMISSION_TO_OP_CODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+7,
- CHECK_AUDIO_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+8,
+ PERMISSION_TO_OP_CODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+6,
+ CHECK_AUDIO_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+7,
+ SHOULD_COLLECT_NOTES_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+8,
+ SET_CAMERA_AUDIO_RESTRICTION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+9,
};
enum {
@@ -81,8 +90,4 @@
} // namespace android
-#else // __ANDROID_VNDK__
-#error "This header is not visible to vendors"
-#endif // __ANDROID_VNDK__
-
#endif // ANDROID_IAPP_OPS_SERVICE_H
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
index cabfc7f..7116154 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -259,7 +259,6 @@
"android.media.IDrmClient",
"android.media.IEffect",
"android.media.IEffectClient",
- "android.media.IMediaAnalyticsService",
"android.media.IMediaCodecList",
"android.media.IMediaDrmService",
"android.media.IMediaExtractor",
@@ -268,6 +267,7 @@
"android.media.IMediaHTTPService",
"android.media.IMediaLogService",
"android.media.IMediaMetadataRetriever",
+ "android.media.IMediaMetricsService",
"android.media.IMediaPlayer",
"android.media.IMediaPlayerClient",
"android.media.IMediaPlayerService",
diff --git a/libs/binder/include/binder/IMemory.h b/libs/binder/include/binder/IMemory.h
index 98e92c4..1a36eb0 100644
--- a/libs/binder/include/binder/IMemory.h
+++ b/libs/binder/include/binder/IMemory.h
@@ -76,13 +76,34 @@
// NOLINTNEXTLINE(google-default-arguments)
virtual sp<IMemoryHeap> getMemory(ssize_t* offset=nullptr, size_t* size=nullptr) const = 0;
+ // helpers
+
+ // Accessing the underlying pointer must be done with caution, as there are
+ // some inherent security risks associated with it. When receiving an
+ // IMemory from an untrusted process, there is currently no way to guarantee
+ // that this process would't change the content after the fact. This may
+ // lead to TOC/TOU class of security bugs. In most cases, when performance
+ // is not an issue, the recommended practice is to immediately copy the
+ // buffer upon reception, then work with the copy, e.g.:
+ //
+ // std::string private_copy(mem.size(), '\0');
+ // memcpy(private_copy.data(), mem.unsecurePointer(), mem.size());
+ //
+ // In cases where performance is an issue, this matter must be addressed on
+ // an ad-hoc basis.
void* unsecurePointer() const;
- // helpers
- void* fastPointer(const sp<IBinder>& heap, ssize_t offset) const;
- void* pointer() const;
size_t size() const;
ssize_t offset() const;
+
+private:
+ // These are now deprecated and are left here for backward-compatibility
+ // with prebuilts that may reference these symbol at runtime.
+ // Instead, new code should use unsecurePointer()/unsecureFastPointer(),
+ // which do the same thing, but make it more obvious that there are some
+ // security-related pitfalls associated with them.
+ void* pointer() const;
+ void* fastPointer(const sp<IBinder>& heap, ssize_t offset) const;
};
class BnMemory : public BnInterface<IMemory>
diff --git a/libs/binder/include/binder/IUidObserver.h b/libs/binder/include/binder/IUidObserver.h
index 09e50a9..d070390 100644
--- a/libs/binder/include/binder/IUidObserver.h
+++ b/libs/binder/include/binder/IUidObserver.h
@@ -34,7 +34,8 @@
virtual void onUidGone(uid_t uid, bool disabled) = 0;
virtual void onUidActive(uid_t uid) = 0;
virtual void onUidIdle(uid_t uid, bool disabled) = 0;
- virtual void onUidStateChanged(uid_t uid, int32_t procState, int64_t procStateSeq) = 0;
+ virtual void onUidStateChanged(uid_t uid, int32_t procState, int64_t procStateSeq,
+ int32_t capability) = 0;
enum {
ON_UID_GONE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index 97f1aee..b6cfb8e 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -119,6 +119,7 @@
status_t writeDouble(double val);
status_t writeCString(const char* str);
status_t writeString8(const String8& str);
+ status_t writeString8(const char* str, size_t len);
status_t writeString16(const String16& str);
status_t writeString16(const std::optional<String16>& str);
status_t writeString16(const std::unique_ptr<String16>& str);
@@ -312,6 +313,7 @@
const char* readCString() const;
String8 readString8() const;
status_t readString8(String8* pArg) const;
+ const char* readString8Inplace(size_t* outLen) const;
String16 readString16() const;
status_t readString16(String16* pArg) const;
status_t readString16(std::optional<String16>* pArg) const;
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp
index ab00914..4fd0657 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -29,7 +29,7 @@
},
}
-cc_library_shared {
+cc_library {
name: "libbinder_ndk",
defaults: ["libbinder_ndk_host_user"],
@@ -73,6 +73,12 @@
],
target: {
+ android: {
+ // Only one copy of this library on an Android device
+ static: {
+ enabled: false,
+ },
+ },
linux: {
version_script: "libbinder_ndk.map.txt",
},
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index ae761d8..a03835b 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -27,7 +27,9 @@
defaults: ["binder_test_defaults"],
srcs: ["binderDriverInterfaceTest.cpp"],
compile_multilib: "32",
+ multilib: { lib32: { suffix: "" } },
cflags: ["-DBINDER_IPC_32BIT=1"],
+ test_suites: ["vts"],
}
cc_test {
@@ -52,7 +54,10 @@
"libutils",
],
compile_multilib: "32",
+ multilib: { lib32: { suffix: "" } },
cflags: ["-DBINDER_IPC_32BIT=1"],
+ test_suites: ["vts"],
+ require_root: true,
}
cc_test {
diff --git a/libs/binder/tests/binderAbiHelper.h b/libs/binder/tests/binderAbiHelper.h
new file mode 100644
index 0000000..369b55d
--- /dev/null
+++ b/libs/binder/tests/binderAbiHelper.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#pragma once
+
+#include <stdlib.h>
+#include <iostream>
+
+#ifdef BINDER_IPC_32BIT
+static constexpr bool kBuild32Abi = true;
+#else
+static constexpr bool kBuild32Abi = false;
+#endif
+
+// TODO: remove when CONFIG_ANDROID_BINDER_IPC_32BIT is no longer supported
+static inline bool ReadKernelConfigIs32BitAbi() {
+ // failure case implies we run with standard ABI
+ return 0 == system("zcat /proc/config.gz | grep -E \"^CONFIG_ANDROID_BINDER_IPC_32BIT=y$\"");
+}
+
+static inline void ExitIfWrongAbi() {
+ bool runtime32Abi = ReadKernelConfigIs32BitAbi();
+
+ if (kBuild32Abi != runtime32Abi) {
+ std::cout << "[==========] Running 1 test from 1 test suite." << std::endl;
+ std::cout << "[----------] Global test environment set-up." << std::endl;
+ std::cout << "[----------] 1 tests from BinderLibTest" << std::endl;
+ std::cout << "[ RUN ] BinderTest.AbortForWrongAbi" << std::endl;
+ std::cout << "[ INFO ] test build abi 32: " << kBuild32Abi << " runtime abi 32: " << runtime32Abi << " so, skipping tests " << std::endl;
+ std::cout << "[ OK ] BinderTest.AbortForWrongAbi (0 ms) " << std::endl;
+ std::cout << "[----------] 1 tests from BinderTest (0 ms total)" << std::endl;
+ std::cout << "" << std::endl;
+ std::cout << "[----------] Global test environment tear-down" << std::endl;
+ std::cout << "[==========] 1 test from 1 test suite ran. (0 ms total)" << std::endl;
+ std::cout << "[ PASSED ] 1 tests." << std::endl;
+ exit(0);
+ }
+}
+
diff --git a/libs/binder/tests/binderDriverInterfaceTest.cpp b/libs/binder/tests/binderDriverInterfaceTest.cpp
index f3ed6a6..8cc3054 100644
--- a/libs/binder/tests/binderDriverInterfaceTest.cpp
+++ b/libs/binder/tests/binderDriverInterfaceTest.cpp
@@ -25,6 +25,8 @@
#include <sys/mman.h>
#include <poll.h>
+#include "binderAbiHelper.h"
+
#define BINDER_DEV_NAME "/dev/binder"
testing::Environment* binder_env;
@@ -361,6 +363,7 @@
}
int main(int argc, char **argv) {
+ ExitIfWrongAbi();
::testing::InitGoogleTest(&argc, argv);
binder_env = AddGlobalTestEnvironment(new BinderDriverInterfaceTestEnv());
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index f8ee32c..917751e 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -32,6 +32,8 @@
#include <sys/epoll.h>
#include <sys/prctl.h>
+#include "binderAbiHelper.h"
+
#define ARRAY_SIZE(array) (sizeof array / sizeof array[0])
using namespace android;
@@ -78,6 +80,7 @@
BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION,
BINDER_LIB_TEST_GET_SCHEDULING_POLICY,
BINDER_LIB_TEST_ECHO_VECTOR,
+ BINDER_LIB_TEST_REJECT_BUF,
};
pid_t start_server_process(int arg2, bool usePoll = false)
@@ -1047,6 +1050,34 @@
EXPECT_EQ(readValue, testValue);
}
+TEST_F(BinderLibTest, BufRejected) {
+ Parcel data, reply;
+ uint32_t buf;
+ sp<IBinder> server = addServer();
+ ASSERT_TRUE(server != nullptr);
+
+ binder_buffer_object obj {
+ .hdr = { .type = BINDER_TYPE_PTR },
+ .flags = 0,
+ .buffer = reinterpret_cast<binder_uintptr_t>((void*)&buf),
+ .length = 4,
+ };
+ data.setDataCapacity(1024);
+ // Write a bogus object at offset 0 to get an entry in the offset table
+ data.writeFileDescriptor(0);
+ EXPECT_EQ(data.objectsCount(), 1);
+ uint8_t *parcelData = const_cast<uint8_t*>(data.data());
+ // And now, overwrite it with the buffer object
+ memcpy(parcelData, &obj, sizeof(obj));
+ data.setDataSize(sizeof(obj));
+
+ status_t ret = server->transact(BINDER_LIB_TEST_REJECT_BUF, data, &reply);
+ // Either the kernel should reject this transaction (if it's correct), but
+ // if it's not, the server implementation should return an error if it
+ // finds an object in the received Parcel.
+ EXPECT_NE(NO_ERROR, ret);
+}
+
class BinderLibTestService : public BBinder
{
public:
@@ -1339,6 +1370,9 @@
reply->writeUint64Vector(vector);
return NO_ERROR;
}
+ case BINDER_LIB_TEST_REJECT_BUF: {
+ return data.objectsCount() == 0 ? BAD_VALUE : NO_ERROR;
+ }
default:
return UNKNOWN_TRANSACTION;
};
@@ -1372,6 +1406,9 @@
*/
testService->setExtension(new BBinder());
+ // Required for test "BufRejected'
+ testService->setRequestingSid(true);
+
/*
* We need this below, but can't hold a sp<> because it prevents the
* node from being cleaned up automatically. It's safe in this case
@@ -1448,6 +1485,8 @@
}
int main(int argc, char **argv) {
+ ExitIfWrongAbi();
+
if (argc == 4 && !strcmp(argv[1], "--servername")) {
binderservername = argv[2];
} else {
diff --git a/libs/bufferqueueconverter/Android.bp b/libs/bufferqueueconverter/Android.bp
new file mode 100644
index 0000000..bab2674
--- /dev/null
+++ b/libs/bufferqueueconverter/Android.bp
@@ -0,0 +1,28 @@
+cc_library_headers {
+ name: "libbufferqueueconverter_headers",
+ vendor_available: true,
+ export_include_dirs: ["include"],
+}
+
+cc_library_shared {
+ name: "libbufferqueueconverter",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
+ double_loadable: true,
+
+ srcs: [
+ "BufferQueueConverter.cpp",
+ ],
+
+ shared_libs: [
+ "libgui",
+ "libui",
+ "libutils",
+ "libbinder",
+ "libbase",
+ "liblog",
+ ],
+ export_include_dirs: ["include"],
+}
diff --git a/libs/bufferqueueconverter/BufferQueueConverter.cpp b/libs/bufferqueueconverter/BufferQueueConverter.cpp
new file mode 100644
index 0000000..b1896fa
--- /dev/null
+++ b/libs/bufferqueueconverter/BufferQueueConverter.cpp
@@ -0,0 +1,74 @@
+/*
+ * 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 <gui/Surface.h>
+#include <gui/bufferqueue/2.0/H2BGraphicBufferProducer.h>
+
+#include "include/bufferqueueconverter/BufferQueueConverter.h"
+
+
+using ::android::Surface;
+using ::android::IGraphicBufferProducer;
+using ::android::hardware::graphics::bufferqueue::V2_0::utils::H2BGraphicBufferProducer;
+
+
+namespace android {
+
+struct SurfaceHolder {
+ sp<Surface> surface;
+ SurfaceHolder(const sp<Surface>& s) : surface(s) {}
+};
+
+/**
+ * Custom deleter for SurfaceHolder unique pointer
+ */
+void destroySurfaceHolder(SurfaceHolder* surfaceHolder) {
+ delete surfaceHolder;
+}
+
+
+SurfaceHolderUniquePtr getSurfaceFromHGBP(const sp<HGraphicBufferProducer>& token) {
+ if (token == nullptr) {
+ ALOGE("Passed IGraphicBufferProducer handle is invalid.");
+ return SurfaceHolderUniquePtr(nullptr, nullptr);
+ }
+
+ sp<IGraphicBufferProducer> bufferProducer = new H2BGraphicBufferProducer(token);
+ if (bufferProducer == nullptr) {
+ ALOGE("Failed to get IGraphicBufferProducer.");
+ return SurfaceHolderUniquePtr(nullptr, nullptr);
+ }
+
+ sp<Surface> newSurface(new Surface(bufferProducer, true));
+ if (newSurface == nullptr) {
+ ALOGE("Failed to create Surface from HGBP.");
+ return SurfaceHolderUniquePtr(nullptr, nullptr);
+ }
+
+ return SurfaceHolderUniquePtr(new SurfaceHolder(newSurface), destroySurfaceHolder);
+}
+
+
+ANativeWindow* getNativeWindow(SurfaceHolder* handle) {
+ if (handle == nullptr) {
+ ALOGE("SurfaceHolder is invalid.");
+ return nullptr;
+ }
+
+ return static_cast<ANativeWindow*>(handle->surface.get());
+}
+
+} // namespace android
diff --git a/libs/bufferqueueconverter/include/bufferqueueconverter/BufferQueueConverter.h b/libs/bufferqueueconverter/include/bufferqueueconverter/BufferQueueConverter.h
new file mode 100644
index 0000000..84bceba
--- /dev/null
+++ b/libs/bufferqueueconverter/include/bufferqueueconverter/BufferQueueConverter.h
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_BUFFER_QUEUE_CONVERTER_H
+#define ANDROID_BUFFER_QUEUE_CONVERTER_H
+
+#include <gui/IGraphicBufferProducer.h>
+#include <android/native_window.h>
+
+using ::android::sp;
+using HGraphicBufferProducer =
+ ::android::hardware::graphics::bufferqueue::V2_0::IGraphicBufferProducer;
+
+namespace android {
+ /**
+ * Opaque handle for a data structure holding Surface.
+ */
+ typedef struct SurfaceHolder SurfaceHolder;
+
+ /**
+ * SurfaceHolder unique pointer type
+ */
+ using SurfaceHolderUniquePtr = std::unique_ptr<SurfaceHolder, void(*)(SurfaceHolder*)>;
+
+ /**
+ * Returns a SurfaceHolder that wraps a Surface generated from a given HGBP.
+ *
+ * @param token Hardware IGraphicBufferProducer to create a
+ * Surface.
+ * @return SurfaceHolder Unique pointer to created SurfaceHolder object.
+ */
+ SurfaceHolderUniquePtr getSurfaceFromHGBP(const sp<HGraphicBufferProducer>& token);
+
+ /**
+ * Returns ANativeWindow pointer from a given SurfaceHolder. Returned
+ * pointer is valid only while the containing SurfaceHolder is alive.
+ *
+ * @param surfaceHolder SurfaceHolder to generate a native window.
+ * @return ANativeWindow* a pointer to a generated native window.
+ */
+ ANativeWindow* getNativeWindow(SurfaceHolder* surfaceHolder);
+
+} // namespace android
+
+#endif // ANDROID_BUFFER_QUEUE_CONVERTER_H
diff --git a/libs/cputimeinstate/cputimeinstate.cpp b/libs/cputimeinstate/cputimeinstate.cpp
index e56c799..5e785b6 100644
--- a/libs/cputimeinstate/cputimeinstate.cpp
+++ b/libs/cputimeinstate/cputimeinstate.cpp
@@ -489,7 +489,7 @@
if (deleteMapEntry(gTisMapFd, &key) && errno != ENOENT) return false;
}
- concurrent_val_t czeros = {.policy = {0}, .active = {0}};
+ concurrent_val_t czeros = { .active = {0}, .policy = {0}, };
std::vector<concurrent_val_t> cvals(gNCpus, czeros);
for (key.bucket = 0; key.bucket <= (gNCpus - 1) / CPUS_PER_ENTRY; ++key.bucket) {
if (writeToMapEntry(gConcurrentMapFd, &key, cvals.data(), BPF_EXIST) && errno != ENOENT)
diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp
index 32baa5f..2e14408 100644
--- a/libs/dumputils/dump_utils.cpp
+++ b/libs/dumputils/dump_utils.cpp
@@ -35,7 +35,7 @@
"/system/bin/mediaserver",
"/system/bin/netd",
"/system/bin/sdcard",
- "/system/bin/statsd",
+ "/apex/com.android.os.statsd/bin/statsd",
"/system/bin/surfaceflinger",
"/system/bin/vehicle_network_service",
"/vendor/bin/hw/android.hardware.media.omx@1.0-service", // media.codec
@@ -57,9 +57,11 @@
"android.hardware.audio@5.0::IDevicesFactory",
"android.hardware.audio@6.0::IDevicesFactory",
"android.hardware.automotive.audiocontrol@1.0::IAudioControl",
+ "android.hardware.automotive.audiocontrol@2.0::IAudioControl",
"android.hardware.automotive.evs@1.0::IEvsCamera",
"android.hardware.automotive.vehicle@2.0::IVehicle",
"android.hardware.biometrics.face@1.0::IBiometricsFace",
+ "android.hardware.biometrics.fingerprint@2.1::IBiometricsFingerprint",
"android.hardware.bluetooth@1.0::IBluetoothHci",
"android.hardware.camera.provider@2.4::ICameraProvider",
"android.hardware.drm@1.0::IDrmFactory",
diff --git a/libs/gralloc/types/Android.bp b/libs/gralloc/types/Android.bp
new file mode 100644
index 0000000..66fb295
--- /dev/null
+++ b/libs/gralloc/types/Android.bp
@@ -0,0 +1,60 @@
+// Copyright (C) 2019 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.
+
+cc_library {
+ name: "libgralloctypes",
+ defaults: ["libbinder_ndk_host_user"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-enum-compare",
+ ],
+ host_supported: true,
+
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ support_system_process: true,
+ },
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.media.swcodec",
+ ],
+ min_sdk_version: "29",
+
+ srcs: [
+ "Gralloc4.cpp"
+ ],
+
+ shared_libs: [
+ "android.hardware.graphics.common-ndk_platform",
+ "android.hardware.graphics.mapper@4.0",
+ "libhidlbase",
+ "liblog",
+ ],
+
+ export_shared_lib_headers: [
+ "android.hardware.graphics.common-ndk_platform",
+ "android.hardware.graphics.mapper@4.0",
+ "libhidlbase",
+ ],
+
+ export_include_dirs: [
+ "include",
+ ],
+
+ sanitize: {
+ misc_undefined: ["integer"],
+ },
+}
diff --git a/libs/gralloc/types/Gralloc4.cpp b/libs/gralloc/types/Gralloc4.cpp
new file mode 100644
index 0000000..53c68b7
--- /dev/null
+++ b/libs/gralloc/types/Gralloc4.cpp
@@ -0,0 +1,1329 @@
+/*
+ * Copyright 2019 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 "libgralloctypes"
+
+#include <cstring>
+#include <cinttypes>
+#include <limits>
+
+#include <hidl/HidlSupport.h>
+#include <log/log.h>
+
+#include "gralloctypes/Gralloc4.h"
+
+using android::hardware::hidl_vec;
+
+using aidl::android::hardware::graphics::common::BlendMode;
+using aidl::android::hardware::graphics::common::ChromaSiting;
+using aidl::android::hardware::graphics::common::Compression;
+using aidl::android::hardware::graphics::common::Cta861_3;
+using aidl::android::hardware::graphics::common::Dataspace;
+using aidl::android::hardware::graphics::common::ExtendableType;
+using aidl::android::hardware::graphics::common::Interlaced;
+using aidl::android::hardware::graphics::common::PlaneLayout;
+using aidl::android::hardware::graphics::common::PlaneLayoutComponent;
+using aidl::android::hardware::graphics::common::PlaneLayoutComponentType;
+using aidl::android::hardware::graphics::common::Rect;
+using aidl::android::hardware::graphics::common::Smpte2086;
+using aidl::android::hardware::graphics::common::StandardMetadataType;
+using aidl::android::hardware::graphics::common::XyColor;
+
+using BufferDescriptorInfo = android::hardware::graphics::mapper::V4_0::IMapper::BufferDescriptorInfo;
+using MetadataType = android::hardware::graphics::mapper::V4_0::IMapper::MetadataType;
+
+namespace android {
+
+namespace gralloc4 {
+
+static inline bool hasAdditionOverflow(size_t a, size_t b) {
+ return a > SIZE_MAX - b;
+}
+
+/**
+ * OutputHidlVec represents the hidl_vec that is outputed when a type is encoded into a byte stream.
+ * This class is used to track the current state of a hidl_vec as it is filled with the encoded
+ * byte stream.
+ *
+ * This type is needed because hidl_vec's resize() allocates a new backing array every time.
+ * This type does not need an copies and only needs one resize operation.
+ */
+class OutputHidlVec {
+public:
+ OutputHidlVec(hidl_vec<uint8_t>* vec)
+ : mVec(vec) {}
+
+ status_t resize() {
+ if (!mVec) {
+ return BAD_VALUE;
+ }
+ mVec->resize(mNeededResize);
+ mResized = true;
+ return NO_ERROR;
+ }
+
+ status_t encode(const uint8_t* data, size_t size) {
+ if (!mVec) {
+ return BAD_VALUE;
+ }
+ if (!mResized) {
+ if (hasAdditionOverflow(mNeededResize, size)) {
+ clear();
+ return BAD_VALUE;
+ }
+ /**
+ * Update mNeededResize and return NO_ERROR here because if (!mResized), the
+ * caller hasn't called resize(). No data will be written into the mVec until
+ * the caller resizes. We can't resize here for the caller because hidl_vec::resize()
+ * allocates a new backing array every time.
+ */
+ mNeededResize += size;
+ return NO_ERROR;
+ }
+
+ if (hasAdditionOverflow(mOffset, size) || (mVec->size() < size + mOffset)) {
+ clear();
+ return BAD_VALUE;
+ }
+
+ std::copy(data, data + size, mVec->data() + mOffset);
+
+ mOffset += size;
+ return NO_ERROR;
+ }
+
+ void clear() {
+ if (mVec) {
+ mVec->resize(0);
+ }
+ mNeededResize = 0;
+ mResized = false;
+ mOffset = 0;
+ }
+
+private:
+ hidl_vec<uint8_t>* mVec;
+ size_t mNeededResize = 0;
+ size_t mResized = false;
+ size_t mOffset = 0;
+};
+
+/**
+ * InputHidlVec represents the hidl_vec byte stream that is inputed when a type is decoded.
+ * This class is used to track the current index of the byte stream of the hidl_vec as it is
+ * decoded.
+ */
+class InputHidlVec {
+public:
+ InputHidlVec(const hidl_vec<uint8_t>* vec)
+ : mVec(vec) {}
+
+ status_t decode(uint8_t* data, size_t size) {
+ if (!mVec || hasAdditionOverflow(mOffset, size) || mOffset + size > mVec->size()) {
+ return BAD_VALUE;
+ }
+
+ std::copy(mVec->data() + mOffset, mVec->data() + mOffset + size, data);
+
+ mOffset += size;
+ return NO_ERROR;
+ }
+
+ status_t decode(std::string* string, size_t size) {
+ if (!mVec || hasAdditionOverflow(mOffset, size) || mOffset + size > mVec->size()) {
+ return BAD_VALUE;
+ }
+
+ string->assign(mVec->data() + mOffset, mVec->data() + mOffset + size);
+
+ mOffset += size;
+ return NO_ERROR;
+ }
+
+ bool hasRemainingData() {
+ if (!mVec) {
+ return false;
+ }
+ return mVec->size() > mOffset;
+ }
+
+ size_t getRemainingSize() {
+ if (!mVec) {
+ return 0;
+ }
+ return mVec->size() - mOffset;
+ }
+
+private:
+ const hidl_vec<uint8_t>* mVec;
+ size_t mOffset = 0;
+};
+
+/**
+ * EncodeHelper is a function type that encodes T into the OutputHidlVec.
+ */
+template<class T>
+using EncodeHelper = status_t(*)(const T&, OutputHidlVec*);
+
+/**
+ * DecodeHelper is a function type that decodes InputHidlVec into T.
+ */
+template<class T>
+using DecodeHelper = status_t(*)(InputHidlVec*, T*);
+
+/**
+ * ErrorHandler is a function type that is called when the corresponding DecodeHelper function
+ * fails. ErrorHandler cleans up the object T so the caller doesn't receive a partially created
+ * T.
+ */
+template<class T>
+using ErrorHandler = void(*)(T*);
+
+status_t encodeMetadataType(const MetadataType& input, OutputHidlVec* output);
+status_t validateMetadataType(InputHidlVec* input, const MetadataType& expectedMetadataType);
+
+/**
+ * encode/encodeMetadata are the main encoding functions. They take in T and uses the encodeHelper
+ * function to turn T into the hidl_vec byte stream.
+ *
+ * These functions first call the encodeHelper function to determine how large the hidl_vec
+ * needs to be. They resize the hidl_vec. Finally, it reruns the encodeHelper function which
+ * encodes T into the hidl_vec byte stream.
+ */
+template <class T>
+status_t encode(const T& input, hidl_vec<uint8_t>* output, EncodeHelper<T> encodeHelper) {
+ OutputHidlVec outputHidlVec{output};
+
+ status_t err = encodeHelper(input, &outputHidlVec);
+ if (err) {
+ return err;
+ }
+
+ err = outputHidlVec.resize();
+ if (err) {
+ return err;
+ }
+
+ return encodeHelper(input, &outputHidlVec);
+}
+
+template <class T>
+status_t encodeMetadata(const MetadataType& metadataType, const T& input, hidl_vec<uint8_t>* output,
+ EncodeHelper<T> encodeHelper) {
+ OutputHidlVec outputHidlVec{output};
+
+ status_t err = encodeMetadataType(metadataType, &outputHidlVec);
+ if (err) {
+ return err;
+ }
+
+ err = encodeHelper(input, &outputHidlVec);
+ if (err) {
+ return err;
+ }
+
+ err = outputHidlVec.resize();
+ if (err) {
+ return err;
+ }
+
+ err = encodeMetadataType(metadataType, &outputHidlVec);
+ if (err) {
+ return err;
+ }
+
+ return encodeHelper(input, &outputHidlVec);
+}
+
+template <class T>
+status_t encodeOptionalMetadata(const MetadataType& metadataType, const std::optional<T>& input,
+ hidl_vec<uint8_t>* output, EncodeHelper<T> encodeHelper) {
+ if (!input) {
+ return NO_ERROR;
+ }
+ return encodeMetadata(metadataType, *input, output, encodeHelper);
+}
+
+/**
+ * decode/decodeMetadata are the main decoding functions. They take in a hidl_vec and use the
+ * decodeHelper function to turn the hidl_vec byte stream into T. If an error occurs, the
+ * errorHandler function cleans up T.
+ */
+template <class T>
+status_t decode(const hidl_vec<uint8_t>& input, T* output, DecodeHelper<T> decodeHelper,
+ ErrorHandler<T> errorHandler = nullptr) {
+ InputHidlVec inputHidlVec{&input};
+
+ status_t err = decodeHelper(&inputHidlVec, output);
+ if (err) {
+ return err;
+ }
+
+ err = inputHidlVec.hasRemainingData();
+ if (err) {
+ if (errorHandler) {
+ errorHandler(output);
+ }
+ return BAD_VALUE;
+ }
+
+ return NO_ERROR;
+}
+
+template <class T>
+status_t decodeMetadata(const MetadataType& metadataType, const hidl_vec<uint8_t>& input, T* output,
+ DecodeHelper<T> decodeHelper, ErrorHandler<T> errorHandler = nullptr) {
+ InputHidlVec inputHidlVec{&input};
+
+ status_t err = validateMetadataType(&inputHidlVec, metadataType);
+ if (err) {
+ return err;
+ }
+
+ err = decodeHelper(&inputHidlVec, output);
+ if (err) {
+ return err;
+ }
+
+ err = inputHidlVec.hasRemainingData();
+ if (err) {
+ if (errorHandler) {
+ errorHandler(output);
+ }
+ return BAD_VALUE;
+ }
+
+ return NO_ERROR;
+}
+
+template <class T>
+status_t decodeOptionalMetadata(const MetadataType& metadataType, const hidl_vec<uint8_t>& input,
+ std::optional<T>* output, DecodeHelper<T> decodeHelper) {
+ if (!output) {
+ return BAD_VALUE;
+ }
+ if (input.size() <= 0) {
+ output->reset();
+ return NO_ERROR;
+ }
+ T tmp;
+ status_t err = decodeMetadata(metadataType, input, &tmp, decodeHelper);
+ if (!err) {
+ *output = tmp;
+ }
+ return err;
+}
+
+/**
+ * Private helper functions
+ */
+template <class T>
+status_t encodeInteger(const T& input, OutputHidlVec* output) {
+ static_assert(std::is_same<T, uint32_t>::value || std::is_same<T, int32_t>::value ||
+ std::is_same<T, uint64_t>::value || std::is_same<T, int64_t>::value ||
+ std::is_same<T, float>::value || std::is_same<T, double>::value);
+ if (!output) {
+ return BAD_VALUE;
+ }
+
+ const uint8_t* tmp = reinterpret_cast<const uint8_t*>(&input);
+ return output->encode(tmp, sizeof(input));
+}
+
+template <class T>
+status_t decodeInteger(InputHidlVec* input, T* output) {
+ static_assert(std::is_same<T, uint32_t>::value || std::is_same<T, int32_t>::value ||
+ std::is_same<T, uint64_t>::value || std::is_same<T, int64_t>::value ||
+ std::is_same<T, float>::value || std::is_same<T, double>::value);
+ if (!output) {
+ return BAD_VALUE;
+ }
+
+ uint8_t* tmp = reinterpret_cast<uint8_t*>(output);
+ return input->decode(tmp, sizeof(*output));
+}
+
+status_t encodeString(const std::string& input, OutputHidlVec* output) {
+ if (!output) {
+ return BAD_VALUE;
+ }
+
+ status_t err = encodeInteger<int64_t>(input.size(), output);
+ if (err) {
+ return err;
+ }
+
+ return output->encode(reinterpret_cast<const uint8_t*>(input.data()), input.size());
+}
+
+status_t decodeString(InputHidlVec* input, std::string* output) {
+ if (!output) {
+ return BAD_VALUE;
+ }
+
+ int64_t size = 0;
+ status_t err = decodeInteger<int64_t>(input, &size);
+ if (err) {
+ return err;
+ }
+ if (size < 0) {
+ return BAD_VALUE;
+ }
+
+ return input->decode(output, size);
+}
+
+status_t encodeByteVector(const std::vector<uint8_t>& input, OutputHidlVec* output) {
+ if (!output) {
+ return BAD_VALUE;
+ }
+
+ status_t err = encodeInteger<int64_t>(input.size(), output);
+ if (err) {
+ return err;
+ }
+
+ return output->encode(input.data(), input.size());
+}
+
+status_t decodeByteVector(InputHidlVec* input, std::vector<uint8_t>* output) {
+ if (!output) {
+ return BAD_VALUE;
+ }
+
+ int64_t size = 0;
+ status_t err = decodeInteger<int64_t>(input, &size);
+ if (err || size < 0) {
+ return err;
+ }
+
+ if (size > input->getRemainingSize()) {
+ return BAD_VALUE;
+ }
+ output->resize(size);
+
+ return input->decode(output->data(), size);
+}
+
+status_t encodeExtendableType(const ExtendableType& input, OutputHidlVec* output) {
+ status_t err = encodeString(input.name, output);
+ if (err) {
+ return err;
+ }
+
+ err = encodeInteger<int64_t>(input.value, output);
+ if (err) {
+ return err;
+ }
+
+ return NO_ERROR;
+}
+
+status_t decodeExtendableType(InputHidlVec* input, ExtendableType* output) {
+ status_t err = decodeString(input, &output->name);
+ if (err) {
+ return err;
+ }
+
+ err = decodeInteger<int64_t>(input, &output->value);
+ if (err) {
+ return err;
+ }
+
+ return NO_ERROR;
+}
+
+void clearExtendableType(ExtendableType* output) {
+ if (!output) {
+ return;
+ }
+ output->name.clear();
+ output->value = 0;
+}
+
+status_t encodeMetadataType(const MetadataType& input, OutputHidlVec* output) {
+ status_t err = encodeString(input.name, output);
+ if (err) {
+ return err;
+ }
+
+ err = encodeInteger<int64_t>(input.value, output);
+ if (err) {
+ return err;
+ }
+
+ return NO_ERROR;
+}
+
+status_t decodeMetadataType(InputHidlVec* input, MetadataType* output) {
+ std::string name;
+ status_t err = decodeString(input, &name);
+ if (err) {
+ return err;
+ }
+ output->name = name;
+
+ err = decodeInteger<int64_t>(input, &output->value);
+ if (err) {
+ return err;
+ }
+
+ return NO_ERROR;
+}
+
+status_t validateMetadataType(InputHidlVec* input, const MetadataType& expectedMetadataType) {
+ MetadataType receivedMetadataType;
+
+ status_t err = decodeMetadataType(input, &receivedMetadataType);
+ if (err) {
+ return err;
+ }
+
+ if (expectedMetadataType.name != receivedMetadataType.name) {
+ return BAD_VALUE;
+ }
+
+ if (receivedMetadataType.value != expectedMetadataType.value) {
+ return BAD_VALUE;
+ }
+
+ return NO_ERROR;
+}
+
+status_t encodeXyColor(const XyColor& input, OutputHidlVec* output) {
+ status_t err = encodeInteger<float>(input.x, output);
+ if (err) {
+ return err;
+ }
+ return encodeInteger<float>(input.y, output);
+}
+
+status_t decodeXyColor(InputHidlVec* input, XyColor* output) {
+ status_t err = decodeInteger<float>(input, &output->x);
+ if (err) {
+ return err;
+ }
+ return decodeInteger<float>(input, &output->y);
+}
+
+void clearXyColor(XyColor* output) {
+ if (!output) {
+ return;
+ }
+ output->x = 0;
+ output->y = 0;
+}
+
+status_t encodeRect(const Rect& input, OutputHidlVec* output) {
+ status_t err = encodeInteger<int32_t>(static_cast<int32_t>(input.left), output);
+ if (err) {
+ return err;
+ }
+ err = encodeInteger<int32_t>(static_cast<int32_t>(input.top), output);
+ if (err) {
+ return err;
+ }
+ err = encodeInteger<int32_t>(static_cast<int32_t>(input.right), output);
+ if (err) {
+ return err;
+ }
+ return encodeInteger<int32_t>(static_cast<int32_t>(input.bottom), output);
+}
+
+status_t decodeRect(InputHidlVec* input, Rect* output) {
+ status_t err = decodeInteger<int32_t>(input, &output->left);
+ if (err) {
+ return err;
+ }
+ err = decodeInteger<int32_t>(input, &output->top);
+ if (err) {
+ return err;
+ }
+ err = decodeInteger<int32_t>(input, &output->right);
+ if (err) {
+ return err;
+ }
+ return decodeInteger<int32_t>(input, &output->bottom);
+}
+
+status_t encodeBufferDescriptorInfoHelper(const BufferDescriptorInfo& input,
+ OutputHidlVec* output) {
+ status_t err = encodeString(input.name, output);
+ if (err) {
+ return err;
+ }
+ err = encodeInteger<uint32_t>(input.width, output);
+ if (err) {
+ return err;
+ }
+ err = encodeInteger<uint32_t>(input.height, output);
+ if (err) {
+ return err;
+ }
+ err = encodeInteger<uint32_t>(input.layerCount, output);
+ if (err) {
+ return err;
+ }
+ err = encodeInteger<int32_t>(static_cast<int32_t>(input.format), output);
+ if (err) {
+ return err;
+ }
+ err = encodeInteger<uint64_t>(input.usage, output);
+ if (err) {
+ return err;
+ }
+ return encodeInteger<uint64_t>(input.reservedSize, output);
+}
+
+status_t decodeBufferDescriptorInfoHelper(InputHidlVec* input, BufferDescriptorInfo* output) {
+ std::string name;
+ status_t err = decodeString(input, &name);
+ if (err) {
+ return err;
+ }
+ output->name = name;
+
+ err = decodeInteger<uint32_t>(input, &output->width);
+ if (err) {
+ return err;
+ }
+ err = decodeInteger<uint32_t>(input, &output->height);
+ if (err) {
+ return err;
+ }
+ err = decodeInteger<uint32_t>(input, &output->layerCount);
+ if (err) {
+ return err;
+ }
+ err = decodeInteger<int32_t>(input, reinterpret_cast<int32_t*>(&output->format));
+ if (err) {
+ return err;
+ }
+ err = decodeInteger<uint64_t>(input, &output->usage);
+ if (err) {
+ return err;
+ }
+ return decodeInteger<uint64_t>(input, &output->reservedSize);
+}
+
+status_t encodePlaneLayoutComponent(const PlaneLayoutComponent& input, OutputHidlVec* output) {
+ if (!output) {
+ return BAD_VALUE;
+ }
+
+ status_t err = encodeExtendableType(input.type, output);
+ if (err) {
+ return err;
+ }
+ err = encodeInteger<int64_t>(static_cast<int64_t>(input.offsetInBits), output);
+ if (err) {
+ return err;
+ }
+ return encodeInteger<int64_t>(static_cast<int64_t>(input.sizeInBits), output);
+}
+
+status_t decodePlaneLayoutComponent(InputHidlVec* input, PlaneLayoutComponent* output) {
+ if (!output) {
+ return BAD_VALUE;
+ }
+
+ status_t err = decodeExtendableType(input, &output->type);
+ if (err) {
+ return err;
+ }
+ err = decodeInteger<int64_t>(input, &output->offsetInBits);
+ if (err) {
+ return err;
+ }
+ return decodeInteger<int64_t>(input, &output->sizeInBits);
+}
+
+status_t encodePlaneLayoutComponents(const std::vector<PlaneLayoutComponent>& input, OutputHidlVec* output) {
+ if (!output) {
+ return BAD_VALUE;
+ }
+
+ status_t err = encodeInteger<int64_t>(static_cast<int64_t>(input.size()), output);
+ if (err) {
+ return err;
+ }
+
+ for (const auto& planeLayoutComponent: input) {
+ err = encodePlaneLayoutComponent(planeLayoutComponent, output);
+ if (err) {
+ return err;
+ }
+ }
+
+ return NO_ERROR;
+}
+
+status_t decodePlaneLayoutComponents(InputHidlVec* input, std::vector<PlaneLayoutComponent>* output) {
+ if (!output) {
+ return BAD_VALUE;
+ }
+
+ int64_t size = 0;
+ status_t err = decodeInteger<int64_t>(input, &size);
+ if (err) {
+ return err;
+ }
+ if (size < 0 || size > 10000) {
+ return BAD_VALUE;
+ }
+
+ output->resize(size);
+
+ for (auto& planeLayoutComponent : *output) {
+ err = decodePlaneLayoutComponent(input, &planeLayoutComponent);
+ if (err) {
+ return err;
+ }
+ }
+ return NO_ERROR;
+}
+
+status_t encodePlaneLayout(const PlaneLayout& input, OutputHidlVec* output) {
+ if (!output) {
+ return BAD_VALUE;
+ }
+
+ status_t err = encodePlaneLayoutComponents(input.components, output);
+ if (err) {
+ return err;
+ }
+
+ err = encodeInteger<int64_t>(static_cast<int32_t>(input.offsetInBytes), output);
+ if (err) {
+ return err;
+ }
+ err = encodeInteger<int64_t>(static_cast<int32_t>(input.sampleIncrementInBits), output);
+ if (err) {
+ return err;
+ }
+ err = encodeInteger<int64_t>(static_cast<int32_t>(input.strideInBytes), output);
+ if (err) {
+ return err;
+ }
+ err = encodeInteger<int64_t>(static_cast<int32_t>(input.widthInSamples), output);
+ if (err) {
+ return err;
+ }
+ err = encodeInteger<int64_t>(static_cast<int32_t>(input.heightInSamples), output);
+ if (err) {
+ return err;
+ }
+ err = encodeInteger<int64_t>(static_cast<int32_t>(input.totalSizeInBytes), output);
+ if (err) {
+ return err;
+ }
+ err = encodeInteger<int64_t>(static_cast<int32_t>(input.horizontalSubsampling), output);
+ if (err) {
+ return err;
+ }
+ return encodeInteger<int64_t>(static_cast<int32_t>(input.verticalSubsampling), output);
+}
+
+status_t decodePlaneLayout(InputHidlVec* input, PlaneLayout* output) {
+ if (!output) {
+ return BAD_VALUE;
+ }
+
+ status_t err = decodePlaneLayoutComponents(input, &output->components);
+ if (err) {
+ return err;
+ }
+
+ err = decodeInteger<int64_t>(input, &output->offsetInBytes);
+ if (err) {
+ return err;
+ }
+ err = decodeInteger<int64_t>(input, &output->sampleIncrementInBits);
+ if (err) {
+ return err;
+ }
+ err = decodeInteger<int64_t>(input, &output->strideInBytes);
+ if (err) {
+ return err;
+ }
+ err = decodeInteger<int64_t>(input, &output->widthInSamples);
+ if (err) {
+ return err;
+ }
+ err = decodeInteger<int64_t>(input, &output->heightInSamples);
+ if (err) {
+ return err;
+ }
+ err = decodeInteger<int64_t>(input, &output->totalSizeInBytes);
+ if (err) {
+ return err;
+ }
+ err = decodeInteger<int64_t>(input, &output->horizontalSubsampling);
+ if (err) {
+ return err;
+ }
+ return decodeInteger<int64_t>(input, &output->verticalSubsampling);
+}
+
+status_t encodePlaneLayoutsHelper(const std::vector<PlaneLayout>& planeLayouts, OutputHidlVec* outOutputHidlVec) {
+ status_t err = encodeInteger<int64_t>(static_cast<int64_t>(planeLayouts.size()), outOutputHidlVec);
+ if (err) {
+ return err;
+ }
+
+ for (const auto& planeLayout : planeLayouts) {
+ err = encodePlaneLayout(planeLayout, outOutputHidlVec);
+ if (err) {
+ return err;
+ }
+ }
+
+ return NO_ERROR;
+}
+
+status_t decodePlaneLayoutsHelper(InputHidlVec* inputHidlVec, std::vector<PlaneLayout>* outPlaneLayouts) {
+ int64_t size = 0;
+ status_t err = decodeInteger<int64_t>(inputHidlVec, &size);
+ if (err) {
+ return err;
+ }
+ if (size < 0) {
+ return BAD_VALUE;
+ }
+
+ for (size_t i = 0; i < size; i++) {
+ outPlaneLayouts->emplace_back();
+ err = decodePlaneLayout(inputHidlVec, &outPlaneLayouts->back());
+ if (err) {
+ return err;
+ }
+ }
+ return NO_ERROR;
+}
+
+void clearPlaneLayouts(std::vector<PlaneLayout>* output) {
+ if (!output) {
+ return;
+ }
+ output->clear();
+}
+
+status_t encodeCropHelper(const std::vector<Rect>& crops, OutputHidlVec* outOutputHidlVec) {
+ status_t err = encodeInteger<int64_t>(static_cast<int64_t>(crops.size()), outOutputHidlVec);
+ if (err) {
+ return err;
+ }
+
+ for (const auto& crop : crops) {
+ err = encodeRect(crop, outOutputHidlVec);
+ if (err) {
+ return err;
+ }
+ }
+
+ return NO_ERROR;
+}
+
+status_t decodeCropHelper(InputHidlVec* inputHidlVec, std::vector<Rect>* outCrops) {
+ int64_t size = 0;
+ status_t err = decodeInteger<int64_t>(inputHidlVec, &size);
+ if (err) {
+ return err;
+ }
+ if (size < 0) {
+ return BAD_VALUE;
+ }
+
+ for (size_t i = 0; i < size; i++) {
+ outCrops->emplace_back();
+ err = decodeRect(inputHidlVec, &outCrops->back());
+ if (err) {
+ return err;
+ }
+ }
+ return NO_ERROR;
+}
+
+void clearCrop(std::vector<Rect>* output) {
+ if (!output) {
+ return;
+ }
+ output->clear();
+}
+
+status_t encodeSmpte2086Helper(const Smpte2086& smpte2086, OutputHidlVec* outOutputHidlVec) {
+ status_t err = encodeXyColor(smpte2086.primaryRed, outOutputHidlVec);
+ if (err) {
+ return err;
+ }
+ err = encodeXyColor(smpte2086.primaryGreen, outOutputHidlVec);
+ if (err) {
+ return err;
+ }
+ err = encodeXyColor(smpte2086.primaryBlue, outOutputHidlVec);
+ if (err) {
+ return err;
+ }
+ err = encodeXyColor(smpte2086.whitePoint, outOutputHidlVec);
+ if (err) {
+ return err;
+ }
+ err = encodeInteger<float>(smpte2086.maxLuminance, outOutputHidlVec);
+ if (err) {
+ return err;
+ }
+ return encodeInteger<float>(smpte2086.minLuminance, outOutputHidlVec);
+}
+
+status_t decodeSmpte2086Helper(InputHidlVec* inputHidlVec, Smpte2086* outSmpte2086) {
+ status_t err = decodeXyColor(inputHidlVec, &outSmpte2086->primaryRed);
+ if (err) {
+ return err;
+ }
+ err = decodeXyColor(inputHidlVec, &outSmpte2086->primaryGreen);
+ if (err) {
+ return err;
+ }
+ err = decodeXyColor(inputHidlVec, &outSmpte2086->primaryBlue);
+ if (err) {
+ return err;
+ }
+ err = decodeXyColor(inputHidlVec, &outSmpte2086->whitePoint);
+ if (err) {
+ return err;
+ }
+ err = decodeInteger<float>(inputHidlVec, &outSmpte2086->maxLuminance);
+ if (err) {
+ return err;
+ }
+ return decodeInteger<float>(inputHidlVec, &outSmpte2086->minLuminance);
+}
+
+status_t encodeCta861_3Helper(const Cta861_3& cta861_3, OutputHidlVec* outOutputHidlVec) {
+ status_t err = encodeInteger<float>(cta861_3.maxContentLightLevel, outOutputHidlVec);
+ if (err) {
+ return err;
+ }
+ return encodeInteger<float>(cta861_3.maxFrameAverageLightLevel, outOutputHidlVec);
+}
+
+status_t decodeCta861_3Helper(InputHidlVec* inputHidlVec, Cta861_3* outCta861_3) {
+ status_t err = decodeInteger<float>(inputHidlVec, &outCta861_3->maxContentLightLevel);
+ if (err) {
+ return err;
+ }
+ return decodeInteger<float>(inputHidlVec, &outCta861_3->maxFrameAverageLightLevel);
+}
+
+/**
+ * Public API functions
+ */
+status_t encodeBufferDescriptorInfo(const BufferDescriptorInfo& bufferDescriptorInfo,
+ hidl_vec<uint8_t>* outBufferDescriptorInfo) {
+ return encode(bufferDescriptorInfo, outBufferDescriptorInfo, encodeBufferDescriptorInfoHelper);
+}
+
+status_t decodeBufferDescriptorInfo(const hidl_vec<uint8_t>& bufferDescriptorInfo,
+ BufferDescriptorInfo* outBufferDescriptorInfo) {
+ return decode(bufferDescriptorInfo, outBufferDescriptorInfo, decodeBufferDescriptorInfoHelper);
+}
+
+status_t encodeBufferId(uint64_t bufferId, hidl_vec<uint8_t>* outBufferId) {
+ return encodeMetadata(MetadataType_BufferId, bufferId, outBufferId, encodeInteger);
+}
+
+status_t decodeBufferId(const hidl_vec<uint8_t>& bufferId, uint64_t* outBufferId) {
+ return decodeMetadata(MetadataType_BufferId, bufferId, outBufferId, decodeInteger);
+}
+
+status_t encodeName(const std::string& name, hidl_vec<uint8_t>* outName) {
+ return encodeMetadata(MetadataType_Name, name, outName, encodeString);
+}
+
+status_t decodeName(const hidl_vec<uint8_t>& name, std::string* outName) {
+ return decodeMetadata(MetadataType_Name, name, outName, decodeString);
+}
+
+status_t encodeWidth(uint64_t width, hidl_vec<uint8_t>* outWidth) {
+ return encodeMetadata(MetadataType_Width, width, outWidth, encodeInteger);
+}
+
+status_t decodeWidth(const hidl_vec<uint8_t>& width, uint64_t* outWidth) {
+ return decodeMetadata(MetadataType_Width, width, outWidth, decodeInteger);
+}
+
+status_t encodeHeight(uint64_t height, hidl_vec<uint8_t>* outHeight) {
+ return encodeMetadata(MetadataType_Height, height, outHeight, encodeInteger);
+}
+
+status_t decodeHeight(const hidl_vec<uint8_t>& height, uint64_t* outHeight) {
+ return decodeMetadata(MetadataType_Height, height, outHeight, decodeInteger);
+}
+
+status_t encodeLayerCount(uint64_t layerCount, hidl_vec<uint8_t>* outLayerCount) {
+ return encodeMetadata(MetadataType_LayerCount, layerCount, outLayerCount, encodeInteger);
+}
+
+status_t decodeLayerCount(const hidl_vec<uint8_t>& layerCount, uint64_t* outLayerCount) {
+ return decodeMetadata(MetadataType_LayerCount, layerCount, outLayerCount, decodeInteger);
+}
+
+status_t encodePixelFormatRequested(const hardware::graphics::common::V1_2::PixelFormat& pixelFormatRequested,
+ hidl_vec<uint8_t>* outPixelFormatRequested) {
+ return encodeMetadata(MetadataType_PixelFormatRequested, static_cast<int32_t>(pixelFormatRequested),
+ outPixelFormatRequested, encodeInteger);
+}
+
+status_t decodePixelFormatRequested(const hidl_vec<uint8_t>& pixelFormatRequested,
+ hardware::graphics::common::V1_2::PixelFormat* outPixelFormatRequested) {
+ return decodeMetadata(MetadataType_PixelFormatRequested, pixelFormatRequested,
+ reinterpret_cast<int32_t*>(outPixelFormatRequested), decodeInteger);
+}
+
+status_t encodePixelFormatFourCC(uint32_t pixelFormatFourCC, hidl_vec<uint8_t>* outPixelFormatFourCC) {
+ return encodeMetadata(MetadataType_PixelFormatFourCC, pixelFormatFourCC, outPixelFormatFourCC,
+ encodeInteger);
+}
+
+status_t decodePixelFormatFourCC(const hidl_vec<uint8_t>& pixelFormatFourCC, uint32_t* outPixelFormatFourCC) {
+ return decodeMetadata(MetadataType_PixelFormatFourCC, pixelFormatFourCC, outPixelFormatFourCC,
+ decodeInteger);
+}
+
+status_t encodePixelFormatModifier(uint64_t pixelFormatModifier, hidl_vec<uint8_t>* outPixelFormatModifier) {
+ return encodeMetadata(MetadataType_PixelFormatModifier, pixelFormatModifier, outPixelFormatModifier,
+ encodeInteger);
+}
+
+status_t decodePixelFormatModifier(const hidl_vec<uint8_t>& pixelFormatModifier, uint64_t* outPixelFormatModifier) {
+ return decodeMetadata(MetadataType_PixelFormatModifier, pixelFormatModifier, outPixelFormatModifier,
+ decodeInteger);
+}
+
+status_t encodeUsage(uint64_t usage, hidl_vec<uint8_t>* outUsage) {
+ return encodeMetadata(MetadataType_Usage, usage, outUsage, encodeInteger);
+}
+
+status_t decodeUsage(const hidl_vec<uint8_t>& usage, uint64_t* outUsage) {
+ return decodeMetadata(MetadataType_Usage, usage, outUsage, decodeInteger);
+}
+
+status_t encodeAllocationSize(uint64_t allocationSize, hidl_vec<uint8_t>* outAllocationSize) {
+ return encodeMetadata(MetadataType_AllocationSize, allocationSize, outAllocationSize, encodeInteger);
+}
+
+status_t decodeAllocationSize(const hidl_vec<uint8_t>& allocationSize, uint64_t* outAllocationSize) {
+ return decodeMetadata(MetadataType_AllocationSize, allocationSize, outAllocationSize, decodeInteger);
+}
+
+status_t encodeProtectedContent(uint64_t protectedContent, hidl_vec<uint8_t>* outProtectedContent) {
+ return encodeMetadata(MetadataType_ProtectedContent, protectedContent, outProtectedContent,
+ encodeInteger);
+}
+
+status_t decodeProtectedContent(const hidl_vec<uint8_t>& protectedContent, uint64_t* outProtectedContent) {
+ return decodeMetadata(MetadataType_ProtectedContent, protectedContent, outProtectedContent,
+ decodeInteger);
+}
+
+status_t encodeCompression(const ExtendableType& compression, hidl_vec<uint8_t>* outCompression) {
+ return encodeMetadata(MetadataType_Compression, compression, outCompression, encodeExtendableType);
+}
+
+status_t decodeCompression(const hidl_vec<uint8_t>& compression, ExtendableType* outCompression) {
+ return decodeMetadata(MetadataType_Compression, compression, outCompression, decodeExtendableType,
+ clearExtendableType);
+}
+
+status_t encodeInterlaced(const ExtendableType& interlaced, hidl_vec<uint8_t>* outInterlaced) {
+ return encodeMetadata(MetadataType_Interlaced, interlaced, outInterlaced, encodeExtendableType);
+}
+
+status_t decodeInterlaced(const hidl_vec<uint8_t>& interlaced, ExtendableType* outInterlaced) {
+ return decodeMetadata(MetadataType_Interlaced, interlaced, outInterlaced, decodeExtendableType,
+ clearExtendableType);
+}
+
+status_t encodeChromaSiting(const ExtendableType& chromaSiting, hidl_vec<uint8_t>* outChromaSiting) {
+ return encodeMetadata(MetadataType_ChromaSiting, chromaSiting, outChromaSiting, encodeExtendableType);
+}
+
+status_t decodeChromaSiting(const hidl_vec<uint8_t>& chromaSiting, ExtendableType* outChromaSiting) {
+ return decodeMetadata(MetadataType_ChromaSiting, chromaSiting, outChromaSiting, decodeExtendableType,
+ clearExtendableType);
+}
+
+status_t encodePlaneLayouts(const std::vector<PlaneLayout>& planeLayouts, hidl_vec<uint8_t>* outPlaneLayouts) {
+ return encodeMetadata(MetadataType_PlaneLayouts, planeLayouts, outPlaneLayouts,
+ encodePlaneLayoutsHelper);
+}
+
+status_t decodePlaneLayouts(const hidl_vec<uint8_t>& planeLayouts, std::vector<PlaneLayout>* outPlaneLayouts) {
+ return decodeMetadata(MetadataType_PlaneLayouts, planeLayouts, outPlaneLayouts,
+ decodePlaneLayoutsHelper, clearPlaneLayouts);
+}
+
+status_t encodeCrop(const std::vector<Rect>& crop, hidl_vec<uint8_t>* outCrop) {
+ return encodeMetadata(MetadataType_Crop, crop, outCrop, encodeCropHelper);
+}
+
+status_t decodeCrop(const hidl_vec<uint8_t>& crop, std::vector<Rect>* outCrop) {
+ return decodeMetadata(MetadataType_Crop, crop, outCrop, decodeCropHelper, clearCrop);
+}
+
+status_t encodeDataspace(const Dataspace& dataspace, hidl_vec<uint8_t>* outDataspace) {
+ return encodeMetadata(MetadataType_Dataspace, static_cast<int32_t>(dataspace), outDataspace,
+ encodeInteger);
+}
+
+status_t decodeDataspace(const hidl_vec<uint8_t>& dataspace, Dataspace* outDataspace) {
+ return decodeMetadata(MetadataType_Dataspace, dataspace, reinterpret_cast<int32_t*>(outDataspace),
+ decodeInteger);
+}
+
+status_t encodeBlendMode(const BlendMode& blendMode, hidl_vec<uint8_t>* outBlendMode) {
+ return encodeMetadata(MetadataType_BlendMode, static_cast<int32_t>(blendMode), outBlendMode,
+ encodeInteger);
+}
+
+status_t decodeBlendMode(const hidl_vec<uint8_t>& blendMode, BlendMode* outBlendMode) {
+ return decodeMetadata(MetadataType_BlendMode, blendMode, reinterpret_cast<int32_t*>(outBlendMode),
+ decodeInteger);
+}
+
+status_t encodeSmpte2086(const std::optional<Smpte2086>& smpte2086,
+ hidl_vec<uint8_t>* outSmpte2086) {
+ return encodeOptionalMetadata(MetadataType_Smpte2086, smpte2086, outSmpte2086, encodeSmpte2086Helper);
+}
+
+status_t decodeSmpte2086(const hidl_vec<uint8_t>& smpte2086,
+ std::optional<Smpte2086>* outSmpte2086) {
+ return decodeOptionalMetadata(MetadataType_Smpte2086, smpte2086, outSmpte2086, decodeSmpte2086Helper);
+}
+
+status_t encodeCta861_3(const std::optional<Cta861_3>& cta861_3, hidl_vec<uint8_t>* outCta861_3) {
+ return encodeOptionalMetadata(MetadataType_Cta861_3, cta861_3, outCta861_3, encodeCta861_3Helper);
+}
+
+status_t decodeCta861_3(const hidl_vec<uint8_t>& cta861_3, std::optional<Cta861_3>* outCta861_3) {
+ return decodeOptionalMetadata(MetadataType_Cta861_3, cta861_3, outCta861_3, decodeCta861_3Helper);
+}
+
+status_t encodeSmpte2094_40(const std::optional<std::vector<uint8_t>>& smpte2094_40,
+ hidl_vec<uint8_t>* outSmpte2094_40) {
+ return encodeOptionalMetadata(MetadataType_Smpte2094_40, smpte2094_40, outSmpte2094_40,
+ encodeByteVector);
+}
+
+status_t decodeSmpte2094_40(const hidl_vec<uint8_t>& smpte2094_40,
+ std::optional<std::vector<uint8_t>>* outSmpte2094_40) {
+ return decodeOptionalMetadata(MetadataType_Smpte2094_40, smpte2094_40, outSmpte2094_40,
+ decodeByteVector);
+}
+
+status_t encodeUint32(const MetadataType& metadataType, uint32_t input,
+ hidl_vec<uint8_t>* output) {
+ return encodeMetadata(metadataType, input, output, encodeInteger);
+}
+
+status_t decodeUint32(const MetadataType& metadataType, const hidl_vec<uint8_t>& input,
+ uint32_t* output) {
+ return decodeMetadata(metadataType, input, output, decodeInteger);
+}
+
+status_t encodeInt32(const MetadataType& metadataType, int32_t input,
+ hidl_vec<uint8_t>* output) {
+ return encodeMetadata(metadataType, input, output, encodeInteger);
+}
+
+status_t decodeInt32(const MetadataType& metadataType, const hidl_vec<uint8_t>& input,
+ int32_t* output) {
+ return decodeMetadata(metadataType, input, output, decodeInteger);
+}
+
+status_t encodeUint64(const MetadataType& metadataType, uint64_t input,
+ hidl_vec<uint8_t>* output) {
+ return encodeMetadata(metadataType, input, output, encodeInteger);
+}
+
+status_t decodeUint64(const MetadataType& metadataType, const hidl_vec<uint8_t>& input,
+ uint64_t* output) {
+ return decodeMetadata(metadataType, input, output, decodeInteger);
+}
+
+status_t encodeInt64(const MetadataType& metadataType, int64_t input,
+ hidl_vec<uint8_t>* output) {
+ return encodeMetadata(metadataType, input, output, encodeInteger);
+}
+
+status_t decodeInt64(const MetadataType& metadataType, const hidl_vec<uint8_t>& input,
+ int64_t* output) {
+ return decodeMetadata(metadataType, input, output, decodeInteger);
+}
+
+status_t encodeFloat(const MetadataType& metadataType, float input,
+ hidl_vec<uint8_t>* output) {
+ return encodeMetadata(metadataType, input, output, encodeInteger);
+}
+
+status_t decodeFloat(const MetadataType& metadataType, const hidl_vec<uint8_t>& input,
+ float* output) {
+ return decodeMetadata(metadataType, input, output, decodeInteger);
+}
+
+status_t encodeDouble(const MetadataType& metadataType, double input,
+ hidl_vec<uint8_t>* output) {
+ return encodeMetadata(metadataType, input, output, encodeInteger);
+}
+
+status_t decodeDouble(const MetadataType& metadataType, const hidl_vec<uint8_t>& input,
+ double* output) {
+ return decodeMetadata(metadataType, input, output, decodeInteger);
+}
+
+status_t encodeString(const MetadataType& metadataType, const std::string& input,
+ hidl_vec<uint8_t>* output) {
+ return encodeMetadata(metadataType, input, output, encodeString);
+}
+
+status_t decodeString(const MetadataType& metadataType, const hidl_vec<uint8_t>& input,
+ std::string* output) {
+ return decodeMetadata(metadataType, input, output, decodeString);
+}
+
+bool isStandardMetadataType(const MetadataType& metadataType) {
+ return !std::strncmp(metadataType.name.c_str(), GRALLOC4_STANDARD_METADATA_TYPE,
+ metadataType.name.size());
+}
+
+bool isStandardCompression(const ExtendableType& compression) {
+ return !std::strncmp(compression.name.c_str(), GRALLOC4_STANDARD_COMPRESSION,
+ compression.name.size());
+}
+
+bool isStandardInterlaced(const ExtendableType& interlaced) {
+ return !std::strncmp(interlaced.name.c_str(), GRALLOC4_STANDARD_INTERLACED,
+ interlaced.name.size());
+}
+
+bool isStandardChromaSiting(const ExtendableType& chromaSiting) {
+ return !std::strncmp(chromaSiting.name.c_str(), GRALLOC4_STANDARD_CHROMA_SITING,
+ chromaSiting.name.size());
+}
+
+bool isStandardPlaneLayoutComponentType(const ExtendableType& planeLayoutComponentType) {
+ return !std::strncmp(planeLayoutComponentType.name.c_str(), GRALLOC4_STANDARD_PLANE_LAYOUT_COMPONENT_TYPE,
+ planeLayoutComponentType.name.size());
+}
+
+StandardMetadataType getStandardMetadataTypeValue(const MetadataType& metadataType) {
+ return static_cast<StandardMetadataType>(metadataType.value);
+}
+
+Compression getStandardCompressionValue(const ExtendableType& compression) {
+ return static_cast<Compression>(compression.value);
+}
+
+Interlaced getStandardInterlacedValue(const ExtendableType& interlaced) {
+ return static_cast<Interlaced>(interlaced.value);
+}
+
+ChromaSiting getStandardChromaSitingValue(const ExtendableType& chromaSiting) {
+ return static_cast<ChromaSiting>(chromaSiting.value);
+}
+
+PlaneLayoutComponentType getStandardPlaneLayoutComponentTypeValue(
+ const ExtendableType& planeLayoutComponentType) {
+ return static_cast<PlaneLayoutComponentType>(planeLayoutComponentType.value);
+}
+
+std::string getCompressionName(const ExtendableType& compression) {
+ if (!isStandardCompression(compression)) {
+ std::ostringstream stream;
+ stream << compression.name << "#" << compression.value;
+ return stream.str();
+ }
+ switch (getStandardCompressionValue(compression)) {
+ case Compression::NONE:
+ return "None";
+ case Compression::DISPLAY_STREAM_COMPRESSION:
+ return "DisplayStreamCompression";
+ }
+}
+
+std::string getInterlacedName(const ExtendableType& interlaced) {
+ if (!isStandardInterlaced(interlaced)) {
+ std::ostringstream stream;
+ stream << interlaced.name << "#" << interlaced.value;
+ return stream.str();
+ }
+ switch (getStandardInterlacedValue(interlaced)) {
+ case Interlaced::NONE:
+ return "None";
+ case Interlaced::TOP_BOTTOM:
+ return "TopBottom";
+ case Interlaced::RIGHT_LEFT:
+ return "RightLeft";
+ }
+}
+
+std::string getChromaSitingName(const ExtendableType& chromaSiting) {
+ if (!isStandardChromaSiting(chromaSiting)) {
+ std::ostringstream stream;
+ stream << chromaSiting.name << "#" << chromaSiting.value;
+ return stream.str();
+ }
+ switch (getStandardChromaSitingValue(chromaSiting)) {
+ case ChromaSiting::NONE:
+ return "None";
+ case ChromaSiting::UNKNOWN:
+ return "Unknown";
+ case ChromaSiting::SITED_INTERSTITIAL:
+ return "SitedInterstitial";
+ case ChromaSiting::COSITED_HORIZONTAL:
+ return "CositedHorizontal";
+ }
+}
+
+std::string getPlaneLayoutComponentTypeName(const ExtendableType& planeLayoutComponentType) {
+ if (!isStandardPlaneLayoutComponentType(planeLayoutComponentType)) {
+ std::ostringstream stream;
+ stream << planeLayoutComponentType.name << "#" << planeLayoutComponentType.value;
+ return stream.str();
+ }
+ switch (getStandardPlaneLayoutComponentTypeValue(planeLayoutComponentType)) {
+ case PlaneLayoutComponentType::Y:
+ return "Y";
+ case PlaneLayoutComponentType::CB:
+ return "Cb";
+ case PlaneLayoutComponentType::CR:
+ return "Cr";
+ case PlaneLayoutComponentType::R:
+ return "R";
+ case PlaneLayoutComponentType::G:
+ return "G";
+ case PlaneLayoutComponentType::B:
+ return "B";
+ case PlaneLayoutComponentType::RAW:
+ return "RAW";
+ case PlaneLayoutComponentType::A:
+ return "A";
+ }
+}
+
+} // namespace gralloc4
+
+} // namespace android
diff --git a/libs/gralloc/types/fuzzer/Android.bp b/libs/gralloc/types/fuzzer/Android.bp
new file mode 100644
index 0000000..8444883
--- /dev/null
+++ b/libs/gralloc/types/fuzzer/Android.bp
@@ -0,0 +1,32 @@
+cc_fuzz {
+ name: "libgralloctypes_fuzzer",
+ defaults: ["libbinder_ndk_host_user"],
+ host_supported: true,
+
+ fuzz_config: {
+ cc: ["marissaw@google.com"],
+ },
+
+ srcs: [
+ "gralloctypes.cpp",
+ "main.cpp",
+ "util.cpp",
+ ],
+ static_libs: [
+ "libbase",
+ "libcgrouprc",
+ "libcgrouprc_format",
+ "libcutils",
+ "libgralloctypes",
+ "libhidlbase",
+ "liblog",
+ "libprocessgroup",
+ "libjsoncpp",
+ "libutils",
+ ],
+
+ // This flag enables verbose output in the fuzz target, and is very useful
+ // for debugging a failure. If you are trying to diagnose how a crash was
+ // produced, you may find uncommenting the below line very useful.
+ // cflags: ["-DENABLE_LOG_FUZZ"],
+}
diff --git a/libs/gralloc/types/fuzzer/gralloctypes.cpp b/libs/gralloc/types/fuzzer/gralloctypes.cpp
new file mode 100644
index 0000000..dc22385
--- /dev/null
+++ b/libs/gralloc/types/fuzzer/gralloctypes.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2019 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 FUZZ_LOG_TAG "gralloctypes"
+
+#include <cstdint>
+
+#include <aidl/android/hardware/graphics/common/BlendMode.h>
+#include <aidl/android/hardware/graphics/common/Dataspace.h>
+#include <aidl/android/hardware/graphics/common/ExtendableType.h>
+#include <aidl/android/hardware/graphics/common/PlaneLayout.h>
+#include <android/hardware/graphics/common/1.2/types.h>
+#include <gralloctypes/Gralloc4.h>
+#include <hidl/HidlSupport.h>
+#include <utils/Errors.h>
+
+#include "gralloctypes.h"
+#include "util.h"
+
+using ::android::status_t;
+using MetadataType = android::hardware::graphics::mapper::V4_0::IMapper::MetadataType;
+using BufferDescriptorInfo = android::hardware::graphics::mapper::V4_0::IMapper::BufferDescriptorInfo;
+
+#define GRALLOCTYPES_DECODE(T, FUNC) \
+ [] (const ::android::hardware::hidl_vec<uint8_t>& vec) {\
+ FUZZ_LOG() << "about to read " #T " using " #FUNC;\
+ T t;\
+ status_t err = FUNC(vec, &t);\
+ (void) err;\
+ FUZZ_LOG() << #T " done " /* << "err: " << err*/;\
+ }
+
+#define GRALLOCTYPES_DECODE_VENDOR_HELPER(T, FUNC) \
+ [] (const MetadataType& metadataType, const ::android::hardware::hidl_vec<uint8_t>& vec) {\
+ FUZZ_LOG() << "about to read " #T " using " #FUNC;\
+ T t;\
+ status_t err = FUNC(metadataType, vec, &t);\
+ (void) err;\
+ FUZZ_LOG() << #T " done " /* << "err: " << err*/;\
+ }
+
+
+// clang-format off
+std::vector<GrallocTypesDecode> GRALLOCTYPES_DECODE_FUNCTIONS {
+ GRALLOCTYPES_DECODE(BufferDescriptorInfo, ::android::gralloc4::decodeBufferDescriptorInfo),
+ GRALLOCTYPES_DECODE(uint64_t, ::android::gralloc4::decodeBufferId),
+ GRALLOCTYPES_DECODE(std::string, ::android::gralloc4::decodeName),
+ GRALLOCTYPES_DECODE(uint64_t, ::android::gralloc4::decodeWidth),
+ GRALLOCTYPES_DECODE(uint64_t, ::android::gralloc4::decodeHeight),
+ GRALLOCTYPES_DECODE(uint64_t, ::android::gralloc4::decodeLayerCount),
+ GRALLOCTYPES_DECODE(::android::hardware::graphics::common::V1_2::PixelFormat, ::android::gralloc4::decodePixelFormatRequested),
+ GRALLOCTYPES_DECODE(uint32_t, ::android::gralloc4::decodePixelFormatFourCC),
+ GRALLOCTYPES_DECODE(uint64_t, ::android::gralloc4::decodePixelFormatModifier),
+ GRALLOCTYPES_DECODE(uint64_t, ::android::gralloc4::decodeUsage),
+ GRALLOCTYPES_DECODE(uint64_t, ::android::gralloc4::decodeAllocationSize),
+ GRALLOCTYPES_DECODE(uint64_t, ::android::gralloc4::decodeProtectedContent),
+ GRALLOCTYPES_DECODE(aidl::android::hardware::graphics::common::ExtendableType, ::android::gralloc4::decodeCompression),
+ GRALLOCTYPES_DECODE(aidl::android::hardware::graphics::common::ExtendableType, ::android::gralloc4::decodeInterlaced),
+ GRALLOCTYPES_DECODE(aidl::android::hardware::graphics::common::ExtendableType, ::android::gralloc4::decodeChromaSiting),
+ GRALLOCTYPES_DECODE(std::vector<aidl::android::hardware::graphics::common::PlaneLayout>, ::android::gralloc4::decodePlaneLayouts),
+ GRALLOCTYPES_DECODE(std::vector<aidl::android::hardware::graphics::common::Rect>, ::android::gralloc4::decodeCrop),
+ GRALLOCTYPES_DECODE(aidl::android::hardware::graphics::common::Dataspace, ::android::gralloc4::decodeDataspace),
+ GRALLOCTYPES_DECODE(aidl::android::hardware::graphics::common::BlendMode, ::android::gralloc4::decodeBlendMode),
+ GRALLOCTYPES_DECODE(std::optional<aidl::android::hardware::graphics::common::Smpte2086>, ::android::gralloc4::decodeSmpte2086),
+ GRALLOCTYPES_DECODE(std::optional<aidl::android::hardware::graphics::common::Cta861_3>, ::android::gralloc4::decodeCta861_3),
+ GRALLOCTYPES_DECODE(std::optional<std::vector<uint8_t>>, ::android::gralloc4::decodeSmpte2094_40),
+};
+
+std::vector<GrallocTypesVendorHelperDecode> GRALLOCTYPES_DECODE_VENDOR_HELPER_FUNCTIONS {
+ GRALLOCTYPES_DECODE_VENDOR_HELPER(uint32_t, ::android::gralloc4::decodeUint32),
+ GRALLOCTYPES_DECODE_VENDOR_HELPER(int32_t, ::android::gralloc4::decodeInt32),
+ GRALLOCTYPES_DECODE_VENDOR_HELPER(uint64_t, ::android::gralloc4::decodeUint64),
+ GRALLOCTYPES_DECODE_VENDOR_HELPER(int64_t, ::android::gralloc4::decodeInt64),
+ GRALLOCTYPES_DECODE_VENDOR_HELPER(float, ::android::gralloc4::decodeFloat),
+ GRALLOCTYPES_DECODE_VENDOR_HELPER(double, ::android::gralloc4::decodeDouble),
+ GRALLOCTYPES_DECODE_VENDOR_HELPER(std::string, ::android::gralloc4::decodeString),
+};
+// clang-format on
diff --git a/libs/gralloc/types/fuzzer/gralloctypes.h b/libs/gralloc/types/fuzzer/gralloctypes.h
new file mode 100644
index 0000000..a3fe2d9
--- /dev/null
+++ b/libs/gralloc/types/fuzzer/gralloctypes.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2019 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 <gralloctypes/Gralloc4.h>
+#include <hidl/HidlSupport.h>
+
+#include <vector>
+
+using GrallocTypesDecode = std::function<void(const ::android::hardware::hidl_vec<uint8_t>& vec)>;
+using GrallocTypesVendorHelperDecode = std::function<void(const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType&, const ::android::hardware::hidl_vec<uint8_t>& vec)>;
+
+extern std::vector<GrallocTypesDecode> GRALLOCTYPES_DECODE_FUNCTIONS;
+extern std::vector<GrallocTypesVendorHelperDecode> GRALLOCTYPES_DECODE_VENDOR_HELPER_FUNCTIONS;
diff --git a/libs/gralloc/types/fuzzer/main.cpp b/libs/gralloc/types/fuzzer/main.cpp
new file mode 100644
index 0000000..8779fa2
--- /dev/null
+++ b/libs/gralloc/types/fuzzer/main.cpp
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2019 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 FUZZ_LOG_TAG "main"
+
+#include "gralloctypes.h"
+#include "util.h"
+
+#include <android-base/logging.h>
+#include <log/log.h>
+
+#include <cstdlib>
+#include <ctime>
+
+using MetadataType = android::hardware::graphics::mapper::V4_0::IMapper::MetadataType;
+
+void doFuzz(
+ const std::vector<GrallocTypesDecode>& decodes, uint8_t instruction,
+ const std::vector<uint8_t>& input) {
+
+ ::android::hardware::hidl_vec<uint8_t> vec;
+ vec.setToExternal(const_cast<uint8_t*>(input.data()), input.size(), false /*shouldOwn*/);
+
+ // since we are only using a byte to index
+ CHECK(decodes.size() <= 255) << decodes.size();
+ uint8_t decodeIdx = instruction % decodes.size();
+
+ FUZZ_LOG() << "Instruction: " << instruction << " idx: " << static_cast<size_t>(decodeIdx)
+ << " size: " << vec.size();
+
+ decodes[decodeIdx](vec);
+}
+
+size_t fillInMetadataType(const std::vector<uint8_t>& input, MetadataType* outMetadataType) {
+ if (input.size() < sizeof(outMetadataType->value) + 1) {
+ return 0;
+ }
+ size_t size = 0;
+
+ outMetadataType->value = *(reinterpret_cast<const int64_t*>(input.data()));
+ size += sizeof(outMetadataType->value);
+
+ uint8_t nameLen = *(input.data() + size);
+ size += 1;
+
+ if (input.size() < size + nameLen) {
+ return 0;
+ }
+ std::string name(reinterpret_cast<const char*>(input.data()) + size, nameLen);
+ outMetadataType->name = name;
+ return size + nameLen;
+}
+
+void doFuzzVendorHelper(
+ const std::vector<GrallocTypesVendorHelperDecode>& decodes, uint8_t instruction,
+ const std::vector<uint8_t>& input) {
+
+ MetadataType metadataType;
+ size_t sizeUsed = fillInMetadataType(input, &metadataType);
+ if (sizeUsed <= 0) {
+ return;
+ }
+
+ ::android::hardware::hidl_vec<uint8_t> vec;
+ vec.setToExternal(const_cast<uint8_t*>(input.data() + sizeUsed), input.size() - sizeUsed,
+ false /*shouldOwn*/);
+
+ // since we are only using a byte to index
+ CHECK(decodes.size() <= 255) << decodes.size();
+ uint8_t decodeIdx = instruction % decodes.size();
+
+ FUZZ_LOG() << "Vendor Helper instruction: " << instruction << " idx: "
+ << static_cast<size_t>(decodeIdx) << " size: " << vec.size();
+
+ decodes[decodeIdx](metadataType, vec);
+}
+
+void fuzz(uint8_t options, uint8_t instruction, const std::vector<uint8_t>& input) {
+ uint8_t option = options & 0x1;
+
+ switch (option) {
+ case 0x0:
+ doFuzz(GRALLOCTYPES_DECODE_FUNCTIONS, instruction, input);
+ break;
+ case 0x1:
+ doFuzzVendorHelper(GRALLOCTYPES_DECODE_VENDOR_HELPER_FUNCTIONS, instruction, input);
+ break;
+ default:
+ LOG_ALWAYS_FATAL("unknown gralloc types %d", static_cast<int>(option));
+ }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ if (size <= 1) return 0; // no use
+
+ uint8_t options = *data;
+ data++;
+ size--;
+
+ uint8_t instruction = *data;
+ data++;
+ size--;
+
+ std::vector<uint8_t> input(data, data + size);
+
+ FUZZ_LOG() << "input: " << hexString(input);
+
+ fuzz(options, instruction, input);
+ return 0;
+}
diff --git a/libs/gralloc/types/fuzzer/util.cpp b/libs/gralloc/types/fuzzer/util.cpp
new file mode 100644
index 0000000..479f406
--- /dev/null
+++ b/libs/gralloc/types/fuzzer/util.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2019 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 FUZZ_LOG_TAG "util"
+#include "util.h"
+
+#include <android-base/logging.h>
+
+#include <iomanip>
+#include <sstream>
+
+std::string hexString(const void* bytes, size_t len) {
+ if (bytes == nullptr) return "<null>";
+
+ const uint8_t* bytes8 = static_cast<const uint8_t*>(bytes);
+ char chars[] = "0123456789abcdef";
+ std::string result;
+ result.resize(len * 2);
+
+ for (size_t i = 0; i < len; i++) {
+ result[2 * i] = chars[bytes8[i] >> 4];
+ result[2 * i + 1] = chars[bytes8[i] & 0xf];
+ }
+
+ return result;
+}
+std::string hexString(const std::vector<uint8_t>& bytes) {
+ return hexString(bytes.data(), bytes.size());
+}
diff --git a/libs/gralloc/types/fuzzer/util.h b/libs/gralloc/types/fuzzer/util.h
new file mode 100644
index 0000000..aa504d2
--- /dev/null
+++ b/libs/gralloc/types/fuzzer/util.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2019 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 <iostream>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#ifndef FUZZ_LOG_TAG
+#error "Must define FUZZ_LOG_TAG"
+#endif
+
+#define FUZZ_LOG() FuzzLog(FUZZ_LOG_TAG).log()
+
+#ifdef ENABLE_LOG_FUZZ
+class FuzzLog {
+public:
+ FuzzLog(const char* tag) : mTag(tag) {}
+ ~FuzzLog() { std::cout << mTag << ": " << mOs.str() << std::endl; }
+
+ std::stringstream& log() { return mOs; }
+
+private:
+ const char* mTag = nullptr;
+ std::stringstream mOs;
+};
+#else
+class FuzzLog {
+public:
+ FuzzLog(const char* /*tag*/) {}
+ template <typename T>
+ FuzzLog& operator<<(const T& /*t*/) {
+ return *this;
+ }
+ FuzzLog& log() { return *this; }
+};
+#endif
+
+std::string hexString(const void* bytes, size_t len);
+std::string hexString(const std::vector<uint8_t>& bytes);
diff --git a/libs/gralloc/types/include/gralloctypes/Gralloc4.h b/libs/gralloc/types/include/gralloctypes/Gralloc4.h
new file mode 100644
index 0000000..1a7c2c9
--- /dev/null
+++ b/libs/gralloc/types/include/gralloctypes/Gralloc4.h
@@ -0,0 +1,618 @@
+/*
+ * Copyright 2019 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 <aidl/android/hardware/graphics/common/BlendMode.h>
+#include <aidl/android/hardware/graphics/common/ChromaSiting.h>
+#include <aidl/android/hardware/graphics/common/Compression.h>
+#include <aidl/android/hardware/graphics/common/Cta861_3.h>
+#include <aidl/android/hardware/graphics/common/Dataspace.h>
+#include <aidl/android/hardware/graphics/common/ExtendableType.h>
+#include <aidl/android/hardware/graphics/common/Interlaced.h>
+#include <aidl/android/hardware/graphics/common/PlaneLayout.h>
+#include <aidl/android/hardware/graphics/common/PlaneLayoutComponentType.h>
+#include <aidl/android/hardware/graphics/common/Rect.h>
+#include <aidl/android/hardware/graphics/common/Smpte2086.h>
+#include <aidl/android/hardware/graphics/common/StandardMetadataType.h>
+#include <aidl/android/hardware/graphics/common/XyColor.h>
+#include <android/hardware/graphics/mapper/4.0/IMapper.h>
+
+namespace android {
+
+/**
+ * Define equality operators for Stable AIDL types.
+ */
+inline bool operator==(const aidl::android::hardware::graphics::common::ExtendableType& lhs,
+ const aidl::android::hardware::graphics::common::ExtendableType& rhs) {
+ return !std::strcmp(lhs.name.c_str(), rhs.name.c_str()) && lhs.value == rhs.value;
+}
+
+inline bool operator!=(const aidl::android::hardware::graphics::common::ExtendableType& lhs,
+ const aidl::android::hardware::graphics::common::ExtendableType& rhs) {
+ return !(lhs == rhs);
+}
+
+inline bool operator==(const aidl::android::hardware::graphics::common::PlaneLayoutComponent& lhs,
+ const aidl::android::hardware::graphics::common::PlaneLayoutComponent& rhs) {
+ if (lhs.type.name != rhs.type.name) {
+ return false;
+ }
+ if (lhs.type.value != rhs.type.value) {
+ return false;
+ }
+ if (lhs.sizeInBits != rhs.sizeInBits) {
+ return false;
+ }
+ if (lhs.offsetInBits != rhs.offsetInBits) {
+ return false;
+ }
+ return true;
+}
+
+inline bool operator!=(const aidl::android::hardware::graphics::common::PlaneLayoutComponent& lhs,
+ const aidl::android::hardware::graphics::common::PlaneLayoutComponent& rhs) {
+ return !(lhs == rhs);
+}
+
+inline bool operator==(const aidl::android::hardware::graphics::common::Rect& lhs,
+ const aidl::android::hardware::graphics::common::Rect& rhs) {
+ if (lhs.left != rhs.left) {
+ return false;
+ }
+ if (lhs.top != rhs.top) {
+ return false;
+ }
+ if (lhs.right != rhs.right) {
+ return false;
+ }
+ if (lhs.bottom != rhs.bottom) {
+ return false;
+ }
+ return true;
+}
+
+inline bool operator!=(const aidl::android::hardware::graphics::common::Rect& lhs,
+ const aidl::android::hardware::graphics::common::Rect& rhs) {
+ return !(lhs == rhs);
+}
+
+inline bool operator==(const std::vector<aidl::android::hardware::graphics::common::Rect>& lhs,
+ const std::vector<aidl::android::hardware::graphics::common::Rect>& rhs) {
+ if (lhs.size() != rhs.size()) {
+ return false;
+ }
+ for (size_t i = 0; i < lhs.size(); i++) {
+ if (lhs[i] != rhs[i]) {
+ return false;
+ }
+ }
+ return true;
+}
+
+inline bool operator!=(const std::vector<aidl::android::hardware::graphics::common::Rect>& lhs,
+ const std::vector<aidl::android::hardware::graphics::common::Rect>& rhs) {
+ return !(lhs == rhs);
+}
+
+inline bool operator==(const aidl::android::hardware::graphics::common::PlaneLayout& lhs,
+ const aidl::android::hardware::graphics::common::PlaneLayout& rhs) {
+ if (lhs.offsetInBytes != rhs.offsetInBytes) {
+ return false;
+ }
+ if (lhs.sampleIncrementInBits != rhs.sampleIncrementInBits) {
+ return false;
+ }
+ if (lhs.strideInBytes != rhs.strideInBytes) {
+ return false;
+ }
+ if (lhs.widthInSamples != rhs.widthInSamples) {
+ return false;
+ }
+ if (lhs.heightInSamples != rhs.heightInSamples) {
+ return false;
+ }
+ if (lhs.totalSizeInBytes != rhs.totalSizeInBytes) {
+ return false;
+ }
+ if (lhs.horizontalSubsampling != rhs.horizontalSubsampling) {
+ return false;
+ }
+ if (lhs.verticalSubsampling != rhs.verticalSubsampling) {
+ return false;
+ }
+ if (lhs.components.size() != rhs.components.size()) {
+ return false;
+ }
+ for (size_t i = 0; i < lhs.components.size(); i++) {
+ if (lhs.components[i] != rhs.components[i]) {
+ return false;
+ }
+ }
+ return true;
+}
+
+inline bool operator!=(const aidl::android::hardware::graphics::common::PlaneLayout& lhs,
+ const aidl::android::hardware::graphics::common::PlaneLayout& rhs) {
+ return !(lhs == rhs);
+}
+
+inline bool operator==(const std::vector<aidl::android::hardware::graphics::common::PlaneLayout>& lhs,
+ const std::vector<aidl::android::hardware::graphics::common::PlaneLayout>& rhs) {
+ if (lhs.size() != rhs.size()) {
+ return false;
+ }
+ for (size_t i = 0; i < lhs.size(); i++) {
+ if (lhs[i] != rhs[i]) {
+ return false;
+ }
+ }
+ return true;
+}
+
+inline bool operator!=(const std::vector<aidl::android::hardware::graphics::common::PlaneLayout>& lhs,
+ const std::vector<aidl::android::hardware::graphics::common::PlaneLayout>& rhs) {
+ return !(lhs == rhs);
+}
+
+inline bool operator==(const aidl::android::hardware::graphics::common::XyColor& lhs,
+ const aidl::android::hardware::graphics::common::XyColor& rhs) {
+ if (lhs.x != rhs.x) {
+ return false;
+ }
+ if (lhs.y != rhs.y) {
+ return false;
+ }
+ return true;
+}
+
+inline bool operator!=(const aidl::android::hardware::graphics::common::XyColor& lhs,
+ const aidl::android::hardware::graphics::common::XyColor& rhs) {
+ return !(lhs == rhs);
+}
+
+inline bool operator==(const aidl::android::hardware::graphics::common::Smpte2086& lhs,
+ const aidl::android::hardware::graphics::common::Smpte2086& rhs) {
+ if (lhs.primaryRed != rhs.primaryRed) {
+ return false;
+ }
+ if (lhs.primaryGreen != rhs.primaryGreen) {
+ return false;
+ }
+ if (lhs.primaryBlue != rhs.primaryBlue) {
+ return false;
+ }
+ if (lhs.whitePoint != rhs.whitePoint) {
+ return false;
+ }
+ if (lhs.maxLuminance != rhs.maxLuminance) {
+ return false;
+ }
+ if (lhs.minLuminance != rhs.minLuminance) {
+ return false;
+ }
+ return true;
+}
+
+inline bool operator!=(const aidl::android::hardware::graphics::common::Smpte2086& lhs,
+ const aidl::android::hardware::graphics::common::Smpte2086& rhs) {
+ return !(lhs == rhs);
+}
+
+inline bool operator==(const aidl::android::hardware::graphics::common::Cta861_3& lhs,
+ const aidl::android::hardware::graphics::common::Cta861_3& rhs) {
+ if (lhs.maxContentLightLevel != rhs.maxContentLightLevel) {
+ return false;
+ }
+ if (lhs.maxFrameAverageLightLevel != rhs.maxFrameAverageLightLevel) {
+ return false;
+ }
+ return true;
+}
+
+inline bool operator!=(const aidl::android::hardware::graphics::common::Cta861_3& lhs,
+ const aidl::android::hardware::graphics::common::Cta861_3& rhs) {
+ return !(lhs == rhs);
+}
+
+namespace gralloc4 {
+
+#define GRALLOC4_STANDARD_METADATA_TYPE "android.hardware.graphics.common.StandardMetadataType"
+#define GRALLOC4_STANDARD_CHROMA_SITING "android.hardware.graphics.common.ChromaSiting"
+#define GRALLOC4_STANDARD_COMPRESSION "android.hardware.graphics.common.Compression"
+#define GRALLOC4_STANDARD_INTERLACED "android.hardware.graphics.common.Interlaced"
+#define GRALLOC4_STANDARD_PLANE_LAYOUT_COMPONENT_TYPE \
+ "android.hardware.graphics.common.PlaneLayoutComponentType"
+
+/*---------------------------------------------------------------------------------------------*/
+/**
+ * Definitions of the standard buffer metadata types. It is recommended that everyone uses
+ * these definitions directly for standard buffer metadata types.
+ */
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_BufferId = {
+ GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::BUFFER_ID)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_Name = {
+ GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::NAME)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_Width = {
+ GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::WIDTH)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_Height = {
+ GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::HEIGHT)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_LayerCount = {
+ GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::LAYER_COUNT)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_PixelFormatRequested = {
+ GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::PIXEL_FORMAT_REQUESTED)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_PixelFormatFourCC = {
+ GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::PIXEL_FORMAT_FOURCC)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_PixelFormatModifier = {
+ GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::PIXEL_FORMAT_MODIFIER)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_Usage = {
+ GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::USAGE)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_AllocationSize = {
+ GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::ALLOCATION_SIZE)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_ProtectedContent = {
+ GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::PROTECTED_CONTENT)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_Compression = {
+ GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::COMPRESSION)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_Interlaced = {
+ GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::INTERLACED)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_ChromaSiting = {
+ GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::CHROMA_SITING)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_PlaneLayouts = {
+ GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::PLANE_LAYOUTS)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_Crop = {
+ GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::CROP)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_Dataspace = {
+ GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::DATASPACE)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_BlendMode = {
+ GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::BLEND_MODE)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType
+ MetadataType_Smpte2086 = {GRALLOC4_STANDARD_METADATA_TYPE,
+ static_cast<int64_t>(aidl::android::hardware::graphics::common::
+ StandardMetadataType::SMPTE2086)};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType
+ MetadataType_Cta861_3 = {GRALLOC4_STANDARD_METADATA_TYPE,
+ static_cast<int64_t>(aidl::android::hardware::graphics::common::
+ StandardMetadataType::CTA861_3)};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType
+ MetadataType_Smpte2094_40 = {GRALLOC4_STANDARD_METADATA_TYPE,
+ static_cast<int64_t>(
+ aidl::android::hardware::graphics::common::
+ StandardMetadataType::SMPTE2094_40)};
+
+/*---------------------------------------------------------------------------------------------*/
+
+/**
+ * Definitions of the standard compression strategies. It is recommended that everyone uses
+ * these definitions directly for standard compression strategies.
+ */
+static const aidl::android::hardware::graphics::common::ExtendableType Compression_None =
+ {GRALLOC4_STANDARD_COMPRESSION,
+ static_cast<int64_t>(aidl::android::hardware::graphics::common::Compression::NONE)};
+
+static const aidl::android::hardware::graphics::common::ExtendableType
+ Compression_DisplayStreamCompression =
+ {GRALLOC4_STANDARD_COMPRESSION,
+ static_cast<int64_t>(aidl::android::hardware::graphics::common::Compression::
+ DISPLAY_STREAM_COMPRESSION)};
+
+/*---------------------------------------------------------------------------------------------*/
+
+/**
+ * Definitions of the standard interlaced strategies. It is recommended that everyone uses
+ * these definitions directly for standard interlaced strategies.
+ */
+static const aidl::android::hardware::graphics::common::ExtendableType Interlaced_None =
+ {GRALLOC4_STANDARD_INTERLACED,
+ static_cast<int64_t>(aidl::android::hardware::graphics::common::Interlaced::NONE)};
+
+static const aidl::android::hardware::graphics::common::ExtendableType Interlaced_TopBottom =
+ {GRALLOC4_STANDARD_INTERLACED,
+ static_cast<int64_t>(aidl::android::hardware::graphics::common::Interlaced::TOP_BOTTOM)};
+
+static const aidl::android::hardware::graphics::common::ExtendableType Interlaced_RightLeft =
+ {GRALLOC4_STANDARD_INTERLACED,
+ static_cast<int64_t>(aidl::android::hardware::graphics::common::Interlaced::RIGHT_LEFT)};
+
+/*---------------------------------------------------------------------------------------------*/
+
+/**
+ * Definitions of the standard chroma siting. It is recommended that everyone uses
+ * these definitions directly for standard chroma siting.
+ */
+static const aidl::android::hardware::graphics::common::ExtendableType ChromaSiting_None =
+ {GRALLOC4_STANDARD_CHROMA_SITING,
+ static_cast<int64_t>(aidl::android::hardware::graphics::common::ChromaSiting::NONE)};
+
+static const aidl::android::hardware::graphics::common::ExtendableType ChromaSiting_Unknown =
+ {GRALLOC4_STANDARD_CHROMA_SITING,
+ static_cast<int64_t>(aidl::android::hardware::graphics::common::ChromaSiting::UNKNOWN)};
+
+static const aidl::android::hardware::graphics::common::ExtendableType
+ ChromaSiting_SitedInterstitial = {GRALLOC4_STANDARD_CHROMA_SITING,
+ static_cast<int64_t>(
+ aidl::android::hardware::graphics::common::
+ ChromaSiting::SITED_INTERSTITIAL)};
+
+static const aidl::android::hardware::graphics::common::ExtendableType
+ ChromaSiting_CositedHorizontal = {GRALLOC4_STANDARD_CHROMA_SITING,
+ static_cast<int64_t>(
+ aidl::android::hardware::graphics::common::
+ ChromaSiting::COSITED_HORIZONTAL)};
+
+/*---------------------------------------------------------------------------------------------*/
+
+/**
+ * Definitions of the standard plane layout component types. It is recommended that everyone uses
+ * these definitions directly for standard plane layout component types
+ */
+static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_Y =
+ {GRALLOC4_STANDARD_PLANE_LAYOUT_COMPONENT_TYPE,
+ static_cast<int64_t>(
+ aidl::android::hardware::graphics::common::PlaneLayoutComponentType::Y)};
+
+static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_CB =
+ {GRALLOC4_STANDARD_PLANE_LAYOUT_COMPONENT_TYPE,
+ static_cast<int64_t>(
+ aidl::android::hardware::graphics::common::PlaneLayoutComponentType::CB)};
+
+static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_CR =
+ {GRALLOC4_STANDARD_PLANE_LAYOUT_COMPONENT_TYPE,
+ static_cast<int64_t>(
+ aidl::android::hardware::graphics::common::PlaneLayoutComponentType::CR)};
+
+static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_R =
+ {GRALLOC4_STANDARD_PLANE_LAYOUT_COMPONENT_TYPE,
+ static_cast<int64_t>(
+ aidl::android::hardware::graphics::common::PlaneLayoutComponentType::R)};
+
+static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_G =
+ {GRALLOC4_STANDARD_PLANE_LAYOUT_COMPONENT_TYPE,
+ static_cast<int64_t>(
+ aidl::android::hardware::graphics::common::PlaneLayoutComponentType::G)};
+
+static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_B =
+ {GRALLOC4_STANDARD_PLANE_LAYOUT_COMPONENT_TYPE,
+ static_cast<int64_t>(
+ aidl::android::hardware::graphics::common::PlaneLayoutComponentType::B)};
+
+static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_A =
+ {GRALLOC4_STANDARD_PLANE_LAYOUT_COMPONENT_TYPE,
+ static_cast<int64_t>(
+ aidl::android::hardware::graphics::common::PlaneLayoutComponentType::A)};
+
+static const aidl::android::hardware::graphics::common::ExtendableType
+ PlaneLayoutComponentType_RAW =
+ {GRALLOC4_STANDARD_PLANE_LAYOUT_COMPONENT_TYPE,
+ static_cast<int64_t>(
+ aidl::android::hardware::graphics::common::PlaneLayoutComponentType::RAW)};
+
+/*---------------------------------------------------------------------------------------------*/
+
+/**
+ * The functions below encode and decode BufferDescriptorInfo into a byte stream.
+ */
+status_t encodeBufferDescriptorInfo(const android::hardware::graphics::mapper::V4_0::IMapper::BufferDescriptorInfo& bufferDescriptorInfo, android::hardware::hidl_vec<uint8_t>* outBufferDescriptorInfo);
+status_t decodeBufferDescriptorInfo(const android::hardware::hidl_vec<uint8_t>& bufferDescriptorInfo, android::hardware::graphics::mapper::V4_0::IMapper::BufferDescriptorInfo* outBufferDescriptorInfo);
+
+/**
+ * The functions below encode and decode standard metadata into a byte stream. It is STRONGLY
+ * recommended that both the vendor and system partitions use these functions when getting
+ * and setting metadata through gralloc 4 (IMapper 4.0).
+ */
+status_t encodeBufferId(uint64_t bufferId, android::hardware::hidl_vec<uint8_t>* outBufferId);
+status_t decodeBufferId(const android::hardware::hidl_vec<uint8_t>& bufferId, uint64_t* outBufferId);
+
+status_t encodeName(const std::string& name, android::hardware::hidl_vec<uint8_t>* outName);
+status_t decodeName(const android::hardware::hidl_vec<uint8_t>& name, std::string* outName);
+
+status_t encodeWidth(uint64_t width, android::hardware::hidl_vec<uint8_t>* outWidth);
+status_t decodeWidth(const android::hardware::hidl_vec<uint8_t>& width, uint64_t* outWidth);
+
+status_t encodeHeight(uint64_t height, android::hardware::hidl_vec<uint8_t>* outHeight);
+status_t decodeHeight(const android::hardware::hidl_vec<uint8_t>& height, uint64_t* outHeight);
+
+status_t encodeLayerCount(uint64_t layerCount, android::hardware::hidl_vec<uint8_t>* outLayerCount);
+status_t decodeLayerCount(const android::hardware::hidl_vec<uint8_t>& layerCount, uint64_t* outLayerCount);
+
+status_t encodePixelFormatRequested(const hardware::graphics::common::V1_2::PixelFormat& pixelFormatRequested, android::hardware::hidl_vec<uint8_t>* outPixelFormatRequested);
+status_t decodePixelFormatRequested(const android::hardware::hidl_vec<uint8_t>& pixelFormatRequested, hardware::graphics::common::V1_2::PixelFormat* outPixelFormatRequested);
+
+status_t encodePixelFormatFourCC(uint32_t pixelFormatFourCC, android::hardware::hidl_vec<uint8_t>* outPixelFormatFourCC);
+status_t decodePixelFormatFourCC(const android::hardware::hidl_vec<uint8_t>& pixelFormatFourCC, uint32_t* outPixelFormatFourCC);
+
+status_t encodePixelFormatModifier(uint64_t pixelFormatModifier, android::hardware::hidl_vec<uint8_t>* outPixelFormatModifier);
+status_t decodePixelFormatModifier(const android::hardware::hidl_vec<uint8_t>& pixelFormatModifier, uint64_t* outPixelFormatModifier);
+
+status_t encodeUsage(uint64_t usage, android::hardware::hidl_vec<uint8_t>* outUsage);
+status_t decodeUsage(const android::hardware::hidl_vec<uint8_t>& usage, uint64_t* outUsage);
+
+status_t encodeAllocationSize(uint64_t allocationSize, android::hardware::hidl_vec<uint8_t>* outAllocationSize);
+status_t decodeAllocationSize(const android::hardware::hidl_vec<uint8_t>& allocationSize, uint64_t* outAllocationSize);
+
+status_t encodeProtectedContent(uint64_t protectedContent, android::hardware::hidl_vec<uint8_t>* outProtectedContent);
+status_t decodeProtectedContent(const android::hardware::hidl_vec<uint8_t>& protectedContent, uint64_t* outProtectedContent);
+
+status_t encodeCompression(const aidl::android::hardware::graphics::common::ExtendableType& compression, android::hardware::hidl_vec<uint8_t>* outCompression);
+status_t decodeCompression(const android::hardware::hidl_vec<uint8_t>& compression, aidl::android::hardware::graphics::common::ExtendableType* outCompression);
+
+status_t encodeInterlaced(const aidl::android::hardware::graphics::common::ExtendableType& interlaced, android::hardware::hidl_vec<uint8_t>* outInterlaced);
+status_t decodeInterlaced(const android::hardware::hidl_vec<uint8_t>& interlaced, aidl::android::hardware::graphics::common::ExtendableType* outInterlaced);
+
+status_t encodeChromaSiting(const aidl::android::hardware::graphics::common::ExtendableType& chromaSiting, android::hardware::hidl_vec<uint8_t>* outChromaSiting);
+status_t decodeChromaSiting(const android::hardware::hidl_vec<uint8_t>& chromaSiting, aidl::android::hardware::graphics::common::ExtendableType* outChromaSiting);
+
+status_t encodePlaneLayouts(const std::vector<aidl::android::hardware::graphics::common::PlaneLayout>& planeLayouts, android::hardware::hidl_vec<uint8_t>* outPlaneLayouts);
+status_t decodePlaneLayouts(const android::hardware::hidl_vec<uint8_t>& planeLayouts, std::vector<aidl::android::hardware::graphics::common::PlaneLayout>* outPlaneLayouts);
+
+status_t encodeCrop(const std::vector<aidl::android::hardware::graphics::common::Rect>& crop, android::hardware::hidl_vec<uint8_t>* outCrop);
+status_t decodeCrop(const android::hardware::hidl_vec<uint8_t>& crop, std::vector<aidl::android::hardware::graphics::common::Rect>* outCrop);
+
+status_t encodeDataspace(const aidl::android::hardware::graphics::common::Dataspace& dataspace, android::hardware::hidl_vec<uint8_t>* outDataspace);
+status_t decodeDataspace(const android::hardware::hidl_vec<uint8_t>& dataspace, aidl::android::hardware::graphics::common::Dataspace* outDataspace);
+
+status_t encodeBlendMode(const aidl::android::hardware::graphics::common::BlendMode& blendMode, android::hardware::hidl_vec<uint8_t>* outBlendMode);
+status_t decodeBlendMode(const android::hardware::hidl_vec<uint8_t>& blendMode, aidl::android::hardware::graphics::common::BlendMode* outBlendMode);
+
+status_t encodeSmpte2086(
+ const std::optional<aidl::android::hardware::graphics::common::Smpte2086>& smpte2086,
+ android::hardware::hidl_vec<uint8_t>* outSmpte2086);
+status_t decodeSmpte2086(
+ const android::hardware::hidl_vec<uint8_t>& smpte2086,
+ std::optional<aidl::android::hardware::graphics::common::Smpte2086>* outSmpte2086);
+
+status_t encodeCta861_3(
+ const std::optional<aidl::android::hardware::graphics::common::Cta861_3>& cta861_3,
+ android::hardware::hidl_vec<uint8_t>* outCta861_3);
+status_t decodeCta861_3(
+ const android::hardware::hidl_vec<uint8_t>& cta861_3,
+ std::optional<aidl::android::hardware::graphics::common::Cta861_3>* outCta861_3);
+
+status_t encodeSmpte2094_40(const std::optional<std::vector<uint8_t>>& smpte2094_40,
+ android::hardware::hidl_vec<uint8_t>* outSmpte2094_40);
+status_t decodeSmpte2094_40(const android::hardware::hidl_vec<uint8_t>& smpte2094_40,
+ std::optional<std::vector<uint8_t>>* outSmpte2094_40);
+
+/**
+ * The functions below can be used to encode and decode vendor metadata types.
+ */
+status_t encodeUint32(
+ const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+ uint32_t input, android::hardware::hidl_vec<uint8_t>* output);
+status_t decodeUint32(
+ const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+ const android::hardware::hidl_vec<uint8_t>& input, uint32_t* output);
+
+status_t encodeInt32(
+ const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+ int32_t input, android::hardware::hidl_vec<uint8_t>* output);
+status_t decodeInt32(
+ const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+ const android::hardware::hidl_vec<uint8_t>& input, int32_t* output);
+
+status_t encodeUint64(
+ const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+ uint64_t input, android::hardware::hidl_vec<uint8_t>* output);
+status_t decodeUint64(
+ const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+ const android::hardware::hidl_vec<uint8_t>& input, uint64_t* output);
+
+status_t encodeInt64(
+ const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+ int64_t input, android::hardware::hidl_vec<uint8_t>* output);
+status_t decodeInt64(
+ const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+ const android::hardware::hidl_vec<uint8_t>& input, int64_t* output);
+
+status_t encodeFloat(
+ const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+ float input, android::hardware::hidl_vec<uint8_t>* output);
+status_t decodeFloat(
+ const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+ const android::hardware::hidl_vec<uint8_t>& input, float* output);
+
+status_t encodeDouble(
+ const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+ double input, android::hardware::hidl_vec<uint8_t>* output);
+status_t decodeDouble(
+ const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+ const android::hardware::hidl_vec<uint8_t>& input, double* output);
+
+status_t encodeString(
+ const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+ const std::string& input, android::hardware::hidl_vec<uint8_t>* output);
+status_t decodeString(
+ const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+ const android::hardware::hidl_vec<uint8_t>& input, std::string* output);
+
+/**
+ * The functions below can be used to parse extendable types.
+ */
+bool isStandardMetadataType(
+ const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType);
+bool isStandardCompression(
+ const aidl::android::hardware::graphics::common::ExtendableType& compression);
+bool isStandardInterlaced(
+ const aidl::android::hardware::graphics::common::ExtendableType& interlaced);
+bool isStandardChromaSiting(
+ const aidl::android::hardware::graphics::common::ExtendableType& chromaSiting);
+bool isStandardPlaneLayoutComponentType(
+ const aidl::android::hardware::graphics::common::ExtendableType& planeLayoutComponentType);
+
+aidl::android::hardware::graphics::common::StandardMetadataType getStandardMetadataTypeValue(
+ const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType);
+aidl::android::hardware::graphics::common::Compression getStandardCompressionValue(
+ const aidl::android::hardware::graphics::common::ExtendableType& compression);
+aidl::android::hardware::graphics::common::Interlaced getStandardInterlacedValue(
+ const aidl::android::hardware::graphics::common::ExtendableType& interlaced);
+aidl::android::hardware::graphics::common::ChromaSiting getStandardChromaSitingValue(
+ const aidl::android::hardware::graphics::common::ExtendableType& chromaSiting);
+aidl::android::hardware::graphics::common::PlaneLayoutComponentType
+getStandardPlaneLayoutComponentTypeValue(
+ const aidl::android::hardware::graphics::common::ExtendableType& planeLayoutComponentType);
+
+/**
+ * The functions below return string representations of ExtendableTypes
+ */
+std::string getCompressionName(
+ const aidl::android::hardware::graphics::common::ExtendableType& compression);
+std::string getInterlacedName(
+ const aidl::android::hardware::graphics::common::ExtendableType& interlaced);
+std::string getChromaSitingName(
+ const aidl::android::hardware::graphics::common::ExtendableType& chromaSiting);
+std::string getPlaneLayoutComponentTypeName(
+ const aidl::android::hardware::graphics::common::ExtendableType& planeLayoutComponentType);
+
+} // namespace gralloc4
+
+} // namespace android
diff --git a/libs/gralloc/types/tests/Android.bp b/libs/gralloc/types/tests/Android.bp
new file mode 100644
index 0000000..b939c1d
--- /dev/null
+++ b/libs/gralloc/types/tests/Android.bp
@@ -0,0 +1,25 @@
+//
+// Copyright 2019 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.
+//
+
+cc_test {
+ name: "GrallocTypes_test",
+ shared_libs: [
+ "libgralloctypes",
+ "libhidlbase",
+ ],
+ srcs: ["Gralloc4_test.cpp"],
+ cflags: ["-Wall", "-Werror"],
+}
diff --git a/libs/gralloc/types/tests/Gralloc4_test.cpp b/libs/gralloc/types/tests/Gralloc4_test.cpp
new file mode 100644
index 0000000..89cbf4a
--- /dev/null
+++ b/libs/gralloc/types/tests/Gralloc4_test.cpp
@@ -0,0 +1,585 @@
+/*
+ * Copyright 2019 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 "Gralloc4Test"
+
+#include <limits>
+
+#include <gralloctypes/Gralloc4.h>
+
+#include <gtest/gtest.h>
+
+using android::hardware::hidl_vec;
+
+using android::hardware::graphics::common::V1_2::PixelFormat;
+using android::hardware::graphics::common::V1_2::BufferUsage;
+
+using aidl::android::hardware::graphics::common::BlendMode;
+using aidl::android::hardware::graphics::common::ChromaSiting;
+using aidl::android::hardware::graphics::common::Compression;
+using aidl::android::hardware::graphics::common::Cta861_3;
+using aidl::android::hardware::graphics::common::Dataspace;
+using aidl::android::hardware::graphics::common::ExtendableType;
+using aidl::android::hardware::graphics::common::Interlaced;
+using aidl::android::hardware::graphics::common::PlaneLayout;
+using aidl::android::hardware::graphics::common::PlaneLayoutComponent;
+using aidl::android::hardware::graphics::common::PlaneLayoutComponentType;
+using aidl::android::hardware::graphics::common::Rect;
+using aidl::android::hardware::graphics::common::Smpte2086;
+using aidl::android::hardware::graphics::common::StandardMetadataType;
+using aidl::android::hardware::graphics::common::XyColor;
+
+using BufferDescriptorInfo = android::hardware::graphics::mapper::V4_0::IMapper::BufferDescriptorInfo;
+using MetadataType = android::hardware::graphics::mapper::V4_0::IMapper::MetadataType;
+
+namespace android {
+
+template<class T>
+using EncodeFunction = status_t(*)(T, hidl_vec<uint8_t>*);
+
+template<class T>
+using EncodeConstFunction = status_t(*)(const T&, hidl_vec<uint8_t>*);
+
+template<class T>
+using EncodeMetadataTypeFunction = status_t(*)(const MetadataType&, T, hidl_vec<uint8_t>*);
+
+template<class T>
+using EncodeMetadataTypeConstFunction = status_t(*)(const MetadataType&, const T&, hidl_vec<uint8_t>*);
+
+template<class T>
+using EncodeOptionalFunction = status_t(*)(const std::optional<T>&, hidl_vec<uint8_t>*);
+
+template<class T>
+using DecodeFunction = status_t(*)(const hidl_vec<uint8_t>&, T*);
+
+template<class T>
+using DecodeMetadataTypeFunction = status_t(*)(const MetadataType&, const hidl_vec<uint8_t>&, T*);
+
+template<class T>
+using DecodeOptionalFunction = status_t(*)(const hidl_vec<uint8_t>&, std::optional<T>*);
+
+template<class T>
+void testHelper(const T& input, EncodeFunction<T> encode, DecodeFunction<T> decode) {
+ hidl_vec<uint8_t> vec;
+ T output;
+ ASSERT_EQ(NO_ERROR, encode(input, &vec));
+ ASSERT_EQ(NO_ERROR, decode(vec, &output));
+ ASSERT_EQ(input, output);
+}
+
+template<class T>
+void testHelperConst(const T& input, EncodeConstFunction<T> encode, DecodeFunction<T> decode) {
+ hidl_vec<uint8_t> vec;
+ T output;
+ ASSERT_EQ(NO_ERROR, encode(input, &vec));
+ ASSERT_EQ(NO_ERROR, decode(vec, &output));
+ ASSERT_EQ(input, output);
+}
+
+template<class T>
+void testHelperMetadataType(const T& input, EncodeMetadataTypeFunction<T> encode, DecodeMetadataTypeFunction<T> decode) {
+ hidl_vec<uint8_t> vec;
+ MetadataType metadataType{"vendor.mycompanyname.graphics.common.MetadataType", 0};
+ T output;
+ ASSERT_EQ(NO_ERROR, encode(metadataType, input, &vec));
+ ASSERT_EQ(NO_ERROR, decode(metadataType, vec, &output));
+ ASSERT_EQ(input, output);
+}
+
+template<class T>
+void testHelperMetadataTypeConst(const T& input, EncodeMetadataTypeConstFunction<T> encode, DecodeMetadataTypeFunction<T> decode) {
+ hidl_vec<uint8_t> vec;
+ MetadataType metadataType{"vendor.mycompanyname.graphics.common.MetadataType", 0};
+ T output;
+ ASSERT_EQ(NO_ERROR, encode(metadataType, input, &vec));
+ ASSERT_EQ(NO_ERROR, decode(metadataType, vec, &output));
+ ASSERT_EQ(input, output);
+}
+
+template<class T>
+void testHelperStableAidlType(const T& input, EncodeConstFunction<T> encode, DecodeFunction<T> decode) {
+ hidl_vec<uint8_t> vec;
+ T output;
+ ASSERT_EQ(NO_ERROR, encode(input, &vec));
+ ASSERT_EQ(NO_ERROR, decode(vec, &output));
+ ASSERT_TRUE(input == output);
+}
+
+template<class T>
+void testHelperStableAidlTypeOptional(const std::optional<T>& input, EncodeOptionalFunction<T> encode,
+ DecodeOptionalFunction<T> decode) {
+ hidl_vec<uint8_t> vec;
+ std::optional<T> tmp = input;
+ std::optional<T> output;
+ ASSERT_EQ(NO_ERROR, encode(tmp, &vec));
+ ASSERT_EQ(NO_ERROR, decode(vec, &output));
+ ASSERT_EQ(tmp.has_value(), output.has_value());
+ if (!tmp.has_value()) {
+ return;
+ }
+ ASSERT_TRUE(*tmp == *output);
+}
+
+class Gralloc4TestUint32 : public testing::TestWithParam<uint32_t> { };
+
+INSTANTIATE_TEST_CASE_P(
+ Gralloc4TestUint32Params, Gralloc4TestUint32,
+ ::testing::Values(0, -1, 1, 5, 100, 0xFF, std::numeric_limits<uint32_t>::min(),
+ std::numeric_limits<uint32_t>::max()));
+
+TEST_P(Gralloc4TestUint32, Uint32) {
+ ASSERT_NO_FATAL_FAILURE(testHelperMetadataType(GetParam(), gralloc4::encodeUint32, gralloc4::decodeUint32));
+}
+
+TEST_P(Gralloc4TestUint32, PixelFormatFourCC) {
+ ASSERT_NO_FATAL_FAILURE(testHelper(GetParam(), gralloc4::encodePixelFormatFourCC, gralloc4::decodePixelFormatFourCC));
+}
+
+class Gralloc4TestInt32 : public testing::TestWithParam<int32_t> { };
+
+INSTANTIATE_TEST_CASE_P(
+ Gralloc4TestInt32Params, Gralloc4TestInt32,
+ ::testing::Values(0, 1, 5, 100, 0xFF, std::numeric_limits<int32_t>::min(),
+ std::numeric_limits<int32_t>::max()));
+
+TEST_P(Gralloc4TestInt32, Int32) {
+ ASSERT_NO_FATAL_FAILURE(testHelperMetadataType(GetParam(), gralloc4::encodeInt32, gralloc4::decodeInt32));
+}
+
+class Gralloc4TestUint64 : public testing::TestWithParam<uint64_t> { };
+
+INSTANTIATE_TEST_CASE_P(
+ Gralloc4TestUint64Params, Gralloc4TestUint64,
+ ::testing::Values(0, -1, 1, 5, 100, 0xFF, std::numeric_limits<uint64_t>::min(),
+ std::numeric_limits<uint64_t>::max()));
+
+TEST_P(Gralloc4TestUint64, Uint64) {
+ ASSERT_NO_FATAL_FAILURE(testHelperMetadataType(GetParam(), gralloc4::encodeUint64, gralloc4::decodeUint64));
+}
+
+TEST_P(Gralloc4TestUint64, BufferId) {
+ ASSERT_NO_FATAL_FAILURE(testHelper(GetParam(), gralloc4::encodeBufferId, gralloc4::decodeBufferId));
+}
+
+TEST_P(Gralloc4TestUint64, Width) {
+ ASSERT_NO_FATAL_FAILURE(testHelper(GetParam(), gralloc4::encodeWidth, gralloc4::decodeWidth));
+}
+
+TEST_P(Gralloc4TestUint64, Height) {
+ ASSERT_NO_FATAL_FAILURE(testHelper(GetParam(), gralloc4::encodeHeight, gralloc4::decodeHeight));
+}
+
+TEST_P(Gralloc4TestUint64, LayerCount) {
+ ASSERT_NO_FATAL_FAILURE(testHelper(GetParam(), gralloc4::encodeLayerCount, gralloc4::decodeLayerCount));
+}
+
+TEST_P(Gralloc4TestUint64, PixelFormatModifier) {
+ ASSERT_NO_FATAL_FAILURE(testHelper(GetParam(), gralloc4::encodePixelFormatModifier, gralloc4::decodePixelFormatModifier));
+}
+
+TEST_P(Gralloc4TestUint64, Usage) {
+ ASSERT_NO_FATAL_FAILURE(testHelper(GetParam(), gralloc4::encodeUsage, gralloc4::decodeUsage));
+}
+
+TEST_P(Gralloc4TestUint64, AllocationSize) {
+ ASSERT_NO_FATAL_FAILURE(testHelper(GetParam(), gralloc4::encodeAllocationSize, gralloc4::decodeAllocationSize));
+}
+
+TEST_P(Gralloc4TestUint64, ProtectedContent) {
+ ASSERT_NO_FATAL_FAILURE(testHelper(GetParam(), gralloc4::encodeProtectedContent, gralloc4::decodeProtectedContent));
+}
+
+class Gralloc4TestInt64 : public testing::TestWithParam<int64_t> { };
+
+INSTANTIATE_TEST_CASE_P(
+ Gralloc4TestInt64Params, Gralloc4TestInt64,
+ ::testing::Values(0, 1, 5, 100, 0xFF, std::numeric_limits<int64_t>::min(),
+ std::numeric_limits<int64_t>::max()));
+
+TEST_P(Gralloc4TestInt64, Int64) {
+ ASSERT_NO_FATAL_FAILURE(testHelperMetadataType(GetParam(), gralloc4::encodeInt64, gralloc4::decodeInt64));
+}
+
+class Gralloc4TestFloat : public testing::TestWithParam<float> { };
+
+INSTANTIATE_TEST_CASE_P(
+ Gralloc4TestFloatParams, Gralloc4TestFloat,
+ ::testing::Values(0.0, 1.999999, 5.5, 100.1, 1234.5678, std::numeric_limits<float>::min(),
+ std::numeric_limits<float>::max()));
+
+TEST_P(Gralloc4TestFloat, Float) {
+ ASSERT_NO_FATAL_FAILURE(testHelperMetadataType(GetParam(), gralloc4::encodeFloat, gralloc4::decodeFloat));
+}
+
+class Gralloc4TestDouble : public testing::TestWithParam<double> { };
+
+INSTANTIATE_TEST_CASE_P(
+ Gralloc4TestDoubleParams, Gralloc4TestDouble,
+ ::testing::Values(0.0, 1.999999, 5.5, 100.1, 1234.5678, std::numeric_limits<double>::min(),
+ std::numeric_limits<double>::max()));
+
+TEST_P(Gralloc4TestDouble, Double) {
+ ASSERT_NO_FATAL_FAILURE(testHelperMetadataType(GetParam(), gralloc4::encodeDouble, gralloc4::decodeDouble));
+}
+
+class Gralloc4TestString : public testing::TestWithParam<std::string> { };
+
+INSTANTIATE_TEST_CASE_P(
+ Gralloc4TestStringParams, Gralloc4TestString,
+ ::testing::Values("name", "aaaaa", "", "abcdefghijklmnopqrstuvwxyz", "0xFF"));
+
+TEST_P(Gralloc4TestString, String) {
+ ASSERT_NO_FATAL_FAILURE(testHelperMetadataTypeConst(GetParam(), gralloc4::encodeString, gralloc4::decodeString));
+}
+
+TEST_P(Gralloc4TestString, Name) {
+ ASSERT_NO_FATAL_FAILURE(testHelperConst(GetParam(), gralloc4::encodeName, gralloc4::decodeName));
+}
+
+class Gralloc4TestPixelFormat : public testing::TestWithParam<PixelFormat> { };
+
+INSTANTIATE_TEST_CASE_P(
+ Gralloc4TestPixelFormatParams, Gralloc4TestPixelFormat,
+ ::testing::Values(PixelFormat::RGBA_8888, PixelFormat::BLOB,
+ PixelFormat::IMPLEMENTATION_DEFINED, PixelFormat::YCBCR_420_888,
+ PixelFormat::YV12));
+
+TEST_P(Gralloc4TestPixelFormat, PixelFormatRequested) {
+ ASSERT_NO_FATAL_FAILURE(testHelperConst(GetParam(), gralloc4::encodePixelFormatRequested, gralloc4::decodePixelFormatRequested));
+}
+
+class Gralloc4TestCompression : public testing::TestWithParam<ExtendableType> { };
+
+INSTANTIATE_TEST_CASE_P(
+ Gralloc4TestCompressionParams, Gralloc4TestCompression,
+ ::testing::Values(gralloc4::Compression_None, gralloc4::Compression_DisplayStreamCompression,
+ ExtendableType{"", 0},
+ ExtendableType{"vendor.mycompanyname.graphics.common.Compression", 0xFF},
+ ExtendableType{"vendor.mycompanyname.graphics.common.Compression", std::numeric_limits<int64_t>::max()}));
+
+TEST_P(Gralloc4TestCompression, Compression) {
+ ASSERT_NO_FATAL_FAILURE(testHelperStableAidlType(GetParam(), gralloc4::encodeCompression, gralloc4::decodeCompression));
+}
+
+class Gralloc4TestInterlaced : public testing::TestWithParam<ExtendableType> { };
+
+INSTANTIATE_TEST_CASE_P(
+ Gralloc4TestInterlacedParams, Gralloc4TestInterlaced,
+ ::testing::Values(gralloc4::Interlaced_None, gralloc4::Interlaced_TopBottom,
+ gralloc4::Interlaced_RightLeft,
+ ExtendableType{"", 0},
+ ExtendableType{"vendor.mycompanyname.graphics.common.Interlaced", 0xFF},
+ ExtendableType{"vendor.mycompanyname.graphics.common.Interlaced", std::numeric_limits<int64_t>::max()}));
+
+TEST_P(Gralloc4TestInterlaced, Interlaced) {
+ ASSERT_NO_FATAL_FAILURE(testHelperStableAidlType(GetParam(), gralloc4::encodeInterlaced, gralloc4::decodeInterlaced));
+}
+
+class Gralloc4TestChromaSiting : public testing::TestWithParam<ExtendableType> { };
+
+INSTANTIATE_TEST_CASE_P(
+ Gralloc4TestChromaSitingParams, Gralloc4TestChromaSiting,
+ ::testing::Values(gralloc4::ChromaSiting_None, gralloc4::ChromaSiting_Unknown,
+ gralloc4::ChromaSiting_SitedInterstitial, gralloc4::ChromaSiting_CositedHorizontal,
+ ExtendableType{"", 0},
+ ExtendableType{"vendor.mycompanyname.graphics.common.ChromaSiting", 0xFF},
+ ExtendableType{"vendor.mycompanyname.graphics.common.ChromaSiting", std::numeric_limits<int64_t>::max()}));
+
+TEST_P(Gralloc4TestChromaSiting, ChromaSiting) {
+ ASSERT_NO_FATAL_FAILURE(testHelperStableAidlType(GetParam(), gralloc4::encodeChromaSiting, gralloc4::decodeChromaSiting));
+}
+
+class Gralloc4TestPlaneLayouts : public testing::Test { };
+
+TEST_F(Gralloc4TestPlaneLayouts, PlaneLayouts) {
+ uint32_t width = 64;
+ uint32_t height = 64;
+
+ std::vector<PlaneLayout> planeLayouts;
+ PlaneLayout planeLayoutA;
+ PlaneLayout planeLayoutRGB;
+ PlaneLayoutComponent component;
+
+ planeLayoutA.offsetInBytes = 0;
+ planeLayoutA.sampleIncrementInBits = 8;
+ planeLayoutA.strideInBytes = width + 20;
+ planeLayoutA.widthInSamples = width;
+ planeLayoutA.heightInSamples = height;
+ planeLayoutA.totalSizeInBytes = planeLayoutA.strideInBytes * height;
+ planeLayoutA.horizontalSubsampling = 1;
+ planeLayoutA.verticalSubsampling = 1;
+
+ component.type = gralloc4::PlaneLayoutComponentType_A;
+ component.offsetInBits = 0;
+ component.sizeInBits = 8;
+ planeLayoutA.components.push_back(component);
+
+ planeLayouts.push_back(planeLayoutA);
+
+ planeLayoutRGB.offsetInBytes = 0;
+ planeLayoutRGB.sampleIncrementInBits = 32;
+ planeLayoutRGB.strideInBytes = width + 20;
+ planeLayoutRGB.widthInSamples = width;
+ planeLayoutRGB.heightInSamples = height;
+ planeLayoutRGB.totalSizeInBytes = planeLayoutRGB.strideInBytes * height;
+ planeLayoutRGB.horizontalSubsampling = 1;
+ planeLayoutRGB.verticalSubsampling = 1;
+ component.type = gralloc4::PlaneLayoutComponentType_R;
+ planeLayoutRGB.components.push_back(component);
+ component.type = gralloc4::PlaneLayoutComponentType_G;
+ planeLayoutRGB.components.push_back(component);
+ component.type = gralloc4::PlaneLayoutComponentType_B;
+ planeLayoutRGB.components.push_back(component);
+
+ planeLayouts.push_back(planeLayoutRGB);
+
+ ASSERT_NO_FATAL_FAILURE(testHelperStableAidlType(planeLayouts, gralloc4::encodePlaneLayouts, gralloc4::decodePlaneLayouts));
+}
+
+class Gralloc4TestCrop : public testing::Test { };
+
+TEST_F(Gralloc4TestCrop, Crop) {
+ std::vector<Rect> crops;
+ Rect crop1, crop2, crop3;
+
+ crop1.left = 0;
+ crop1.top = 0;
+ crop1.right = 64;
+ crop1.bottom = 64;
+ crops.push_back(crop1);
+
+ crop2.left = std::numeric_limits<int32_t>::min();
+ crop2.top = 0xFF;
+ crop2.right = std::numeric_limits<int32_t>::max();
+ crop2.bottom = 0xFFFF;
+ crops.push_back(crop2);
+
+ crop3.left = 0;
+ crop3.top = 0;
+ crop3.right = -1;
+ crop3.bottom = -1;
+ crops.push_back(crop3);
+
+ ASSERT_NO_FATAL_FAILURE(testHelperStableAidlType(crops, gralloc4::encodeCrop, gralloc4::decodeCrop));
+}
+
+class Gralloc4TestDataspace : public testing::TestWithParam<Dataspace> { };
+
+INSTANTIATE_TEST_CASE_P(
+ Gralloc4TestDataspaceParams, Gralloc4TestDataspace,
+ ::testing::Values(Dataspace::UNKNOWN, Dataspace::ARBITRARY, Dataspace::DISPLAY_P3,
+ Dataspace::ADOBE_RGB));
+
+TEST_P(Gralloc4TestDataspace, DataspaceRequested) {
+ ASSERT_NO_FATAL_FAILURE(testHelperConst(GetParam(), gralloc4::encodeDataspace, gralloc4::decodeDataspace));
+}
+
+class Gralloc4TestBlendMode : public testing::TestWithParam<BlendMode> { };
+
+INSTANTIATE_TEST_CASE_P(
+ Gralloc4TestBlendModeParams, Gralloc4TestBlendMode,
+ ::testing::Values(BlendMode::INVALID, BlendMode::NONE, BlendMode::PREMULTIPLIED,
+ BlendMode::COVERAGE));
+
+TEST_P(Gralloc4TestBlendMode, BlendMode) {
+ ASSERT_NO_FATAL_FAILURE(testHelperConst(GetParam(), gralloc4::encodeBlendMode, gralloc4::decodeBlendMode));
+}
+
+class Gralloc4TestSmpte2086 : public testing::TestWithParam<std::optional<Smpte2086>> { };
+
+INSTANTIATE_TEST_CASE_P(
+ Gralloc4TestSmpte2086Params, Gralloc4TestSmpte2086,
+ ::testing::Values(std::optional<Smpte2086>(Smpte2086{XyColor{0.680, 0.320},
+ XyColor{0.265, 0.690},
+ XyColor{0.150, 0.060},
+ XyColor{0.3127, 0.3290},
+ 100.0, 0.1}),
+ std::optional<Smpte2086>(Smpte2086{XyColor{-1.0, 100.0},
+ XyColor{0xFF, -0xFF},
+ XyColor{999.9, 0.0},
+ XyColor{0.0, -1.0},
+ -0.1, -100.0}),
+ std::nullopt));
+
+TEST_P(Gralloc4TestSmpte2086, Smpte2086) {
+ ASSERT_NO_FATAL_FAILURE(testHelperStableAidlTypeOptional(GetParam(), gralloc4::encodeSmpte2086, gralloc4::decodeSmpte2086));
+}
+
+class Gralloc4TestCta861_3 : public testing::TestWithParam<std::optional<Cta861_3>> { };
+
+INSTANTIATE_TEST_CASE_P(
+ Gralloc4TestCta861_3Params, Gralloc4TestCta861_3,
+ ::testing::Values(std::optional<Cta861_3>(Cta861_3{78.0, 62.0}),
+ std::optional<Cta861_3>(Cta861_3{10.0, 10.0}),
+ std::optional<Cta861_3>(Cta861_3{0.0, 0.0}),
+ std::optional<Cta861_3>(Cta861_3{std::numeric_limits<float>::min(), std::numeric_limits<float>::min()}),
+ std::optional<Cta861_3>(Cta861_3{std::numeric_limits<float>::max(), std::numeric_limits<float>::max()}),
+ std::nullopt));
+
+TEST_P(Gralloc4TestCta861_3, Cta861_3) {
+ ASSERT_NO_FATAL_FAILURE(testHelperStableAidlTypeOptional(GetParam(), gralloc4::encodeCta861_3, gralloc4::decodeCta861_3));
+}
+
+class Gralloc4TestSmpte2094_40 : public testing::TestWithParam<std::optional<std::vector<uint8_t>>> { };
+
+INSTANTIATE_TEST_CASE_P(
+ Gralloc4TestSmpte2094_40Params, Gralloc4TestSmpte2094_40,
+ ::testing::Values(std::optional<std::vector<uint8_t>>({}),
+ std::optional<std::vector<uint8_t>>({0, 1, 2, 3, 4, 5, 6, 7, 8, 9}),
+ std::optional<std::vector<uint8_t>>({std::numeric_limits<uint8_t>::min(),
+ std::numeric_limits<uint8_t>::min() + 1,
+ std::numeric_limits<uint8_t>::min() + 2,
+ std::numeric_limits<uint8_t>::min() + 3,
+ std::numeric_limits<uint8_t>::min() + 4}),
+ std::optional<std::vector<uint8_t>>({std::numeric_limits<uint8_t>::max(),
+ std::numeric_limits<uint8_t>::max() - 1,
+ std::numeric_limits<uint8_t>::max() - 2,
+ std::numeric_limits<uint8_t>::max() - 3,
+ std::numeric_limits<uint8_t>::max() - 4}),
+ std::nullopt));
+
+TEST_P(Gralloc4TestSmpte2094_40, Smpte2094_40) {
+ ASSERT_NO_FATAL_FAILURE(testHelperStableAidlTypeOptional(GetParam(), gralloc4::encodeSmpte2094_40, gralloc4::decodeSmpte2094_40));
+}
+
+class Gralloc4TestBufferDescriptorInfo : public testing::TestWithParam<BufferDescriptorInfo> { };
+
+INSTANTIATE_TEST_CASE_P(
+ Gralloc4TestBufferDescriptorInfoParams, Gralloc4TestBufferDescriptorInfo,
+ ::testing::Values(BufferDescriptorInfo{"BufferName", 64, 64, 1,
+ PixelFormat::RGBA_8888,
+ static_cast<uint64_t>(BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN),
+ 1024}));
+
+TEST_P(Gralloc4TestBufferDescriptorInfo, BufferDescriptorInfo) {
+ ASSERT_NO_FATAL_FAILURE(testHelperConst(GetParam(), gralloc4::encodeBufferDescriptorInfo, gralloc4::decodeBufferDescriptorInfo));
+}
+
+class Gralloc4TestErrors : public testing::Test { };
+
+TEST_F(Gralloc4TestErrors, Gralloc4TestEncodeNull) {
+ ASSERT_NE(NO_ERROR, gralloc4::encodeBufferId(0, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodeName("", nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodeWidth(0, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodeHeight(0, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodeLayerCount(0, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodePixelFormatRequested(PixelFormat::RGBA_8888, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodePixelFormatFourCC(0, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodePixelFormatModifier(0, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodeUsage(0, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodeAllocationSize(0, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodeProtectedContent(0, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodeCompression(gralloc4::Compression_None, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodeInterlaced(gralloc4::Interlaced_None, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodeChromaSiting(gralloc4::ChromaSiting_None, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodePlaneLayouts({}, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodeDataspace(Dataspace::UNKNOWN, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodeBlendMode(BlendMode::NONE, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodeSmpte2086({{}}, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodeCta861_3({{}}, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodeSmpte2094_40({{}}, nullptr));
+}
+
+TEST_F(Gralloc4TestErrors, Gralloc4TestDecodeNull) {
+ hidl_vec<uint8_t> vec;
+
+ ASSERT_NE(NO_ERROR, gralloc4::decodeBufferId(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeName(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeWidth(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeHeight(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeLayerCount(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodePixelFormatRequested(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodePixelFormatFourCC(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodePixelFormatModifier(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeUsage(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeAllocationSize(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeProtectedContent(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeCompression(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeInterlaced(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeChromaSiting(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodePlaneLayouts(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeDataspace(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeBlendMode(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeSmpte2086(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeCta861_3(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeSmpte2094_40(vec, nullptr));
+}
+
+TEST_F(Gralloc4TestErrors, Gralloc4TestDecodeBadVec) {
+ hidl_vec<uint8_t> vec = { 0 };
+
+ uint64_t bufferId, width, height, layerCount, pixelFormatModifier, usage, allocationSize,
+ protectedContent;
+ std::string name;
+ PixelFormat pixelFormatRequested;
+ uint32_t pixelFormatFourCC;
+ ExtendableType compression, interlaced, chromaSiting;
+ std::vector<PlaneLayout> planeLayouts;
+ Dataspace dataspace;
+ BlendMode blendMode;
+ std::optional<Smpte2086> smpte2086;
+ std::optional<Cta861_3> cta861_3;
+ std::optional<std::vector<uint8_t>> smpte2094_40;
+
+ ASSERT_NE(NO_ERROR, gralloc4::decodeBufferId(vec, &bufferId));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeName(vec, &name));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeWidth(vec, &width));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeHeight(vec, &height));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeLayerCount(vec, &layerCount));
+ ASSERT_NE(NO_ERROR, gralloc4::decodePixelFormatRequested(vec, &pixelFormatRequested));
+ ASSERT_NE(NO_ERROR, gralloc4::decodePixelFormatFourCC(vec, &pixelFormatFourCC));
+ ASSERT_NE(NO_ERROR, gralloc4::decodePixelFormatModifier(vec, &pixelFormatModifier));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeUsage(vec, &usage));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeAllocationSize(vec, &allocationSize));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeProtectedContent(vec, &protectedContent));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeCompression(vec, &compression));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeInterlaced(vec, &interlaced));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeChromaSiting(vec, &chromaSiting));
+ ASSERT_NE(NO_ERROR, gralloc4::decodePlaneLayouts(vec, &planeLayouts));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeDataspace(vec, &dataspace));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeBlendMode(vec, &blendMode));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeSmpte2086(vec, &smpte2086));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeCta861_3(vec, &cta861_3));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeSmpte2094_40(vec, &smpte2094_40));
+}
+
+class Gralloc4TestHelpers : public testing::Test { };
+
+TEST_F(Gralloc4TestHelpers, Gralloc4TestIsStandard) {
+ ASSERT_TRUE(gralloc4::isStandardMetadataType(gralloc4::MetadataType_BufferId));
+ ASSERT_TRUE(gralloc4::isStandardCompression(gralloc4::Compression_None));
+ ASSERT_TRUE(gralloc4::isStandardInterlaced(gralloc4::Interlaced_None));
+ ASSERT_TRUE(gralloc4::isStandardChromaSiting(gralloc4::ChromaSiting_None));
+ ASSERT_TRUE(gralloc4::isStandardPlaneLayoutComponentType(gralloc4::PlaneLayoutComponentType_Y));
+}
+
+TEST_F(Gralloc4TestHelpers, Gralloc4TestIsNotStandard) {
+ ASSERT_FALSE(gralloc4::isStandardMetadataType({"vendor.mycompanyname.graphics.common.MetadataType", 0}));
+ ASSERT_FALSE(gralloc4::isStandardCompression({"vendor.mycompanyname.graphics.common.Compression", 0}));
+ ASSERT_FALSE(gralloc4::isStandardInterlaced({"vendor.mycompanyname.graphics.common.Interlaced", 0}));
+ ASSERT_FALSE(gralloc4::isStandardChromaSiting({"vendor.mycompanyname.graphics.common.ChromaSiting", 0}));
+ ASSERT_FALSE(gralloc4::isStandardPlaneLayoutComponentType({"vendor.mycompanyname.graphics.common.PlaneLayoutComponentType", 0}));
+}
+
+TEST_F(Gralloc4TestHelpers, Gralloc4TestGetStandardValue) {
+ ASSERT_EQ(StandardMetadataType::BUFFER_ID, gralloc4::getStandardMetadataTypeValue(gralloc4::MetadataType_BufferId));
+ ASSERT_EQ(Compression::NONE, gralloc4::getStandardCompressionValue(gralloc4::Compression_None));
+ ASSERT_EQ(Interlaced::NONE, gralloc4::getStandardInterlacedValue(gralloc4::Interlaced_None));
+ ASSERT_EQ(ChromaSiting::NONE, gralloc4::getStandardChromaSitingValue(gralloc4::ChromaSiting_None));
+ ASSERT_EQ(PlaneLayoutComponentType::Y, gralloc4::getStandardPlaneLayoutComponentTypeValue(gralloc4::PlaneLayoutComponentType_Y));
+}
+
+} // namespace android
diff --git a/libs/graphicsenv/Android.bp b/libs/graphicsenv/Android.bp
index 56521bf..642c5f2 100644
--- a/libs/graphicsenv/Android.bp
+++ b/libs/graphicsenv/Android.bp
@@ -32,5 +32,9 @@
"libutils",
],
+ header_libs: [
+ "libnativeloader-headers",
+ ],
+
export_include_dirs: ["include"],
}
diff --git a/libs/graphicsenv/GpuStatsInfo.cpp b/libs/graphicsenv/GpuStatsInfo.cpp
index 4a801be..f2d0943 100644
--- a/libs/graphicsenv/GpuStatsInfo.cpp
+++ b/libs/graphicsenv/GpuStatsInfo.cpp
@@ -86,6 +86,8 @@
if ((status = parcel->writeInt64Vector(vkDriverLoadingTime)) != OK) return status;
if ((status = parcel->writeInt64Vector(angleDriverLoadingTime)) != OK) return status;
if ((status = parcel->writeBool(cpuVulkanInUse)) != OK) return status;
+ if ((status = parcel->writeBool(falsePrerotation)) != OK) return status;
+ if ((status = parcel->writeBool(gles1InUse)) != OK) return status;
return OK;
}
@@ -97,6 +99,8 @@
if ((status = parcel->readInt64Vector(&vkDriverLoadingTime)) != OK) return status;
if ((status = parcel->readInt64Vector(&angleDriverLoadingTime)) != OK) return status;
if ((status = parcel->readBool(&cpuVulkanInUse)) != OK) return status;
+ if ((status = parcel->readBool(&falsePrerotation)) != OK) return status;
+ if ((status = parcel->readBool(&gles1InUse)) != OK) return status;
return OK;
}
@@ -105,6 +109,8 @@
StringAppendF(&result, "appPackageName = %s\n", appPackageName.c_str());
StringAppendF(&result, "driverVersionCode = %" PRIu64 "\n", driverVersionCode);
StringAppendF(&result, "cpuVulkanInUse = %d\n", cpuVulkanInUse);
+ StringAppendF(&result, "falsePrerotation = %d\n", falsePrerotation);
+ StringAppendF(&result, "gles1InUse = %d\n", gles1InUse);
result.append("glDriverLoadingTime:");
for (int32_t loadingTime : glDriverLoadingTime) {
StringAppendF(&result, " %d", loadingTime);
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index 5d51d2b..3d0f8bb 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -32,6 +32,7 @@
#include <cutils/properties.h>
#include <graphicsenv/IGpuService.h>
#include <log/log.h>
+#include <nativeloader/dlext_namespaces.h>
#include <sys/prctl.h>
#include <utils/Trace.h>
@@ -39,21 +40,12 @@
#include <string>
#include <thread>
-// TODO(b/37049319) Get this from a header once one exists
-extern "C" {
-android_namespace_t* android_get_exported_namespace(const char*);
-android_namespace_t* android_create_namespace(const char* name, const char* ld_library_path,
- const char* default_library_path, uint64_t type,
- const char* permitted_when_isolated_path,
- android_namespace_t* parent);
-bool android_link_namespaces(android_namespace_t* from, android_namespace_t* to,
- const char* shared_libs_sonames);
-
-enum {
- ANDROID_NAMESPACE_TYPE_ISOLATED = 1,
- ANDROID_NAMESPACE_TYPE_SHARED = 2,
-};
-}
+// TODO(b/159240322): Extend this to x86 ABI.
+#if defined(__LP64__)
+#define UPDATABLE_DRIVER_ABI "arm64-v8a"
+#else
+#define UPDATABLE_DRIVER_ABI "armeabi-v7a"
+#endif // defined(__LP64__)
// TODO(ianelliott@): Get the following from an ANGLE header:
#define CURRENT_ANGLE_API_VERSION 2 // Current API verion we are targetting
@@ -155,17 +147,23 @@
void GraphicsEnv::hintActivityLaunch() {
ATRACE_CALL();
+ {
+ std::lock_guard<std::mutex> lock(mStatsLock);
+ if (mActivityLaunched) return;
+ mActivityLaunched = true;
+ }
+
std::thread trySendGpuStatsThread([this]() {
// If there's already graphics driver preloaded in the process, just send
// the stats info to GpuStats directly through async binder.
std::lock_guard<std::mutex> lock(mStatsLock);
if (mGpuStats.glDriverToSend) {
mGpuStats.glDriverToSend = false;
- sendGpuStatsLocked(GraphicsEnv::Api::API_GL, true, mGpuStats.glDriverLoadingTime);
+ sendGpuStatsLocked(GpuStatsInfo::Api::API_GL, true, mGpuStats.glDriverLoadingTime);
}
if (mGpuStats.vkDriverToSend) {
mGpuStats.vkDriverToSend = false;
- sendGpuStatsLocked(GraphicsEnv::Api::API_VK, true, mGpuStats.vkDriverLoadingTime);
+ sendGpuStatsLocked(GpuStatsInfo::Api::API_VK, true, mGpuStats.vkDriverLoadingTime);
}
});
trySendGpuStatsThread.detach();
@@ -196,34 +194,34 @@
mGpuStats.vulkanVersion = vulkanVersion;
}
-void GraphicsEnv::setDriverToLoad(GraphicsEnv::Driver driver) {
+void GraphicsEnv::setDriverToLoad(GpuStatsInfo::Driver driver) {
ATRACE_CALL();
std::lock_guard<std::mutex> lock(mStatsLock);
switch (driver) {
- case GraphicsEnv::Driver::GL:
- case GraphicsEnv::Driver::GL_UPDATED:
- case GraphicsEnv::Driver::ANGLE: {
- if (mGpuStats.glDriverToLoad == GraphicsEnv::Driver::NONE ||
- mGpuStats.glDriverToLoad == GraphicsEnv::Driver::GL) {
+ case GpuStatsInfo::Driver::GL:
+ case GpuStatsInfo::Driver::GL_UPDATED:
+ case GpuStatsInfo::Driver::ANGLE: {
+ if (mGpuStats.glDriverToLoad == GpuStatsInfo::Driver::NONE ||
+ mGpuStats.glDriverToLoad == GpuStatsInfo::Driver::GL) {
mGpuStats.glDriverToLoad = driver;
break;
}
- if (mGpuStats.glDriverFallback == GraphicsEnv::Driver::NONE) {
+ if (mGpuStats.glDriverFallback == GpuStatsInfo::Driver::NONE) {
mGpuStats.glDriverFallback = driver;
}
break;
}
- case Driver::VULKAN:
- case Driver::VULKAN_UPDATED: {
- if (mGpuStats.vkDriverToLoad == GraphicsEnv::Driver::NONE ||
- mGpuStats.vkDriverToLoad == GraphicsEnv::Driver::VULKAN) {
+ case GpuStatsInfo::Driver::VULKAN:
+ case GpuStatsInfo::Driver::VULKAN_UPDATED: {
+ if (mGpuStats.vkDriverToLoad == GpuStatsInfo::Driver::NONE ||
+ mGpuStats.vkDriverToLoad == GpuStatsInfo::Driver::VULKAN) {
mGpuStats.vkDriverToLoad = driver;
break;
}
- if (mGpuStats.vkDriverFallback == GraphicsEnv::Driver::NONE) {
+ if (mGpuStats.vkDriverFallback == GpuStatsInfo::Driver::NONE) {
mGpuStats.vkDriverFallback = driver;
}
break;
@@ -233,17 +231,16 @@
}
}
-void GraphicsEnv::setDriverLoaded(GraphicsEnv::Api api, bool isDriverLoaded,
+void GraphicsEnv::setDriverLoaded(GpuStatsInfo::Api api, bool isDriverLoaded,
int64_t driverLoadingTime) {
ATRACE_CALL();
std::lock_guard<std::mutex> lock(mStatsLock);
- const bool doNotSend = mGpuStats.appPackageName.empty();
- if (api == GraphicsEnv::Api::API_GL) {
- if (doNotSend) mGpuStats.glDriverToSend = true;
+ if (api == GpuStatsInfo::Api::API_GL) {
+ mGpuStats.glDriverToSend = true;
mGpuStats.glDriverLoadingTime = driverLoadingTime;
} else {
- if (doNotSend) mGpuStats.vkDriverToSend = true;
+ mGpuStats.vkDriverToSend = true;
mGpuStats.vkDriverLoadingTime = driverLoadingTime;
}
@@ -251,7 +248,7 @@
}
static sp<IGpuService> getGpuService() {
- const sp<IBinder> binder = defaultServiceManager()->checkService(String16("gpu"));
+ static const sp<IBinder> binder = defaultServiceManager()->checkService(String16("gpu"));
if (!binder) {
ALOGE("Failed to get gpu service");
return nullptr;
@@ -260,10 +257,18 @@
return interface_cast<IGpuService>(binder);
}
-void GraphicsEnv::setTargetStats(const Stats stats, const uint64_t value) {
+bool GraphicsEnv::readyToSendGpuStatsLocked() {
+ // Only send stats for processes having at least one activity launched and that process doesn't
+ // skip the GraphicsEnvironment setup.
+ return mActivityLaunched && !mGpuStats.appPackageName.empty();
+}
+
+void GraphicsEnv::setTargetStats(const GpuStatsInfo::Stats stats, const uint64_t value) {
ATRACE_CALL();
std::lock_guard<std::mutex> lock(mStatsLock);
+ if (!readyToSendGpuStatsLocked()) return;
+
const sp<IGpuService> gpuService = getGpuService();
if (gpuService) {
gpuService->setTargetStats(mGpuStats.appPackageName, mGpuStats.driverVersionCode, stats,
@@ -271,12 +276,11 @@
}
}
-void GraphicsEnv::sendGpuStatsLocked(GraphicsEnv::Api api, bool isDriverLoaded,
+void GraphicsEnv::sendGpuStatsLocked(GpuStatsInfo::Api api, bool isDriverLoaded,
int64_t driverLoadingTime) {
ATRACE_CALL();
- // Do not sendGpuStats for those skipping the GraphicsEnvironment setup
- if (mGpuStats.appPackageName.empty()) return;
+ if (!readyToSendGpuStatsLocked()) return;
ALOGV("sendGpuStats:\n"
"\tdriverPackageName[%s]\n"
@@ -292,16 +296,16 @@
mGpuStats.driverVersionCode, mGpuStats.driverBuildTime, mGpuStats.appPackageName.c_str(),
mGpuStats.vulkanVersion, static_cast<int32_t>(api), isDriverLoaded, driverLoadingTime);
- GraphicsEnv::Driver driver = GraphicsEnv::Driver::NONE;
+ GpuStatsInfo::Driver driver = GpuStatsInfo::Driver::NONE;
bool isIntendedDriverLoaded = false;
- if (api == GraphicsEnv::Api::API_GL) {
+ if (api == GpuStatsInfo::Api::API_GL) {
driver = mGpuStats.glDriverToLoad;
isIntendedDriverLoaded =
- isDriverLoaded && (mGpuStats.glDriverFallback == GraphicsEnv::Driver::NONE);
+ isDriverLoaded && (mGpuStats.glDriverFallback == GpuStatsInfo::Driver::NONE);
} else {
driver = mGpuStats.vkDriverToLoad;
isIntendedDriverLoaded =
- isDriverLoaded && (mGpuStats.vkDriverFallback == GraphicsEnv::Driver::NONE);
+ isDriverLoaded && (mGpuStats.vkDriverFallback == GpuStatsInfo::Driver::NONE);
}
const sp<IGpuService> gpuService = getGpuService();
@@ -313,6 +317,13 @@
}
}
+bool GraphicsEnv::setInjectLayersPrSetDumpable() {
+ if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) == -1) {
+ return false;
+ }
+ return true;
+}
+
void* GraphicsEnv::loadLibrary(std::string name) {
const android_dlextinfo dlextinfo = {
.flags = ANDROID_DLEXT_USE_NAMESPACE,
@@ -580,7 +591,28 @@
}
if (mDriverPath.empty()) {
- return nullptr;
+ // For an application process, driver path is empty means this application is not opted in
+ // to use updatable driver. Application process doesn't have the ability to set up
+ // environment variables and hence before `getenv` call will return.
+ // For a process that is not an application process, if it's run from an environment,
+ // for example shell, where environment variables can be set, then it can opt into using
+ // udpatable driver by setting UPDATABLE_GFX_DRIVER to 1. By setting to 1 the developer
+ // driver will be used currently.
+ // TODO(b/159240322) Support the production updatable driver.
+ const char* id = getenv("UPDATABLE_GFX_DRIVER");
+ if (id == nullptr || std::strcmp(id, "1")) {
+ return nullptr;
+ }
+ const sp<IGpuService> gpuService = getGpuService();
+ if (!gpuService) {
+ return nullptr;
+ }
+ mDriverPath = gpuService->getUpdatableDriverPath();
+ if (mDriverPath.empty()) {
+ return nullptr;
+ }
+ mDriverPath.append(UPDATABLE_DRIVER_ABI);
+ ALOGI("Driver path is setup via UPDATABLE_GFX_DRIVER: %s", mDriverPath.c_str());
}
auto vndkNamespace = android_get_exported_namespace("vndk");
@@ -602,6 +634,10 @@
return mDriverNamespace;
}
+std::string GraphicsEnv::getDriverPath() const {
+ return mDriverPath;
+}
+
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 db16f3c..fa25c55 100644
--- a/libs/graphicsenv/IGpuService.cpp
+++ b/libs/graphicsenv/IGpuService.cpp
@@ -27,11 +27,11 @@
public:
explicit BpGpuService(const sp<IBinder>& impl) : BpInterface<IGpuService>(impl) {}
- virtual void setGpuStats(const std::string& driverPackageName,
- const std::string& driverVersionName, uint64_t driverVersionCode,
- int64_t driverBuildTime, const std::string& appPackageName,
- const int32_t vulkanVersion, GraphicsEnv::Driver driver,
- bool isDriverLoaded, int64_t driverLoadingTime) {
+ void setGpuStats(const std::string& driverPackageName, const std::string& driverVersionName,
+ uint64_t driverVersionCode, int64_t driverBuildTime,
+ const std::string& appPackageName, const int32_t vulkanVersion,
+ GpuStatsInfo::Driver driver, bool isDriverLoaded,
+ int64_t driverLoadingTime) override {
Parcel data, reply;
data.writeInterfaceToken(IGpuService::getInterfaceDescriptor());
@@ -48,52 +48,8 @@
remote()->transact(BnGpuService::SET_GPU_STATS, data, &reply, IBinder::FLAG_ONEWAY);
}
- virtual status_t getGpuStatsGlobalInfo(std::vector<GpuStatsGlobalInfo>* outStats) const {
- if (!outStats) return UNEXPECTED_NULL;
-
- Parcel data, reply;
- status_t status;
-
- if ((status = data.writeInterfaceToken(IGpuService::getInterfaceDescriptor())) != OK)
- return status;
-
- if ((status = remote()->transact(BnGpuService::GET_GPU_STATS_GLOBAL_INFO, data, &reply)) !=
- OK)
- return status;
-
- int32_t result = 0;
- if ((status = reply.readInt32(&result)) != OK) return status;
- if (result != OK) return result;
-
- outStats->clear();
- return reply.readParcelableVector(outStats);
- }
-
- virtual status_t getGpuStatsAppInfo(std::vector<GpuStatsAppInfo>* outStats) const {
- if (!outStats) return UNEXPECTED_NULL;
-
- Parcel data, reply;
- status_t status;
-
- if ((status = data.writeInterfaceToken(IGpuService::getInterfaceDescriptor())) != OK) {
- return status;
- }
-
- if ((status = remote()->transact(BnGpuService::GET_GPU_STATS_APP_INFO, data, &reply)) !=
- OK) {
- return status;
- }
-
- int32_t result = 0;
- if ((status = reply.readInt32(&result)) != OK) return status;
- if (result != OK) return result;
-
- outStats->clear();
- return reply.readParcelableVector(outStats);
- }
-
- virtual void setTargetStats(const std::string& appPackageName, const uint64_t driverVersionCode,
- const GraphicsEnv::Stats stats, const uint64_t value) {
+ void setTargetStats(const std::string& appPackageName, const uint64_t driverVersionCode,
+ const GpuStatsInfo::Stats stats, const uint64_t value) override {
Parcel data, reply;
data.writeInterfaceToken(IGpuService::getInterfaceDescriptor());
@@ -104,6 +60,27 @@
remote()->transact(BnGpuService::SET_TARGET_STATS, data, &reply, IBinder::FLAG_ONEWAY);
}
+
+ void setUpdatableDriverPath(const std::string& driverPath) override {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGpuService::getInterfaceDescriptor());
+ data.writeUtf8AsUtf16(driverPath);
+
+ remote()->transact(BnGpuService::SET_UPDATABLE_DRIVER_PATH, data, &reply,
+ IBinder::FLAG_ONEWAY);
+ }
+
+ std::string getUpdatableDriverPath() override {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGpuService::getInterfaceDescriptor());
+
+ status_t error = remote()->transact(BnGpuService::GET_UPDATABLE_DRIVER_PATH, data, &reply);
+ std::string driverPath;
+ if (error == OK) {
+ error = reply.readUtf8FromUtf16(&driverPath);
+ }
+ return driverPath;
+ }
};
IMPLEMENT_META_INTERFACE(GpuService, "android.graphicsenv.IGpuService");
@@ -145,37 +122,11 @@
if ((status = data.readInt64(&driverLoadingTime)) != OK) return status;
setGpuStats(driverPackageName, driverVersionName, driverVersionCode, driverBuildTime,
- appPackageName, vulkanVersion, static_cast<GraphicsEnv::Driver>(driver),
+ appPackageName, vulkanVersion, static_cast<GpuStatsInfo::Driver>(driver),
isDriverLoaded, driverLoadingTime);
return OK;
}
- case GET_GPU_STATS_GLOBAL_INFO: {
- CHECK_INTERFACE(IGpuService, data, reply);
-
- std::vector<GpuStatsGlobalInfo> stats;
- const status_t result = getGpuStatsGlobalInfo(&stats);
-
- if ((status = reply->writeInt32(result)) != OK) return status;
- if (result != OK) return result;
-
- if ((status = reply->writeParcelableVector(stats)) != OK) return status;
-
- return OK;
- }
- case GET_GPU_STATS_APP_INFO: {
- CHECK_INTERFACE(IGpuService, data, reply);
-
- std::vector<GpuStatsAppInfo> stats;
- const status_t result = getGpuStatsAppInfo(&stats);
-
- if ((status = reply->writeInt32(result)) != OK) return status;
- if (result != OK) return result;
-
- if ((status = reply->writeParcelableVector(stats)) != OK) return status;
-
- return OK;
- }
case SET_TARGET_STATS: {
CHECK_INTERFACE(IGpuService, data, reply);
@@ -192,10 +143,25 @@
if ((status = data.readUint64(&value)) != OK) return status;
setTargetStats(appPackageName, driverVersionCode,
- static_cast<GraphicsEnv::Stats>(stats), value);
+ static_cast<GpuStatsInfo::Stats>(stats), value);
return OK;
}
+ case SET_UPDATABLE_DRIVER_PATH: {
+ CHECK_INTERFACE(IGpuService, data, reply);
+
+ std::string driverPath;
+ if ((status = data.readUtf8FromUtf16(&driverPath)) != OK) return status;
+
+ setUpdatableDriverPath(driverPath);
+ return OK;
+ }
+ case GET_UPDATABLE_DRIVER_PATH: {
+ CHECK_INTERFACE(IGpuService, data, reply);
+
+ std::string driverPath = getUpdatableDriverPath();
+ return reply->writeUtf8AsUtf16(driverPath);
+ }
case SHELL_COMMAND_TRANSACTION: {
int in = data.readFileDescriptor();
int out = data.readFileDescriptor();
diff --git a/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h b/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h
index edcccfe..9aba69f 100644
--- a/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h
+++ b/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h
@@ -70,6 +70,53 @@
std::vector<int64_t> vkDriverLoadingTime = {};
std::vector<int64_t> angleDriverLoadingTime = {};
bool cpuVulkanInUse = false;
+ bool falsePrerotation = false;
+ bool gles1InUse = false;
+};
+
+/*
+ * class for holding the gpu stats in GraphicsEnv before sending to GpuService.
+ */
+class GpuStatsInfo {
+public:
+ enum Api {
+ API_GL = 0,
+ API_VK = 1,
+ };
+
+ enum Driver {
+ NONE = 0,
+ GL = 1,
+ GL_UPDATED = 2,
+ VULKAN = 3,
+ VULKAN_UPDATED = 4,
+ ANGLE = 5,
+ };
+
+ enum Stats {
+ CPU_VULKAN_IN_USE = 0,
+ FALSE_PREROTATION = 1,
+ GLES_1_IN_USE = 2,
+ };
+
+ GpuStatsInfo() = default;
+ GpuStatsInfo(const GpuStatsInfo&) = default;
+ virtual ~GpuStatsInfo() = default;
+
+ std::string driverPackageName = "";
+ std::string driverVersionName = "";
+ uint64_t driverVersionCode = 0;
+ int64_t driverBuildTime = 0;
+ std::string appPackageName = "";
+ int32_t vulkanVersion = 0;
+ Driver glDriverToLoad = Driver::NONE;
+ Driver glDriverFallback = Driver::NONE;
+ Driver vkDriverToLoad = Driver::NONE;
+ Driver vkDriverFallback = Driver::NONE;
+ bool glDriverToSend = false;
+ bool vkDriverToSend = false;
+ int64_t glDriverLoadingTime = 0;
+ int64_t vkDriverLoadingTime = 0;
};
} // namespace android
diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
index 227b458..22a2332 100644
--- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
+++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
@@ -17,6 +17,8 @@
#ifndef ANDROID_UI_GRAPHICS_ENV_H
#define ANDROID_UI_GRAPHICS_ENV_H 1
+#include <graphicsenv/GpuStatsInfo.h>
+
#include <mutex>
#include <string>
#include <vector>
@@ -29,59 +31,6 @@
class GraphicsEnv {
public:
- enum Api {
- API_GL = 0,
- API_VK = 1,
- };
-
- enum Driver {
- NONE = 0,
- GL = 1,
- GL_UPDATED = 2,
- VULKAN = 3,
- VULKAN_UPDATED = 4,
- ANGLE = 5,
- };
-
- enum Stats {
- CPU_VULKAN_IN_USE = 0,
- };
-
-private:
- struct GpuStats {
- std::string driverPackageName;
- std::string driverVersionName;
- uint64_t driverVersionCode;
- int64_t driverBuildTime;
- std::string appPackageName;
- int32_t vulkanVersion;
- Driver glDriverToLoad;
- Driver glDriverFallback;
- Driver vkDriverToLoad;
- Driver vkDriverFallback;
- bool glDriverToSend;
- bool vkDriverToSend;
- int64_t glDriverLoadingTime;
- int64_t vkDriverLoadingTime;
-
- GpuStats()
- : driverPackageName(""),
- driverVersionName(""),
- driverVersionCode(0),
- driverBuildTime(0),
- appPackageName(""),
- vulkanVersion(0),
- glDriverToLoad(Driver::NONE),
- glDriverFallback(Driver::NONE),
- vkDriverToLoad(Driver::NONE),
- vkDriverFallback(Driver::NONE),
- glDriverToSend(false),
- vkDriverToSend(false),
- glDriverLoadingTime(0),
- vkDriverLoadingTime(0) {}
- };
-
-public:
static GraphicsEnv& getInstance();
// Check if the process is debuggable. It returns false except in any of the
@@ -95,6 +44,9 @@
// in the application manifest.
bool isDebuggable();
+ /*
+ * Apis for updatable driver
+ */
// Set a search path for loading graphics drivers. The path is a list of
// directories separated by ':'. A directory can be contained in a zip file
// (drivers must be stored uncompressed and page aligned); such elements
@@ -104,17 +56,40 @@
// graphics drivers. The string is a list of libraries separated by ':',
// which is required by android_link_namespaces.
void setDriverPathAndSphalLibraries(const std::string path, const std::string sphalLibraries);
+ // Get the updatable driver namespace.
android_namespace_t* getDriverNamespace();
+ std::string getDriverPath() const;
+
+ /*
+ * Apis for GpuStats
+ */
+ // Hint there's real activity launching on the app process.
void hintActivityLaunch();
+ // Set the initial GpuStats.
void setGpuStats(const std::string& driverPackageName, const std::string& driverVersionName,
uint64_t versionCode, int64_t driverBuildTime,
const std::string& appPackageName, const int32_t vulkanVersion);
- void setTargetStats(const Stats stats, const uint64_t value = 0);
- void setDriverToLoad(Driver driver);
- void setDriverLoaded(Api api, bool isDriverLoaded, int64_t driverLoadingTime);
- void sendGpuStatsLocked(Api api, bool isDriverLoaded, int64_t driverLoadingTime);
+ // Set stats for target GpuStatsInfo::Stats type.
+ void setTargetStats(const GpuStatsInfo::Stats stats, const uint64_t value = 0);
+ // Set which driver is intended to load.
+ void setDriverToLoad(GpuStatsInfo::Driver driver);
+ // Set which driver is actually loaded.
+ void setDriverLoaded(GpuStatsInfo::Api api, bool isDriverLoaded, int64_t driverLoadingTime);
+ /*
+ * Api for Vk/GL layer injection. Presently, drivers enable certain
+ * profiling features when prctl(PR_GET_DUMPABLE) returns true.
+ * Calling this when layer injection metadata is present allows the driver
+ * to enable profiling even when in a non-debuggable app
+ */
+ bool setInjectLayersPrSetDumpable();
+
+ /*
+ * Apis for ANGLE
+ */
+ // Check if the requested app should use ANGLE.
bool shouldUseAngle(std::string appName);
+ // Check if this app process should use ANGLE.
bool shouldUseAngle();
// Set a search path for loading ANGLE libraries. The path is a list of
// directories separated by ':'. A directory can be contained in a zip file
@@ -123,43 +98,79 @@
// /system/app/ANGLEPrebuilt/ANGLEPrebuilt.apk!/lib/arm64-v8a
void setAngleInfo(const std::string path, const std::string appName, std::string devOptIn,
const int rulesFd, const long rulesOffset, const long rulesLength);
+ // Get the ANGLE driver namespace.
android_namespace_t* getAngleNamespace();
+ // Get the app name for ANGLE debug message.
std::string& getAngleAppName();
+ /*
+ * Apis for debug layer
+ */
+ // Set additional layer search paths.
void setLayerPaths(NativeLoaderNamespace* appNamespace, const std::string layerPaths);
+ // Get the app namespace for loading layers.
NativeLoaderNamespace* getAppNamespace();
-
+ // Get additional layer search paths.
const std::string& getLayerPaths();
-
+ // Set the Vulkan debug layers.
void setDebugLayers(const std::string layers);
+ // Set the GL debug layers.
void setDebugLayersGLES(const std::string layers);
+ // Get the debug layers to load.
const std::string& getDebugLayers();
+ // Get the debug layers to load.
const std::string& getDebugLayersGLES();
private:
enum UseAngle { UNKNOWN, YES, NO };
+ // Load requested ANGLE library.
void* loadLibrary(std::string name);
+ // Check ANGLE support with the rules.
bool checkAngleRules(void* so);
+ // Update whether ANGLE should be used.
void updateUseAngle();
+ // Link updatable driver namespace with llndk and vndk-sp libs.
bool linkDriverNamespaceLocked(android_namespace_t* vndkNamespace);
+ // Check whether this process is ready to send stats.
+ bool readyToSendGpuStatsLocked();
+ // Send the initial complete GpuStats to GpuService.
+ void sendGpuStatsLocked(GpuStatsInfo::Api api, bool isDriverLoaded, int64_t driverLoadingTime);
GraphicsEnv() = default;
+ // Path to updatable driver libs.
std::string mDriverPath;
+ // Path to additional sphal libs linked to updatable driver namespace.
std::string mSphalLibraries;
+ // This mutex protects mGpuStats and get gpuservice call.
std::mutex mStatsLock;
- GpuStats mGpuStats;
+ // Cache the activity launch info
+ bool mActivityLaunched = false;
+ // Information bookkept for GpuStats.
+ GpuStatsInfo mGpuStats;
+ // Path to ANGLE libs.
std::string mAnglePath;
+ // This App's name.
std::string mAngleAppName;
+ // ANGLE developer opt in status.
std::string mAngleDeveloperOptIn;
+ // ANGLE rules.
std::vector<char> mRulesBuffer;
+ // Use ANGLE flag.
UseAngle mUseAngle = UNKNOWN;
+ // Vulkan debug layers libs.
std::string mDebugLayers;
+ // GL debug layers libs.
std::string mDebugLayersGLES;
+ // Additional debug layers search path.
std::string mLayerPaths;
+ // This mutex protects the namespace creation.
std::mutex mNamespaceMutex;
+ // Updatable driver namespace.
android_namespace_t* mDriverNamespace = nullptr;
+ // ANGLE namespace.
android_namespace_t* mAngleNamespace = nullptr;
+ // This App's namespace.
NativeLoaderNamespace* mAppNamespace = nullptr;
};
diff --git a/libs/graphicsenv/include/graphicsenv/IGpuService.h b/libs/graphicsenv/include/graphicsenv/IGpuService.h
index b8d0bd1..2d59fa0 100644
--- a/libs/graphicsenv/include/graphicsenv/IGpuService.h
+++ b/libs/graphicsenv/include/graphicsenv/IGpuService.h
@@ -16,12 +16,11 @@
#pragma once
-#include <vector>
-
#include <binder/IInterface.h>
#include <cutils/compiler.h>
#include <graphicsenv/GpuStatsInfo.h>
-#include <graphicsenv/GraphicsEnv.h>
+
+#include <vector>
namespace android {
@@ -37,27 +36,25 @@
virtual void setGpuStats(const std::string& driverPackageName,
const std::string& driverVersionName, uint64_t driverVersionCode,
int64_t driverBuildTime, const std::string& appPackageName,
- const int32_t vulkanVersion, GraphicsEnv::Driver driver,
+ const int32_t vulkanVersion, GpuStatsInfo::Driver driver,
bool isDriverLoaded, int64_t driverLoadingTime) = 0;
// set target stats.
virtual void setTargetStats(const std::string& appPackageName, const uint64_t driverVersionCode,
- const GraphicsEnv::Stats stats, const uint64_t value = 0) = 0;
+ const GpuStatsInfo::Stats stats, const uint64_t value = 0) = 0;
- // get GPU global stats from GpuStats module.
- virtual status_t getGpuStatsGlobalInfo(std::vector<GpuStatsGlobalInfo>* outStats) const = 0;
-
- // get GPU app stats from GpuStats module.
- virtual status_t getGpuStatsAppInfo(std::vector<GpuStatsAppInfo>* outStats) const = 0;
+ // setter and getter for updatable driver path.
+ virtual void setUpdatableDriverPath(const std::string& driverPath) = 0;
+ virtual std::string getUpdatableDriverPath() = 0;
};
class BnGpuService : public BnInterface<IGpuService> {
public:
enum IGpuServiceTag {
SET_GPU_STATS = IBinder::FIRST_CALL_TRANSACTION,
- GET_GPU_STATS_GLOBAL_INFO,
- GET_GPU_STATS_APP_INFO,
SET_TARGET_STATS,
+ SET_UPDATABLE_DRIVER_PATH,
+ GET_UPDATABLE_DRIVER_PATH,
// Always append new enum to the end.
};
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 9fc16ba..4a4510e 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -41,17 +41,26 @@
defaults: ["libgui_bufferqueue-defaults"],
srcs: [
+ ":framework_native_aidl",
+ ":libgui_bufferqueue_sources",
+
"BitTube.cpp",
+ "BLASTBufferQueue.cpp",
"BufferHubConsumer.cpp",
"BufferHubProducer.cpp",
"BufferItemConsumer.cpp",
"ConsumerBase.cpp",
"CpuConsumer.cpp",
"DebugEGLImageTracker.cpp",
+ "DisplayEventDispatcher.cpp",
"DisplayEventReceiver.cpp",
"GLConsumer.cpp",
"GuiConfig.cpp",
+ "IConsumerListener.cpp",
"IDisplayEventConnection.cpp",
+ "IGraphicBufferConsumer.cpp",
+ "IGraphicBufferProducer.cpp",
+ "IProducerListener.cpp",
"IRegionSamplingListener.cpp",
"ISurfaceComposer.cpp",
"ISurfaceComposerClient.cpp",
@@ -59,22 +68,32 @@
"LayerDebugInfo.cpp",
"LayerMetadata.cpp",
"LayerState.cpp",
+ "OccupancyTracker.cpp",
"StreamSplitter.cpp",
"Surface.cpp",
"SurfaceControl.cpp",
"SurfaceComposerClient.cpp",
"SyncFeatures.cpp",
"view/Surface.cpp",
+ "bufferqueue/1.0/B2HProducerListener.cpp",
+ "bufferqueue/1.0/H2BGraphicBufferProducer.cpp",
+ "bufferqueue/2.0/B2HProducerListener.cpp",
+ "bufferqueue/2.0/H2BGraphicBufferProducer.cpp",
],
shared_libs: [
"android.frameworks.bufferhub@1.0",
+ "libbinder",
"libbufferhub",
"libbufferhubqueue", // TODO(b/70046255): Remove this once BufferHub is integrated into libgui.
"libinput",
"libpdx_default_transport",
],
+ export_shared_lib_headers: [
+ "libbinder",
+ ],
+
// bufferhub is not used when building libgui for vendors
target: {
vendor: {
@@ -100,6 +119,10 @@
"libdvr_headers",
"libpdx_headers",
],
+
+ aidl: {
+ export_aidl_headers: true,
+ }
}
// Used by media codec services exclusively as a static lib for
@@ -115,9 +138,37 @@
cflags: [
"-DNO_BUFFERHUB",
+ "-DNO_BINDER",
],
defaults: ["libgui_bufferqueue-defaults"],
+
+ srcs: [
+ ":libgui_bufferqueue_sources",
+ ],
+}
+
+filegroup {
+ name: "libgui_bufferqueue_sources",
+ srcs: [
+ "BufferItem.cpp",
+ "BufferQueue.cpp",
+ "BufferQueueConsumer.cpp",
+ "BufferQueueCore.cpp",
+ "BufferQueueProducer.cpp",
+ "BufferQueueThreadState.cpp",
+ "BufferSlot.cpp",
+ "FrameTimestamps.cpp",
+ "GLConsumerUtils.cpp",
+ "HdrMetadata.cpp",
+ "QueueBufferInputOutput.cpp",
+ "bufferqueue/1.0/Conversion.cpp",
+ "bufferqueue/1.0/H2BProducerListener.cpp",
+ "bufferqueue/1.0/WProducerListener.cpp",
+ "bufferqueue/2.0/B2HGraphicBufferProducer.cpp",
+ "bufferqueue/2.0/H2BProducerListener.cpp",
+ "bufferqueue/2.0/types.cpp",
+ ],
}
// Common build config shared by libgui and libgui_bufferqueue_static.
@@ -144,34 +195,6 @@
},
},
- srcs: [
- "BufferItem.cpp",
- "BufferQueue.cpp",
- "BufferQueueConsumer.cpp",
- "BufferQueueCore.cpp",
- "BufferQueueProducer.cpp",
- "BufferQueueThreadState.cpp",
- "BufferSlot.cpp",
- "FrameTimestamps.cpp",
- "GLConsumerUtils.cpp",
- "HdrMetadata.cpp",
- "IConsumerListener.cpp",
- "IGraphicBufferConsumer.cpp",
- "IGraphicBufferProducer.cpp",
- "IProducerListener.cpp",
- "OccupancyTracker.cpp",
- "bufferqueue/1.0/B2HProducerListener.cpp",
- "bufferqueue/1.0/Conversion.cpp",
- "bufferqueue/1.0/H2BGraphicBufferProducer.cpp",
- "bufferqueue/1.0/H2BProducerListener.cpp",
- "bufferqueue/1.0/WProducerListener.cpp",
- "bufferqueue/2.0/B2HGraphicBufferProducer.cpp",
- "bufferqueue/2.0/B2HProducerListener.cpp",
- "bufferqueue/2.0/H2BGraphicBufferProducer.cpp",
- "bufferqueue/2.0/H2BProducerListener.cpp",
- "bufferqueue/2.0/types.cpp",
- ],
-
whole_static_libs: [
"LibGuiProperties",
],
@@ -183,7 +206,6 @@
"android.hardware.graphics.common@1.2",
"android.hidl.token@1.0-utils",
"libbase",
- "libbinder",
"libcutils",
"libEGL",
"libGLESv2",
@@ -206,7 +228,6 @@
],
export_shared_lib_headers: [
- "libbinder",
"libEGL",
"libnativewindow",
"libui",
@@ -226,4 +247,21 @@
],
}
+// GMocks for use by external code
+cc_library_static {
+ name: "libgui_mocks",
+ vendor_available: false,
+
+ defaults: ["libgui_bufferqueue-defaults"],
+ static_libs: [
+ "libgtest",
+ "libgmock",
+ ],
+
+ srcs: [
+ "mock/GraphicBufferConsumer.cpp",
+ "mock/GraphicBufferProducer.cpp",
+ ],
+}
+
subdirs = ["tests"]
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
new file mode 100644
index 0000000..56591bd
--- /dev/null
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "BLASTBufferQueue"
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include <gui/BLASTBufferQueue.h>
+#include <gui/BufferItemConsumer.h>
+#include <gui/GLConsumer.h>
+
+#include <utils/Trace.h>
+
+#include <chrono>
+
+using namespace std::chrono_literals;
+
+namespace android {
+
+void BLASTBufferItemConsumer::onDisconnect() {
+ Mutex::Autolock lock(mFrameEventHistoryMutex);
+ mPreviouslyConnected = mCurrentlyConnected;
+ mCurrentlyConnected = false;
+ if (mPreviouslyConnected) {
+ mDisconnectEvents.push(mCurrentFrameNumber);
+ }
+ mFrameEventHistory.onDisconnect();
+}
+
+void BLASTBufferItemConsumer::addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
+ FrameEventHistoryDelta* outDelta) {
+ Mutex::Autolock lock(mFrameEventHistoryMutex);
+ if (newTimestamps) {
+ // BufferQueueProducer only adds a new timestamp on
+ // queueBuffer
+ mCurrentFrameNumber = newTimestamps->frameNumber;
+ mFrameEventHistory.addQueue(*newTimestamps);
+ }
+ if (outDelta) {
+ // frame event histories will be processed
+ // only after the producer connects and requests
+ // deltas for the first time. Forward this intent
+ // to SF-side to turn event processing back on
+ mPreviouslyConnected = mCurrentlyConnected;
+ mCurrentlyConnected = true;
+ mFrameEventHistory.getAndResetDelta(outDelta);
+ }
+}
+
+void BLASTBufferItemConsumer::updateFrameTimestamps(uint64_t frameNumber, nsecs_t refreshStartTime,
+ const sp<Fence>& glDoneFence,
+ const sp<Fence>& presentFence,
+ const sp<Fence>& prevReleaseFence,
+ CompositorTiming compositorTiming,
+ nsecs_t latchTime, nsecs_t dequeueReadyTime) {
+ Mutex::Autolock lock(mFrameEventHistoryMutex);
+
+ // if the producer is not connected, don't bother updating,
+ // the next producer that connects won't access this frame event
+ if (!mCurrentlyConnected) return;
+ std::shared_ptr<FenceTime> glDoneFenceTime = std::make_shared<FenceTime>(glDoneFence);
+ std::shared_ptr<FenceTime> presentFenceTime = std::make_shared<FenceTime>(presentFence);
+ std::shared_ptr<FenceTime> releaseFenceTime = std::make_shared<FenceTime>(prevReleaseFence);
+
+ mFrameEventHistory.addLatch(frameNumber, latchTime);
+ mFrameEventHistory.addRelease(frameNumber, dequeueReadyTime, std::move(releaseFenceTime));
+ mFrameEventHistory.addPreComposition(frameNumber, refreshStartTime);
+ mFrameEventHistory.addPostComposition(frameNumber, glDoneFenceTime, presentFenceTime,
+ compositorTiming);
+}
+
+void BLASTBufferItemConsumer::getConnectionEvents(uint64_t frameNumber, bool* needsDisconnect) {
+ bool disconnect = false;
+ Mutex::Autolock lock(mFrameEventHistoryMutex);
+ while (!mDisconnectEvents.empty() && mDisconnectEvents.front() <= frameNumber) {
+ disconnect = true;
+ mDisconnectEvents.pop();
+ }
+ if (needsDisconnect != nullptr) *needsDisconnect = disconnect;
+}
+
+BLASTBufferQueue::BLASTBufferQueue(const sp<SurfaceControl>& surface, int width, int height,
+ bool enableTripleBuffering)
+ : mSurfaceControl(surface),
+ mWidth(width),
+ mHeight(height),
+ mNextTransaction(nullptr) {
+ BufferQueue::createBufferQueue(&mProducer, &mConsumer);
+ // since the adapter is in the client process, set dequeue timeout
+ // explicitly so that dequeueBuffer will block
+ mProducer->setDequeueTimeout(std::numeric_limits<int64_t>::max());
+
+ if (enableTripleBuffering) {
+ mProducer->setMaxDequeuedBufferCount(2);
+ }
+ mBufferItemConsumer =
+ new BLASTBufferItemConsumer(mConsumer, GraphicBuffer::USAGE_HW_COMPOSER, 1, true);
+ static int32_t id = 0;
+ auto name = std::string("BLAST Consumer") + std::to_string(id);
+ id++;
+ mBufferItemConsumer->setName(String8(name.c_str()));
+ mBufferItemConsumer->setFrameAvailableListener(this);
+ mBufferItemConsumer->setBufferFreedListener(this);
+ mBufferItemConsumer->setDefaultBufferSize(mWidth, mHeight);
+ mBufferItemConsumer->setDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888);
+
+ mTransformHint = mSurfaceControl->getTransformHint();
+ mBufferItemConsumer->setTransformHint(mTransformHint);
+
+ mNumAcquired = 0;
+ mNumFrameAvailable = 0;
+ mPendingReleaseItem.item = BufferItem();
+ mPendingReleaseItem.releaseFence = nullptr;
+}
+
+void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, int width, int height) {
+ std::unique_lock _lock{mMutex};
+ mSurfaceControl = surface;
+
+ if (mWidth != width || mHeight != height) {
+ mWidth = width;
+ mHeight = height;
+ mBufferItemConsumer->setDefaultBufferSize(mWidth, mHeight);
+ }
+}
+
+static void transactionCallbackThunk(void* context, nsecs_t latchTime,
+ const sp<Fence>& presentFence,
+ const std::vector<SurfaceControlStats>& stats) {
+ if (context == nullptr) {
+ return;
+ }
+ BLASTBufferQueue* bq = static_cast<BLASTBufferQueue*>(context);
+ bq->transactionCallback(latchTime, presentFence, stats);
+}
+
+void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence>& /*presentFence*/,
+ const std::vector<SurfaceControlStats>& stats) {
+ std::unique_lock _lock{mMutex};
+ ATRACE_CALL();
+
+ if (!stats.empty()) {
+ mTransformHint = stats[0].transformHint;
+ mBufferItemConsumer->setTransformHint(mTransformHint);
+ mBufferItemConsumer->updateFrameTimestamps(stats[0].frameEventStats.frameNumber,
+ stats[0].frameEventStats.refreshStartTime,
+ stats[0].frameEventStats.gpuCompositionDoneFence,
+ stats[0].presentFence,
+ stats[0].previousReleaseFence,
+ stats[0].frameEventStats.compositorTiming,
+ stats[0].latchTime,
+ stats[0].frameEventStats.dequeueReadyTime);
+ }
+ if (mPendingReleaseItem.item.mGraphicBuffer != nullptr) {
+ if (!stats.empty()) {
+ mPendingReleaseItem.releaseFence = stats[0].previousReleaseFence;
+ } else {
+ ALOGE("Warning: no SurfaceControlStats returned in BLASTBufferQueue callback");
+ mPendingReleaseItem.releaseFence = nullptr;
+ }
+ mBufferItemConsumer->releaseBuffer(mPendingReleaseItem.item,
+ mPendingReleaseItem.releaseFence
+ ? mPendingReleaseItem.releaseFence
+ : Fence::NO_FENCE);
+ mNumAcquired--;
+ mPendingReleaseItem.item = BufferItem();
+ mPendingReleaseItem.releaseFence = nullptr;
+ }
+
+ if (mSubmitted.empty()) {
+ ALOGE("ERROR: callback with no corresponding submitted buffer item");
+ }
+ mPendingReleaseItem.item = std::move(mSubmitted.front());
+ mSubmitted.pop();
+
+ processNextBufferLocked(false);
+
+ mCallbackCV.notify_all();
+ decStrong((void*)transactionCallbackThunk);
+}
+
+void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) {
+ ATRACE_CALL();
+ if (mNumFrameAvailable == 0 || mNumAcquired == MAX_ACQUIRED_BUFFERS + 1) {
+ return;
+ }
+
+ if (mSurfaceControl == nullptr) {
+ ALOGE("ERROR : surface control is null");
+ return;
+ }
+
+ SurfaceComposerClient::Transaction localTransaction;
+ bool applyTransaction = true;
+ SurfaceComposerClient::Transaction* t = &localTransaction;
+ if (mNextTransaction != nullptr && useNextTransaction) {
+ t = mNextTransaction;
+ mNextTransaction = nullptr;
+ applyTransaction = false;
+ }
+
+ BufferItem bufferItem;
+
+ status_t status = mBufferItemConsumer->acquireBuffer(&bufferItem, -1, false);
+ if (status != OK) {
+ return;
+ }
+ auto buffer = bufferItem.mGraphicBuffer;
+ mNumFrameAvailable--;
+
+ if (buffer == nullptr) {
+ mBufferItemConsumer->releaseBuffer(bufferItem, Fence::NO_FENCE);
+ return;
+ }
+
+ mNumAcquired++;
+ mSubmitted.push(bufferItem);
+
+ bool needsDisconnect = false;
+ mBufferItemConsumer->getConnectionEvents(bufferItem.mFrameNumber, &needsDisconnect);
+
+ // if producer disconnected before, notify SurfaceFlinger
+ if (needsDisconnect) {
+ t->notifyProducerDisconnect(mSurfaceControl);
+ }
+
+ // Ensure BLASTBufferQueue stays alive until we receive the transaction complete callback.
+ incStrong((void*)transactionCallbackThunk);
+
+ t->setBuffer(mSurfaceControl, buffer);
+ t->setAcquireFence(mSurfaceControl,
+ bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE);
+ t->addTransactionCompletedCallback(transactionCallbackThunk, static_cast<void*>(this));
+
+ t->setFrame(mSurfaceControl, {0, 0, mWidth, mHeight});
+ t->setCrop(mSurfaceControl, computeCrop(bufferItem));
+ t->setTransform(mSurfaceControl, bufferItem.mTransform);
+ t->setTransformToDisplayInverse(mSurfaceControl, bufferItem.mTransformToDisplayInverse);
+ t->setDesiredPresentTime(bufferItem.mTimestamp);
+
+ if (applyTransaction) {
+ t->apply();
+ }
+}
+
+Rect BLASTBufferQueue::computeCrop(const BufferItem& item) {
+ if (item.mScalingMode == NATIVE_WINDOW_SCALING_MODE_SCALE_CROP) {
+ return GLConsumer::scaleDownCrop(item.mCrop, mWidth, mHeight);
+ }
+ return item.mCrop;
+}
+
+void BLASTBufferQueue::onFrameAvailable(const BufferItem& /*item*/) {
+ ATRACE_CALL();
+ std::unique_lock _lock{mMutex};
+
+ if (mNextTransaction != nullptr) {
+ while (mNumFrameAvailable > 0 || mNumAcquired == MAX_ACQUIRED_BUFFERS + 1) {
+ mCallbackCV.wait(_lock);
+ }
+ }
+ // add to shadow queue
+ mNumFrameAvailable++;
+ processNextBufferLocked(true);
+}
+
+void BLASTBufferQueue::setNextTransaction(SurfaceComposerClient::Transaction* t) {
+ std::lock_guard _lock{mMutex};
+ mNextTransaction = t;
+}
+
+} // namespace android
diff --git a/libs/gui/BufferHubProducer.cpp b/libs/gui/BufferHubProducer.cpp
index 4be014f..1f71e23 100644
--- a/libs/gui/BufferHubProducer.cpp
+++ b/libs/gui/BufferHubProducer.cpp
@@ -19,7 +19,6 @@
#include <inttypes.h>
#include <log/log.h>
#include <system/window.h>
-#include <ui/BufferHubBuffer.h>
namespace android {
@@ -697,7 +696,7 @@
// relationship, thus |getConsumerName| from the producer side does not
// make any sense.
ALOGE("BufferHubProducer::getConsumerName not supported.");
- return String8("BufferHubQueue::DummyConsumer");
+ return String8("BufferHubQueue::StubConsumer");
}
status_t BufferHubProducer::setSharedBufferMode(bool shared_buffer_mode) {
diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp
index 5fb3f0b..c1d92a2 100644
--- a/libs/gui/BufferQueue.cpp
+++ b/libs/gui/BufferQueue.cpp
@@ -43,6 +43,27 @@
}
}
+void BufferQueue::ProxyConsumerListener::onFrameDequeued(const uint64_t bufferId) {
+ sp<ConsumerListener> listener(mConsumerListener.promote());
+ if (listener != nullptr) {
+ listener->onFrameDequeued(bufferId);
+ }
+}
+
+void BufferQueue::ProxyConsumerListener::onFrameCancelled(const uint64_t bufferId) {
+ sp<ConsumerListener> listener(mConsumerListener.promote());
+ if (listener != nullptr) {
+ listener->onFrameCancelled(bufferId);
+ }
+}
+
+void BufferQueue::ProxyConsumerListener::onFrameDetached(const uint64_t bufferId) {
+ sp<ConsumerListener> listener(mConsumerListener.promote());
+ if (listener != nullptr) {
+ listener->onFrameDetached(bufferId);
+ }
+}
+
void BufferQueue::ProxyConsumerListener::onFrameAvailable(
const BufferItem& item) {
sp<ConsumerListener> listener(mConsumerListener.promote());
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index 3a7cb44..da6143c 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -44,6 +44,30 @@
namespace android {
+// Macros for include BufferQueueCore information in log messages
+#define BQ_LOGV(x, ...) \
+ ALOGV("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \
+ mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
+ ##__VA_ARGS__)
+#define BQ_LOGD(x, ...) \
+ ALOGD("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \
+ mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
+ ##__VA_ARGS__)
+#define BQ_LOGI(x, ...) \
+ ALOGI("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \
+ mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
+ ##__VA_ARGS__)
+#define BQ_LOGW(x, ...) \
+ ALOGW("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \
+ mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
+ ##__VA_ARGS__)
+#define BQ_LOGE(x, ...) \
+ ALOGE("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \
+ mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
+ ##__VA_ARGS__)
+
+ConsumerListener::~ConsumerListener() = default;
+
BufferQueueConsumer::BufferQueueConsumer(const sp<BufferQueueCore>& core) :
mCore(core),
mSlots(core->mSlots),
@@ -269,8 +293,9 @@
ATRACE_INT(mCore->mConsumerName.string(),
static_cast<int32_t>(mCore->mQueue.size()));
+#ifndef NO_BINDER
mCore->mOccupancyTracker.registerOccupancyChange(mCore->mQueue.size());
-
+#endif
VALIDATE_CONSISTENCY();
}
@@ -743,7 +768,12 @@
status_t BufferQueueConsumer::getOccupancyHistory(bool forceFlush,
std::vector<OccupancyTracker::Segment>* outHistory) {
std::lock_guard<std::mutex> lock(mCore->mMutex);
+#ifndef NO_BINDER
*outHistory = mCore->mOccupancyTracker.getSegmentHistory(forceFlush);
+#else
+ (void)forceFlush;
+ outHistory->clear();
+#endif
return NO_ERROR;
}
@@ -764,7 +794,7 @@
bool denied = false;
const uid_t uid = BufferQueueThreadState::getCallingUid();
-#ifndef __ANDROID_VNDK__
+#if !defined(__ANDROID_VNDK__) && !defined(NO_BINDER)
// permission check can't be done for vendors as vendors have no access to
// the PermissionController. We need to do a runtime check as well, since
// the system variant of libgui can be loaded in a vendor process. For eg:
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index 0264bd2..5023b6b 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -28,7 +28,6 @@
#include <inttypes.h>
-#include <cutils/properties.h>
#include <cutils/atomic.h>
#include <gui/BufferItem.h>
@@ -42,6 +41,23 @@
namespace android {
+// Macros for include BufferQueueCore information in log messages
+#define BQ_LOGV(x, ...) \
+ ALOGV("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), mUniqueId, \
+ mConnectedApi, mConnectedPid, mUniqueId >> 32, ##__VA_ARGS__)
+#define BQ_LOGD(x, ...) \
+ ALOGD("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), mUniqueId, \
+ mConnectedApi, mConnectedPid, mUniqueId >> 32, ##__VA_ARGS__)
+#define BQ_LOGI(x, ...) \
+ ALOGI("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), mUniqueId, \
+ mConnectedApi, mConnectedPid, mUniqueId >> 32, ##__VA_ARGS__)
+#define BQ_LOGW(x, ...) \
+ ALOGW("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), mUniqueId, \
+ mConnectedApi, mConnectedPid, mUniqueId >> 32, ##__VA_ARGS__)
+#define BQ_LOGE(x, ...) \
+ ALOGE("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), mUniqueId, \
+ mConnectedApi, mConnectedPid, mUniqueId >> 32, ##__VA_ARGS__)
+
static String8 getUniqueName() {
static volatile int32_t counter = 0;
return String8::format("unnamed-%d-%d", getpid(),
@@ -54,52 +70,68 @@
return id | counter++;
}
-BufferQueueCore::BufferQueueCore() :
- mMutex(),
- mIsAbandoned(false),
- mConsumerControlledByApp(false),
- mConsumerName(getUniqueName()),
- mConsumerListener(),
- mConsumerUsageBits(0),
- mConsumerIsProtected(false),
- mConnectedApi(NO_CONNECTED_API),
- mLinkedToDeath(),
- mConnectedProducerListener(),
- mBufferReleasedCbEnabled(false),
- mSlots(),
- mQueue(),
- mFreeSlots(),
- mFreeBuffers(),
- mUnusedSlots(),
- mActiveBuffers(),
- mDequeueCondition(),
- mDequeueBufferCannotBlock(false),
- mQueueBufferCanDrop(false),
- mLegacyBufferDrop(true),
- mDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888),
- mDefaultWidth(1),
- mDefaultHeight(1),
- mDefaultBufferDataSpace(HAL_DATASPACE_UNKNOWN),
- mMaxBufferCount(BufferQueueDefs::NUM_BUFFER_SLOTS),
- mMaxAcquiredBufferCount(1),
- mMaxDequeuedBufferCount(1),
- mBufferHasBeenQueued(false),
- mFrameCounter(0),
- mTransformHint(0),
- mIsAllocating(false),
- mIsAllocatingCondition(),
- mAllowAllocation(true),
- mBufferAge(0),
- mGenerationNumber(0),
- mAsyncMode(false),
- mSharedBufferMode(false),
- mAutoRefresh(false),
- mSharedBufferSlot(INVALID_BUFFER_SLOT),
- mSharedBufferCache(Rect::INVALID_RECT, 0, NATIVE_WINDOW_SCALING_MODE_FREEZE,
- HAL_DATASPACE_UNKNOWN),
- mLastQueuedSlot(INVALID_BUFFER_SLOT),
- mUniqueId(getUniqueId())
-{
+static status_t getProcessName(int pid, String8& name) {
+ FILE* fp = fopen(String8::format("/proc/%d/cmdline", pid), "r");
+ if (NULL != fp) {
+ const size_t size = 64;
+ char proc_name[size];
+ char* result = fgets(proc_name, size, fp);
+ fclose(fp);
+ if (result != nullptr) {
+ name = proc_name;
+ return NO_ERROR;
+ }
+ }
+ return INVALID_OPERATION;
+}
+
+BufferQueueCore::BufferQueueCore()
+ : mMutex(),
+ mIsAbandoned(false),
+ mConsumerControlledByApp(false),
+ mConsumerName(getUniqueName()),
+ mConsumerListener(),
+ mConsumerUsageBits(0),
+ mConsumerIsProtected(false),
+ mConnectedApi(NO_CONNECTED_API),
+ mLinkedToDeath(),
+ mConnectedProducerListener(),
+ mBufferReleasedCbEnabled(false),
+ mSlots(),
+ mQueue(),
+ mFreeSlots(),
+ mFreeBuffers(),
+ mUnusedSlots(),
+ mActiveBuffers(),
+ mDequeueCondition(),
+ mDequeueBufferCannotBlock(false),
+ mQueueBufferCanDrop(false),
+ mLegacyBufferDrop(true),
+ mDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888),
+ mDefaultWidth(1),
+ mDefaultHeight(1),
+ mDefaultBufferDataSpace(HAL_DATASPACE_UNKNOWN),
+ mMaxBufferCount(BufferQueueDefs::NUM_BUFFER_SLOTS),
+ mMaxAcquiredBufferCount(1),
+ mMaxDequeuedBufferCount(1),
+ mBufferHasBeenQueued(false),
+ mFrameCounter(0),
+ mTransformHint(0),
+ mIsAllocating(false),
+ mIsAllocatingCondition(),
+ mAllowAllocation(true),
+ mBufferAge(0),
+ mGenerationNumber(0),
+ mAsyncMode(false),
+ mSharedBufferMode(false),
+ mAutoRefresh(false),
+ mSharedBufferSlot(INVALID_BUFFER_SLOT),
+ mSharedBufferCache(Rect::INVALID_RECT, 0, NATIVE_WINDOW_SCALING_MODE_FREEZE,
+ HAL_DATASPACE_UNKNOWN),
+ mLastQueuedSlot(INVALID_BUFFER_SLOT),
+ mUniqueId(getUniqueId()),
+ mAutoPrerotation(false),
+ mTransformHintInUse(0) {
int numStartingBuffers = getMaxBufferCountLocked();
for (int s = 0; s < numStartingBuffers; s++) {
mFreeSlots.insert(s);
@@ -124,10 +156,26 @@
mQueueBufferCanDrop, mLegacyBufferDrop);
outResult->appendFormat("%s default-size=[%dx%d] default-format=%d ", prefix.string(),
mDefaultWidth, mDefaultHeight, mDefaultBufferFormat);
- outResult->appendFormat("transform-hint=%02x frame-counter=%" PRIu64, mTransformHint,
- mFrameCounter);
+ outResult->appendFormat("%s transform-hint=%02x frame-counter=%" PRIu64 "\n", prefix.string(),
+ mTransformHint, mFrameCounter);
+ outResult->appendFormat("%s mTransformHintInUse=%02x mAutoPrerotation=%d\n", prefix.string(),
+ mTransformHintInUse, mAutoPrerotation);
- outResult->appendFormat("\n%sFIFO(%zu):\n", prefix.string(), mQueue.size());
+ outResult->appendFormat("%sFIFO(%zu):\n", prefix.string(), mQueue.size());
+
+ outResult->appendFormat("%s(mConsumerName=%s, ", prefix.string(), mConsumerName.string());
+
+ outResult->appendFormat("mConnectedApi=%d, mConsumerUsageBits=%" PRIu64 ", ", mConnectedApi,
+ mConsumerUsageBits);
+
+ String8 producerProcName = String8("\?\?\?");
+ String8 consumerProcName = String8("\?\?\?");
+ int32_t pid = getpid();
+ getProcessName(mConnectedPid, producerProcName);
+ getProcessName(pid, consumerProcName);
+ outResult->appendFormat("mId=%" PRIx64 ", producer=[%d:%s], consumer=[%d:%s])\n", mUniqueId,
+ mConnectedPid, producerProcName.string(), pid,
+ consumerProcName.string());
Fifo::const_iterator current(mQueue.begin());
while (current != mQueue.end()) {
double timestamp = current->mTimestamp / 1e9;
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 4f8eb6b..a7cf39a 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -44,7 +44,30 @@
namespace android {
+// Macros for include BufferQueueCore information in log messages
+#define BQ_LOGV(x, ...) \
+ ALOGV("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \
+ mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
+ ##__VA_ARGS__)
+#define BQ_LOGD(x, ...) \
+ ALOGD("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \
+ mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
+ ##__VA_ARGS__)
+#define BQ_LOGI(x, ...) \
+ ALOGI("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \
+ mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
+ ##__VA_ARGS__)
+#define BQ_LOGW(x, ...) \
+ ALOGW("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \
+ mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
+ ##__VA_ARGS__)
+#define BQ_LOGE(x, ...) \
+ ALOGE("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \
+ mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
+ ##__VA_ARGS__)
+
static constexpr uint32_t BQ_LAYER_COUNT = 1;
+ProducerListener::~ProducerListener() = default;
BufferQueueProducer::BufferQueueProducer(const sp<BufferQueueCore>& core,
bool consumerIsSurfaceFlinger) :
@@ -408,6 +431,10 @@
if (useDefaultSize) {
width = mCore->mDefaultWidth;
height = mCore->mDefaultHeight;
+ if (mCore->mAutoPrerotation &&
+ (mCore->mTransformHintInUse & NATIVE_WINDOW_TRANSFORM_ROT_90)) {
+ std::swap(width, height);
+ }
}
int found = BufferItem::INVALID_BUFFER_SLOT;
@@ -508,6 +535,12 @@
mCore->mSharedBufferSlot = found;
mSlots[found].mBufferState.mShared = true;
}
+
+ if (!(returnFlags & BUFFER_NEEDS_REALLOCATION)) {
+ if (mCore->mConsumerListener != nullptr) {
+ mCore->mConsumerListener->onFrameDequeued(mSlots[*outSlot].mGraphicBuffer->getId());
+ }
+ }
} // Autolock scope
if (returnFlags & BUFFER_NEEDS_REALLOCATION) {
@@ -524,6 +557,10 @@
if (error == NO_ERROR && !mCore->mIsAbandoned) {
graphicBuffer->setGenerationNumber(mCore->mGenerationNumber);
mSlots[*outSlot].mGraphicBuffer = graphicBuffer;
+ if (mCore->mConsumerListener != nullptr) {
+ mCore->mConsumerListener->onFrameDequeued(
+ mSlots[*outSlot].mGraphicBuffer->getId());
+ }
}
mCore->mIsAllocating = false;
@@ -617,13 +654,17 @@
return BAD_VALUE;
}
+ listener = mCore->mConsumerListener;
+ auto gb = mSlots[slot].mGraphicBuffer;
+ if (listener != nullptr && gb != nullptr) {
+ listener->onFrameDetached(gb->getId());
+ }
mSlots[slot].mBufferState.detachProducer();
mCore->mActiveBuffers.erase(slot);
mCore->mFreeSlots.insert(slot);
mCore->clearBufferSlotLocked(slot);
mCore->mDequeueCondition.notify_all();
VALIDATE_CONSISTENCY();
- listener = mCore->mConsumerListener;
}
if (listener != nullptr) {
@@ -960,14 +1001,15 @@
output->width = mCore->mDefaultWidth;
output->height = mCore->mDefaultHeight;
- output->transformHint = mCore->mTransformHint;
+ output->transformHint = mCore->mTransformHintInUse = mCore->mTransformHint;
output->numPendingBuffers = static_cast<uint32_t>(mCore->mQueue.size());
output->nextFrameNumber = mCore->mFrameCounter + 1;
ATRACE_INT(mCore->mConsumerName.string(),
static_cast<int32_t>(mCore->mQueue.size()));
+#ifndef NO_BINDER
mCore->mOccupancyTracker.registerOccupancyChange(mCore->mQueue.size());
-
+#endif
// Take a ticket for the callback functions
callbackTicket = mNextCallbackTicket++;
@@ -1079,6 +1121,10 @@
mCore->mFreeBuffers.push_back(slot);
}
+ auto gb = mSlots[slot].mGraphicBuffer;
+ if (mCore->mConsumerListener != nullptr && gb != nullptr) {
+ mCore->mConsumerListener->onFrameCancelled(gb->getId());
+ }
mSlots[slot].mFence = fence;
mCore->mDequeueCondition.notify_all();
VALIDATE_CONSISTENCY();
@@ -1141,9 +1187,6 @@
case NATIVE_WINDOW_CONSUMER_IS_PROTECTED:
value = static_cast<int32_t>(mCore->mConsumerIsProtected);
break;
- case NATIVE_WINDOW_MAX_BUFFER_COUNT:
- value = static_cast<int32_t>(mCore->mMaxBufferCount);
- break;
default:
return BAD_VALUE;
}
@@ -1203,15 +1246,17 @@
output->width = mCore->mDefaultWidth;
output->height = mCore->mDefaultHeight;
- output->transformHint = mCore->mTransformHint;
+ output->transformHint = mCore->mTransformHintInUse = mCore->mTransformHint;
output->numPendingBuffers =
static_cast<uint32_t>(mCore->mQueue.size());
output->nextFrameNumber = mCore->mFrameCounter + 1;
output->bufferReplaced = false;
+ output->maxBufferCount = mCore->mMaxBufferCount;
if (listener != nullptr) {
// Set up a death notification so that we can disconnect
// automatically if the remote producer dies
+#ifndef NO_BINDER
if (IInterface::asBinder(listener)->remoteBinder() != nullptr) {
status = IInterface::asBinder(listener)->linkToDeath(
static_cast<IBinder::DeathRecipient*>(this));
@@ -1221,6 +1266,7 @@
}
mCore->mLinkedToDeath = listener;
}
+#endif
mCore->mConnectedProducerListener = listener;
mCore->mBufferReleasedCbEnabled = listener->needsReleaseNotify();
}
@@ -1289,6 +1335,7 @@
if (mCore->mConnectedApi == api) {
mCore->freeAllBuffersLocked();
+#ifndef NO_BINDER
// Remove our death notification callback if we have one
if (mCore->mLinkedToDeath != nullptr) {
sp<IBinder> token =
@@ -1298,6 +1345,7 @@
token->unlinkToDeath(
static_cast<IBinder::DeathRecipient*>(this));
}
+#endif
mCore->mSharedBufferSlot =
BufferQueueCore::INVALID_BUFFER_SLOT;
mCore->mLinkedToDeath = nullptr;
@@ -1306,6 +1354,7 @@
mCore->mConnectedPid = -1;
mCore->mSidebandStream.clear();
mCore->mDequeueCondition.notify_all();
+ mCore->mAutoPrerotation = false;
listener = mCore->mConsumerListener;
} else if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {
BQ_LOGE("disconnect: not connected (req=%d)", api);
@@ -1349,6 +1398,8 @@
void BufferQueueProducer::allocateBuffers(uint32_t width, uint32_t height,
PixelFormat format, uint64_t usage) {
ATRACE_CALL();
+
+ const bool useDefaultSize = !width && !height;
while (true) {
size_t newBufferCount = 0;
uint32_t allocWidth = 0;
@@ -1375,6 +1426,11 @@
allocWidth = width > 0 ? width : mCore->mDefaultWidth;
allocHeight = height > 0 ? height : mCore->mDefaultHeight;
+ if (useDefaultSize && mCore->mAutoPrerotation &&
+ (mCore->mTransformHintInUse & NATIVE_WINDOW_TRANSFORM_ROT_90)) {
+ std::swap(allocWidth, allocHeight);
+ }
+
allocFormat = format != 0 ? format : mCore->mDefaultBufferFormat;
allocUsage = usage | mCore->mConsumerUsageBits;
allocName.assign(mCore->mConsumerName.string(), mCore->mConsumerName.size());
@@ -1405,6 +1461,11 @@
std::unique_lock<std::mutex> lock(mCore->mMutex);
uint32_t checkWidth = width > 0 ? width : mCore->mDefaultWidth;
uint32_t checkHeight = height > 0 ? height : mCore->mDefaultHeight;
+ if (useDefaultSize && mCore->mAutoPrerotation &&
+ (mCore->mTransformHintInUse & NATIVE_WINDOW_TRANSFORM_ROT_90)) {
+ std::swap(checkWidth, checkHeight);
+ }
+
PixelFormat checkFormat = format != 0 ?
format : mCore->mDefaultBufferFormat;
uint64_t checkUsage = usage | mCore->mConsumerUsageBits;
@@ -1607,4 +1668,14 @@
return NO_ERROR;
}
+status_t BufferQueueProducer::setAutoPrerotation(bool autoPrerotation) {
+ ATRACE_CALL();
+ BQ_LOGV("setAutoPrerotation: %d", autoPrerotation);
+
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
+
+ mCore->mAutoPrerotation = autoPrerotation;
+ return NO_ERROR;
+}
+
} // namespace android
diff --git a/libs/gui/BufferQueueThreadState.cpp b/libs/gui/BufferQueueThreadState.cpp
index c13030b..976c9b9 100644
--- a/libs/gui/BufferQueueThreadState.cpp
+++ b/libs/gui/BufferQueueThreadState.cpp
@@ -14,8 +14,10 @@
* limitations under the License.
*/
+#ifndef NO_BINDER
#include <binder/IPCThreadState.h>
#include <binderthreadstate/CallerUtils.h>
+#endif // NO_BINDER
#include <hwbinder/IPCThreadState.h>
#include <private/gui/BufferQueueThreadState.h>
#include <unistd.h>
@@ -23,17 +25,25 @@
namespace android {
uid_t BufferQueueThreadState::getCallingUid() {
+#ifndef NO_BINDER
if (getCurrentServingCall() == BinderCallType::HWBINDER) {
return hardware::IPCThreadState::self()->getCallingUid();
}
return IPCThreadState::self()->getCallingUid();
+#else // NO_BINDER
+ return hardware::IPCThreadState::self()->getCallingUid();
+#endif // NO_BINDER
}
pid_t BufferQueueThreadState::getCallingPid() {
+#ifndef NO_BINDER
if (getCurrentServingCall() == BinderCallType::HWBINDER) {
return hardware::IPCThreadState::self()->getCallingPid();
}
return IPCThreadState::self()->getCallingPid();
+#else // NO_BINDER
+ return hardware::IPCThreadState::self()->getCallingPid();
+#endif // NO_BINDER
}
} // namespace android
diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp
index abd9921..9f91d9d 100644
--- a/libs/gui/ConsumerBase.cpp
+++ b/libs/gui/ConsumerBase.cpp
@@ -101,6 +101,48 @@
mSlots[slotIndex].mFrameNumber = 0;
}
+void ConsumerBase::onFrameDequeued(const uint64_t bufferId) {
+ CB_LOGV("onFrameDequeued");
+
+ sp<FrameAvailableListener> listener;
+ {
+ Mutex::Autolock lock(mFrameAvailableMutex);
+ listener = mFrameAvailableListener.promote();
+ }
+
+ if (listener != nullptr) {
+ listener->onFrameDequeued(bufferId);
+ }
+}
+
+void ConsumerBase::onFrameCancelled(const uint64_t bufferId) {
+ CB_LOGV("onFrameCancelled");
+
+ sp<FrameAvailableListener> listener;
+ {
+ Mutex::Autolock lock(mFrameAvailableMutex);
+ listener = mFrameAvailableListener.promote();
+ }
+
+ if (listener != nullptr) {
+ listener->onFrameCancelled(bufferId);
+ }
+}
+
+void ConsumerBase::onFrameDetached(const uint64_t bufferId) {
+ CB_LOGV("onFrameDetached");
+
+ sp<FrameAvailableListener> listener;
+ {
+ Mutex::Autolock lock(mFrameAvailableMutex);
+ listener = mFrameAvailableListener.promote();
+ }
+
+ if (listener != nullptr) {
+ listener->onFrameDetached(bufferId);
+ }
+}
+
void ConsumerBase::onFrameAvailable(const BufferItem& item) {
CB_LOGV("onFrameAvailable");
diff --git a/libs/gui/DebugEGLImageTracker.cpp b/libs/gui/DebugEGLImageTracker.cpp
index ab6f364..5762dab 100644
--- a/libs/gui/DebugEGLImageTracker.cpp
+++ b/libs/gui/DebugEGLImageTracker.cpp
@@ -14,13 +14,14 @@
* limitations under the License.
*/
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
-#include <cutils/properties.h>
#include <gui/DebugEGLImageTracker.h>
#include <cinttypes>
#include <unordered_map>
+using android::base::GetBoolProperty;
using android::base::StringAppendF;
std::mutex DebugEGLImageTracker::mInstanceLock;
@@ -57,10 +58,7 @@
DebugEGLImageTracker *DebugEGLImageTracker::getInstance() {
std::lock_guard lock(mInstanceLock);
if (mInstance == nullptr) {
- char value[PROPERTY_VALUE_MAX];
- property_get("debug.sf.enable_egl_image_tracker", value, "0");
- const bool enabled = static_cast<bool>(atoi(value));
-
+ const bool enabled = GetBoolProperty("debug.sf.enable_egl_image_tracker", false);
if (enabled) {
mInstance = new DebugEGLImageTrackerImpl();
} else {
diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp
new file mode 100644
index 0000000..b33bc9e
--- /dev/null
+++ b/libs/gui/DisplayEventDispatcher.cpp
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2015 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 "DisplayEventDispatcher"
+
+#include <cinttypes>
+#include <cstdint>
+
+#include <gui/DisplayEventDispatcher.h>
+#include <gui/DisplayEventReceiver.h>
+#include <utils/Log.h>
+#include <utils/Looper.h>
+
+#include <utils/Timers.h>
+
+namespace android {
+
+// Number of events to read at a time from the DisplayEventDispatcher pipe.
+// The value should be large enough that we can quickly drain the pipe
+// using just a few large reads.
+static const size_t EVENT_BUFFER_SIZE = 100;
+
+DisplayEventDispatcher::DisplayEventDispatcher(const sp<Looper>& looper,
+ ISurfaceComposer::VsyncSource vsyncSource,
+ ISurfaceComposer::ConfigChanged configChanged)
+ : mLooper(looper), mReceiver(vsyncSource, configChanged), mWaitingForVsync(false) {
+ ALOGV("dispatcher %p ~ Initializing display event dispatcher.", this);
+}
+
+status_t DisplayEventDispatcher::initialize() {
+ status_t result = mReceiver.initCheck();
+ if (result) {
+ ALOGW("Failed to initialize display event receiver, status=%d", result);
+ return result;
+ }
+
+ if (mLooper != nullptr) {
+ int rc = mLooper->addFd(mReceiver.getFd(), 0, Looper::EVENT_INPUT, this, NULL);
+ if (rc < 0) {
+ return UNKNOWN_ERROR;
+ }
+ }
+
+ return OK;
+}
+
+void DisplayEventDispatcher::dispose() {
+ ALOGV("dispatcher %p ~ Disposing display event dispatcher.", this);
+
+ if (!mReceiver.initCheck() && mLooper != nullptr) {
+ mLooper->removeFd(mReceiver.getFd());
+ }
+}
+
+status_t DisplayEventDispatcher::scheduleVsync() {
+ if (!mWaitingForVsync) {
+ ALOGV("dispatcher %p ~ Scheduling vsync.", this);
+
+ // Drain all pending events.
+ nsecs_t vsyncTimestamp;
+ PhysicalDisplayId vsyncDisplayId;
+ uint32_t vsyncCount;
+ if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) {
+ ALOGE("dispatcher %p ~ last event processed while scheduling was for %" PRId64 "", this,
+ ns2ms(static_cast<nsecs_t>(vsyncTimestamp)));
+ }
+
+ status_t status = mReceiver.requestNextVsync();
+ if (status) {
+ ALOGW("Failed to request next vsync, status=%d", status);
+ return status;
+ }
+
+ mWaitingForVsync = true;
+ }
+ return OK;
+}
+
+void DisplayEventDispatcher::requestLatestConfig() {
+ status_t status = mReceiver.requestLatestConfig();
+ if (status) {
+ ALOGW("Failed enable config events, status=%d", status);
+ return;
+ }
+}
+
+int DisplayEventDispatcher::getFd() const {
+ return mReceiver.getFd();
+}
+
+int DisplayEventDispatcher::handleEvent(int, int events, void*) {
+ if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) {
+ ALOGE("Display event receiver pipe was closed or an error occurred. "
+ "events=0x%x",
+ events);
+ return 0; // remove the callback
+ }
+
+ if (!(events & Looper::EVENT_INPUT)) {
+ ALOGW("Received spurious callback for unhandled poll event. "
+ "events=0x%x",
+ events);
+ return 1; // keep the callback
+ }
+
+ // Drain all pending events, keep the last vsync.
+ nsecs_t vsyncTimestamp;
+ PhysicalDisplayId vsyncDisplayId;
+ uint32_t vsyncCount;
+ if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) {
+ ALOGV("dispatcher %p ~ Vsync pulse: timestamp=%" PRId64
+ ", displayId=%" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", count=%d",
+ this, ns2ms(vsyncTimestamp), vsyncDisplayId, vsyncCount);
+ mWaitingForVsync = false;
+ dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount);
+ }
+
+ return 1; // keep the callback
+}
+
+bool DisplayEventDispatcher::processPendingEvents(nsecs_t* outTimestamp,
+ PhysicalDisplayId* outDisplayId,
+ uint32_t* outCount) {
+ bool gotVsync = false;
+ DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE];
+ ssize_t n;
+ while ((n = mReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
+ ALOGV("dispatcher %p ~ Read %d events.", this, int(n));
+ for (ssize_t i = 0; i < n; i++) {
+ const DisplayEventReceiver::Event& ev = buf[i];
+ switch (ev.header.type) {
+ case DisplayEventReceiver::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;
+ *outTimestamp = ev.header.timestamp;
+ *outDisplayId = ev.header.displayId;
+ *outCount = ev.vsync.count;
+ break;
+ case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
+ dispatchHotplug(ev.header.timestamp, ev.header.displayId, ev.hotplug.connected);
+ break;
+ case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED:
+ dispatchConfigChanged(ev.header.timestamp, ev.header.displayId,
+ ev.config.configId, ev.config.vsyncPeriod);
+ break;
+ default:
+ ALOGW("dispatcher %p ~ ignoring unknown event type %#x", this, ev.header.type);
+ break;
+ }
+ }
+ }
+ if (n < 0) {
+ ALOGW("Failed to get events from display event dispatcher, status=%d", status_t(n));
+ }
+ return gotVsync;
+}
+} // namespace android
diff --git a/libs/gui/DisplayEventReceiver.cpp b/libs/gui/DisplayEventReceiver.cpp
index b8faa2d..1fed509 100644
--- a/libs/gui/DisplayEventReceiver.cpp
+++ b/libs/gui/DisplayEventReceiver.cpp
@@ -79,6 +79,13 @@
return NO_INIT;
}
+status_t DisplayEventReceiver::requestLatestConfig() {
+ if (mEventConnection != nullptr) {
+ mEventConnection->requestLatestConfig();
+ return NO_ERROR;
+ }
+ return NO_INIT;
+}
ssize_t DisplayEventReceiver::getEvents(DisplayEventReceiver::Event* events,
size_t count) {
diff --git a/libs/gui/FrameTimestamps.cpp b/libs/gui/FrameTimestamps.cpp
index 3215eca..e2ea3f9 100644
--- a/libs/gui/FrameTimestamps.cpp
+++ b/libs/gui/FrameTimestamps.cpp
@@ -364,6 +364,10 @@
mProducerWantsEvents = false;
}
+void ConsumerFrameEventHistory::setProducerWantsEvents() {
+ mProducerWantsEvents = true;
+}
+
void ConsumerFrameEventHistory::initializeCompositorTiming(
const CompositorTiming& compositorTiming) {
mCompositorTiming = compositorTiming;
diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp
index 8199c98..30d19e3 100644
--- a/libs/gui/GLConsumer.cpp
+++ b/libs/gui/GLConsumer.cpp
@@ -46,7 +46,6 @@
#include <utils/String8.h>
#include <utils/Trace.h>
-extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
#define PROT_CONTENT_EXT_STR "EGL_EXT_protected_content"
#define EGL_PROTECTED_CONTENT_EXT 0x32C0
@@ -281,7 +280,7 @@
mCurrentFenceTime = FenceTime::NO_FENCE;
if (mAttached) {
- // This binds a dummy buffer (mReleasedTexImage).
+ // This binds a buffer placeholder (mReleasedTexImage).
status_t result = bindTextureImageLocked();
if (result != NO_ERROR) {
return result;
diff --git a/libs/gui/HdrMetadata.cpp b/libs/gui/HdrMetadata.cpp
index add3ef0..058cd9a 100644
--- a/libs/gui/HdrMetadata.cpp
+++ b/libs/gui/HdrMetadata.cpp
@@ -28,8 +28,8 @@
size += sizeof(cta8613);
}
if (validTypes & HDR10PLUS) {
- size += sizeof(size_t);
- size += hdr10plus.size();
+ size += sizeof(uint32_t);
+ size += hdr10plus.size() * sizeof(hdr10plus[0]);
}
return size;
}
@@ -47,10 +47,11 @@
FlattenableUtils::write(buffer, size, cta8613);
}
if (validTypes & HDR10PLUS) {
- size_t metadataSize = hdr10plus.size();
+ uint32_t metadataSize = hdr10plus.size();
FlattenableUtils::write(buffer, size, metadataSize);
- memcpy(buffer, hdr10plus.data(), metadataSize);
- FlattenableUtils::advance(buffer, size, metadataSize);
+ size_t metadataSizeinByte = metadataSize * sizeof(hdr10plus[0]);
+ memcpy(buffer, hdr10plus.data(), metadataSizeinByte);
+ FlattenableUtils::advance(buffer, size, metadataSizeinByte);
}
return NO_ERROR;
@@ -74,20 +75,21 @@
FlattenableUtils::read(buffer, size, cta8613);
}
if (validTypes & HDR10PLUS) {
- if (size < sizeof(size_t)) {
+ if (size < sizeof(uint32_t)) {
return NO_MEMORY;
}
- size_t metadataSize;
+ uint32_t metadataSize;
FlattenableUtils::read(buffer, size, metadataSize);
- if (size < metadataSize) {
+ size_t metadataSizeinByte = metadataSize * sizeof(hdr10plus[0]);
+ if (size < metadataSizeinByte) {
return NO_MEMORY;
}
hdr10plus.resize(metadataSize);
- memcpy(hdr10plus.data(), buffer, metadataSize);
- FlattenableUtils::advance(buffer, size, metadataSize);
+ memcpy(hdr10plus.data(), buffer, metadataSizeinByte);
+ FlattenableUtils::advance(buffer, size, metadataSizeinByte);
}
return NO_ERROR;
diff --git a/libs/gui/IConsumerListener.cpp b/libs/gui/IConsumerListener.cpp
index 85ac304..f3bd90c 100644
--- a/libs/gui/IConsumerListener.cpp
+++ b/libs/gui/IConsumerListener.cpp
@@ -28,7 +28,10 @@
ON_FRAME_REPLACED,
ON_BUFFERS_RELEASED,
ON_SIDEBAND_STREAM_CHANGED,
- LAST = ON_SIDEBAND_STREAM_CHANGED,
+ ON_FRAME_DEQUEUED,
+ ON_FRAME_CANCELLED,
+ ON_FRAME_DETACHED,
+ LAST = ON_FRAME_DETACHED,
};
} // Anonymous namespace
@@ -44,6 +47,21 @@
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);
@@ -72,7 +90,6 @@
// Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see
// clang warning -Wweak-vtables)
BpConsumerListener::~BpConsumerListener() = default;
-ConsumerListener::~ConsumerListener() = default;
IMPLEMENT_META_INTERFACE(ConsumerListener, "android.gui.IConsumerListener");
@@ -93,6 +110,12 @@
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);
}
}
diff --git a/libs/gui/IDisplayEventConnection.cpp b/libs/gui/IDisplayEventConnection.cpp
index c0e246f..aa74bfd 100644
--- a/libs/gui/IDisplayEventConnection.cpp
+++ b/libs/gui/IDisplayEventConnection.cpp
@@ -26,7 +26,8 @@
STEAL_RECEIVE_CHANNEL = IBinder::FIRST_CALL_TRANSACTION,
SET_VSYNC_RATE,
REQUEST_NEXT_VSYNC,
- LAST = REQUEST_NEXT_VSYNC,
+ REQUEST_LATEST_CONFIG,
+ LAST = REQUEST_LATEST_CONFIG,
};
} // Anonymous namespace
@@ -53,6 +54,11 @@
callRemoteAsync<decltype(&IDisplayEventConnection::requestNextVsync)>(
Tag::REQUEST_NEXT_VSYNC);
}
+
+ void requestLatestConfig() override {
+ callRemoteAsync<decltype(&IDisplayEventConnection::requestLatestConfig)>(
+ Tag::REQUEST_LATEST_CONFIG);
+ }
};
// Out-of-line virtual method definition to trigger vtable emission in this translation unit (see
@@ -74,6 +80,8 @@
return callLocal(data, reply, &IDisplayEventConnection::setVsyncRate);
case Tag::REQUEST_NEXT_VSYNC:
return callLocalAsync(data, reply, &IDisplayEventConnection::requestNextVsync);
+ case Tag::REQUEST_LATEST_CONFIG:
+ return callLocalAsync(data, reply, &IDisplayEventConnection::requestLatestConfig);
}
}
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index 0e03b7d..ad00939 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -73,6 +73,7 @@
GET_UNIQUE_ID,
GET_CONSUMER_USAGE,
SET_LEGACY_BUFFER_DROP,
+ SET_AUTO_PREROTATION,
};
class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer>
@@ -547,6 +548,17 @@
}
return actualResult;
}
+
+ virtual status_t setAutoPrerotation(bool autoPrerotation) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
+ data.writeBool(autoPrerotation);
+ status_t result = remote()->transact(SET_AUTO_PREROTATION, data, &reply);
+ if (result == NO_ERROR) {
+ result = reply.readInt32();
+ }
+ return result;
+ }
};
// Out-of-line virtual method definition to trigger vtable emission in this
@@ -675,6 +687,10 @@
status_t getConsumerUsage(uint64_t* outUsage) const override {
return mBase->getConsumerUsage(outUsage);
}
+
+ status_t setAutoPrerotation(bool autoPrerotation) override {
+ return mBase->setAutoPrerotation(autoPrerotation);
+ }
};
IMPLEMENT_HYBRID_META_INTERFACE(GraphicBufferProducer,
@@ -688,6 +704,12 @@
return INVALID_OPERATION;
}
+status_t IGraphicBufferProducer::setAutoPrerotation(bool autoPrerotation) {
+ // No-op for IGBP other than BufferQueue.
+ (void)autoPrerotation;
+ return INVALID_OPERATION;
+}
+
status_t IGraphicBufferProducer::exportToParcel(Parcel* parcel) {
status_t res = OK;
res = parcel->writeUint32(USE_BUFFER_QUEUE);
@@ -1050,6 +1072,13 @@
reply->writeInt32(result);
return NO_ERROR;
}
+ case SET_AUTO_PREROTATION: {
+ CHECK_INTERFACE(IGraphicBuffer, data, reply);
+ bool autoPrerotation = data.readBool();
+ status_t result = setAutoPrerotation(autoPrerotation);
+ reply->writeInt32(result);
+ return NO_ERROR;
+ }
}
return BBinder::onTransact(code, data, reply, flags);
}
@@ -1060,135 +1089,5 @@
parcel.read(*this);
}
-constexpr size_t IGraphicBufferProducer::QueueBufferInput::minFlattenedSize() {
- return sizeof(timestamp) +
- sizeof(isAutoTimestamp) +
- sizeof(dataSpace) +
- sizeof(crop) +
- sizeof(scalingMode) +
- sizeof(transform) +
- sizeof(stickyTransform) +
- sizeof(getFrameTimestamps);
-}
-
-size_t IGraphicBufferProducer::QueueBufferInput::getFlattenedSize() const {
- return minFlattenedSize() +
- fence->getFlattenedSize() +
- surfaceDamage.getFlattenedSize() +
- hdrMetadata.getFlattenedSize();
-}
-
-size_t IGraphicBufferProducer::QueueBufferInput::getFdCount() const {
- return fence->getFdCount();
-}
-
-status_t IGraphicBufferProducer::QueueBufferInput::flatten(
- void*& buffer, size_t& size, int*& fds, size_t& count) const
-{
- if (size < getFlattenedSize()) {
- return NO_MEMORY;
- }
-
- FlattenableUtils::write(buffer, size, timestamp);
- FlattenableUtils::write(buffer, size, isAutoTimestamp);
- FlattenableUtils::write(buffer, size, dataSpace);
- FlattenableUtils::write(buffer, size, crop);
- FlattenableUtils::write(buffer, size, scalingMode);
- FlattenableUtils::write(buffer, size, transform);
- FlattenableUtils::write(buffer, size, stickyTransform);
- FlattenableUtils::write(buffer, size, getFrameTimestamps);
-
- status_t result = fence->flatten(buffer, size, fds, count);
- if (result != NO_ERROR) {
- return result;
- }
- result = surfaceDamage.flatten(buffer, size);
- if (result != NO_ERROR) {
- return result;
- }
- FlattenableUtils::advance(buffer, size, surfaceDamage.getFlattenedSize());
- return hdrMetadata.flatten(buffer, size);
-}
-
-status_t IGraphicBufferProducer::QueueBufferInput::unflatten(
- void const*& buffer, size_t& size, int const*& fds, size_t& count)
-{
- if (size < minFlattenedSize()) {
- return NO_MEMORY;
- }
-
- FlattenableUtils::read(buffer, size, timestamp);
- FlattenableUtils::read(buffer, size, isAutoTimestamp);
- FlattenableUtils::read(buffer, size, dataSpace);
- FlattenableUtils::read(buffer, size, crop);
- FlattenableUtils::read(buffer, size, scalingMode);
- FlattenableUtils::read(buffer, size, transform);
- FlattenableUtils::read(buffer, size, stickyTransform);
- FlattenableUtils::read(buffer, size, getFrameTimestamps);
-
- fence = new Fence();
- status_t result = fence->unflatten(buffer, size, fds, count);
- if (result != NO_ERROR) {
- return result;
- }
- result = surfaceDamage.unflatten(buffer, size);
- if (result != NO_ERROR) {
- return result;
- }
- FlattenableUtils::advance(buffer, size, surfaceDamage.getFlattenedSize());
- return hdrMetadata.unflatten(buffer, size);
-}
-
-// ----------------------------------------------------------------------------
-constexpr size_t IGraphicBufferProducer::QueueBufferOutput::minFlattenedSize() {
- return sizeof(width) +
- sizeof(height) +
- sizeof(transformHint) +
- sizeof(numPendingBuffers) +
- sizeof(nextFrameNumber) +
- sizeof(bufferReplaced);
-}
-
-size_t IGraphicBufferProducer::QueueBufferOutput::getFlattenedSize() const {
- return minFlattenedSize() + frameTimestamps.getFlattenedSize();
-}
-
-size_t IGraphicBufferProducer::QueueBufferOutput::getFdCount() const {
- return frameTimestamps.getFdCount();
-}
-
-status_t IGraphicBufferProducer::QueueBufferOutput::flatten(
- void*& buffer, size_t& size, int*& fds, size_t& count) const
-{
- if (size < getFlattenedSize()) {
- return NO_MEMORY;
- }
-
- FlattenableUtils::write(buffer, size, width);
- FlattenableUtils::write(buffer, size, height);
- FlattenableUtils::write(buffer, size, transformHint);
- FlattenableUtils::write(buffer, size, numPendingBuffers);
- FlattenableUtils::write(buffer, size, nextFrameNumber);
- FlattenableUtils::write(buffer, size, bufferReplaced);
-
- return frameTimestamps.flatten(buffer, size, fds, count);
-}
-
-status_t IGraphicBufferProducer::QueueBufferOutput::unflatten(
- void const*& buffer, size_t& size, int const*& fds, size_t& count)
-{
- if (size < minFlattenedSize()) {
- return NO_MEMORY;
- }
-
- FlattenableUtils::read(buffer, size, width);
- FlattenableUtils::read(buffer, size, height);
- FlattenableUtils::read(buffer, size, transformHint);
- FlattenableUtils::read(buffer, size, numPendingBuffers);
- FlattenableUtils::read(buffer, size, nextFrameNumber);
- FlattenableUtils::read(buffer, size, bufferReplaced);
-
- return frameTimestamps.unflatten(buffer, size, fds, count);
-}
}; // namespace android
diff --git a/libs/gui/IProducerListener.cpp b/libs/gui/IProducerListener.cpp
index 808e336..0683087 100644
--- a/libs/gui/IProducerListener.cpp
+++ b/libs/gui/IProducerListener.cpp
@@ -119,9 +119,7 @@
return BBinder::onTransact(code, data, reply, flags);
}
-ProducerListener::~ProducerListener() = default;
-
-DummyProducerListener::~DummyProducerListener() = default;
+StubProducerListener::~StubProducerListener() = default;
bool BnProducerListener::needsReleaseNotify() {
return true;
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 12deaf0..e62a61f 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -34,8 +34,10 @@
#include <system/graphics.h>
+#include <ui/DisplayConfig.h>
#include <ui/DisplayInfo.h>
#include <ui/DisplayStatInfo.h>
+#include <ui/DisplayState.h>
#include <ui/HdrCapabilities.h>
#include <utils/Log.h>
@@ -69,7 +71,7 @@
const sp<IBinder>& applyToken,
const InputWindowCommands& commands,
int64_t desiredPresentTime,
- const client_cache_t& uncacheBuffer,
+ const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
const std::vector<ListenerCallbacks>& listenerCallbacks) {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
@@ -90,10 +92,11 @@
data.writeInt64(desiredPresentTime);
data.writeStrongBinder(uncacheBuffer.token.promote());
data.writeUint64(uncacheBuffer.id);
+ data.writeBool(hasListenerCallbacks);
if (data.writeVectorSize(listenerCallbacks) == NO_ERROR) {
for (const auto& [listener, callbackIds] : listenerCallbacks) {
- data.writeStrongBinder(IInterface::asBinder(listener));
+ data.writeStrongBinder(listener);
data.writeInt64Vector(callbackIds);
}
}
@@ -109,10 +112,10 @@
}
virtual status_t captureScreen(const sp<IBinder>& display, sp<GraphicBuffer>* outBuffer,
- bool& outCapturedSecureLayers, const ui::Dataspace reqDataspace,
- const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
+ bool& outCapturedSecureLayers, ui::Dataspace reqDataspace,
+ ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform,
- ISurfaceComposer::Rotation rotation, bool captureSecureLayers) {
+ ui::Rotation rotation, bool captureSecureLayers) {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
data.writeStrongBinder(display);
@@ -350,22 +353,43 @@
remote()->transact(BnSurfaceComposer::SET_POWER_MODE, data, &reply);
}
- virtual status_t getDisplayConfigs(const sp<IBinder>& display,
- Vector<DisplayInfo>* configs)
- {
+ virtual status_t getDisplayState(const sp<IBinder>& display, ui::DisplayState* state) {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ data.writeStrongBinder(display);
+ remote()->transact(BnSurfaceComposer::GET_DISPLAY_STATE, data, &reply);
+ const status_t result = reply.readInt32();
+ if (result == NO_ERROR) {
+ memcpy(state, reply.readInplace(sizeof(ui::DisplayState)), sizeof(ui::DisplayState));
+ }
+ return result;
+ }
+
+ virtual status_t getDisplayInfo(const sp<IBinder>& display, DisplayInfo* info) {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ data.writeStrongBinder(display);
+ remote()->transact(BnSurfaceComposer::GET_DISPLAY_INFO, data, &reply);
+ const status_t result = reply.readInt32();
+ if (result == NO_ERROR) {
+ memcpy(info, reply.readInplace(sizeof(DisplayInfo)), sizeof(DisplayInfo));
+ }
+ return result;
+ }
+
+ virtual status_t getDisplayConfigs(const sp<IBinder>& display, Vector<DisplayConfig>* configs) {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
data.writeStrongBinder(display);
remote()->transact(BnSurfaceComposer::GET_DISPLAY_CONFIGS, data, &reply);
- status_t result = reply.readInt32();
+ const status_t result = reply.readInt32();
if (result == NO_ERROR) {
- size_t numConfigs = reply.readUint32();
+ const size_t numConfigs = reply.readUint32();
configs->clear();
configs->resize(numConfigs);
for (size_t c = 0; c < numConfigs; ++c) {
- memcpy(&(configs->editItemAt(c)),
- reply.readInplace(sizeof(DisplayInfo)),
- sizeof(DisplayInfo));
+ memcpy(&(configs->editItemAt(c)), reply.readInplace(sizeof(DisplayConfig)),
+ sizeof(DisplayConfig));
}
}
return result;
@@ -396,32 +420,6 @@
return reply.readInt32();
}
- virtual status_t setActiveConfig(const sp<IBinder>& display, int id)
- {
- Parcel data, reply;
- status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
- if (result != NO_ERROR) {
- ALOGE("setActiveConfig failed to writeInterfaceToken: %d", result);
- return result;
- }
- result = data.writeStrongBinder(display);
- if (result != NO_ERROR) {
- ALOGE("setActiveConfig failed to writeStrongBinder: %d", result);
- return result;
- }
- result = data.writeInt32(id);
- if (result != NO_ERROR) {
- ALOGE("setActiveConfig failed to writeInt32: %d", result);
- return result;
- }
- result = remote()->transact(BnSurfaceComposer::SET_ACTIVE_CONFIG, data, &reply);
- if (result != NO_ERROR) {
- ALOGE("setActiveConfig failed to transact: %d", result);
- return result;
- }
- return reply.readInt32();
- }
-
virtual status_t getDisplayColorModes(const sp<IBinder>& display,
Vector<ColorMode>* outColorModes) {
Parcel data, reply;
@@ -524,6 +522,88 @@
return static_cast<status_t>(reply.readInt32());
}
+ virtual status_t getAutoLowLatencyModeSupport(const sp<IBinder>& display,
+ bool* outSupport) const {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ status_t result = data.writeStrongBinder(display);
+ if (result != NO_ERROR) {
+ ALOGE("getAutoLowLatencyModeSupport failed to writeStrongBinder: %d", result);
+ return result;
+ }
+ result = remote()->transact(BnSurfaceComposer::GET_AUTO_LOW_LATENCY_MODE_SUPPORT, data,
+ &reply);
+ if (result != NO_ERROR) {
+ ALOGE("getAutoLowLatencyModeSupport failed to transact: %d", result);
+ return result;
+ }
+ return reply.readBool(outSupport);
+ }
+
+ virtual void setAutoLowLatencyMode(const sp<IBinder>& display, bool on) {
+ Parcel data, reply;
+ status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ if (result != NO_ERROR) {
+ ALOGE("setAutoLowLatencyMode failed to writeInterfaceToken: %d", result);
+ return;
+ }
+
+ result = data.writeStrongBinder(display);
+ if (result != NO_ERROR) {
+ ALOGE("setAutoLowLatencyMode failed to writeStrongBinder: %d", result);
+ return;
+ }
+ result = data.writeBool(on);
+ if (result != NO_ERROR) {
+ ALOGE("setAutoLowLatencyMode failed to writeBool: %d", result);
+ return;
+ }
+ result = remote()->transact(BnSurfaceComposer::SET_AUTO_LOW_LATENCY_MODE, data, &reply);
+ if (result != NO_ERROR) {
+ ALOGE("setAutoLowLatencyMode failed to transact: %d", result);
+ return;
+ }
+ }
+
+ virtual status_t getGameContentTypeSupport(const sp<IBinder>& display, bool* outSupport) const {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ status_t result = data.writeStrongBinder(display);
+ if (result != NO_ERROR) {
+ ALOGE("getGameContentTypeSupport failed to writeStrongBinder: %d", result);
+ return result;
+ }
+ result = remote()->transact(BnSurfaceComposer::GET_GAME_CONTENT_TYPE_SUPPORT, data, &reply);
+ if (result != NO_ERROR) {
+ ALOGE("getGameContentTypeSupport failed to transact: %d", result);
+ return result;
+ }
+ return reply.readBool(outSupport);
+ }
+
+ virtual void setGameContentType(const sp<IBinder>& display, bool on) {
+ Parcel data, reply;
+ status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ if (result != NO_ERROR) {
+ ALOGE("setGameContentType failed to writeInterfaceToken: %d", result);
+ return;
+ }
+ result = data.writeStrongBinder(display);
+ if (result != NO_ERROR) {
+ ALOGE("setGameContentType failed to writeStrongBinder: %d", result);
+ return;
+ }
+ result = data.writeBool(on);
+ if (result != NO_ERROR) {
+ ALOGE("setGameContentType failed to writeBool: %d", result);
+ return;
+ }
+ result = remote()->transact(BnSurfaceComposer::SET_GAME_CONTENT_TYPE, data, &reply);
+ if (result != NO_ERROR) {
+ ALOGE("setGameContentType failed to transact: %d", result);
+ }
+ }
+
virtual status_t clearAnimationFrameStats() {
Parcel data, reply;
status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
@@ -611,8 +691,7 @@
return result;
}
- virtual status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const
- {
+ virtual status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) {
if (!outLayers) {
return UNEXPECTED_NULL;
}
@@ -715,8 +794,7 @@
}
virtual status_t setDisplayContentSamplingEnabled(const sp<IBinder>& display, bool enable,
- uint8_t componentMask,
- uint64_t maxFrames) const {
+ uint8_t componentMask, uint64_t maxFrames) {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
data.writeStrongBinder(display);
@@ -851,54 +929,112 @@
return error;
}
- virtual status_t setAllowedDisplayConfigs(const sp<IBinder>& displayToken,
- const std::vector<int32_t>& allowedConfigs) {
+ virtual status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
+ int32_t defaultConfig,
+ float primaryRefreshRateMin,
+ float primaryRefreshRateMax,
+ float appRequestRefreshRateMin,
+ float appRequestRefreshRateMax) {
Parcel data, reply;
status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
if (result != NO_ERROR) {
- ALOGE("setAllowedDisplayConfigs failed to writeInterfaceToken: %d", result);
+ ALOGE("setDesiredDisplayConfigSpecs: failed to writeInterfaceToken: %d", result);
return result;
}
result = data.writeStrongBinder(displayToken);
if (result != NO_ERROR) {
- ALOGE("setAllowedDisplayConfigs failed to writeStrongBinder: %d", result);
+ ALOGE("setDesiredDisplayConfigSpecs: failed to write display token: %d", result);
return result;
}
- result = data.writeInt32Vector(allowedConfigs);
+ result = data.writeInt32(defaultConfig);
if (result != NO_ERROR) {
- ALOGE("setAllowedDisplayConfigs failed to writeInt32Vector: %d", result);
+ ALOGE("setDesiredDisplayConfigSpecs failed to write defaultConfig: %d", result);
return result;
}
- result = remote()->transact(BnSurfaceComposer::SET_ALLOWED_DISPLAY_CONFIGS, data, &reply);
+ result = data.writeFloat(primaryRefreshRateMin);
if (result != NO_ERROR) {
- ALOGE("setAllowedDisplayConfigs failed to transact: %d", result);
+ ALOGE("setDesiredDisplayConfigSpecs failed to write primaryRefreshRateMin: %d", result);
+ return result;
+ }
+ result = data.writeFloat(primaryRefreshRateMax);
+ if (result != NO_ERROR) {
+ ALOGE("setDesiredDisplayConfigSpecs failed to write primaryRefreshRateMax: %d", result);
+ return result;
+ }
+ result = data.writeFloat(appRequestRefreshRateMin);
+ if (result != NO_ERROR) {
+ ALOGE("setDesiredDisplayConfigSpecs failed to write appRequestRefreshRateMin: %d",
+ result);
+ return result;
+ }
+ result = data.writeFloat(appRequestRefreshRateMax);
+ if (result != NO_ERROR) {
+ ALOGE("setDesiredDisplayConfigSpecs failed to write appRequestRefreshRateMax: %d",
+ result);
+ return result;
+ }
+
+ result = remote()->transact(BnSurfaceComposer::SET_DESIRED_DISPLAY_CONFIG_SPECS, data,
+ &reply);
+ if (result != NO_ERROR) {
+ ALOGE("setDesiredDisplayConfigSpecs failed to transact: %d", result);
return result;
}
return reply.readInt32();
}
- virtual status_t getAllowedDisplayConfigs(const sp<IBinder>& displayToken,
- std::vector<int32_t>* outAllowedConfigs) {
- if (!outAllowedConfigs) return BAD_VALUE;
+ virtual status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
+ int32_t* outDefaultConfig,
+ float* outPrimaryRefreshRateMin,
+ float* outPrimaryRefreshRateMax,
+ float* outAppRequestRefreshRateMin,
+ float* outAppRequestRefreshRateMax) {
+ if (!outDefaultConfig || !outPrimaryRefreshRateMin || !outPrimaryRefreshRateMax ||
+ !outAppRequestRefreshRateMin || !outAppRequestRefreshRateMax) {
+ return BAD_VALUE;
+ }
Parcel data, reply;
status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
if (result != NO_ERROR) {
- ALOGE("getAllowedDisplayConfigs failed to writeInterfaceToken: %d", result);
+ ALOGE("getDesiredDisplayConfigSpecs failed to writeInterfaceToken: %d", result);
return result;
}
result = data.writeStrongBinder(displayToken);
if (result != NO_ERROR) {
- ALOGE("getAllowedDisplayConfigs failed to writeStrongBinder: %d", result);
+ ALOGE("getDesiredDisplayConfigSpecs failed to writeStrongBinder: %d", result);
return result;
}
- result = remote()->transact(BnSurfaceComposer::GET_ALLOWED_DISPLAY_CONFIGS, data, &reply);
+ result = remote()->transact(BnSurfaceComposer::GET_DESIRED_DISPLAY_CONFIG_SPECS, data,
+ &reply);
if (result != NO_ERROR) {
- ALOGE("getAllowedDisplayConfigs failed to transact: %d", result);
+ ALOGE("getDesiredDisplayConfigSpecs failed to transact: %d", result);
return result;
}
- result = reply.readInt32Vector(outAllowedConfigs);
+ result = reply.readInt32(outDefaultConfig);
if (result != NO_ERROR) {
- ALOGE("getAllowedDisplayConfigs failed to readInt32Vector: %d", result);
+ ALOGE("getDesiredDisplayConfigSpecs failed to read defaultConfig: %d", result);
+ return result;
+ }
+ result = reply.readFloat(outPrimaryRefreshRateMin);
+ if (result != NO_ERROR) {
+ ALOGE("getDesiredDisplayConfigSpecs failed to read primaryRefreshRateMin: %d", result);
+ return result;
+ }
+ result = reply.readFloat(outPrimaryRefreshRateMax);
+ if (result != NO_ERROR) {
+ ALOGE("getDesiredDisplayConfigSpecs failed to read primaryRefreshRateMax: %d", result);
+ return result;
+ }
+ result = reply.readFloat(outAppRequestRefreshRateMin);
+ if (result != NO_ERROR) {
+ ALOGE("getDesiredDisplayConfigSpecs failed to read appRequestRefreshRateMin: %d",
+ result);
+ return result;
+ }
+ result = reply.readFloat(outAppRequestRefreshRateMax);
+ if (result != NO_ERROR) {
+ ALOGE("getDesiredDisplayConfigSpecs failed to read appRequestRefreshRateMax: %d",
+ result);
return result;
}
return reply.readInt32();
@@ -932,7 +1068,7 @@
return NO_ERROR;
}
- virtual status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness) const {
+ virtual status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness) {
Parcel data, reply;
status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
if (error != NO_ERROR) {
@@ -977,6 +1113,106 @@
}
return NO_ERROR;
}
+
+ virtual status_t setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor,
+ float lightPosY, float lightPosZ, float lightRadius) {
+ Parcel data, reply;
+ status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ if (error != NO_ERROR) {
+ ALOGE("setGlobalShadowSettings: failed to write interface token: %d", error);
+ return error;
+ }
+
+ std::vector<float> shadowConfig = {ambientColor.r, ambientColor.g, ambientColor.b,
+ ambientColor.a, spotColor.r, spotColor.g,
+ spotColor.b, spotColor.a, lightPosY,
+ lightPosZ, lightRadius};
+
+ error = data.writeFloatVector(shadowConfig);
+ if (error != NO_ERROR) {
+ ALOGE("setGlobalShadowSettings: failed to write shadowConfig: %d", error);
+ return error;
+ }
+
+ error = remote()->transact(BnSurfaceComposer::SET_GLOBAL_SHADOW_SETTINGS, data, &reply,
+ IBinder::FLAG_ONEWAY);
+ if (error != NO_ERROR) {
+ ALOGE("setGlobalShadowSettings: failed to transact: %d", error);
+ return error;
+ }
+ return NO_ERROR;
+ }
+
+ virtual status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
+ int8_t compatibility) {
+ Parcel data, reply;
+ status_t err = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ if (err != NO_ERROR) {
+ ALOGE("setFrameRate: failed writing interface token: %s (%d)", strerror(-err), -err);
+ return err;
+ }
+
+ err = data.writeStrongBinder(IInterface::asBinder(surface));
+ if (err != NO_ERROR) {
+ ALOGE("setFrameRate: failed writing strong binder: %s (%d)", strerror(-err), -err);
+ return err;
+ }
+
+ err = data.writeFloat(frameRate);
+ if (err != NO_ERROR) {
+ ALOGE("setFrameRate: failed writing float: %s (%d)", strerror(-err), -err);
+ return err;
+ }
+
+ err = data.writeByte(compatibility);
+ if (err != NO_ERROR) {
+ ALOGE("setFrameRate: failed writing byte: %s (%d)", strerror(-err), -err);
+ return err;
+ }
+
+ err = remote()->transact(BnSurfaceComposer::SET_FRAME_RATE, data, &reply);
+ if (err != NO_ERROR) {
+ ALOGE("setFrameRate: failed to transact: %s (%d)", strerror(-err), err);
+ return err;
+ }
+
+ return reply.readInt32();
+ }
+
+ virtual status_t acquireFrameRateFlexibilityToken(sp<IBinder>* outToken) {
+ if (!outToken) return BAD_VALUE;
+
+ Parcel data, reply;
+ status_t err = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ if (err != NO_ERROR) {
+ ALOGE("acquireFrameRateFlexibilityToken: failed writing interface token: %s (%d)",
+ strerror(-err), -err);
+ return err;
+ }
+
+ err = remote()->transact(BnSurfaceComposer::ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN, data,
+ &reply);
+ if (err != NO_ERROR) {
+ ALOGE("acquireFrameRateFlexibilityToken: failed to transact: %s (%d)", strerror(-err),
+ err);
+ return err;
+ }
+
+ err = reply.readInt32();
+ if (err != NO_ERROR) {
+ ALOGE("acquireFrameRateFlexibilityToken: call failed: %s (%d)", strerror(-err), err);
+ return err;
+ }
+
+ err = reply.readStrongBinder(outToken);
+ if (err != NO_ERROR) {
+ ALOGE("acquireFrameRateFlexibilityToken: failed reading binder token: %s (%d)",
+ strerror(-err), err);
+ return err;
+ }
+
+ return NO_ERROR;
+ }
};
// Out-of-line virtual method definition to trigger vtable emission in this
@@ -1039,18 +1275,19 @@
uncachedBuffer.token = data.readStrongBinder();
uncachedBuffer.id = data.readUint64();
+ bool hasListenerCallbacks = data.readBool();
+
std::vector<ListenerCallbacks> listenerCallbacks;
int32_t listenersSize = data.readInt32();
for (int32_t i = 0; i < listenersSize; i++) {
- auto listener =
- interface_cast<ITransactionCompletedListener>(data.readStrongBinder());
+ auto listener = data.readStrongBinder();
std::vector<CallbackId> callbackIds;
data.readInt64Vector(&callbackIds);
listenerCallbacks.emplace_back(listener, callbackIds);
}
-
setTransactionState(state, displays, stateFlags, applyToken, inputWindowCommands,
- desiredPresentTime, uncachedBuffer, listenerCallbacks);
+ desiredPresentTime, uncachedBuffer, hasListenerCallbacks,
+ listenerCallbacks);
return NO_ERROR;
}
case BOOT_FINISHED: {
@@ -1075,8 +1312,7 @@
bool capturedSecureLayers = false;
status_t res = captureScreen(display, &outBuffer, capturedSecureLayers, reqDataspace,
reqPixelFormat, sourceCrop, reqWidth, reqHeight,
- useIdentityTransform,
- static_cast<ISurfaceComposer::Rotation>(rotation),
+ useIdentityTransform, ui::toRotation(rotation),
captureSecureLayers);
reply->writeInt32(res);
@@ -1110,6 +1346,9 @@
std::unordered_set<sp<IBinder>, SpHash<IBinder>> excludeHandles;
int numExcludeHandles = data.readInt32();
+ if (numExcludeHandles >= static_cast<int>(MAX_LAYERS)) {
+ return BAD_VALUE;
+ }
excludeHandles.reserve(numExcludeHandles);
for (int i = 0; i < numExcludeHandles; i++) {
excludeHandles.emplace(data.readStrongBinder());
@@ -1185,17 +1424,40 @@
reply->writeStrongBinder(display);
return NO_ERROR;
}
+ case GET_DISPLAY_STATE: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ ui::DisplayState state;
+ const sp<IBinder> display = data.readStrongBinder();
+ const status_t result = getDisplayState(display, &state);
+ reply->writeInt32(result);
+ if (result == NO_ERROR) {
+ memcpy(reply->writeInplace(sizeof(ui::DisplayState)), &state,
+ sizeof(ui::DisplayState));
+ }
+ return NO_ERROR;
+ }
+ case GET_DISPLAY_INFO: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ DisplayInfo info;
+ const sp<IBinder> display = data.readStrongBinder();
+ const status_t result = getDisplayInfo(display, &info);
+ reply->writeInt32(result);
+ if (result == NO_ERROR) {
+ memcpy(reply->writeInplace(sizeof(DisplayInfo)), &info, sizeof(DisplayInfo));
+ }
+ return NO_ERROR;
+ }
case GET_DISPLAY_CONFIGS: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
- Vector<DisplayInfo> configs;
- sp<IBinder> display = data.readStrongBinder();
- status_t result = getDisplayConfigs(display, &configs);
+ Vector<DisplayConfig> configs;
+ const sp<IBinder> display = data.readStrongBinder();
+ const status_t result = getDisplayConfigs(display, &configs);
reply->writeInt32(result);
if (result == NO_ERROR) {
reply->writeUint32(static_cast<uint32_t>(configs.size()));
for (size_t c = 0; c < configs.size(); ++c) {
- memcpy(reply->writeInplace(sizeof(DisplayInfo)),
- &configs[c], sizeof(DisplayInfo));
+ memcpy(reply->writeInplace(sizeof(DisplayConfig)), &configs[c],
+ sizeof(DisplayConfig));
}
}
return NO_ERROR;
@@ -1219,14 +1481,6 @@
reply->writeInt32(id);
return NO_ERROR;
}
- case SET_ACTIVE_CONFIG: {
- CHECK_INTERFACE(ISurfaceComposer, data, reply);
- sp<IBinder> display = data.readStrongBinder();
- int id = data.readInt32();
- status_t result = setActiveConfig(display, id);
- reply->writeInt32(result);
- return NO_ERROR;
- }
case GET_DISPLAY_COLOR_MODES: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
Vector<ColorMode> colorModes;
@@ -1297,6 +1551,75 @@
result = reply->writeInt32(result);
return result;
}
+
+ case GET_AUTO_LOW_LATENCY_MODE_SUPPORT: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ sp<IBinder> display = nullptr;
+ status_t result = data.readStrongBinder(&display);
+ if (result != NO_ERROR) {
+ ALOGE("getAutoLowLatencyModeSupport failed to readStrongBinder: %d", result);
+ return result;
+ }
+ bool supported = false;
+ result = getAutoLowLatencyModeSupport(display, &supported);
+ if (result == NO_ERROR) {
+ result = reply->writeBool(supported);
+ }
+ return result;
+ }
+
+ case SET_AUTO_LOW_LATENCY_MODE: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ sp<IBinder> display = nullptr;
+ status_t result = data.readStrongBinder(&display);
+ if (result != NO_ERROR) {
+ ALOGE("setAutoLowLatencyMode failed to readStrongBinder: %d", result);
+ return result;
+ }
+ bool setAllm = false;
+ result = data.readBool(&setAllm);
+ if (result != NO_ERROR) {
+ ALOGE("setAutoLowLatencyMode failed to readBool: %d", result);
+ return result;
+ }
+ setAutoLowLatencyMode(display, setAllm);
+ return result;
+ }
+
+ case GET_GAME_CONTENT_TYPE_SUPPORT: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ sp<IBinder> display = nullptr;
+ status_t result = data.readStrongBinder(&display);
+ if (result != NO_ERROR) {
+ ALOGE("getGameContentTypeSupport failed to readStrongBinder: %d", result);
+ return result;
+ }
+ bool supported = false;
+ result = getGameContentTypeSupport(display, &supported);
+ if (result == NO_ERROR) {
+ result = reply->writeBool(supported);
+ }
+ return result;
+ }
+
+ case SET_GAME_CONTENT_TYPE: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ sp<IBinder> display = nullptr;
+ status_t result = data.readStrongBinder(&display);
+ if (result != NO_ERROR) {
+ ALOGE("setGameContentType failed to readStrongBinder: %d", result);
+ return result;
+ }
+ bool setGameContentTypeOn = false;
+ result = data.readBool(&setGameContentTypeOn);
+ if (result != NO_ERROR) {
+ ALOGE("setGameContentType failed to readBool: %d", result);
+ return result;
+ }
+ setGameContentType(display, setGameContentTypeOn);
+ return result;
+ }
+
case CLEAR_ANIMATION_FRAME_STATS: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
status_t result = clearAnimationFrameStats();
@@ -1534,21 +1857,106 @@
}
return removeRegionSamplingListener(listener);
}
- case SET_ALLOWED_DISPLAY_CONFIGS: {
+ case SET_DESIRED_DISPLAY_CONFIG_SPECS: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
sp<IBinder> displayToken = data.readStrongBinder();
- std::vector<int32_t> allowedConfigs;
- data.readInt32Vector(&allowedConfigs);
- status_t result = setAllowedDisplayConfigs(displayToken, allowedConfigs);
+ int32_t defaultConfig;
+ status_t result = data.readInt32(&defaultConfig);
+ if (result != NO_ERROR) {
+ ALOGE("setDesiredDisplayConfigSpecs: failed to read defaultConfig: %d", result);
+ return result;
+ }
+ float primaryRefreshRateMin;
+ result = data.readFloat(&primaryRefreshRateMin);
+ if (result != NO_ERROR) {
+ ALOGE("setDesiredDisplayConfigSpecs: failed to read primaryRefreshRateMin: %d",
+ result);
+ return result;
+ }
+ float primaryRefreshRateMax;
+ result = data.readFloat(&primaryRefreshRateMax);
+ if (result != NO_ERROR) {
+ ALOGE("setDesiredDisplayConfigSpecs: failed to read primaryRefreshRateMax: %d",
+ result);
+ return result;
+ }
+ float appRequestRefreshRateMin;
+ result = data.readFloat(&appRequestRefreshRateMin);
+ if (result != NO_ERROR) {
+ ALOGE("setDesiredDisplayConfigSpecs: failed to read appRequestRefreshRateMin: %d",
+ result);
+ return result;
+ }
+ float appRequestRefreshRateMax;
+ result = data.readFloat(&appRequestRefreshRateMax);
+ if (result != NO_ERROR) {
+ ALOGE("setDesiredDisplayConfigSpecs: failed to read appRequestRefreshRateMax: %d",
+ result);
+ return result;
+ }
+ result =
+ setDesiredDisplayConfigSpecs(displayToken, defaultConfig, primaryRefreshRateMin,
+ primaryRefreshRateMax, appRequestRefreshRateMin,
+ appRequestRefreshRateMax);
+ if (result != NO_ERROR) {
+ ALOGE("setDesiredDisplayConfigSpecs: failed to call setDesiredDisplayConfigSpecs: "
+ "%d",
+ result);
+ return result;
+ }
reply->writeInt32(result);
return result;
}
- case GET_ALLOWED_DISPLAY_CONFIGS: {
+ case GET_DESIRED_DISPLAY_CONFIG_SPECS: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
sp<IBinder> displayToken = data.readStrongBinder();
- std::vector<int32_t> allowedConfigs;
- status_t result = getAllowedDisplayConfigs(displayToken, &allowedConfigs);
- reply->writeInt32Vector(allowedConfigs);
+ int32_t defaultConfig;
+ float primaryRefreshRateMin;
+ float primaryRefreshRateMax;
+ float appRequestRefreshRateMin;
+ float appRequestRefreshRateMax;
+
+ status_t result =
+ getDesiredDisplayConfigSpecs(displayToken, &defaultConfig,
+ &primaryRefreshRateMin, &primaryRefreshRateMax,
+ &appRequestRefreshRateMin,
+ &appRequestRefreshRateMax);
+ if (result != NO_ERROR) {
+ ALOGE("getDesiredDisplayConfigSpecs: failed to get getDesiredDisplayConfigSpecs: "
+ "%d",
+ result);
+ return result;
+ }
+
+ result = reply->writeInt32(defaultConfig);
+ if (result != NO_ERROR) {
+ ALOGE("getDesiredDisplayConfigSpecs: failed to write defaultConfig: %d", result);
+ return result;
+ }
+ result = reply->writeFloat(primaryRefreshRateMin);
+ if (result != NO_ERROR) {
+ ALOGE("getDesiredDisplayConfigSpecs: failed to write primaryRefreshRateMin: %d",
+ result);
+ return result;
+ }
+ result = reply->writeFloat(primaryRefreshRateMax);
+ if (result != NO_ERROR) {
+ ALOGE("getDesiredDisplayConfigSpecs: failed to write primaryRefreshRateMax: %d",
+ result);
+ return result;
+ }
+ result = reply->writeFloat(appRequestRefreshRateMin);
+ if (result != NO_ERROR) {
+ ALOGE("getDesiredDisplayConfigSpecs: failed to write appRequestRefreshRateMin: %d",
+ result);
+ return result;
+ }
+ result = reply->writeFloat(appRequestRefreshRateMax);
+ if (result != NO_ERROR) {
+ ALOGE("getDesiredDisplayConfigSpecs: failed to write appRequestRefreshRateMax: %d",
+ result);
+ return result;
+ }
reply->writeInt32(result);
return result;
}
@@ -1591,6 +1999,65 @@
}
return notifyPowerHint(hintId);
}
+ case SET_GLOBAL_SHADOW_SETTINGS: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+
+ std::vector<float> shadowConfig;
+ status_t error = data.readFloatVector(&shadowConfig);
+ if (error != NO_ERROR || shadowConfig.size() != 11) {
+ ALOGE("setGlobalShadowSettings: failed to read shadowConfig: %d", error);
+ return error;
+ }
+
+ half4 ambientColor = {shadowConfig[0], shadowConfig[1], shadowConfig[2],
+ shadowConfig[3]};
+ half4 spotColor = {shadowConfig[4], shadowConfig[5], shadowConfig[6], shadowConfig[7]};
+ float lightPosY = shadowConfig[8];
+ float lightPosZ = shadowConfig[9];
+ float lightRadius = shadowConfig[10];
+ return setGlobalShadowSettings(ambientColor, spotColor, lightPosY, lightPosZ,
+ lightRadius);
+ }
+ case SET_FRAME_RATE: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ sp<IBinder> binder;
+ status_t err = data.readStrongBinder(&binder);
+ if (err != NO_ERROR) {
+ ALOGE("setFrameRate: failed to read strong binder: %s (%d)", strerror(-err), -err);
+ return err;
+ }
+ sp<IGraphicBufferProducer> surface = interface_cast<IGraphicBufferProducer>(binder);
+ if (!surface) {
+ ALOGE("setFrameRate: failed to cast to IGraphicBufferProducer: %s (%d)",
+ strerror(-err), -err);
+ return err;
+ }
+ float frameRate;
+ err = data.readFloat(&frameRate);
+ if (err != NO_ERROR) {
+ ALOGE("setFrameRate: failed to read float: %s (%d)", strerror(-err), -err);
+ return err;
+ }
+ int8_t compatibility;
+ err = data.readByte(&compatibility);
+ if (err != NO_ERROR) {
+ ALOGE("setFrameRate: failed to read byte: %s (%d)", strerror(-err), -err);
+ return err;
+ }
+ status_t result = setFrameRate(surface, frameRate, compatibility);
+ reply->writeInt32(result);
+ return NO_ERROR;
+ }
+ case ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ sp<IBinder> token;
+ status_t result = acquireFrameRateFlexibilityToken(&token);
+ reply->writeInt32(result);
+ if (result == NO_ERROR) {
+ reply->writeStrongBinder(token);
+ }
+ return NO_ERROR;
+ }
default: {
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/gui/ISurfaceComposerClient.cpp b/libs/gui/ISurfaceComposerClient.cpp
index 129558b..621cf59 100644
--- a/libs/gui/ISurfaceComposerClient.cpp
+++ b/libs/gui/ISurfaceComposerClient.cpp
@@ -34,7 +34,8 @@
CREATE_WITH_SURFACE_PARENT,
CLEAR_LAYER_FRAME_STATS,
GET_LAYER_FRAME_STATS,
- LAST = GET_LAYER_FRAME_STATS,
+ MIRROR_SURFACE,
+ LAST = MIRROR_SURFACE,
};
} // Anonymous namespace
@@ -48,25 +49,28 @@
status_t createSurface(const String8& name, uint32_t width, uint32_t height, PixelFormat format,
uint32_t flags, const sp<IBinder>& parent, LayerMetadata metadata,
- sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp) override {
+ sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp,
+ uint32_t* outTransformHint) override {
return callRemote<decltype(&ISurfaceComposerClient::createSurface)>(Tag::CREATE_SURFACE,
name, width, height,
format, flags, parent,
std::move(metadata),
- handle, gbp);
+ handle, gbp,
+ outTransformHint);
}
status_t createWithSurfaceParent(const String8& name, uint32_t width, uint32_t height,
PixelFormat format, uint32_t flags,
const sp<IGraphicBufferProducer>& parent,
LayerMetadata metadata, sp<IBinder>* handle,
- sp<IGraphicBufferProducer>* gbp) override {
+ sp<IGraphicBufferProducer>* gbp,
+ uint32_t* outTransformHint) override {
return callRemote<decltype(
&ISurfaceComposerClient::createWithSurfaceParent)>(Tag::CREATE_WITH_SURFACE_PARENT,
name, width, height, format,
flags, parent,
- std::move(metadata), handle,
- gbp);
+ std::move(metadata), handle, gbp,
+ outTransformHint);
}
status_t clearLayerFrameStats(const sp<IBinder>& handle) const override {
@@ -80,6 +84,12 @@
&ISurfaceComposerClient::getLayerFrameStats)>(Tag::GET_LAYER_FRAME_STATS, handle,
outStats);
}
+
+ status_t mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle) override {
+ return callRemote<decltype(&ISurfaceComposerClient::mirrorSurface)>(Tag::MIRROR_SURFACE,
+ mirrorFromHandle,
+ outHandle);
+ }
};
// Out-of-line virtual method definition to trigger vtable emission in this
@@ -105,6 +115,8 @@
return callLocal(data, reply, &ISurfaceComposerClient::clearLayerFrameStats);
case Tag::GET_LAYER_FRAME_STATS:
return callLocal(data, reply, &ISurfaceComposerClient::getLayerFrameStats);
+ case Tag::MIRROR_SURFACE:
+ return callLocal(data, reply, &ISurfaceComposerClient::mirrorSurface);
}
}
diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp
index 74cd4f1..69f7894 100644
--- a/libs/gui/ITransactionCompletedListener.cpp
+++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -30,6 +30,66 @@
} // Anonymous namespace
+status_t FrameEventHistoryStats::writeToParcel(Parcel* output) const {
+ status_t err = output->writeUint64(frameNumber);
+ if (err != NO_ERROR) return err;
+
+ if (gpuCompositionDoneFence) {
+ err = output->writeBool(true);
+ if (err != NO_ERROR) return err;
+
+ err = output->write(*gpuCompositionDoneFence);
+ } else {
+ err = output->writeBool(false);
+ }
+ if (err != NO_ERROR) return err;
+
+ err = output->writeInt64(compositorTiming.deadline);
+ if (err != NO_ERROR) return err;
+
+ err = output->writeInt64(compositorTiming.interval);
+ if (err != NO_ERROR) return err;
+
+ err = output->writeInt64(compositorTiming.presentLatency);
+ if (err != NO_ERROR) return err;
+
+ err = output->writeInt64(refreshStartTime);
+ if (err != NO_ERROR) return err;
+
+ err = output->writeInt64(dequeueReadyTime);
+ return err;
+}
+
+status_t FrameEventHistoryStats::readFromParcel(const Parcel* input) {
+ status_t err = input->readUint64(&frameNumber);
+ if (err != NO_ERROR) return err;
+
+ bool hasFence = false;
+ err = input->readBool(&hasFence);
+ if (err != NO_ERROR) return err;
+
+ if (hasFence) {
+ gpuCompositionDoneFence = new Fence();
+ err = input->read(*gpuCompositionDoneFence);
+ if (err != NO_ERROR) return err;
+ }
+
+ err = input->readInt64(&(compositorTiming.deadline));
+ if (err != NO_ERROR) return err;
+
+ err = input->readInt64(&(compositorTiming.interval));
+ if (err != NO_ERROR) return err;
+
+ err = input->readInt64(&(compositorTiming.presentLatency));
+ if (err != NO_ERROR) return err;
+
+ err = input->readInt64(&refreshStartTime);
+ if (err != NO_ERROR) return err;
+
+ err = input->readInt64(&dequeueReadyTime);
+ return err;
+}
+
status_t SurfaceStats::writeToParcel(Parcel* output) const {
status_t err = output->writeStrongBinder(surfaceControl);
if (err != NO_ERROR) {
@@ -48,6 +108,12 @@
} else {
err = output->writeBool(false);
}
+ err = output->writeUint32(transformHint);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ err = output->writeParcelable(eventStats);
return err;
}
@@ -72,7 +138,13 @@
return err;
}
}
- return NO_ERROR;
+ err = input->readUint32(&transformHint);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ err = input->readParcelable(&eventStats);
+ return err;
}
status_t TransactionStats::writeToParcel(Parcel* output) const {
@@ -151,7 +223,7 @@
return NO_ERROR;
}
-ListenerStats ListenerStats::createEmpty(const sp<ITransactionCompletedListener>& listener,
+ListenerStats ListenerStats::createEmpty(const sp<IBinder>& listener,
const std::unordered_set<CallbackId>& callbackIds) {
ListenerStats listenerStats;
listenerStats.listener = listener;
diff --git a/libs/gui/LayerMetadata.cpp b/libs/gui/LayerMetadata.cpp
index 04d2871..b3eb994 100644
--- a/libs/gui/LayerMetadata.cpp
+++ b/libs/gui/LayerMetadata.cpp
@@ -18,6 +18,8 @@
#include <binder/Parcel.h>
#include <gui/LayerMetadata.h>
+#include "android/view/LayerMetadataKey.h"
+
using android::base::StringPrintf;
namespace android {
@@ -113,12 +115,12 @@
std::string LayerMetadata::itemToString(uint32_t key, const char* separator) const {
if (!has(key)) return std::string();
- switch (key) {
- case METADATA_OWNER_UID:
+ switch (static_cast<view::LayerMetadataKey>(key)) {
+ case view::LayerMetadataKey::METADATA_OWNER_UID:
return StringPrintf("ownerUID%s%d", separator, getInt32(key, 0));
- case METADATA_WINDOW_TYPE:
+ case view::LayerMetadataKey::METADATA_WINDOW_TYPE:
return StringPrintf("windowType%s%d", separator, getInt32(key, 0));
- case METADATA_TASK_ID:
+ case view::LayerMetadataKey::METADATA_TASK_ID:
return StringPrintf("taskId%s%d", separator, getInt32(key, 0));
default:
return StringPrintf("%d%s%dbytes", key, separator,
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 42eb921..0281279 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -24,6 +24,8 @@
#include <gui/IGraphicBufferProducer.h>
#include <gui/LayerState.h>
+#include <cmath>
+
namespace android {
status_t layer_state_t::write(Parcel& output) const
@@ -86,7 +88,7 @@
memcpy(output.writeInplace(16 * sizeof(float)),
colorTransform.asArray(), 16 * sizeof(float));
output.writeFloat(cornerRadius);
- output.writeBool(hasListenerCallbacks);
+ output.writeUint32(backgroundBlurRadius);
output.writeStrongBinder(cachedBuffer.token.promote());
output.writeUint64(cachedBuffer.id);
output.writeParcelable(metadata);
@@ -95,6 +97,26 @@
output.writeUint32(static_cast<uint32_t>(bgColorDataspace));
output.writeBool(colorSpaceAgnostic);
+ auto err = output.writeVectorSize(listeners);
+ if (err) {
+ return err;
+ }
+
+ for (auto listener : listeners) {
+ err = output.writeStrongBinder(listener.transactionCompletedListener);
+ if (err) {
+ return err;
+ }
+ err = output.writeInt64Vector(listener.callbackIds);
+ if (err) {
+ return err;
+ }
+ }
+ output.writeFloat(shadowRadius);
+ output.writeInt32(frameRateSelectionPriority);
+ output.writeFloat(frameRate);
+ output.writeByte(frameRateCompatibility);
+ output.writeUint32(fixedTransformHint);
return NO_ERROR;
}
@@ -154,9 +176,14 @@
sidebandStream = NativeHandle::create(input.readNativeHandle(), true);
}
- colorTransform = mat4(static_cast<const float*>(input.readInplace(16 * sizeof(float))));
+ const void* color_transform_data = input.readInplace(16 * sizeof(float));
+ if (color_transform_data) {
+ colorTransform = mat4(static_cast<const float*>(color_transform_data));
+ } else {
+ return BAD_VALUE;
+ }
cornerRadius = input.readFloat();
- hasListenerCallbacks = input.readBool();
+ backgroundBlurRadius = input.readUint32();
cachedBuffer.token = input.readStrongBinder();
cachedBuffer.id = input.readUint64();
input.readParcelable(&metadata);
@@ -165,16 +192,27 @@
bgColorDataspace = static_cast<ui::Dataspace>(input.readUint32());
colorSpaceAgnostic = input.readBool();
+ int32_t numListeners = input.readInt32();
+ listeners.clear();
+ for (int i = 0; i < numListeners; i++) {
+ auto listener = input.readStrongBinder();
+ std::vector<CallbackId> callbackIds;
+ input.readInt64Vector(&callbackIds);
+ listeners.emplace_back(listener, callbackIds);
+ }
+ shadowRadius = input.readFloat();
+ frameRateSelectionPriority = input.readInt32();
+ frameRate = input.readFloat();
+ frameRateCompatibility = input.readByte();
+ fixedTransformHint = static_cast<ui::Transform::RotationFlags>(input.readUint32());
return NO_ERROR;
}
status_t ComposerState::write(Parcel& output) const {
- output.writeStrongBinder(IInterface::asBinder(client));
return state.write(output);
}
status_t ComposerState::read(const Parcel& input) {
- client = interface_cast<ISurfaceComposerClient>(input.readStrongBinder());
return state.read(input);
}
@@ -182,7 +220,6 @@
DisplayState::DisplayState() :
what(0),
layerStack(0),
- orientation(eOrientationDefault),
viewport(Rect::EMPTY_RECT),
frame(Rect::EMPTY_RECT),
width(0),
@@ -194,7 +231,7 @@
output.writeStrongBinder(IInterface::asBinder(surface));
output.writeUint32(what);
output.writeUint32(layerStack);
- output.writeUint32(orientation);
+ output.writeUint32(toRotationInt(orientation));
output.write(viewport);
output.write(frame);
output.writeUint32(width);
@@ -207,7 +244,7 @@
surface = interface_cast<IGraphicBufferProducer>(input.readStrongBinder());
what = input.readUint32();
layerStack = input.readUint32();
- orientation = input.readUint32();
+ orientation = ui::toRotation(input.readUint32());
input.read(viewport);
input.read(frame);
width = input.readUint32();
@@ -267,8 +304,9 @@
}
if (other.what & eFlagsChanged) {
what |= eFlagsChanged;
- flags = other.flags;
- mask = other.mask;
+ flags &= ~other.mask;
+ flags |= (other.flags & other.mask);
+ mask |= other.mask;
}
if (other.what & eLayerStackChanged) {
what |= eLayerStackChanged;
@@ -282,6 +320,10 @@
what |= eCornerRadiusChanged;
cornerRadius = other.cornerRadius;
}
+ if (other.what & eBackgroundBlurRadiusChanged) {
+ what |= eBackgroundBlurRadiusChanged;
+ backgroundBlurRadius = other.backgroundBlurRadius;
+ }
if (other.what & eDeferTransaction_legacy) {
what |= eDeferTransaction_legacy;
barrierHandle_legacy = other.barrierHandle_legacy;
@@ -292,9 +334,6 @@
what |= eOverrideScalingModeChanged;
overrideScalingMode = other.overrideScalingMode;
}
- if (other.what & eGeometryAppliesWithResize) {
- what |= eGeometryAppliesWithResize;
- }
if (other.what & eReparentChildren) {
what |= eReparentChildren;
reparentHandle = other.reparentHandle;
@@ -365,7 +404,6 @@
}
if (other.what & eHasListenerCallbacksChanged) {
what |= eHasListenerCallbacksChanged;
- hasListenerCallbacks = other.hasListenerCallbacks;
}
#ifndef NO_INPUT
@@ -389,6 +427,23 @@
what |= eMetadataChanged;
metadata.merge(other.metadata);
}
+ if (other.what & eShadowRadiusChanged) {
+ what |= eShadowRadiusChanged;
+ shadowRadius = other.shadowRadius;
+ }
+ if (other.what & eFrameRateSelectionPriority) {
+ what |= eFrameRateSelectionPriority;
+ frameRateSelectionPriority = other.frameRateSelectionPriority;
+ }
+ if (other.what & eFrameRateChanged) {
+ what |= eFrameRateChanged;
+ frameRate = other.frameRate;
+ frameRateCompatibility = other.frameRateCompatibility;
+ }
+ if (other.what & eFixedTransformHintChanged) {
+ what |= eFixedTransformHintChanged;
+ fixedTransformHint = other.fixedTransformHint;
+ }
if ((other.what & what) != other.what) {
ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? "
"other.what=0x%" PRIu64 " what=0x%" PRIu64,
@@ -399,40 +454,36 @@
// ------------------------------- InputWindowCommands ----------------------------------------
void InputWindowCommands::merge(const InputWindowCommands& other) {
- transferTouchFocusCommands
- .insert(transferTouchFocusCommands.end(),
- std::make_move_iterator(other.transferTouchFocusCommands.begin()),
- std::make_move_iterator(other.transferTouchFocusCommands.end()));
-
syncInputWindows |= other.syncInputWindows;
}
void InputWindowCommands::clear() {
- transferTouchFocusCommands.clear();
syncInputWindows = false;
}
void InputWindowCommands::write(Parcel& output) const {
- output.writeUint32(static_cast<uint32_t>(transferTouchFocusCommands.size()));
- for (const auto& transferTouchFocusCommand : transferTouchFocusCommands) {
- output.writeStrongBinder(transferTouchFocusCommand.fromToken);
- output.writeStrongBinder(transferTouchFocusCommand.toToken);
- }
-
output.writeBool(syncInputWindows);
}
void InputWindowCommands::read(const Parcel& input) {
- size_t count = input.readUint32();
- transferTouchFocusCommands.clear();
- for (size_t i = 0; i < count; i++) {
- TransferTouchFocusCommand transferTouchFocusCommand;
- transferTouchFocusCommand.fromToken = input.readStrongBinder();
- transferTouchFocusCommand.toToken = input.readStrongBinder();
- transferTouchFocusCommands.emplace_back(transferTouchFocusCommand);
+ syncInputWindows = input.readBool();
+}
+
+bool ValidateFrameRate(float frameRate, int8_t compatibility, const char* inFunctionName) {
+ const char* functionName = inFunctionName != nullptr ? inFunctionName : "call";
+ int floatClassification = std::fpclassify(frameRate);
+ if (frameRate < 0 || floatClassification == FP_INFINITE || floatClassification == FP_NAN) {
+ ALOGE("%s failed - invalid frame rate %f", functionName, frameRate);
+ return false;
}
- syncInputWindows = input.readBool();
+ if (compatibility != ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT &&
+ compatibility != ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE) {
+ ALOGE("%s failed - invalid compatibility value %d", functionName, compatibility);
+ return false;
+ }
+
+ return true;
}
}; // namespace android
diff --git a/libs/gui/QueueBufferInputOutput.cpp b/libs/gui/QueueBufferInputOutput.cpp
new file mode 100644
index 0000000..30f0ef6
--- /dev/null
+++ b/libs/gui/QueueBufferInputOutput.cpp
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2010 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 <inttypes.h>
+
+#define LOG_TAG "QueueBufferInputOutput"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+//#define LOG_NDEBUG 0
+
+#include <gui/IGraphicBufferProducer.h>
+
+namespace android {
+
+constexpr size_t IGraphicBufferProducer::QueueBufferInput::minFlattenedSize() {
+ return sizeof(timestamp) +
+ sizeof(isAutoTimestamp) +
+ sizeof(dataSpace) +
+ sizeof(crop) +
+ sizeof(scalingMode) +
+ sizeof(transform) +
+ sizeof(stickyTransform) +
+ sizeof(getFrameTimestamps);
+}
+
+IGraphicBufferProducer::QueueBufferInput::QueueBufferInput(const Parcel& parcel) {
+ parcel.read(*this);
+}
+
+size_t IGraphicBufferProducer::QueueBufferInput::getFlattenedSize() const {
+ return minFlattenedSize() +
+ fence->getFlattenedSize() +
+ surfaceDamage.getFlattenedSize() +
+ hdrMetadata.getFlattenedSize();
+}
+
+size_t IGraphicBufferProducer::QueueBufferInput::getFdCount() const {
+ return fence->getFdCount();
+}
+
+status_t IGraphicBufferProducer::QueueBufferInput::flatten(
+ void*& buffer, size_t& size, int*& fds, size_t& count) const
+{
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::write(buffer, size, timestamp);
+ FlattenableUtils::write(buffer, size, isAutoTimestamp);
+ FlattenableUtils::write(buffer, size, dataSpace);
+ FlattenableUtils::write(buffer, size, crop);
+ FlattenableUtils::write(buffer, size, scalingMode);
+ FlattenableUtils::write(buffer, size, transform);
+ FlattenableUtils::write(buffer, size, stickyTransform);
+ FlattenableUtils::write(buffer, size, getFrameTimestamps);
+
+ status_t result = fence->flatten(buffer, size, fds, count);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = surfaceDamage.flatten(buffer, size);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ FlattenableUtils::advance(buffer, size, surfaceDamage.getFlattenedSize());
+ return hdrMetadata.flatten(buffer, size);
+}
+
+status_t IGraphicBufferProducer::QueueBufferInput::unflatten(
+ void const*& buffer, size_t& size, int const*& fds, size_t& count)
+{
+ if (size < minFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::read(buffer, size, timestamp);
+ FlattenableUtils::read(buffer, size, isAutoTimestamp);
+ FlattenableUtils::read(buffer, size, dataSpace);
+ FlattenableUtils::read(buffer, size, crop);
+ FlattenableUtils::read(buffer, size, scalingMode);
+ FlattenableUtils::read(buffer, size, transform);
+ FlattenableUtils::read(buffer, size, stickyTransform);
+ FlattenableUtils::read(buffer, size, getFrameTimestamps);
+
+ fence = new Fence();
+ status_t result = fence->unflatten(buffer, size, fds, count);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = surfaceDamage.unflatten(buffer, size);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ FlattenableUtils::advance(buffer, size, surfaceDamage.getFlattenedSize());
+ return hdrMetadata.unflatten(buffer, size);
+}
+
+////////////////////////////////////////////////////////////////////////
+constexpr size_t IGraphicBufferProducer::QueueBufferOutput::minFlattenedSize() {
+ return sizeof(width) + sizeof(height) + sizeof(transformHint) + sizeof(numPendingBuffers) +
+ sizeof(nextFrameNumber) + sizeof(bufferReplaced) + sizeof(maxBufferCount);
+}
+size_t IGraphicBufferProducer::QueueBufferOutput::getFlattenedSize() const {
+ return minFlattenedSize() + frameTimestamps.getFlattenedSize();
+}
+
+size_t IGraphicBufferProducer::QueueBufferOutput::getFdCount() const {
+ return frameTimestamps.getFdCount();
+}
+
+status_t IGraphicBufferProducer::QueueBufferOutput::flatten(
+ void*& buffer, size_t& size, int*& fds, size_t& count) const
+{
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::write(buffer, size, width);
+ FlattenableUtils::write(buffer, size, height);
+ FlattenableUtils::write(buffer, size, transformHint);
+ FlattenableUtils::write(buffer, size, numPendingBuffers);
+ FlattenableUtils::write(buffer, size, nextFrameNumber);
+ FlattenableUtils::write(buffer, size, bufferReplaced);
+ FlattenableUtils::write(buffer, size, maxBufferCount);
+
+ return frameTimestamps.flatten(buffer, size, fds, count);
+}
+
+status_t IGraphicBufferProducer::QueueBufferOutput::unflatten(
+ void const*& buffer, size_t& size, int const*& fds, size_t& count)
+{
+ if (size < minFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::read(buffer, size, width);
+ FlattenableUtils::read(buffer, size, height);
+ FlattenableUtils::read(buffer, size, transformHint);
+ FlattenableUtils::read(buffer, size, numPendingBuffers);
+ FlattenableUtils::read(buffer, size, nextFrameNumber);
+ FlattenableUtils::read(buffer, size, bufferReplaced);
+ FlattenableUtils::read(buffer, size, maxBufferCount);
+
+ return frameTimestamps.unflatten(buffer, size, fds, count);
+}
+
+} // namespace android
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index b822319..c3323fe 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -43,6 +43,7 @@
#include <gui/IProducerListener.h>
#include <gui/ISurfaceComposer.h>
+#include <gui/LayerState.h>
#include <private/gui/ComposerService.h>
namespace android {
@@ -50,6 +51,18 @@
using ui::ColorMode;
using ui::Dataspace;
+namespace {
+
+bool isInterceptorRegistrationOp(int op) {
+ return op == NATIVE_WINDOW_SET_CANCEL_INTERCEPTOR ||
+ op == NATIVE_WINDOW_SET_DEQUEUE_INTERCEPTOR ||
+ op == NATIVE_WINDOW_SET_PERFORM_INTERCEPTOR ||
+ op == NATIVE_WINDOW_SET_QUEUE_INTERCEPTOR ||
+ op == NATIVE_WINDOW_SET_QUERY_INTERCEPTOR;
+}
+
+} // namespace
+
Surface::Surface(const sp<IGraphicBufferProducer>& bufferProducer, bool controlledByApp)
: mGraphicBufferProducer(bufferProducer),
mCrop(Rect::EMPTY_RECT),
@@ -97,6 +110,7 @@
mConnectedToCpu = false;
mProducerControlledByApp = controlledByApp;
mSwapIntervalZero = false;
+ mMaxBufferCount = NUM_BUFFER_SLOTS;
}
Surface::~Surface() {
@@ -365,18 +379,58 @@
int Surface::hook_dequeueBuffer(ANativeWindow* window,
ANativeWindowBuffer** buffer, int* fenceFd) {
Surface* c = getSelf(window);
+ {
+ std::shared_lock<std::shared_mutex> lock(c->mInterceptorMutex);
+ if (c->mDequeueInterceptor != nullptr) {
+ auto interceptor = c->mDequeueInterceptor;
+ auto data = c->mDequeueInterceptorData;
+ return interceptor(window, Surface::dequeueBufferInternal, data, buffer, fenceFd);
+ }
+ }
+ return c->dequeueBuffer(buffer, fenceFd);
+}
+
+int Surface::dequeueBufferInternal(ANativeWindow* window, ANativeWindowBuffer** buffer,
+ int* fenceFd) {
+ Surface* c = getSelf(window);
return c->dequeueBuffer(buffer, fenceFd);
}
int Surface::hook_cancelBuffer(ANativeWindow* window,
ANativeWindowBuffer* buffer, int fenceFd) {
Surface* c = getSelf(window);
+ {
+ std::shared_lock<std::shared_mutex> lock(c->mInterceptorMutex);
+ if (c->mCancelInterceptor != nullptr) {
+ auto interceptor = c->mCancelInterceptor;
+ auto data = c->mCancelInterceptorData;
+ return interceptor(window, Surface::cancelBufferInternal, data, buffer, fenceFd);
+ }
+ }
+ return c->cancelBuffer(buffer, fenceFd);
+}
+
+int Surface::cancelBufferInternal(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd) {
+ Surface* c = getSelf(window);
return c->cancelBuffer(buffer, fenceFd);
}
int Surface::hook_queueBuffer(ANativeWindow* window,
ANativeWindowBuffer* buffer, int fenceFd) {
Surface* c = getSelf(window);
+ {
+ std::shared_lock<std::shared_mutex> lock(c->mInterceptorMutex);
+ if (c->mQueueInterceptor != nullptr) {
+ auto interceptor = c->mQueueInterceptor;
+ auto data = c->mQueueInterceptorData;
+ return interceptor(window, Surface::queueBufferInternal, data, buffer, fenceFd);
+ }
+ }
+ return c->queueBuffer(buffer, fenceFd);
+}
+
+int Surface::queueBufferInternal(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd) {
+ Surface* c = getSelf(window);
return c->queueBuffer(buffer, fenceFd);
}
@@ -419,21 +473,51 @@
return c->queueBuffer(buffer, -1);
}
-int Surface::hook_query(const ANativeWindow* window,
- int what, int* value) {
- const Surface* c = getSelf(window);
- return c->query(what, value);
-}
-
int Surface::hook_perform(ANativeWindow* window, int operation, ...) {
va_list args;
va_start(args, operation);
Surface* c = getSelf(window);
- int result = c->perform(operation, args);
+ int result;
+ // Don't acquire shared ownership of the interceptor mutex if we're going to
+ // do interceptor registration, as otherwise we'll deadlock on acquiring
+ // exclusive ownership.
+ if (!isInterceptorRegistrationOp(operation)) {
+ std::shared_lock<std::shared_mutex> lock(c->mInterceptorMutex);
+ if (c->mPerformInterceptor != nullptr) {
+ result = c->mPerformInterceptor(window, Surface::performInternal,
+ c->mPerformInterceptorData, operation, args);
+ va_end(args);
+ return result;
+ }
+ }
+ result = c->perform(operation, args);
va_end(args);
return result;
}
+int Surface::performInternal(ANativeWindow* window, int operation, va_list args) {
+ Surface* c = getSelf(window);
+ return c->perform(operation, args);
+}
+
+int Surface::hook_query(const ANativeWindow* window, int what, int* value) {
+ const Surface* c = getSelf(window);
+ {
+ std::shared_lock<std::shared_mutex> lock(c->mInterceptorMutex);
+ if (c->mQueryInterceptor != nullptr) {
+ auto interceptor = c->mQueryInterceptor;
+ auto data = c->mQueryInterceptorData;
+ return interceptor(window, Surface::queryInternal, data, what, value);
+ }
+ }
+ return c->query(what, value);
+}
+
+int Surface::queryInternal(const ANativeWindow* window, int what, int* value) {
+ const Surface* c = getSelf(window);
+ return c->query(what, value);
+}
+
int Surface::setSwapInterval(int interval) {
ATRACE_CALL();
// EGL specification states:
@@ -962,6 +1046,10 @@
*value = static_cast<int>(mDataSpace);
return NO_ERROR;
}
+ case NATIVE_WINDOW_MAX_BUFFER_COUNT: {
+ *value = mMaxBufferCount;
+ return NO_ERROR;
+ }
}
}
return mGraphicBufferProducer->query(what, value);
@@ -1073,6 +1161,46 @@
case NATIVE_WINDOW_GET_CONSUMER_USAGE64:
res = dispatchGetConsumerUsage64(args);
break;
+ case NATIVE_WINDOW_SET_AUTO_PREROTATION:
+ res = dispatchSetAutoPrerotation(args);
+ break;
+ case NATIVE_WINDOW_GET_LAST_DEQUEUE_START:
+ res = dispatchGetLastDequeueStartTime(args);
+ break;
+ case NATIVE_WINDOW_SET_DEQUEUE_TIMEOUT:
+ res = dispatchSetDequeueTimeout(args);
+ break;
+ case NATIVE_WINDOW_GET_LAST_DEQUEUE_DURATION:
+ res = dispatchGetLastDequeueDuration(args);
+ break;
+ case NATIVE_WINDOW_GET_LAST_QUEUE_DURATION:
+ res = dispatchGetLastQueueDuration(args);
+ break;
+ case NATIVE_WINDOW_SET_FRAME_RATE:
+ res = dispatchSetFrameRate(args);
+ break;
+ case NATIVE_WINDOW_SET_CANCEL_INTERCEPTOR:
+ res = dispatchAddCancelInterceptor(args);
+ break;
+ case NATIVE_WINDOW_SET_DEQUEUE_INTERCEPTOR:
+ res = dispatchAddDequeueInterceptor(args);
+ break;
+ case NATIVE_WINDOW_SET_PERFORM_INTERCEPTOR:
+ res = dispatchAddPerformInterceptor(args);
+ break;
+ case NATIVE_WINDOW_SET_QUEUE_INTERCEPTOR:
+ res = dispatchAddQueueInterceptor(args);
+ break;
+ case NATIVE_WINDOW_SET_QUERY_INTERCEPTOR:
+ res = dispatchAddQueryInterceptor(args);
+ break;
+ case NATIVE_WINDOW_ALLOCATE_BUFFERS:
+ allocateBuffers();
+ res = NO_ERROR;
+ break;
+ case NATIVE_WINDOW_GET_LAST_QUEUED_BUFFER:
+ res = dispatchGetLastQueuedBuffer(args);
+ break;
default:
res = NAME_NOT_FOUND;
break;
@@ -1273,13 +1401,119 @@
return getConsumerUsage(usage);
}
+int Surface::dispatchSetAutoPrerotation(va_list args) {
+ bool autoPrerotation = va_arg(args, int);
+ return setAutoPrerotation(autoPrerotation);
+}
+
+int Surface::dispatchGetLastDequeueStartTime(va_list args) {
+ int64_t* lastDequeueStartTime = va_arg(args, int64_t*);
+ *lastDequeueStartTime = mLastDequeueStartTime;
+ return NO_ERROR;
+}
+
+int Surface::dispatchSetDequeueTimeout(va_list args) {
+ nsecs_t timeout = va_arg(args, int64_t);
+ return setDequeueTimeout(timeout);
+}
+
+int Surface::dispatchGetLastDequeueDuration(va_list args) {
+ int64_t* lastDequeueDuration = va_arg(args, int64_t*);
+ *lastDequeueDuration = mLastDequeueDuration;
+ return NO_ERROR;
+}
+
+int Surface::dispatchGetLastQueueDuration(va_list args) {
+ int64_t* lastQueueDuration = va_arg(args, int64_t*);
+ *lastQueueDuration = mLastQueueDuration;
+ return NO_ERROR;
+}
+
+int Surface::dispatchSetFrameRate(va_list args) {
+ float frameRate = static_cast<float>(va_arg(args, double));
+ int8_t compatibility = static_cast<int8_t>(va_arg(args, int));
+ return setFrameRate(frameRate, compatibility);
+}
+
+int Surface::dispatchAddCancelInterceptor(va_list args) {
+ ANativeWindow_cancelBufferInterceptor interceptor =
+ va_arg(args, ANativeWindow_cancelBufferInterceptor);
+ void* data = va_arg(args, void*);
+ std::lock_guard<std::shared_mutex> lock(mInterceptorMutex);
+ mCancelInterceptor = interceptor;
+ mCancelInterceptorData = data;
+ return NO_ERROR;
+}
+
+int Surface::dispatchAddDequeueInterceptor(va_list args) {
+ ANativeWindow_dequeueBufferInterceptor interceptor =
+ va_arg(args, ANativeWindow_dequeueBufferInterceptor);
+ void* data = va_arg(args, void*);
+ std::lock_guard<std::shared_mutex> lock(mInterceptorMutex);
+ mDequeueInterceptor = interceptor;
+ mDequeueInterceptorData = data;
+ return NO_ERROR;
+}
+
+int Surface::dispatchAddPerformInterceptor(va_list args) {
+ ANativeWindow_performInterceptor interceptor = va_arg(args, ANativeWindow_performInterceptor);
+ void* data = va_arg(args, void*);
+ std::lock_guard<std::shared_mutex> lock(mInterceptorMutex);
+ mPerformInterceptor = interceptor;
+ mPerformInterceptorData = data;
+ return NO_ERROR;
+}
+
+int Surface::dispatchAddQueueInterceptor(va_list args) {
+ ANativeWindow_queueBufferInterceptor interceptor =
+ va_arg(args, ANativeWindow_queueBufferInterceptor);
+ void* data = va_arg(args, void*);
+ std::lock_guard<std::shared_mutex> lock(mInterceptorMutex);
+ mQueueInterceptor = interceptor;
+ mQueueInterceptorData = data;
+ return NO_ERROR;
+}
+
+int Surface::dispatchAddQueryInterceptor(va_list args) {
+ ANativeWindow_queryInterceptor interceptor = va_arg(args, ANativeWindow_queryInterceptor);
+ void* data = va_arg(args, void*);
+ std::lock_guard<std::shared_mutex> lock(mInterceptorMutex);
+ mQueryInterceptor = interceptor;
+ mQueryInterceptorData = data;
+ return NO_ERROR;
+}
+
+int Surface::dispatchGetLastQueuedBuffer(va_list args) {
+ AHardwareBuffer** buffer = va_arg(args, AHardwareBuffer**);
+ int* fence = va_arg(args, int*);
+ float* matrix = va_arg(args, float*);
+ sp<GraphicBuffer> graphicBuffer;
+ sp<Fence> spFence;
+
+ int result = mGraphicBufferProducer->getLastQueuedBuffer(&graphicBuffer, &spFence, matrix);
+
+ if (graphicBuffer != nullptr) {
+ *buffer = reinterpret_cast<AHardwareBuffer*>(graphicBuffer.get());
+ AHardwareBuffer_acquire(*buffer);
+ } else {
+ *buffer = nullptr;
+ }
+
+ if (spFence != nullptr) {
+ *fence = spFence->dup();
+ } else {
+ *fence = -1;
+ }
+ return result;
+}
+
bool Surface::transformToDisplayInverse() {
return (mTransform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) ==
NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY;
}
int Surface::connect(int api) {
- static sp<IProducerListener> listener = new DummyProducerListener();
+ static sp<IProducerListener> listener = new StubProducerListener();
return connect(api, listener);
}
@@ -1307,6 +1541,7 @@
mDefaultWidth = output.width;
mDefaultHeight = output.height;
mNextFrameNumber = output.nextFrameNumber;
+ mMaxBufferCount = output.maxBufferCount;
// 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
@@ -1348,6 +1583,9 @@
mScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE;
mTransform = 0;
mStickyTransform = 0;
+ mAutoPrerotation = false;
+ mEnableFrameTimestamps = false;
+ mMaxBufferCount = NUM_BUFFER_SLOTS;
if (api == NATIVE_WINDOW_API_CPU) {
mConnectedToCpu = false;
@@ -1934,11 +2172,6 @@
return mGraphicBufferProducer->getConsumerUsage(outUsage);
}
-nsecs_t Surface::getLastDequeueStartTime() const {
- Mutex::Autolock lock(mMutex);
- return mLastDequeueStartTime;
-}
-
status_t Surface::getAndFlushRemovedBuffers(std::vector<sp<GraphicBuffer>>* out) {
if (out == nullptr) {
ALOGE("%s: out must not be null!", __FUNCTION__);
@@ -1982,6 +2215,24 @@
return err;
}
+int Surface::setAutoPrerotation(bool autoPrerotation) {
+ ATRACE_CALL();
+ ALOGV("Surface::setAutoPrerotation (%d)", autoPrerotation);
+ Mutex::Autolock lock(mMutex);
+
+ if (mAutoPrerotation == autoPrerotation) {
+ return OK;
+ }
+
+ status_t err = mGraphicBufferProducer->setAutoPrerotation(autoPrerotation);
+ if (err == NO_ERROR) {
+ mAutoPrerotation = autoPrerotation;
+ }
+ ALOGE_IF(err, "IGraphicBufferProducer::setAutoPrerotation(%d) returned %s", autoPrerotation,
+ strerror(-err));
+ return err;
+}
+
void Surface::ProducerListenerProxy::onBuffersDiscarded(const std::vector<int32_t>& slots) {
ATRACE_CALL();
sp<Surface> parent = mParent.promote();
@@ -2000,4 +2251,15 @@
mSurfaceListener->onBuffersDiscarded(discardedBufs);
}
+status_t Surface::setFrameRate(float frameRate, int8_t compatibility) {
+ ATRACE_CALL();
+ ALOGV("Surface::setFrameRate");
+
+ if (!ValidateFrameRate(frameRate, compatibility, "Surface::setFrameRate")) {
+ return BAD_VALUE;
+ }
+
+ return composerService()->setFrameRate(mGraphicBufferProducer, frameRate, compatibility);
+}
+
}; // namespace android
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index dfc6185..83bc069 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -31,8 +31,6 @@
#include <system/graphics.h>
-#include <ui/DisplayInfo.h>
-
#include <gui/BufferItemConsumer.h>
#include <gui/CpuConsumer.h>
#include <gui/IGraphicBufferProducer.h>
@@ -41,6 +39,7 @@
#include <gui/LayerState.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
+#include <ui/DisplayConfig.h>
#ifndef NO_INPUT
#include <input/InputWindow.h>
@@ -189,52 +188,84 @@
}
void TransactionCompletedListener::onTransactionCompleted(ListenerStats listenerStats) {
- std::lock_guard<std::mutex> lock(mMutex);
+ std::unordered_map<CallbackId, CallbackTranslation> callbacksMap;
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
- /* This listener knows all the sp<IBinder> to sp<SurfaceControl> for all its registered
- * callbackIds, except for when Transactions are merged together. This probably cannot be
- * solved before this point because the Transactions could be merged together and applied in a
- * different process.
- *
- * Fortunately, we get all the callbacks for this listener for the same frame together at the
- * same time. This means if any Transactions were merged together, we will get their callbacks
- * at the same time. We can combine all the sp<IBinder> to sp<SurfaceControl> maps for all the
- * callbackIds to generate one super map that contains all the sp<IBinder> to sp<SurfaceControl>
- * that could possibly exist for the callbacks.
- */
- std::unordered_map<sp<IBinder>, sp<SurfaceControl>, IBinderHash> surfaceControls;
- for (const auto& transactionStats : listenerStats.transactionStats) {
- for (auto callbackId : transactionStats.callbackIds) {
- auto& [callbackFunction, callbackSurfaceControls] = mCallbacks[callbackId];
- surfaceControls.insert(callbackSurfaceControls.begin(), callbackSurfaceControls.end());
+ /* This listener knows all the sp<IBinder> to sp<SurfaceControl> for all its registered
+ * callbackIds, except for when Transactions are merged together. This probably cannot be
+ * solved before this point because the Transactions could be merged together and applied in
+ * a different process.
+ *
+ * Fortunately, we get all the callbacks for this listener for the same frame together at
+ * the same time. This means if any Transactions were merged together, we will get their
+ * callbacks at the same time. We can combine all the sp<IBinder> to sp<SurfaceControl> maps
+ * for all the callbackIds to generate one super map that contains all the sp<IBinder> to
+ * sp<SurfaceControl> that could possibly exist for the callbacks.
+ */
+ callbacksMap = mCallbacks;
+ for (const auto& transactionStats : listenerStats.transactionStats) {
+ for (auto& callbackId : transactionStats.callbackIds) {
+ mCallbacks.erase(callbackId);
+ }
}
}
-
for (const auto& transactionStats : listenerStats.transactionStats) {
for (auto callbackId : transactionStats.callbackIds) {
- auto& [callbackFunction, callbackSurfaceControls] = mCallbacks[callbackId];
+ auto& [callbackFunction, callbackSurfaceControls] = callbacksMap[callbackId];
if (!callbackFunction) {
ALOGE("cannot call null callback function, skipping");
continue;
}
std::vector<SurfaceControlStats> surfaceControlStats;
for (const auto& surfaceStats : transactionStats.surfaceStats) {
- surfaceControlStats.emplace_back(surfaceControls[surfaceStats.surfaceControl],
- surfaceStats.acquireTime,
- surfaceStats.previousReleaseFence);
+ surfaceControlStats
+ .emplace_back(callbacksMap[callbackId]
+ .surfaceControls[surfaceStats.surfaceControl],
+ transactionStats.latchTime, surfaceStats.acquireTime,
+ transactionStats.presentFence,
+ surfaceStats.previousReleaseFence, surfaceStats.transformHint,
+ surfaceStats.eventStats);
+ if (callbacksMap[callbackId].surfaceControls[surfaceStats.surfaceControl]) {
+ callbacksMap[callbackId]
+ .surfaceControls[surfaceStats.surfaceControl]
+ ->setTransformHint(surfaceStats.transformHint);
+ }
}
callbackFunction(transactionStats.latchTime, transactionStats.presentFence,
surfaceControlStats);
- mCallbacks.erase(callbackId);
}
}
}
// ---------------------------------------------------------------------------
-void bufferCacheCallback(void* /*context*/, uint64_t graphicBufferId);
+void removeDeadBufferCallback(void* /*context*/, uint64_t graphicBufferId);
+/**
+ * We use the BufferCache to reduce the overhead of exchanging GraphicBuffers with
+ * the server. If we were to simply parcel the GraphicBuffer we would pay two overheads
+ * 1. Cost of sending the FD
+ * 2. Cost of importing the GraphicBuffer with the mapper in the receiving process.
+ * To ease this cost we implement the following scheme of caching buffers to integers,
+ * or said-otherwise, naming them with integers. This is the scheme known as slots in
+ * the legacy BufferQueue system.
+ * 1. When sending Buffers to SurfaceFlinger we look up the Buffer in the cache.
+ * 2. If there is a cache-hit we remove the Buffer from the Transaction and instead
+ * send the cached integer.
+ * 3. If there is a cache miss, we cache the new buffer and send the integer
+ * along with the Buffer, SurfaceFlinger on it's side creates a new cache
+ * entry, and we use the integer for further communication.
+ * A few details about lifetime:
+ * 1. The cache evicts by LRU. The server side cache is keyed by BufferCache::getToken
+ * which is per process Unique. The server side cache is larger than the client side
+ * cache so that the server will never evict entries before the client.
+ * 2. When the client evicts an entry it notifies the server via an uncacheBuffer
+ * transaction.
+ * 3. The client only references the Buffers by ID, and uses buffer->addDeathCallback
+ * to auto-evict destroyed buffers.
+ */
class BufferCache : public Singleton<BufferCache> {
public:
BufferCache() : token(new BBinder()) {}
@@ -262,7 +293,7 @@
evictLeastRecentlyUsedBuffer();
}
- buffer->addDeathCallback(bufferCacheCallback, nullptr);
+ buffer->addDeathCallback(removeDeadBufferCallback, nullptr);
mBuffers[buffer->getId()] = getCounter();
return buffer->getId();
@@ -310,7 +341,7 @@
ANDROID_SINGLETON_STATIC_INSTANCE(BufferCache);
-void bufferCacheCallback(void* /*context*/, uint64_t graphicBufferId) {
+void removeDeadBufferCallback(void* /*context*/, uint64_t graphicBufferId) {
// GraphicBuffer id's are used as the cache ids.
BufferCache::getInstance().uncache(graphicBufferId);
}
@@ -322,21 +353,169 @@
mTransactionNestCount(other.mTransactionNestCount),
mAnimation(other.mAnimation),
mEarlyWakeup(other.mEarlyWakeup),
+ mExplicitEarlyWakeupStart(other.mExplicitEarlyWakeupStart),
+ mExplicitEarlyWakeupEnd(other.mExplicitEarlyWakeupEnd),
+ mContainsBuffer(other.mContainsBuffer),
mDesiredPresentTime(other.mDesiredPresentTime) {
mDisplayStates = other.mDisplayStates;
mComposerStates = other.mComposerStates;
mInputWindowCommands = other.mInputWindowCommands;
+ mListenerCallbacks = other.mListenerCallbacks;
+}
+
+std::unique_ptr<SurfaceComposerClient::Transaction>
+SurfaceComposerClient::Transaction::createFromParcel(const Parcel* parcel) {
+ auto transaction = std::make_unique<Transaction>();
+ if (transaction->readFromParcel(parcel) == NO_ERROR) {
+ return transaction;
+ }
+ return nullptr;
+}
+
+status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel) {
+ const uint32_t forceSynchronous = parcel->readUint32();
+ const uint32_t transactionNestCount = parcel->readUint32();
+ const bool animation = parcel->readBool();
+ const bool earlyWakeup = parcel->readBool();
+ const bool explicitEarlyWakeupStart = parcel->readBool();
+ const bool explicitEarlyWakeupEnd = parcel->readBool();
+ const bool containsBuffer = parcel->readBool();
+ const int64_t desiredPresentTime = parcel->readInt64();
+
+ 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++) {
+ sp<ITransactionCompletedListener> listener =
+ interface_cast<ITransactionCompletedListener>(parcel->readStrongBinder());
+ size_t numCallbackIds = parcel->readUint32();
+ if (numCallbackIds > parcel->dataSize()) {
+ return BAD_VALUE;
+ }
+ for (size_t j = 0; j < numCallbackIds; j++) {
+ listenerCallbacks[listener].callbackIds.insert(parcel->readInt64());
+ }
+ size_t numSurfaces = parcel->readUint32();
+ if (numSurfaces > parcel->dataSize()) {
+ return BAD_VALUE;
+ }
+ for (size_t j = 0; j < numSurfaces; j++) {
+ sp<SurfaceControl> surface;
+ surface = SurfaceControl::readFromParcel(parcel);
+ listenerCallbacks[listener].surfaceControls.insert(surface);
+ }
+ }
+
+ 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 = parcel->readStrongBinder();
+
+ ComposerState composerState;
+ if (composerState.read(*parcel) == BAD_VALUE) {
+ return BAD_VALUE;
+ }
+ composerStates[surfaceControlHandle] = composerState;
+ }
+
+ InputWindowCommands inputWindowCommands;
+ inputWindowCommands.read(*parcel);
+
+ // Parsing was successful. Update the object.
+ mForceSynchronous = forceSynchronous;
+ mTransactionNestCount = transactionNestCount;
+ mAnimation = animation;
+ mEarlyWakeup = earlyWakeup;
+ mExplicitEarlyWakeupStart = explicitEarlyWakeupStart;
+ mExplicitEarlyWakeupEnd = explicitEarlyWakeupEnd;
+ mContainsBuffer = containsBuffer;
+ mDesiredPresentTime = desiredPresentTime;
+ mDisplayStates = displayStates;
+ mListenerCallbacks = listenerCallbacks;
+ mComposerStates = composerStates;
+ mInputWindowCommands = inputWindowCommands;
+ return NO_ERROR;
+}
+
+status_t SurfaceComposerClient::Transaction::writeToParcel(Parcel* parcel) const {
+ // If we write the Transaction to a parcel, we want to ensure the Buffers are cached
+ // before crossing the IPC boundary. Otherwise the receiving party will cache the buffers
+ // but is unlikely to use them again as they are owned by the other process.
+ // You may be asking yourself, is this const cast safe? Const cast is safe up
+ // until the point where you try and write to an object that was originally const at which
+ // point we enter undefined behavior. In this case we are safe though, because there are
+ // two possibilities:
+ // 1. The SurfaceComposerClient::Transaction was originally non-const. Safe.
+ // 2. It was originall const! In this case not only was it useless, but it by definition
+ // contains no composer states and so cacheBuffers will not perform any writes.
+
+ const_cast<SurfaceComposerClient::Transaction*>(this)->cacheBuffers();
+
+ parcel->writeUint32(mForceSynchronous);
+ parcel->writeUint32(mTransactionNestCount);
+ parcel->writeBool(mAnimation);
+ parcel->writeBool(mEarlyWakeup);
+ parcel->writeBool(mExplicitEarlyWakeupStart);
+ parcel->writeBool(mExplicitEarlyWakeupEnd);
+ parcel->writeBool(mContainsBuffer);
+ parcel->writeInt64(mDesiredPresentTime);
+ parcel->writeUint32(static_cast<uint32_t>(mDisplayStates.size()));
+ for (auto const& displayState : mDisplayStates) {
+ displayState.write(*parcel);
+ }
+
+ parcel->writeUint32(static_cast<uint32_t>(mListenerCallbacks.size()));
+ for (auto const& [listener, callbackInfo] : mListenerCallbacks) {
+ parcel->writeStrongBinder(ITransactionCompletedListener::asBinder(listener));
+ parcel->writeUint32(static_cast<uint32_t>(callbackInfo.callbackIds.size()));
+ for (auto callbackId : callbackInfo.callbackIds) {
+ parcel->writeInt64(callbackId);
+ }
+ parcel->writeUint32(static_cast<uint32_t>(callbackInfo.surfaceControls.size()));
+ for (auto surfaceControl : callbackInfo.surfaceControls) {
+ surfaceControl->writeToParcel(parcel);
+ }
+ }
+
+ parcel->writeUint32(static_cast<uint32_t>(mComposerStates.size()));
+ for (auto const& [surfaceHandle, composerState] : mComposerStates) {
+ parcel->writeStrongBinder(surfaceHandle);
+ composerState.write(*parcel);
+ }
+
+ mInputWindowCommands.write(*parcel);
+ return NO_ERROR;
}
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::merge(Transaction&& other) {
- for (auto const& kv : other.mComposerStates) {
- if (mComposerStates.count(kv.first) == 0) {
- mComposerStates[kv.first] = kv.second;
+ for (auto const& [surfaceHandle, composerState] : other.mComposerStates) {
+ if (mComposerStates.count(surfaceHandle) == 0) {
+ mComposerStates[surfaceHandle] = composerState;
} else {
- mComposerStates[kv.first].state.merge(kv.second.state);
+ mComposerStates[surfaceHandle].state.merge(composerState.state);
}
}
- other.mComposerStates.clear();
for (auto const& state : other.mDisplayStates) {
ssize_t index = mDisplayStates.indexOf(state);
@@ -346,46 +525,53 @@
mDisplayStates.editItemAt(static_cast<size_t>(index)).merge(state);
}
}
- other.mDisplayStates.clear();
for (const auto& [listener, callbackInfo] : other.mListenerCallbacks) {
auto& [callbackIds, surfaceControls] = callbackInfo;
mListenerCallbacks[listener].callbackIds.insert(std::make_move_iterator(
callbackIds.begin()),
std::make_move_iterator(callbackIds.end()));
- mListenerCallbacks[listener]
- .surfaceControls.insert(std::make_move_iterator(surfaceControls.begin()),
- std::make_move_iterator(surfaceControls.end()));
+
+ mListenerCallbacks[listener].surfaceControls.insert(surfaceControls.begin(),
+ surfaceControls.end());
+
+ auto& currentProcessCallbackInfo =
+ mListenerCallbacks[TransactionCompletedListener::getIInstance()];
+ currentProcessCallbackInfo.surfaceControls
+ .insert(std::make_move_iterator(surfaceControls.begin()),
+ std::make_move_iterator(surfaceControls.end()));
+
+ // register all surface controls for all callbackIds for this listener that is merging
+ for (const auto& surfaceControl : currentProcessCallbackInfo.surfaceControls) {
+ TransactionCompletedListener::getInstance()
+ ->addSurfaceControlToCallbacks(surfaceControl,
+ currentProcessCallbackInfo.callbackIds);
+ }
}
- other.mListenerCallbacks.clear();
mInputWindowCommands.merge(other.mInputWindowCommands);
- other.mInputWindowCommands.clear();
- mContainsBuffer = other.mContainsBuffer;
- other.mContainsBuffer = false;
-
+ mContainsBuffer |= other.mContainsBuffer;
mEarlyWakeup = mEarlyWakeup || other.mEarlyWakeup;
- other.mEarlyWakeup = false;
-
+ mExplicitEarlyWakeupStart = mExplicitEarlyWakeupStart || other.mExplicitEarlyWakeupStart;
+ mExplicitEarlyWakeupEnd = mExplicitEarlyWakeupEnd || other.mExplicitEarlyWakeupEnd;
+ other.clear();
return *this;
}
-void SurfaceComposerClient::doDropReferenceTransaction(const sp<IBinder>& handle,
- const sp<ISurfaceComposerClient>& client) {
- sp<ISurfaceComposer> sf(ComposerService::getComposerService());
- Vector<ComposerState> composerStates;
- Vector<DisplayState> displayStates;
-
- ComposerState s;
- s.client = client;
- s.state.surface = handle;
- s.state.what |= layer_state_t::eReparent;
- s.state.parentHandleForChild = nullptr;
-
- composerStates.add(s);
- sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
- sf->setTransactionState(composerStates, displayStates, 0, applyToken, {}, -1, {}, {});
+void SurfaceComposerClient::Transaction::clear() {
+ mComposerStates.clear();
+ mDisplayStates.clear();
+ mListenerCallbacks.clear();
+ mInputWindowCommands.clear();
+ mContainsBuffer = false;
+ mForceSynchronous = 0;
+ mTransactionNestCount = 0;
+ mAnimation = false;
+ mEarlyWakeup = false;
+ mExplicitEarlyWakeupStart = false;
+ mExplicitEarlyWakeupEnd = false;
+ mDesiredPresentTime = -1;
}
void SurfaceComposerClient::doUncacheBufferTransaction(uint64_t cacheId) {
@@ -396,7 +582,7 @@
uncacheBuffer.id = cacheId;
sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
- sf->setTransactionState({}, {}, 0, applyToken, {}, -1, uncacheBuffer, {});
+ sf->setTransactionState({}, {}, 0, applyToken, {}, -1, uncacheBuffer, false, {});
}
void SurfaceComposerClient::Transaction::cacheBuffers() {
@@ -405,10 +591,15 @@
}
size_t count = 0;
- for (auto& [sc, cs] : mComposerStates) {
- layer_state_t* s = getLayerState(sc);
+ for (auto& [handle, cs] : mComposerStates) {
+ layer_state_t* s = getLayerState(handle);
if (!(s->what & layer_state_t::eBufferChanged)) {
continue;
+ } else if (s->what & layer_state_t::eCachedBufferChanged) {
+ // If eBufferChanged and eCachedBufferChanged are both trued then that means
+ // we already cached the buffer in a previous call to cacheBuffers, perhaps
+ // from writeToParcel on a Transaction that was merged in to this one.
+ continue;
}
// Don't try to cache a null buffer. Sending null buffers is cheap so we shouldn't waste
@@ -420,9 +611,11 @@
uint64_t cacheId = 0;
status_t ret = BufferCache::getInstance().getCacheId(s->buffer, &cacheId);
if (ret == NO_ERROR) {
+ // Cache-hit. Strip the buffer and send only the id.
s->what &= ~static_cast<uint64_t>(layer_state_t::eBufferChanged);
s->buffer = nullptr;
} else {
+ // Cache-miss. Include the buffer and send the new cacheId.
cacheId = BufferCache::getInstance().cache(s->buffer);
}
s->what |= layer_state_t::eCachedBufferChanged;
@@ -445,8 +638,8 @@
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+ bool hasListenerCallbacks = !mListenerCallbacks.empty();
std::vector<ListenerCallbacks> listenerCallbacks;
-
// For every listener with registered callbacks
for (const auto& [listener, callbackInfo] : mListenerCallbacks) {
auto& [callbackIds, surfaceControls] = callbackInfo;
@@ -454,19 +647,24 @@
continue;
}
- listenerCallbacks.emplace_back(listener, std::move(callbackIds));
-
- // If the listener has any SurfaceControls set on this Transaction update the surface state
- for (const auto& surfaceControl : surfaceControls) {
- layer_state_t* s = getLayerState(surfaceControl);
- if (!s) {
- ALOGE("failed to get layer state");
- continue;
+ if (surfaceControls.empty()) {
+ listenerCallbacks.emplace_back(IInterface::asBinder(listener), std::move(callbackIds));
+ } else {
+ // If the listener has any SurfaceControls set on this Transaction update the surface
+ // state
+ for (const auto& surfaceControl : surfaceControls) {
+ layer_state_t* s = getLayerState(surfaceControl);
+ if (!s) {
+ ALOGE("failed to get layer state");
+ continue;
+ }
+ std::vector<CallbackId> callbacks(callbackIds.begin(), callbackIds.end());
+ s->what |= layer_state_t::eHasListenerCallbacksChanged;
+ s->listeners.emplace_back(IInterface::asBinder(listener), callbacks);
}
- s->what |= layer_state_t::eHasListenerCallbacksChanged;
- s->hasListenerCallbacks = true;
}
}
+
mListenerCallbacks.clear();
cacheBuffers();
@@ -496,15 +694,26 @@
flags |= ISurfaceComposer::eEarlyWakeup;
}
+ // If both mExplicitEarlyWakeupStart and mExplicitEarlyWakeupEnd are set
+ // it is equivalent for none
+ if (mExplicitEarlyWakeupStart && !mExplicitEarlyWakeupEnd) {
+ flags |= ISurfaceComposer::eExplicitEarlyWakeupStart;
+ }
+ if (mExplicitEarlyWakeupEnd && !mExplicitEarlyWakeupStart) {
+ flags |= ISurfaceComposer::eExplicitEarlyWakeupEnd;
+ }
+
mForceSynchronous = false;
mAnimation = false;
mEarlyWakeup = false;
+ mExplicitEarlyWakeupStart = false;
+ mExplicitEarlyWakeupEnd = false;
sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
sf->setTransactionState(composerStates, displayStates, flags, applyToken, mInputWindowCommands,
mDesiredPresentTime,
{} /*uncacheBuffer - only set in doUncacheBufferTransaction*/,
- listenerCallbacks);
+ hasListenerCallbacks, listenerCallbacks);
mInputWindowCommands.clear();
mStatus = NO_ERROR;
return NO_ERROR;
@@ -545,16 +754,23 @@
mEarlyWakeup = true;
}
-layer_state_t* SurfaceComposerClient::Transaction::getLayerState(const sp<SurfaceControl>& sc) {
- if (mComposerStates.count(sc) == 0) {
+void SurfaceComposerClient::Transaction::setExplicitEarlyWakeupStart() {
+ mExplicitEarlyWakeupStart = true;
+}
+
+void SurfaceComposerClient::Transaction::setExplicitEarlyWakeupEnd() {
+ mExplicitEarlyWakeupEnd = true;
+}
+
+layer_state_t* SurfaceComposerClient::Transaction::getLayerState(const sp<IBinder>& handle) {
+ if (mComposerStates.count(handle) == 0) {
// we don't have it, add an initialized layer_state to our list
ComposerState s;
- s.client = sc->getClient()->mClient;
- s.state.surface = sc->getHandle();
- mComposerStates[sc] = s;
+ s.state.surface = handle;
+ mComposerStates[handle] = s;
}
- return &(mComposerStates[sc].state);
+ return &(mComposerStates[handle].state);
}
void SurfaceComposerClient::Transaction::registerSurfaceControlForCallback(
@@ -626,6 +842,7 @@
layer_state_t* s = getLayerState(sc);
if (!s) {
mStatus = BAD_INDEX;
+ return *this;
}
s->what |= layer_state_t::eRelativeLayerChanged;
s->what &= ~layer_state_t::eLayerChanged;
@@ -761,6 +978,18 @@
return *this;
}
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBackgroundBlurRadius(
+ const sp<SurfaceControl>& sc, int backgroundBlurRadius) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+ s->what |= layer_state_t::eBackgroundBlurRadiusChanged;
+ s->backgroundBlurRadius = backgroundBlurRadius;
+ return *this;
+}
+
SurfaceComposerClient::Transaction&
SurfaceComposerClient::Transaction::deferTransactionUntil_legacy(const sp<SurfaceControl>& sc,
const sp<IBinder>& handle,
@@ -1035,6 +1264,22 @@
}
SurfaceComposerClient::Transaction&
+SurfaceComposerClient::Transaction::setFrameRateSelectionPriority(const sp<SurfaceControl>& sc,
+ int32_t priority) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+
+ s->what |= layer_state_t::eFrameRateSelectionPriority;
+ s->frameRateSelectionPriority = priority;
+
+ registerSurfaceControlForCallback(sc);
+ return *this;
+}
+
+SurfaceComposerClient::Transaction&
SurfaceComposerClient::Transaction::addTransactionCompletedCallback(
TransactionCompletedCallbackTakesContext callback, void* callbackContext) {
auto listener = TransactionCompletedListener::getInstance();
@@ -1051,11 +1296,24 @@
return *this;
}
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::notifyProducerDisconnect(
+ const sp<SurfaceControl>& sc) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+
+ s->what |= layer_state_t::eProducerDisconnect;
+ return *this;
+}
+
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::detachChildren(
const sp<SurfaceControl>& sc) {
layer_state_t* s = getLayerState(sc);
if (!s) {
mStatus = BAD_INDEX;
+ return *this;
}
s->what |= layer_state_t::eDetachChildren;
@@ -1092,19 +1350,6 @@
return *this;
}
-SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setGeometryAppliesWithResize(
- const sp<SurfaceControl>& sc) {
- layer_state_t* s = getLayerState(sc);
- if (!s) {
- mStatus = BAD_INDEX;
- return *this;
- }
- s->what |= layer_state_t::eGeometryAppliesWithResize;
-
- registerSurfaceControlForCallback(sc);
- return *this;
-}
-
#ifndef NO_INPUT
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setInputWindowInfo(
const sp<SurfaceControl>& sc,
@@ -1119,15 +1364,6 @@
return *this;
}
-SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::transferTouchFocus(
- const sp<IBinder>& fromToken, const sp<IBinder>& toToken) {
- InputWindowCommands::TransferTouchFocusCommand transferTouchFocusCommand;
- transferTouchFocusCommand.fromToken = fromToken;
- transferTouchFocusCommand.toToken = toToken;
- mInputWindowCommands.transferTouchFocusCommands.emplace_back(transferTouchFocusCommand);
- return *this;
-}
-
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::syncInputWindows() {
mInputWindowCommands.syncInputWindows = true;
return *this;
@@ -1196,11 +1432,58 @@
break;
}
setMatrix(sc, matrix[0], matrix[1], matrix[2], matrix[3]);
- setPosition(sc, x, y);
+ float offsetX = xScale * source.left;
+ float offsetY = yScale * source.top;
+ setPosition(sc, x - offsetX, y - offsetY);
return *this;
}
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setShadowRadius(
+ const sp<SurfaceControl>& sc, float shadowRadius) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+ s->what |= layer_state_t::eShadowRadiusChanged;
+ s->shadowRadius = shadowRadius;
+ return *this;
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrameRate(
+ const sp<SurfaceControl>& sc, float frameRate, int8_t compatibility) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+ if (!ValidateFrameRate(frameRate, compatibility, "Transaction::setFrameRate")) {
+ mStatus = BAD_VALUE;
+ return *this;
+ }
+ s->what |= layer_state_t::eFrameRateChanged;
+ s->frameRate = frameRate;
+ s->frameRateCompatibility = compatibility;
+ return *this;
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFixedTransformHint(
+ const sp<SurfaceControl>& sc, int32_t fixedTransformHint) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+
+ const ui::Transform::RotationFlags transform = fixedTransformHint == -1
+ ? ui::Transform::ROT_INVALID
+ : ui::Transform::toRotationFlags(static_cast<ui::Rotation>(fixedTransformHint));
+ s->what |= layer_state_t::eFixedTransformHintChanged;
+ s->fixedTransformHint = transform;
+ return *this;
+}
+
// ---------------------------------------------------------------------------
DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp<IBinder>& token) {
@@ -1242,9 +1525,9 @@
}
void SurfaceComposerClient::Transaction::setDisplayProjection(const sp<IBinder>& token,
- uint32_t orientation,
- const Rect& layerStackRect,
- const Rect& displayRect) {
+ ui::Rotation orientation,
+ const Rect& layerStackRect,
+ const Rect& displayRect) {
DisplayState& s(getDisplayState(token));
s.orientation = orientation;
s.viewport = layerStackRect;
@@ -1317,16 +1600,19 @@
sp<SurfaceControl> SurfaceComposerClient::createSurface(const String8& name, uint32_t w, uint32_t h,
PixelFormat format, uint32_t flags,
SurfaceControl* parent,
- LayerMetadata metadata) {
+ LayerMetadata metadata,
+ uint32_t* outTransformHint) {
sp<SurfaceControl> s;
- createSurfaceChecked(name, w, h, format, &s, flags, parent, std::move(metadata));
+ createSurfaceChecked(name, w, h, format, &s, flags, parent, std::move(metadata),
+ outTransformHint);
return s;
}
sp<SurfaceControl> SurfaceComposerClient::createWithSurfaceParent(const String8& name, uint32_t w,
uint32_t h, PixelFormat format,
uint32_t flags, Surface* parent,
- LayerMetadata metadata) {
+ LayerMetadata metadata,
+ uint32_t* outTransformHint) {
sp<SurfaceControl> sur;
status_t err = mStatus;
@@ -1335,11 +1621,15 @@
sp<IGraphicBufferProducer> parentGbp = parent->getIGraphicBufferProducer();
sp<IGraphicBufferProducer> gbp;
+ uint32_t transformHint = 0;
err = mClient->createWithSurfaceParent(name, w, h, format, flags, parentGbp,
- std::move(metadata), &handle, &gbp);
+ std::move(metadata), &handle, &gbp, &transformHint);
+ if (outTransformHint) {
+ *outTransformHint = transformHint;
+ }
ALOGE_IF(err, "SurfaceComposerClient::createWithSurfaceParent error %s", strerror(-err));
if (err == NO_ERROR) {
- return new SurfaceControl(this, handle, gbp, true /* owned */);
+ return new SurfaceControl(this, handle, gbp, transformHint);
}
}
return nullptr;
@@ -1348,8 +1638,8 @@
status_t SurfaceComposerClient::createSurfaceChecked(const String8& name, uint32_t w, uint32_t h,
PixelFormat format,
sp<SurfaceControl>* outSurface, uint32_t flags,
- SurfaceControl* parent,
- LayerMetadata metadata) {
+ SurfaceControl* parent, LayerMetadata metadata,
+ uint32_t* outTransformHint) {
sp<SurfaceControl> sur;
status_t err = mStatus;
@@ -1362,16 +1652,34 @@
parentHandle = parent->getHandle();
}
+ uint32_t transformHint = 0;
err = mClient->createSurface(name, w, h, format, flags, parentHandle, std::move(metadata),
- &handle, &gbp);
+ &handle, &gbp, &transformHint);
+ if (outTransformHint) {
+ *outTransformHint = transformHint;
+ }
ALOGE_IF(err, "SurfaceComposerClient::createSurface error %s", strerror(-err));
if (err == NO_ERROR) {
- *outSurface = new SurfaceControl(this, handle, gbp, true /* owned */);
+ *outSurface = new SurfaceControl(this, handle, gbp, transformHint);
}
}
return err;
}
+sp<SurfaceControl> SurfaceComposerClient::mirrorSurface(SurfaceControl* mirrorFromSurface) {
+ if (mirrorFromSurface == nullptr) {
+ return nullptr;
+ }
+
+ sp<IBinder> handle;
+ sp<IBinder> mirrorFromHandle = mirrorFromSurface->getHandle();
+ status_t err = mClient->mirrorSurface(mirrorFromHandle, &handle);
+ if (err == NO_ERROR) {
+ return new SurfaceControl(this, handle, nullptr, true /* owned */);
+ }
+ return nullptr;
+}
+
status_t SurfaceComposerClient::clearLayerFrameStats(const sp<IBinder>& token) const {
if (mStatus != NO_ERROR) {
return mStatus;
@@ -1399,15 +1707,23 @@
return sf->injectVSync(when);
}
-status_t SurfaceComposerClient::getDisplayConfigs(
- const sp<IBinder>& display, Vector<DisplayInfo>* configs)
-{
+status_t SurfaceComposerClient::getDisplayState(const sp<IBinder>& display,
+ ui::DisplayState* state) {
+ return ComposerService::getComposerService()->getDisplayState(display, state);
+}
+
+status_t SurfaceComposerClient::getDisplayInfo(const sp<IBinder>& display, DisplayInfo* info) {
+ return ComposerService::getComposerService()->getDisplayInfo(display, info);
+}
+
+status_t SurfaceComposerClient::getDisplayConfigs(const sp<IBinder>& display,
+ Vector<DisplayConfig>* configs) {
return ComposerService::getComposerService()->getDisplayConfigs(display, configs);
}
-status_t SurfaceComposerClient::getDisplayInfo(const sp<IBinder>& display,
- DisplayInfo* info) {
- Vector<DisplayInfo> configs;
+status_t SurfaceComposerClient::getActiveDisplayConfig(const sp<IBinder>& display,
+ DisplayConfig* config) {
+ Vector<DisplayConfig> configs;
status_t result = getDisplayConfigs(display, &configs);
if (result != NO_ERROR) {
return result;
@@ -1419,7 +1735,7 @@
return NAME_NOT_FOUND;
}
- *info = configs[static_cast<size_t>(activeId)];
+ *config = configs[static_cast<size_t>(activeId)];
return NO_ERROR;
}
@@ -1427,20 +1743,28 @@
return ComposerService::getComposerService()->getActiveConfig(display);
}
-status_t SurfaceComposerClient::setActiveConfig(const sp<IBinder>& display, int id) {
- return ComposerService::getComposerService()->setActiveConfig(display, id);
+status_t SurfaceComposerClient::setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
+ int32_t defaultConfig,
+ float primaryRefreshRateMin,
+ float primaryRefreshRateMax,
+ float appRequestRefreshRateMin,
+ float appRequestRefreshRateMax) {
+ return ComposerService::getComposerService()
+ ->setDesiredDisplayConfigSpecs(displayToken, defaultConfig, primaryRefreshRateMin,
+ primaryRefreshRateMax, appRequestRefreshRateMin,
+ appRequestRefreshRateMax);
}
-status_t SurfaceComposerClient::setAllowedDisplayConfigs(
- const sp<IBinder>& displayToken, const std::vector<int32_t>& allowedConfigs) {
- return ComposerService::getComposerService()->setAllowedDisplayConfigs(displayToken,
- allowedConfigs);
-}
-
-status_t SurfaceComposerClient::getAllowedDisplayConfigs(const sp<IBinder>& displayToken,
- std::vector<int32_t>* outAllowedConfigs) {
- return ComposerService::getComposerService()->getAllowedDisplayConfigs(displayToken,
- outAllowedConfigs);
+status_t SurfaceComposerClient::getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
+ int32_t* outDefaultConfig,
+ float* outPrimaryRefreshRateMin,
+ float* outPrimaryRefreshRateMax,
+ float* outAppRequestRefreshRateMin,
+ float* outAppRequestRefreshRateMax) {
+ return ComposerService::getComposerService()
+ ->getDesiredDisplayConfigSpecs(displayToken, outDefaultConfig, outPrimaryRefreshRateMin,
+ outPrimaryRefreshRateMax, outAppRequestRefreshRateMin,
+ outAppRequestRefreshRateMax);
}
status_t SurfaceComposerClient::getDisplayColorModes(const sp<IBinder>& display,
@@ -1462,6 +1786,26 @@
return ComposerService::getComposerService()->setActiveColorMode(display, colorMode);
}
+bool SurfaceComposerClient::getAutoLowLatencyModeSupport(const sp<IBinder>& display) {
+ bool supported = false;
+ ComposerService::getComposerService()->getAutoLowLatencyModeSupport(display, &supported);
+ return supported;
+}
+
+void SurfaceComposerClient::setAutoLowLatencyMode(const sp<IBinder>& display, bool on) {
+ ComposerService::getComposerService()->setAutoLowLatencyMode(display, on);
+}
+
+bool SurfaceComposerClient::getGameContentTypeSupport(const sp<IBinder>& display) {
+ bool supported = false;
+ ComposerService::getComposerService()->getGameContentTypeSupport(display, &supported);
+ return supported;
+}
+
+void SurfaceComposerClient::setGameContentType(const sp<IBinder>& display, bool on) {
+ ComposerService::getComposerService()->setGameContentType(display, on);
+}
+
void SurfaceComposerClient::setDisplayPowerMode(const sp<IBinder>& token,
int mode) {
ComposerService::getComposerService()->setPowerMode(token, mode);
@@ -1553,30 +1897,36 @@
return ComposerService::getComposerService()->notifyPowerHint(hintId);
}
+status_t SurfaceComposerClient::setGlobalShadowSettings(const half4& ambientColor,
+ const half4& spotColor, float lightPosY,
+ float lightPosZ, float lightRadius) {
+ return ComposerService::getComposerService()->setGlobalShadowSettings(ambientColor, spotColor,
+ lightPosY, lightPosZ,
+ lightRadius);
+}
+
// ----------------------------------------------------------------------------
-status_t ScreenshotClient::capture(const sp<IBinder>& display, const ui::Dataspace reqDataSpace,
- const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
+status_t ScreenshotClient::capture(const sp<IBinder>& display, ui::Dataspace reqDataSpace,
+ ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform,
- uint32_t rotation, bool captureSecureLayers,
+ ui::Rotation rotation, bool captureSecureLayers,
sp<GraphicBuffer>* outBuffer, bool& outCapturedSecureLayers) {
sp<ISurfaceComposer> s(ComposerService::getComposerService());
if (s == nullptr) return NO_INIT;
- status_t ret =
- s->captureScreen(display, outBuffer, outCapturedSecureLayers, reqDataSpace,
- reqPixelFormat, sourceCrop, reqWidth, reqHeight, useIdentityTransform,
- static_cast<ISurfaceComposer::Rotation>(rotation),
- captureSecureLayers);
+ status_t ret = s->captureScreen(display, outBuffer, outCapturedSecureLayers, reqDataSpace,
+ reqPixelFormat, sourceCrop, reqWidth, reqHeight,
+ useIdentityTransform, rotation, captureSecureLayers);
if (ret != NO_ERROR) {
return ret;
}
return ret;
}
-status_t ScreenshotClient::capture(const sp<IBinder>& display, const ui::Dataspace reqDataSpace,
- const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
+status_t ScreenshotClient::capture(const sp<IBinder>& display, ui::Dataspace reqDataSpace,
+ ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform,
- uint32_t rotation, sp<GraphicBuffer>* outBuffer) {
+ ui::Rotation rotation, sp<GraphicBuffer>* outBuffer) {
bool ignored;
return capture(display, reqDataSpace, reqPixelFormat, sourceCrop, reqWidth, reqHeight,
useIdentityTransform, rotation, false, outBuffer, ignored);
@@ -1589,9 +1939,8 @@
return s->captureScreen(displayOrLayerStack, outDataspace, outBuffer);
}
-status_t ScreenshotClient::captureLayers(const sp<IBinder>& layerHandle,
- const ui::Dataspace reqDataSpace,
- const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
+status_t ScreenshotClient::captureLayers(const sp<IBinder>& layerHandle, ui::Dataspace reqDataSpace,
+ ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
float frameScale, sp<GraphicBuffer>* outBuffer) {
sp<ISurfaceComposer> s(ComposerService::getComposerService());
if (s == nullptr) return NO_INIT;
@@ -1601,8 +1950,8 @@
}
status_t ScreenshotClient::captureChildLayers(
- const sp<IBinder>& layerHandle, const ui::Dataspace reqDataSpace,
- const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
+ const sp<IBinder>& layerHandle, ui::Dataspace reqDataSpace, ui::PixelFormat reqPixelFormat,
+ const Rect& sourceCrop,
const std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>& excludeHandles,
float frameScale, sp<GraphicBuffer>* outBuffer) {
sp<ISurfaceComposer> s(ComposerService::getComposerService());
@@ -1612,5 +1961,5 @@
excludeHandles, frameScale, true /* childrenOnly */);
return ret;
}
-// ----------------------------------------------------------------------------
-}; // namespace android
+
+} // namespace android
diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp
index 55488da..a332a1f 100644
--- a/libs/gui/SurfaceControl.cpp
+++ b/libs/gui/SurfaceControl.cpp
@@ -45,42 +45,23 @@
// SurfaceControl
// ============================================================================
-SurfaceControl::SurfaceControl(
- const sp<SurfaceComposerClient>& client,
- const sp<IBinder>& handle,
- const sp<IGraphicBufferProducer>& gbp,
- bool owned)
- : mClient(client), mHandle(handle), mGraphicBufferProducer(gbp), mOwned(owned)
-{
-}
+SurfaceControl::SurfaceControl(const sp<SurfaceComposerClient>& client, const sp<IBinder>& handle,
+ const sp<IGraphicBufferProducer>& gbp,
+ uint32_t transform)
+ : mClient(client),
+ mHandle(handle),
+ mGraphicBufferProducer(gbp),
+ mTransformHint(transform) {}
SurfaceControl::SurfaceControl(const sp<SurfaceControl>& other) {
mClient = other->mClient;
mHandle = other->mHandle;
mGraphicBufferProducer = other->mGraphicBufferProducer;
- mOwned = false;
+ mTransformHint = other->mTransformHint;
}
SurfaceControl::~SurfaceControl()
{
- // Avoid reparenting the server-side surface to null if we are not the owner of it,
- // meaning that we retrieved it from another process.
- if (mClient != nullptr && mHandle != nullptr && mOwned) {
- SurfaceComposerClient::doDropReferenceTransaction(mHandle, mClient->getClient());
- }
- release();
-}
-
-void SurfaceControl::destroy()
-{
- if (isValid()) {
- SurfaceComposerClient::Transaction().reparent(this, nullptr).apply();
- }
- release();
-}
-
-void SurfaceControl::release()
-{
// Trigger an IPC now, to make sure things
// happen without delay, since these resources are quite heavy.
mClient.clear();
@@ -164,7 +145,6 @@
sp<IBinder> SurfaceControl::getHandle() const
{
- Mutex::Autolock lock(mLock);
return mHandle;
}
@@ -179,15 +159,25 @@
return mClient;
}
+uint32_t SurfaceControl::getTransformHint() const {
+ Mutex::Autolock _l(mLock);
+ return mTransformHint;
+}
+
+void SurfaceControl::setTransformHint(uint32_t hint) {
+ Mutex::Autolock _l(mLock);
+ mTransformHint = hint;
+}
+
void SurfaceControl::writeToParcel(Parcel* parcel)
{
parcel->writeStrongBinder(ISurfaceComposerClient::asBinder(mClient->getClient()));
parcel->writeStrongBinder(mHandle);
parcel->writeStrongBinder(IGraphicBufferProducer::asBinder(mGraphicBufferProducer));
+ parcel->writeUint32(mTransformHint);
}
-sp<SurfaceControl> SurfaceControl::readFromParcel(Parcel* parcel)
-{
+sp<SurfaceControl> SurfaceControl::readFromParcel(const Parcel* parcel) {
sp<IBinder> client = parcel->readStrongBinder();
sp<IBinder> handle = parcel->readStrongBinder();
if (client == nullptr || handle == nullptr)
@@ -198,10 +188,12 @@
sp<IBinder> gbp;
parcel->readNullableStrongBinder(&gbp);
+ uint32_t transformHint = parcel->readUint32();
// We aren't the original owner of the surface.
return new SurfaceControl(new SurfaceComposerClient(
- interface_cast<ISurfaceComposerClient>(client)),
- handle.get(), interface_cast<IGraphicBufferProducer>(gbp), false /* owned */);
+ interface_cast<ISurfaceComposerClient>(client)),
+ handle.get(), interface_cast<IGraphicBufferProducer>(gbp),
+ transformHint);
}
// ----------------------------------------------------------------------------
diff --git a/libs/gui/TEST_MAPPING b/libs/gui/TEST_MAPPING
new file mode 100644
index 0000000..1c43530
--- /dev/null
+++ b/libs/gui/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "imports": [
+ {
+ "path": "frameworks/native/libs/nativewindow"
+ }
+ ]
+}
diff --git a/libs/gui/bufferqueue/1.0/Conversion.cpp b/libs/gui/bufferqueue/1.0/Conversion.cpp
index 5cb3593..3e20a37 100644
--- a/libs/gui/bufferqueue/1.0/Conversion.cpp
+++ b/libs/gui/bufferqueue/1.0/Conversion.cpp
@@ -109,20 +109,6 @@
}
/**
- * \brief Convert `Return<void>` to `binder::Status`.
- *
- * \param[in] t The source `Return<void>`.
- * \return The corresponding `binder::Status`.
- */
-// convert: Return<void> -> ::android::binder::Status
-::android::binder::Status toBinderStatus(
- Return<void> const& t) {
- return ::android::binder::Status::fromExceptionCode(
- toStatusT(t),
- t.description().c_str());
-}
-
-/**
* \brief Wrap `native_handle_t*` in `hidl_handle`.
*
* \param[in] nh The source `native_handle_t*`.
@@ -1337,57 +1323,6 @@
return unflatten(&(t->surfaceDamage), buffer, size);
}
-/**
- * \brief Wrap `BGraphicBufferProducer::QueueBufferInput` in
- * `HGraphicBufferProducer::QueueBufferInput`.
- *
- * \param[out] t The wrapper of type
- * `HGraphicBufferProducer::QueueBufferInput`.
- * \param[out] nh The underlying native handle for `t->fence`.
- * \param[in] l The source `BGraphicBufferProducer::QueueBufferInput`.
- *
- * If the return value is `true` and `t->fence` contains a valid file
- * descriptor, \p nh will be a newly created native handle holding that file
- * descriptor. \p nh needs to be deleted with `native_handle_delete()`
- * afterwards.
- */
-bool wrapAs(
- HGraphicBufferProducer::QueueBufferInput* t,
- native_handle_t** nh,
- BGraphicBufferProducer::QueueBufferInput const& l) {
-
- size_t const baseSize = l.getFlattenedSize();
- std::unique_ptr<uint8_t[]> baseBuffer(
- new (std::nothrow) uint8_t[baseSize]);
- if (!baseBuffer) {
- return false;
- }
-
- size_t const baseNumFds = l.getFdCount();
- std::unique_ptr<int[]> baseFds(
- new (std::nothrow) int[baseNumFds]);
- if (!baseFds) {
- return false;
- }
-
- void* buffer = static_cast<void*>(baseBuffer.get());
- size_t size = baseSize;
- int* fds = baseFds.get();
- size_t numFds = baseNumFds;
- if (l.flatten(buffer, size, fds, numFds) != NO_ERROR) {
- return false;
- }
-
- void const* constBuffer = static_cast<void const*>(baseBuffer.get());
- size = baseSize;
- int const* constFds = static_cast<int const*>(baseFds.get());
- numFds = baseNumFds;
- if (unflatten(t, nh, constBuffer, size, constFds, numFds) != NO_ERROR) {
- return false;
- }
-
- return true;
-}
/**
* \brief Convert `HGraphicBufferProducer::QueueBufferInput` to
diff --git a/libs/gui/bufferqueue/1.0/H2BProducerListener.cpp b/libs/gui/bufferqueue/1.0/H2BProducerListener.cpp
index 2712f42..598c8de 100644
--- a/libs/gui/bufferqueue/1.0/H2BProducerListener.cpp
+++ b/libs/gui/bufferqueue/1.0/H2BProducerListener.cpp
@@ -32,7 +32,11 @@
using ::android::hardware::Return;
H2BProducerListener::H2BProducerListener(sp<HProducerListener> const& base)
+#ifndef NO_BINDER
: CBase{base} {
+#else
+ : mBase(base) {
+#endif
}
void H2BProducerListener::onBufferReleased() {
diff --git a/libs/gui/bufferqueue/1.0/WProducerListener.cpp b/libs/gui/bufferqueue/1.0/WProducerListener.cpp
index 78dc4e8..56b64b9 100644
--- a/libs/gui/bufferqueue/1.0/WProducerListener.cpp
+++ b/libs/gui/bufferqueue/1.0/WProducerListener.cpp
@@ -46,5 +46,7 @@
bool LWProducerListener::needsReleaseNotify() {
return static_cast<bool>(mBase->needsReleaseNotify());
}
+void LWProducerListener::onBuffersDiscarded(const std::vector<int32_t>& /*slots*/) {
+}
} // namespace android
diff --git a/libs/gui/bufferqueue/2.0/B2HGraphicBufferProducer.cpp b/libs/gui/bufferqueue/2.0/B2HGraphicBufferProducer.cpp
index e891ec5..c76d771 100644
--- a/libs/gui/bufferqueue/2.0/B2HGraphicBufferProducer.cpp
+++ b/libs/gui/bufferqueue/2.0/B2HGraphicBufferProducer.cpp
@@ -249,6 +249,24 @@
return {};
}
+struct Obituary : public hardware::hidl_death_recipient {
+ wp<B2HGraphicBufferProducer> producer;
+ sp<HProducerListener> listener;
+ HConnectionType apiType;
+ Obituary(const wp<B2HGraphicBufferProducer>& p,
+ const sp<HProducerListener>& l,
+ HConnectionType t)
+ : producer(p), listener(l), apiType(t) {}
+
+ void serviceDied(uint64_t /* cookie */,
+ const wp<::android::hidl::base::V1_0::IBase>& /* who */) override {
+ sp<B2HGraphicBufferProducer> dr = producer.promote();
+ if (dr != nullptr) {
+ (void)dr->disconnect(apiType);
+ }
+ }
+};
+
Return<void> B2HGraphicBufferProducer::connect(
sp<HProducerListener> const& hListener,
HConnectionType hConnectionType,
@@ -270,6 +288,12 @@
&bOutput),
&hStatus) &&
b2h(bOutput, &hOutput);
+#ifdef NO_BINDER
+ if (converted && hListener != nullptr) {
+ mObituary = new Obituary(this, hListener, hConnectionType);
+ hListener->linkToDeath(mObituary, 0);
+ }
+#endif // NO_BINDER
_hidl_cb(converted ? hStatus : HStatus::UNKNOWN_ERROR, hOutput);
return {};
}
@@ -282,6 +306,12 @@
}
HStatus hStatus{};
bool converted = b2h(mBase->disconnect(bConnectionType), &hStatus);
+#ifdef NO_BINDER
+ if (mObituary != nullptr) {
+ mObituary->listener->unlinkToDeath(mObituary);
+ mObituary.clear();
+ }
+#endif // NO_BINDER
return {converted ? hStatus : HStatus::UNKNOWN_ERROR};
}
diff --git a/libs/gui/bufferqueue/2.0/H2BProducerListener.cpp b/libs/gui/bufferqueue/2.0/H2BProducerListener.cpp
index b81a357..b2bd117 100644
--- a/libs/gui/bufferqueue/2.0/H2BProducerListener.cpp
+++ b/libs/gui/bufferqueue/2.0/H2BProducerListener.cpp
@@ -32,7 +32,11 @@
using ::android::hardware::Return;
H2BProducerListener::H2BProducerListener(sp<HProducerListener> const& base)
+#ifndef NO_BINDER
: CBase{base} {
+#else
+ : mBase(base) {
+#endif
}
void H2BProducerListener::onBufferReleased() {
@@ -48,6 +52,9 @@
return static_cast<bool>(mBase);
}
+void H2BProducerListener::onBuffersDiscarded(const std::vector<int32_t>& /*slots*/) {
+}
+
} // namespace utils
} // namespace V2_0
} // namespace bufferqueue
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
new file mode 100644
index 0000000..2320771
--- /dev/null
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#ifndef ANDROID_GUI_BLAST_BUFFER_QUEUE_H
+#define ANDROID_GUI_BLAST_BUFFER_QUEUE_H
+
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/BufferItemConsumer.h>
+#include <gui/BufferItem.h>
+#include <gui/SurfaceComposerClient.h>
+
+#include <utils/Condition.h>
+#include <utils/Mutex.h>
+#include <utils/RefBase.h>
+
+#include <system/window.h>
+#include <thread>
+
+namespace android {
+
+class BufferItemConsumer;
+
+class BLASTBufferItemConsumer : public BufferItemConsumer {
+public:
+ BLASTBufferItemConsumer(const sp<IGraphicBufferConsumer>& consumer, uint64_t consumerUsage,
+ int bufferCount, bool controlledByApp)
+ : BufferItemConsumer(consumer, consumerUsage, bufferCount, controlledByApp),
+ mCurrentlyConnected(false),
+ mPreviouslyConnected(false) {}
+
+ void onDisconnect() override;
+ void addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
+ FrameEventHistoryDelta* outDelta) override
+ REQUIRES(mFrameEventHistoryMutex);
+ void updateFrameTimestamps(uint64_t frameNumber, nsecs_t refreshStartTime,
+ const sp<Fence>& gpuCompositionDoneFence,
+ const sp<Fence>& presentFence, const sp<Fence>& prevReleaseFence,
+ CompositorTiming compositorTiming, nsecs_t latchTime,
+ nsecs_t dequeueReadyTime) REQUIRES(mFrameEventHistoryMutex);
+ void getConnectionEvents(uint64_t frameNumber, bool* needsDisconnect);
+
+private:
+ uint64_t mCurrentFrameNumber = 0;
+
+ Mutex mFrameEventHistoryMutex;
+ ConsumerFrameEventHistory mFrameEventHistory GUARDED_BY(mFrameEventHistoryMutex);
+ std::queue<uint64_t> mDisconnectEvents GUARDED_BY(mFrameEventHistoryMutex);
+ bool mCurrentlyConnected GUARDED_BY(mFrameEventHistoryMutex);
+ bool mPreviouslyConnected GUARDED_BY(mFrameEventHistoryMutex);
+};
+
+class BLASTBufferQueue
+ : public ConsumerBase::FrameAvailableListener, public BufferItemConsumer::BufferFreedListener
+{
+public:
+ BLASTBufferQueue(const sp<SurfaceControl>& surface, int width, int height,
+ bool enableTripleBuffering = true);
+
+ sp<IGraphicBufferProducer> getIGraphicBufferProducer() const {
+ return mProducer;
+ }
+
+ void onBufferFreed(const wp<GraphicBuffer>&/* graphicBuffer*/) override { /* TODO */ }
+ void onFrameReplaced(const BufferItem& item) override {onFrameAvailable(item);}
+ void onFrameAvailable(const BufferItem& item) override;
+
+ void transactionCallback(nsecs_t latchTime, const sp<Fence>& presentFence,
+ const std::vector<SurfaceControlStats>& stats);
+ void setNextTransaction(SurfaceComposerClient::Transaction *t);
+
+ void update(const sp<SurfaceControl>& surface, int width, int height);
+
+ virtual ~BLASTBufferQueue() = default;
+
+private:
+ friend class BLASTBufferQueueHelper;
+
+ // can't be copied
+ BLASTBufferQueue& operator = (const BLASTBufferQueue& rhs);
+ BLASTBufferQueue(const BLASTBufferQueue& rhs);
+
+ void processNextBufferLocked(bool useNextTransaction) REQUIRES(mMutex);
+ Rect computeCrop(const BufferItem& item);
+
+ sp<SurfaceControl> mSurfaceControl;
+
+ std::mutex mMutex;
+ std::condition_variable mCallbackCV;
+
+ // BufferQueue internally allows 1 more than
+ // the max to be acquired
+ static const int MAX_ACQUIRED_BUFFERS = 1;
+
+ int32_t mNumFrameAvailable GUARDED_BY(mMutex);
+ int32_t mNumAcquired GUARDED_BY(mMutex);
+
+ struct PendingReleaseItem {
+ BufferItem item;
+ sp<Fence> releaseFence;
+ };
+
+ std::queue<const BufferItem> mSubmitted GUARDED_BY(mMutex);
+ PendingReleaseItem mPendingReleaseItem GUARDED_BY(mMutex);
+
+ int mWidth GUARDED_BY(mMutex);
+ int mHeight GUARDED_BY(mMutex);
+
+ uint32_t mTransformHint GUARDED_BY(mMutex);
+
+ sp<IGraphicBufferConsumer> mConsumer;
+ sp<IGraphicBufferProducer> mProducer;
+ sp<BLASTBufferItemConsumer> mBufferItemConsumer;
+
+ SurfaceComposerClient::Transaction* mNextTransaction GUARDED_BY(mMutex);
+};
+
+} // namespace android
+
+#endif // ANDROID_GUI_SURFACE_H
diff --git a/libs/gui/include/gui/BufferQueue.h b/libs/gui/include/gui/BufferQueue.h
index da95274..91f80d2 100644
--- a/libs/gui/include/gui/BufferQueue.h
+++ b/libs/gui/include/gui/BufferQueue.h
@@ -63,6 +63,9 @@
void onFrameReplaced(const BufferItem& item) override;
void onBuffersReleased() override;
void onSidebandStreamChanged() override;
+ void onFrameDequeued(const uint64_t bufferId) override;
+ void onFrameCancelled(const uint64_t bufferId) override;
+ void onFrameDetached(const uint64_t bufferID) override;
void addAndGetFrameTimestamps(
const NewFrameEventsEntry* newTimestamps,
FrameEventHistoryDelta* outDelta) override;
diff --git a/libs/gui/include/gui/BufferQueueCore.h b/libs/gui/include/gui/BufferQueueCore.h
index 17617bc..557c28b 100644
--- a/libs/gui/include/gui/BufferQueueCore.h
+++ b/libs/gui/include/gui/BufferQueueCore.h
@@ -34,12 +34,6 @@
#include <mutex>
#include <condition_variable>
-#define BQ_LOGV(x, ...) ALOGV("[%s] " x, mConsumerName.string(), ##__VA_ARGS__)
-#define BQ_LOGD(x, ...) ALOGD("[%s] " x, mConsumerName.string(), ##__VA_ARGS__)
-#define BQ_LOGI(x, ...) ALOGI("[%s] " x, mConsumerName.string(), ##__VA_ARGS__)
-#define BQ_LOGW(x, ...) ALOGW("[%s] " x, mConsumerName.string(), ##__VA_ARGS__)
-#define BQ_LOGE(x, ...) ALOGE("[%s] " x, mConsumerName.string(), ##__VA_ARGS__)
-
#define ATRACE_BUFFER_INDEX(index) \
do { \
if (ATRACE_ENABLED()) { \
@@ -352,6 +346,14 @@
const uint64_t mUniqueId;
+ // When buffer size is driven by the consumer and mTransformHint specifies
+ // a 90 or 270 degree rotation, this indicates whether the width and height
+ // used by dequeueBuffer will be additionally swapped.
+ bool mAutoPrerotation;
+
+ // mTransformHintInUse is to cache the mTransformHint used by the producer.
+ uint32_t mTransformHintInUse;
+
}; // class BufferQueueCore
} // namespace android
diff --git a/libs/gui/include/gui/BufferQueueProducer.h b/libs/gui/include/gui/BufferQueueProducer.h
index d2a47a6..a7f7d1d 100644
--- a/libs/gui/include/gui/BufferQueueProducer.h
+++ b/libs/gui/include/gui/BufferQueueProducer.h
@@ -22,10 +22,15 @@
namespace android {
+class IBinder;
struct BufferSlot;
+#ifndef NO_BINDER
class BufferQueueProducer : public BnGraphicBufferProducer,
private IBinder::DeathRecipient {
+#else
+class BufferQueueProducer : public BnGraphicBufferProducer {
+#endif
public:
friend class BufferQueue; // Needed to access binderDied
@@ -190,6 +195,9 @@
// See IGraphicBufferProducer::getConsumerUsage
virtual status_t getConsumerUsage(uint64_t* outUsage) const override;
+ // See IGraphicBufferProducer::setAutoPrerotation
+ virtual status_t setAutoPrerotation(bool autoPrerotation);
+
private:
// This is required by the IBinder::DeathRecipient interface
virtual void binderDied(const wp<IBinder>& who);
diff --git a/libs/gui/include/gui/ConsumerBase.h b/libs/gui/include/gui/ConsumerBase.h
index 366ced3..8ff0cd0 100644
--- a/libs/gui/include/gui/ConsumerBase.h
+++ b/libs/gui/include/gui/ConsumerBase.h
@@ -45,6 +45,9 @@
// See IConsumerListener::onFrame{Available,Replaced}
virtual void onFrameAvailable(const BufferItem& item) = 0;
virtual void onFrameReplaced(const BufferItem& /* item */) {}
+ virtual void onFrameDequeued(const uint64_t){};
+ virtual void onFrameCancelled(const uint64_t){};
+ virtual void onFrameDetached(const uint64_t){};
};
~ConsumerBase() override;
@@ -141,6 +144,9 @@
// classes if they want the notification.
virtual void onFrameAvailable(const BufferItem& item) override;
virtual void onFrameReplaced(const BufferItem& item) override;
+ virtual void onFrameDequeued(const uint64_t bufferId) override;
+ virtual void onFrameCancelled(const uint64_t bufferId) override;
+ virtual void onFrameDetached(const uint64_t bufferId) override;
virtual void onBuffersReleased() override;
virtual void onSidebandStreamChanged() override;
diff --git a/libs/gui/include/gui/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h
new file mode 100644
index 0000000..f210c34
--- /dev/null
+++ b/libs/gui/include/gui/DisplayEventDispatcher.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2015 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/DisplayEventReceiver.h>
+#include <utils/Log.h>
+#include <utils/Looper.h>
+
+namespace android {
+
+class DisplayEventDispatcher : public LooperCallback {
+public:
+ explicit DisplayEventDispatcher(
+ const sp<Looper>& looper,
+ ISurfaceComposer::VsyncSource vsyncSource = ISurfaceComposer::eVsyncSourceApp,
+ ISurfaceComposer::ConfigChanged configChanged =
+ ISurfaceComposer::eConfigChangedSuppress);
+
+ status_t initialize();
+ void dispose();
+ status_t scheduleVsync();
+ void requestLatestConfig();
+ int getFd() const;
+ virtual int handleEvent(int receiveFd, int events, void* data);
+
+protected:
+ virtual ~DisplayEventDispatcher() = default;
+
+private:
+ sp<Looper> mLooper;
+ DisplayEventReceiver mReceiver;
+ bool mWaitingForVsync;
+
+ virtual void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) = 0;
+ virtual void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId,
+ bool connected) = 0;
+ virtual void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId,
+ int32_t configId, nsecs_t vsyncPeriod) = 0;
+
+ bool processPendingEvents(nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId,
+ uint32_t* outCount);
+};
+} // namespace android
diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h
index a558cf9..8d49184 100644
--- a/libs/gui/include/gui/DisplayEventReceiver.h
+++ b/libs/gui/include/gui/DisplayEventReceiver.h
@@ -65,6 +65,7 @@
struct VSync {
uint32_t count;
+ nsecs_t expectedVSyncTimestamp;
};
struct Hotplug {
@@ -73,6 +74,7 @@
struct Config {
int32_t configId;
+ nsecs_t vsyncPeriod;
};
Header header;
@@ -144,6 +146,12 @@
*/
status_t requestNextVsync();
+ /*
+ * requestLatestConfig() force-requests the current config for the primary
+ * display.
+ */
+ status_t requestLatestConfig();
+
private:
sp<IDisplayEventConnection> mEventConnection;
std::unique_ptr<gui::BitTube> mDataChannel;
diff --git a/libs/gui/include/gui/FrameTimestamps.h b/libs/gui/include/gui/FrameTimestamps.h
index 4670edd..0750080 100644
--- a/libs/gui/include/gui/FrameTimestamps.h
+++ b/libs/gui/include/gui/FrameTimestamps.h
@@ -175,7 +175,6 @@
std::shared_ptr<FenceTime> acquireFence{FenceTime::NO_FENCE};
};
-
// Used by the consumer to keep track of which fields it already sent to
// the producer.
class FrameEventDirtyFields {
@@ -209,6 +208,7 @@
~ConsumerFrameEventHistory() override;
void onDisconnect();
+ void setProducerWantsEvents();
void initializeCompositorTiming(const CompositorTiming& compositorTiming);
diff --git a/libs/gui/include/gui/GLConsumer.h b/libs/gui/include/gui/GLConsumer.h
index ddd868d..2f538ff 100644
--- a/libs/gui/include/gui/GLConsumer.h
+++ b/libs/gui/include/gui/GLConsumer.h
@@ -499,7 +499,7 @@
// protects static initialization
static Mutex sStaticInitLock;
- // mReleasedTexImageBuffer is a dummy buffer used when in single buffer
+ // mReleasedTexImageBuffer is a buffer placeholder used when in single buffer
// mode and releaseTexImage() has been called
static sp<GraphicBuffer> sReleasedTexImageBuffer;
sp<EglImage> mReleasedTexImage;
diff --git a/libs/gui/include/gui/IConsumerListener.h b/libs/gui/include/gui/IConsumerListener.h
index c082882..0ab2399 100644
--- a/libs/gui/include/gui/IConsumerListener.h
+++ b/libs/gui/include/gui/IConsumerListener.h
@@ -43,6 +43,17 @@
// onDisconnect is called when a producer disconnects from the BufferQueue.
virtual void onDisconnect() {} /* Asynchronous */
+ // onFrameDequeued is called when a call to the BufferQueueProducer::dequeueBuffer successfully
+ // returns a slot from the BufferQueue.
+ virtual void onFrameDequeued(const uint64_t) {}
+
+ // onFrameCancelled is called when the client calls cancelBuffer, thereby releasing the slot
+ // back to the BufferQueue.
+ virtual void onFrameCancelled(const uint64_t) {}
+
+ // onFrameDetached is called after a successful detachBuffer() call while in asynchronous mode.
+ virtual void onFrameDetached(const uint64_t) {}
+
// onFrameAvailable is called from queueBuffer each time an additional frame becomes available
// for consumption. This means that frames that are queued while in asynchronous mode only
// trigger the callback if no previous frames are pending. Frames queued while in synchronous
@@ -81,6 +92,7 @@
FrameEventHistoryDelta* /*outDelta*/) {}
};
+#ifndef NO_BINDER
class IConsumerListener : public ConsumerListener, public IInterface {
public:
DECLARE_META_INTERFACE(ConsumerListener)
@@ -94,4 +106,11 @@
uint32_t flags = 0) override;
};
+#else
+class IConsumerListener : public ConsumerListener {
+};
+class BnConsumerListener : public IConsumerListener {
+};
+#endif
+
} // namespace android
diff --git a/libs/gui/include/gui/IDisplayEventConnection.h b/libs/gui/include/gui/IDisplayEventConnection.h
index d783f74..674aafd 100644
--- a/libs/gui/include/gui/IDisplayEventConnection.h
+++ b/libs/gui/include/gui/IDisplayEventConnection.h
@@ -18,7 +18,7 @@
#include <binder/IInterface.h>
#include <binder/SafeInterface.h>
-
+#include <gui/ISurfaceComposer.h>
#include <utils/Errors.h>
#include <cstdint>
@@ -51,6 +51,11 @@
* requestNextVsync() schedules the next vsync event. It has no effect if the vsync rate is > 0.
*/
virtual void requestNextVsync() = 0; // Asynchronous
+
+ /*
+ * requestLatestConfig() requests the config for the primary display.
+ */
+ virtual void requestLatestConfig() = 0; // Asynchronous
};
class BnDisplayEventConnection : public SafeBnInterface<IDisplayEventConnection> {
diff --git a/libs/gui/include/gui/IGraphicBufferConsumer.h b/libs/gui/include/gui/IGraphicBufferConsumer.h
index 9fb7580..0b92e7d 100644
--- a/libs/gui/include/gui/IGraphicBufferConsumer.h
+++ b/libs/gui/include/gui/IGraphicBufferConsumer.h
@@ -35,10 +35,14 @@
class GraphicBuffer;
class IConsumerListener;
class NativeHandle;
-
+#ifndef NO_BINDER
class IGraphicBufferConsumer : public IInterface {
public:
DECLARE_META_INTERFACE(GraphicBufferConsumer)
+#else
+class IGraphicBufferConsumer : public RefBase {
+public:
+#endif
enum {
// Returned by releaseBuffer, after which the consumer must free any references to the
@@ -282,6 +286,7 @@
}
};
+#ifndef NO_BINDER
class BnGraphicBufferConsumer : public SafeBnInterface<IGraphicBufferConsumer> {
public:
BnGraphicBufferConsumer()
@@ -290,5 +295,9 @@
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 3dde8c8..45e0a13 100644
--- a/libs/gui/include/gui/IGraphicBufferProducer.h
+++ b/libs/gui/include/gui/IGraphicBufferProducer.h
@@ -45,6 +45,13 @@
class NativeHandle;
class Surface;
+using HGraphicBufferProducerV1_0 =
+ ::android::hardware::graphics::bufferqueue::V1_0::
+ IGraphicBufferProducer;
+using HGraphicBufferProducerV2_0 =
+ ::android::hardware::graphics::bufferqueue::V2_0::
+ IGraphicBufferProducer;
+
/*
* This class defines the Binder IPC interface for the producer side of
* a queue of graphics buffers. It's used to send graphics data from one
@@ -59,20 +66,15 @@
*
* This class was previously called ISurfaceTexture.
*/
-class IGraphicBufferProducer : public IInterface
-{
-public:
- using HGraphicBufferProducerV1_0 =
- ::android::hardware::graphics::bufferqueue::V1_0::
- IGraphicBufferProducer;
- using HGraphicBufferProducerV2_0 =
- ::android::hardware::graphics::bufferqueue::V2_0::
- IGraphicBufferProducer;
-
+#ifndef NO_BINDER
+class IGraphicBufferProducer : public IInterface {
DECLARE_HYBRID_META_INTERFACE(GraphicBufferProducer,
HGraphicBufferProducerV1_0,
HGraphicBufferProducerV2_0)
-
+#else
+class IGraphicBufferProducer : public RefBase {
+#endif
+public:
enum {
// A flag returned by dequeueBuffer when the client needs to call
// requestBuffer immediately thereafter.
@@ -286,36 +288,6 @@
virtual status_t attachBuffer(int* outSlot,
const sp<GraphicBuffer>& buffer) = 0;
- // queueBuffer indicates that the client has finished filling in the
- // contents of the buffer associated with slot and transfers ownership of
- // that slot back to the server.
- //
- // It is not valid to call queueBuffer on a slot that is not owned
- // by the client or one for which a buffer associated via requestBuffer
- // (an attempt to do so will fail with a return value of BAD_VALUE).
- //
- // In addition, the input must be described by the client (as documented
- // 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).
- //
- // Upon success, the output will be filled with meaningful values
- // (refer to the documentation below).
- //
- // 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
- // connected.
- // * BAD_VALUE - one of the below conditions occurred:
- // * fence was NULL
- // * scaling mode was unknown
- // * both in async mode and buffer count was less than the
- // max numbers of buffers that can be allocated at once
- // * slot index was out of range (see above).
- // * the slot was not in the dequeued state
- // * the slot was enqueued without requesting a buffer
- // * crop rect is out of bounds of the buffer dimensions
-
struct QueueBufferInput : public Flattenable<QueueBufferInput> {
friend class Flattenable<QueueBufferInput>;
explicit inline QueueBufferInput(const Parcel& parcel);
@@ -412,8 +384,38 @@
uint64_t nextFrameNumber{0};
FrameEventHistoryDelta frameTimestamps;
bool bufferReplaced{false};
+ int maxBufferCount{0};
};
+ // queueBuffer indicates that the client has finished filling in the
+ // contents of the buffer associated with slot and transfers ownership of
+ // that slot back to the server.
+ //
+ // It is not valid to call queueBuffer on a slot that is not owned
+ // by the client or one for which a buffer associated via requestBuffer
+ // (an attempt to do so will fail with a return value of BAD_VALUE).
+ //
+ // In addition, the input must be described by the client (as documented
+ // 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).
+ //
+ // Upon success, the output will be filled with meaningful values
+ // (refer to the documentation below).
+ //
+ // 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
+ // connected.
+ // * BAD_VALUE - one of the below conditions occurred:
+ // * fence was NULL
+ // * scaling mode was unknown
+ // * both in async mode and buffer count was less than the
+ // max numbers of buffers that can be allocated at once
+ // * slot index was out of range (see above).
+ // * the slot was not in the dequeued state
+ // * the slot was enqueued without requesting a buffer
+ // * crop rect is out of bounds of the buffer dimensions
virtual status_t queueBuffer(int slot, const QueueBufferInput& input,
QueueBufferOutput* output) = 0;
@@ -456,7 +458,7 @@
// the producer wants to be notified when the consumer releases a buffer
// back to the BufferQueue. It is also used to detect the death of the
// producer. If only the latter functionality is desired, there is a
- // DummyProducerListener class in IProducerListener.h that can be used.
+ // StubProducerListener class in IProducerListener.h that can be used.
//
// The api should be one of the NATIVE_WINDOW_API_* values in <window.h>
//
@@ -629,6 +631,15 @@
// NATIVE_WINDOW_CONSUMER_USAGE_BITS attribute.
virtual status_t getConsumerUsage(uint64_t* outUsage) const = 0;
+ // Enable/disable the auto prerotation at buffer allocation when the buffer
+ // size is driven by the consumer.
+ //
+ // When buffer size is driven by the consumer and the transform hint
+ // specifies a 90 or 270 degree rotation, if auto prerotation is enabled,
+ // the width and height used for dequeueBuffer will be additionally swapped.
+ virtual status_t setAutoPrerotation(bool autoPrerotation);
+
+#ifndef NO_BINDER
// Static method exports any IGraphicBufferProducer object to a parcel. It
// handles null producer as well.
static status_t exportToParcel(const sp<IGraphicBufferProducer>& producer,
@@ -646,10 +657,11 @@
// it writes a strong binder object; for BufferHub, it writes a
// ProducerQueueParcelable object.
virtual status_t exportToParcel(Parcel* parcel);
+#endif
};
// ----------------------------------------------------------------------------
-
+#ifndef NO_BINDER
class BnGraphicBufferProducer : public BnInterface<IGraphicBufferProducer>
{
public:
@@ -658,6 +670,10 @@
Parcel* reply,
uint32_t flags = 0);
};
+#else
+class BnGraphicBufferProducer : public IGraphicBufferProducer {
+};
+#endif
// ----------------------------------------------------------------------------
}; // namespace android
diff --git a/libs/gui/include/gui/IProducerListener.h b/libs/gui/include/gui/IProducerListener.h
index 32a3690..f7ffbb9 100644
--- a/libs/gui/include/gui/IProducerListener.h
+++ b/libs/gui/include/gui/IProducerListener.h
@@ -51,6 +51,7 @@
virtual void onBuffersDiscarded(const std::vector<int32_t>& slots) = 0; // Asynchronous
};
+#ifndef NO_BINDER
class IProducerListener : public ProducerListener, public IInterface
{
public:
@@ -73,10 +74,15 @@
virtual void onBuffersDiscarded(const std::vector<int32_t>& slots);
};
-class DummyProducerListener : public BnProducerListener
-{
+#else
+class IProducerListener : public ProducerListener {
+};
+class BnProducerListener : public IProducerListener {
+};
+#endif
+class StubProducerListener : public BnProducerListener {
public:
- virtual ~DummyProducerListener();
+ virtual ~StubProducerListener();
virtual void onBufferReleased() {}
virtual bool needsReleaseNotify() { return false; }
};
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index c84910b..8d3160a 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_GUI_ISURFACE_COMPOSER_H
-#define ANDROID_GUI_ISURFACE_COMPOSER_H
+#pragma once
#include <stdint.h>
#include <sys/types.h>
@@ -25,12 +24,16 @@
#include <gui/ITransactionCompletedListener.h>
+#include <math/vec4.h>
+
#include <ui/ConfigStoreTypes.h>
#include <ui/DisplayedFrameStats.h>
#include <ui/FrameStats.h>
#include <ui/GraphicBuffer.h>
#include <ui/GraphicTypes.h>
+#include <ui/PhysicalDisplayId.h>
#include <ui/PixelFormat.h>
+#include <ui/Rotation.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
@@ -42,13 +45,13 @@
#include <vector>
namespace android {
-// ----------------------------------------------------------------------------
struct client_cache_t;
struct ComposerState;
-struct DisplayState;
+struct DisplayConfig;
struct DisplayInfo;
struct DisplayStatInfo;
+struct DisplayState;
struct InputWindowCommands;
class LayerDebugInfo;
class HdrCapabilities;
@@ -59,6 +62,12 @@
class Rect;
enum class FrameEvent;
+namespace ui {
+
+struct DisplayState;
+
+} // namespace ui
+
/*
* This class defines the Binder IPC interface for accessing various
* SurfaceFlinger features.
@@ -67,22 +76,25 @@
public:
DECLARE_META_INTERFACE(SurfaceComposer)
+ static constexpr size_t MAX_LAYERS = 4096;
+
// flags for setTransactionState()
enum {
eSynchronous = 0x01,
- eAnimation = 0x02,
+ eAnimation = 0x02,
- // Indicates that this transaction will likely result in a lot of layers being composed, and
- // thus, SurfaceFlinger should wake-up earlier to avoid missing frame deadlines. In this
- // case SurfaceFlinger will wake up at (sf vsync offset - debug.sf.early_phase_offset_ns)
- eEarlyWakeup = 0x04
- };
+ // DEPRECATED - use eExplicitEarlyWakeup[Start|End]
+ eEarlyWakeup = 0x04,
- enum Rotation {
- eRotateNone = 0,
- eRotate90 = 1,
- eRotate180 = 2,
- eRotate270 = 3
+ // Explicit indication that this transaction and others to follow will likely result in a
+ // lot of layers being composed, and thus, SurfaceFlinger should wake-up earlier to avoid
+ // missing frame deadlines. In this case SurfaceFlinger will wake up at
+ // (sf vsync offset - debug.sf.early_phase_offset_ns). SurfaceFlinger will continue to be
+ // in the early configuration until it receives eExplicitEarlyWakeupEnd. These flags are
+ // expected to be used by WindowManager only and are guarded by
+ // android.permission.ACCESS_SURFACE_FLINGER
+ eExplicitEarlyWakeupStart = 0x08,
+ eExplicitEarlyWakeupEnd = 0x10,
};
enum VsyncSource {
@@ -140,7 +152,7 @@
const sp<IBinder>& applyToken,
const InputWindowCommands& inputWindowCommands,
int64_t desiredPresentTime,
- const client_cache_t& uncacheBuffer,
+ const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
const std::vector<ListenerCallbacks>& listenerCallbacks) = 0;
/* signal that we're done booting.
@@ -164,10 +176,6 @@
*/
virtual void setPowerMode(const sp<IBinder>& display, int mode) = 0;
- /* returns information for each configuration of the given display
- * intended to be used to get information about built-in displays */
- virtual status_t getDisplayConfigs(const sp<IBinder>& display,
- Vector<DisplayInfo>* configs) = 0;
/* returns display statistics for a given display
* intended to be used by the media framework to properly schedule
@@ -175,13 +183,26 @@
virtual status_t getDisplayStats(const sp<IBinder>& display,
DisplayStatInfo* stats) = 0;
- /* indicates which of the configurations returned by getDisplayInfo is
- * currently active */
- virtual int getActiveConfig(const sp<IBinder>& display) = 0;
+ /**
+ * Get transactional state of given display.
+ */
+ virtual status_t getDisplayState(const sp<IBinder>& display, ui::DisplayState*) = 0;
- /* specifies which configuration (of those returned by getDisplayInfo)
- * should be used */
- virtual status_t setActiveConfig(const sp<IBinder>& display, int id) = 0;
+ /**
+ * Get immutable information about given physical display.
+ */
+ virtual status_t getDisplayInfo(const sp<IBinder>& display, DisplayInfo*) = 0;
+
+ /**
+ * Get configurations supported by given physical display.
+ */
+ virtual status_t getDisplayConfigs(const sp<IBinder>& display, Vector<DisplayConfig>*) = 0;
+
+ /**
+ * Get the index into configurations returned by getDisplayConfigs,
+ * corresponding to the active configuration.
+ */
+ virtual int getActiveConfig(const sp<IBinder>& display) = 0;
virtual status_t getDisplayColorModes(const sp<IBinder>& display,
Vector<ui::ColorMode>* outColorModes) = 0;
@@ -192,6 +213,37 @@
ui::ColorMode colorMode) = 0;
/**
+ * Returns true if the connected display reports support for HDMI 2.1 Auto
+ * Low Latency Mode.
+ * For more information, see the HDMI 2.1 specification.
+ */
+ virtual status_t getAutoLowLatencyModeSupport(const sp<IBinder>& display,
+ bool* outSupport) const = 0;
+
+ /**
+ * Switches Auto Low Latency Mode on/off on the connected display, if it is
+ * available. This should only be called if #getAutoLowLatencyMode returns
+ * true.
+ * For more information, see the HDMI 2.1 specification.
+ */
+ virtual void setAutoLowLatencyMode(const sp<IBinder>& display, bool on) = 0;
+
+ /**
+ * Returns true if the connected display reports support for Game Content Type.
+ * For more information, see the HDMI 1.4 specification.
+ */
+ virtual status_t getGameContentTypeSupport(const sp<IBinder>& display,
+ bool* outSupport) const = 0;
+
+ /**
+ * This will start sending infoframes to the connected display with
+ * ContentType=Game (if on=true). This will switch the disply to Game mode.
+ * This should only be called if #getGameContentTypeSupport returns true.
+ * For more information, see the HDMI 1.4 specification.
+ */
+ virtual void setGameContentType(const sp<IBinder>& display, bool on) = 0;
+
+ /**
* Capture the specified screen. This requires READ_FRAME_BUFFER
* permission. This function will fail if there is a secure window on
* screen.
@@ -215,10 +267,10 @@
* it) around its center.
*/
virtual status_t captureScreen(const sp<IBinder>& display, sp<GraphicBuffer>* outBuffer,
- bool& outCapturedSecureLayers, const ui::Dataspace reqDataspace,
- const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
+ bool& outCapturedSecureLayers, ui::Dataspace reqDataspace,
+ ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform,
- Rotation rotation = eRotateNone,
+ ui::Rotation rotation = ui::ROTATION_0,
bool captureSecureLayers = false) = 0;
/**
* Capture the specified screen. This requires READ_FRAME_BUFFER
@@ -242,8 +294,9 @@
* it) around its center.
*/
virtual status_t captureScreen(const sp<IBinder>& display, sp<GraphicBuffer>* outBuffer,
- Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
- bool useIdentityTransform, Rotation rotation = eRotateNone) {
+ const Rect& sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
+ bool useIdentityTransform,
+ ui::Rotation rotation = ui::ROTATION_0) {
bool outIgnored;
return captureScreen(display, outBuffer, outIgnored, ui::Dataspace::V0_SRGB,
ui::PixelFormat::RGBA_8888, sourceCrop, reqWidth, reqHeight,
@@ -267,8 +320,7 @@
*/
virtual status_t captureLayers(
const sp<IBinder>& layerHandleBinder, sp<GraphicBuffer>* outBuffer,
- const ui::Dataspace reqDataspace, const ui::PixelFormat reqPixelFormat,
- const Rect& sourceCrop,
+ ui::Dataspace reqDataspace, ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
const std::unordered_set<sp<IBinder>, SpHash<IBinder>>& excludeHandles,
float frameScale = 1.0, bool childrenOnly = false) = 0;
@@ -310,7 +362,7 @@
*
* Requires the ACCESS_SURFACE_FLINGER permission.
*/
- virtual status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const = 0;
+ virtual status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) = 0;
virtual status_t getColorManagement(bool* outGetColorManagement) const = 0;
@@ -339,7 +391,7 @@
*/
virtual status_t setDisplayContentSamplingEnabled(const sp<IBinder>& display, bool enable,
uint8_t componentMask,
- uint64_t maxFrames) const = 0;
+ uint64_t maxFrames) = 0;
/* Returns statistics on the color profile of the last frame displayed for a given display
*
@@ -382,21 +434,36 @@
*/
virtual status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) = 0;
- /*
- * Sets the allowed display configurations to be used.
- * The allowedConfigs in a vector of indexes corresponding to the configurations
+ /* Sets the refresh rate boundaries for the display.
+ *
+ * The primary refresh rate range represents display manager's general guidance on the display
+ * configs we'll consider when switching refresh rates. Unless we get an explicit signal from an
+ * app, we should stay within this range.
+ *
+ * The app request refresh rate range allows us to consider more display configs when switching
+ * refresh rates. Although we should generally stay within the primary range, specific
+ * considerations, such as layer frame rate settings specified via the setFrameRate() api, may
+ * cause us to go outside the primary range. We never go outside the app request range. The app
+ * request range will be greater than or equal to the primary refresh rate range, never smaller.
+ *
+ * defaultConfig is used to narrow the list of display configs SurfaceFlinger will consider
+ * switching between. Only configs with a config group and resolution matching defaultConfig
+ * will be considered for switching. The defaultConfig index corresponds to the list of configs
* returned from getDisplayConfigs().
*/
- virtual status_t setAllowedDisplayConfigs(const sp<IBinder>& displayToken,
- const std::vector<int32_t>& allowedConfigs) = 0;
+ virtual status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
+ int32_t defaultConfig,
+ float primaryRefreshRateMin,
+ float primaryRefreshRateMax,
+ float appRequestRefreshRateMin,
+ float appRequestRefreshRateMax) = 0;
- /*
- * Returns the allowed display configurations currently set.
- * The allowedConfigs in a vector of indexes corresponding to the configurations
- * returned from getDisplayConfigs().
- */
- virtual status_t getAllowedDisplayConfigs(const sp<IBinder>& displayToken,
- std::vector<int32_t>* outAllowedConfigs) = 0;
+ virtual status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
+ int32_t* outDefaultConfig,
+ float* outPrimaryRefreshRateMin,
+ float* outPrimaryRefreshRateMax,
+ float* outAppRequestRefreshRateMin,
+ float* outAppRequestRefreshRateMax) = 0;
/*
* Gets whether brightness operations are supported on a display.
*
@@ -426,8 +493,7 @@
* BAD_VALUE if the brightness is invalid, or
* INVALID_OPERATION if brightness operations are not supported.
*/
- virtual status_t setDisplayBrightness(const sp<IBinder>& displayToken,
- float brightness) const = 0;
+ virtual status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness) = 0;
/*
* Sends a power hint to the composer. This function is asynchronous.
@@ -438,6 +504,42 @@
* Returns NO_ERROR upon success.
*/
virtual status_t notifyPowerHint(int32_t hintId) = 0;
+
+ /*
+ * Sets the global configuration for all the shadows drawn by SurfaceFlinger. Shadow follows
+ * material design guidelines.
+ *
+ * ambientColor
+ * Color to the ambient shadow. The alpha is premultiplied.
+ *
+ * spotColor
+ * Color to the spot shadow. The alpha is premultiplied. The position of the spot shadow
+ * depends on the light position.
+ *
+ * lightPosY/lightPosZ
+ * Position of the light used to cast the spot shadow. The X value is always the display
+ * width / 2.
+ *
+ * lightRadius
+ * Radius of the light casting the shadow.
+ */
+ virtual status_t setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor,
+ float lightPosY, float lightPosZ,
+ float lightRadius) = 0;
+
+ /*
+ * Sets the intended frame rate for a surface. See ANativeWindow_setFrameRate() for more info.
+ */
+ virtual status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
+ int8_t compatibility) = 0;
+
+ /*
+ * Acquire a frame rate flexibility token from SurfaceFlinger. While this token is acquired,
+ * surface flinger will freely switch between frame rates in any way it sees fit, regardless of
+ * the current restrictions applied by DisplayManager. This is useful to get consistent behavior
+ * for tests. Release the token by releasing the returned IBinder reference.
+ */
+ virtual status_t acquireFrameRateFlexibilityToken(sp<IBinder>* outToken) = 0;
};
// ----------------------------------------------------------------------------
@@ -449,7 +551,7 @@
// Java by ActivityManagerService.
BOOT_FINISHED = IBinder::FIRST_CALL_TRANSACTION,
CREATE_CONNECTION,
- CREATE_GRAPHIC_BUFFER_ALLOC_UNUSED, // unused, fails permissions check
+ GET_DISPLAY_INFO,
CREATE_DISPLAY_EVENT_CONNECTION,
CREATE_DISPLAY,
DESTROY_DISPLAY,
@@ -459,8 +561,7 @@
GET_SUPPORTED_FRAME_TIMESTAMPS,
GET_DISPLAY_CONFIGS,
GET_ACTIVE_CONFIG,
- SET_ACTIVE_CONFIG,
- CONNECT_DISPLAY_UNUSED, // unused, fails permissions check
+ GET_DISPLAY_STATE,
CAPTURE_SCREEN,
CAPTURE_LAYERS,
CLEAR_ANIMATION_FRAME_STATS,
@@ -485,12 +586,19 @@
GET_PHYSICAL_DISPLAY_IDS,
ADD_REGION_SAMPLING_LISTENER,
REMOVE_REGION_SAMPLING_LISTENER,
- SET_ALLOWED_DISPLAY_CONFIGS,
- GET_ALLOWED_DISPLAY_CONFIGS,
+ SET_DESIRED_DISPLAY_CONFIG_SPECS,
+ GET_DESIRED_DISPLAY_CONFIG_SPECS,
GET_DISPLAY_BRIGHTNESS_SUPPORT,
SET_DISPLAY_BRIGHTNESS,
CAPTURE_SCREEN_BY_ID,
NOTIFY_POWER_HINT,
+ SET_GLOBAL_SHADOW_SETTINGS,
+ GET_AUTO_LOW_LATENCY_MODE_SUPPORT,
+ SET_AUTO_LOW_LATENCY_MODE,
+ GET_GAME_CONTENT_TYPE_SUPPORT,
+ SET_GAME_CONTENT_TYPE,
+ SET_FRAME_RATE,
+ ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN,
// Always append new enum to the end.
};
@@ -498,8 +606,4 @@
Parcel* reply, uint32_t flags = 0);
};
-// ----------------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_GUI_ISURFACE_COMPOSER_H
+} // namespace android
diff --git a/libs/gui/include/gui/ISurfaceComposerClient.h b/libs/gui/include/gui/ISurfaceComposerClient.h
index 32ac9e8..3afbabf 100644
--- a/libs/gui/include/gui/ISurfaceComposerClient.h
+++ b/libs/gui/include/gui/ISurfaceComposerClient.h
@@ -33,7 +33,7 @@
DECLARE_META_INTERFACE(SurfaceComposerClient)
// flags for createSurface()
- enum { // (keep in sync with Surface.java)
+ enum { // (keep in sync with SurfaceControl.java)
eHidden = 0x00000004,
eDestroyBackbuffer = 0x00000020,
eSecure = 0x00000080,
@@ -42,9 +42,10 @@
eProtectedByApp = 0x00000800,
eProtectedByDRM = 0x00001000,
eCursorWindow = 0x00002000,
+ eNoColorFill = 0x00004000,
eFXSurfaceBufferQueue = 0x00000000,
- eFXSurfaceColor = 0x00020000,
+ eFXSurfaceEffect = 0x00020000,
eFXSurfaceBufferState = 0x00040000,
eFXSurfaceContainer = 0x00080000,
eFXSurfaceMask = 0x000F0000,
@@ -56,7 +57,7 @@
virtual status_t createSurface(const String8& name, uint32_t w, uint32_t h, PixelFormat format,
uint32_t flags, const sp<IBinder>& parent,
LayerMetadata metadata, sp<IBinder>* handle,
- sp<IGraphicBufferProducer>* gbp) = 0;
+ sp<IGraphicBufferProducer>* gbp, uint32_t* outTransformHint) = 0;
/*
* Requires ACCESS_SURFACE_FLINGER permission
@@ -65,7 +66,8 @@
PixelFormat format, uint32_t flags,
const sp<IGraphicBufferProducer>& parent,
LayerMetadata metadata, sp<IBinder>* handle,
- sp<IGraphicBufferProducer>* gbp) = 0;
+ sp<IGraphicBufferProducer>* gbp,
+ uint32_t* outTransformHint) = 0;
/*
* Requires ACCESS_SURFACE_FLINGER permission
@@ -76,6 +78,8 @@
* Requires ACCESS_SURFACE_FLINGER permission
*/
virtual status_t getLayerFrameStats(const sp<IBinder>& handle, FrameStats* outStats) const = 0;
+
+ virtual status_t mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle) = 0;
};
class BnSurfaceComposerClient : public SafeBnInterface<ISurfaceComposerClient> {
diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ITransactionCompletedListener.h
index 774ad46..c58634b 100644
--- a/libs/gui/include/gui/ITransactionCompletedListener.h
+++ b/libs/gui/include/gui/ITransactionCompletedListener.h
@@ -21,6 +21,7 @@
#include <binder/Parcelable.h>
#include <binder/SafeInterface.h>
+#include <gui/FrameTimestamps.h>
#include <ui/Fence.h>
#include <utils/Timers.h>
@@ -31,21 +32,50 @@
namespace android {
class ITransactionCompletedListener;
+class ListenerCallbacks;
using CallbackId = int64_t;
+class FrameEventHistoryStats : public Parcelable {
+public:
+ status_t writeToParcel(Parcel* output) const override;
+ status_t readFromParcel(const Parcel* input) override;
+
+ FrameEventHistoryStats() = default;
+ FrameEventHistoryStats(uint64_t fn, const sp<Fence>& gpuCompFence, CompositorTiming compTiming,
+ nsecs_t refreshTime, nsecs_t dequeueReadyTime)
+ : frameNumber(fn),
+ gpuCompositionDoneFence(gpuCompFence),
+ compositorTiming(compTiming),
+ refreshStartTime(refreshTime),
+ dequeueReadyTime(dequeueReadyTime) {}
+
+ uint64_t frameNumber;
+ sp<Fence> gpuCompositionDoneFence;
+ CompositorTiming compositorTiming;
+ nsecs_t refreshStartTime;
+ nsecs_t dequeueReadyTime;
+};
+
class SurfaceStats : public Parcelable {
public:
status_t writeToParcel(Parcel* output) const override;
status_t readFromParcel(const Parcel* input) override;
SurfaceStats() = default;
- SurfaceStats(const sp<IBinder>& sc, nsecs_t time, const sp<Fence>& prevReleaseFence)
- : surfaceControl(sc), acquireTime(time), previousReleaseFence(prevReleaseFence) {}
+ SurfaceStats(const sp<IBinder>& sc, nsecs_t time, const sp<Fence>& prevReleaseFence,
+ uint32_t hint, FrameEventHistoryStats frameEventStats)
+ : surfaceControl(sc),
+ acquireTime(time),
+ previousReleaseFence(prevReleaseFence),
+ transformHint(hint),
+ eventStats(frameEventStats) {}
sp<IBinder> surfaceControl;
nsecs_t acquireTime = -1;
sp<Fence> previousReleaseFence;
+ uint32_t transformHint = 0;
+ FrameEventHistoryStats eventStats;
};
class TransactionStats : public Parcelable {
@@ -72,10 +102,10 @@
status_t writeToParcel(Parcel* output) const override;
status_t readFromParcel(const Parcel* input) override;
- static ListenerStats createEmpty(const sp<ITransactionCompletedListener>& listener,
+ static ListenerStats createEmpty(const sp<IBinder>& listener,
const std::unordered_set<CallbackId>& callbackIds);
- sp<ITransactionCompletedListener> listener;
+ sp<IBinder> listener;
std::vector<TransactionStats> transactionStats;
};
@@ -97,17 +127,59 @@
class ListenerCallbacks {
public:
- ListenerCallbacks(const sp<ITransactionCompletedListener>& listener,
- const std::unordered_set<CallbackId>& callbacks)
+ ListenerCallbacks(const sp<IBinder>& listener, const std::unordered_set<CallbackId>& callbacks)
: transactionCompletedListener(listener),
callbackIds(callbacks.begin(), callbacks.end()) {}
- ListenerCallbacks(const sp<ITransactionCompletedListener>& listener,
- const std::vector<CallbackId>& ids)
+ ListenerCallbacks(const sp<IBinder>& listener, const std::vector<CallbackId>& ids)
: transactionCompletedListener(listener), callbackIds(ids) {}
- sp<ITransactionCompletedListener> transactionCompletedListener;
+ bool operator==(const ListenerCallbacks& rhs) const {
+ if (transactionCompletedListener != rhs.transactionCompletedListener) {
+ return false;
+ }
+ if (callbackIds.empty()) {
+ return rhs.callbackIds.empty();
+ }
+ return callbackIds.front() == rhs.callbackIds.front();
+ }
+
+ sp<IBinder> transactionCompletedListener;
std::vector<CallbackId> callbackIds;
};
+struct IListenerHash {
+ std::size_t operator()(const sp<IBinder>& strongPointer) const {
+ return std::hash<IBinder*>{}(strongPointer.get());
+ }
+};
+
+struct CallbackIdsHash {
+ // CallbackId vectors have several properties that let us get away with this simple hash.
+ // 1) CallbackIds are never 0 so if something has gone wrong and our CallbackId vector is
+ // empty we can still hash 0.
+ // 2) CallbackId vectors for the same listener either are identical or contain none of the
+ // same members. It is sufficient to just check the first CallbackId in the vectors. If
+ // they match, they are the same. If they do not match, they are not the same.
+ std::size_t operator()(const std::vector<CallbackId>& callbackIds) const {
+ return std::hash<CallbackId>{}((callbackIds.empty()) ? 0 : callbackIds.front());
+ }
+};
+
+struct ListenerCallbacksHash {
+ std::size_t HashCombine(size_t value1, size_t value2) const {
+ return value1 ^ (value2 + 0x9e3779b9 + (value1 << 6) + (value1 >> 2));
+ }
+
+ std::size_t operator()(const ListenerCallbacks& listenerCallbacks) const {
+ struct IListenerHash listenerHasher;
+ struct CallbackIdsHash callbackIdsHasher;
+
+ std::size_t listenerHash = listenerHasher(listenerCallbacks.transactionCompletedListener);
+ std::size_t callbackIdsHash = callbackIdsHasher(listenerCallbacks.callbackIds);
+
+ return HashCombine(listenerHash, callbackIdsHash);
+ }
+};
+
} // namespace android
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index f438eb3..e60f677 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -20,9 +20,9 @@
#include <stdint.h>
#include <sys/types.h>
-#include <utils/Errors.h>
-
+#include <android/native_window.h>
#include <gui/IGraphicBufferProducer.h>
+#include <gui/ITransactionCompletedListener.h>
#include <math/mat4.h>
#ifndef NO_INPUT
@@ -34,6 +34,9 @@
#include <ui/GraphicTypes.h>
#include <ui/Rect.h>
#include <ui/Region.h>
+#include <ui/Rotation.h>
+#include <ui/Transform.h>
+#include <utils/Errors.h>
namespace android {
@@ -71,7 +74,7 @@
eCropChanged_legacy = 0x00000100,
eDeferTransaction_legacy = 0x00000200,
eOverrideScalingModeChanged = 0x00000400,
- eGeometryAppliesWithResize = 0x00000800,
+ eShadowRadiusChanged = 0x00000800,
eReparentChildren = 0x00001000,
eDetachChildren = 0x00002000,
eRelativeLayerChanged = 0x00004000,
@@ -97,6 +100,11 @@
eBackgroundColorChanged = 0x4'00000000,
eMetadataChanged = 0x8'00000000,
eColorSpaceAgnosticChanged = 0x10'00000000,
+ eFrameRateSelectionPriority = 0x20'00000000,
+ eFrameRateChanged = 0x40'00000000,
+ eBackgroundBlurRadiusChanged = 0x80'00000000,
+ eProducerDisconnect = 0x100'00000000,
+ eFixedTransformHintChanged = 0x200'00000000,
};
layer_state_t()
@@ -113,6 +121,7 @@
reserved(0),
crop_legacy(Rect::INVALID_RECT),
cornerRadius(0.0f),
+ backgroundBlurRadius(0),
frameNumber_legacy(0),
overrideScalingMode(-1),
transform(0),
@@ -123,10 +132,14 @@
surfaceDamageRegion(),
api(-1),
colorTransform(mat4()),
- hasListenerCallbacks(false),
bgColorAlpha(0),
bgColorDataspace(ui::Dataspace::UNKNOWN),
- colorSpaceAgnostic(false) {
+ colorSpaceAgnostic(false),
+ shadowRadius(0.0f),
+ frameRateSelectionPriority(-1),
+ frameRate(0.0f),
+ frameRateCompatibility(ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT),
+ fixedTransformHint(ui::Transform::ROT_INVALID) {
matrix.dsdx = matrix.dtdy = 1.0f;
matrix.dsdy = matrix.dtdx = 0.0f;
hdrMetadata.validTypes = 0;
@@ -157,6 +170,7 @@
matrix22_t matrix;
Rect crop_legacy;
float cornerRadius;
+ uint32_t backgroundBlurRadius;
sp<IBinder> barrierHandle_legacy;
sp<IBinder> reparentHandle;
uint64_t frameNumber_legacy;
@@ -186,7 +200,6 @@
sp<NativeHandle> sidebandStream;
mat4 colorTransform;
- bool hasListenerCallbacks;
#ifndef NO_INPUT
InputWindowInfo inputInfo;
#endif
@@ -203,10 +216,30 @@
// A color space agnostic layer means the color of this layer can be
// interpreted in any color space.
bool colorSpaceAgnostic;
+
+ std::vector<ListenerCallbacks> listeners;
+
+ // Draws a shadow around the surface.
+ float shadowRadius;
+
+ // Priority of the layer assigned by Window Manager.
+ int32_t frameRateSelectionPriority;
+
+ // Layer frame rate and compatibility. See ANativeWindow_setFrameRate().
+ float frameRate;
+ int8_t frameRateCompatibility;
+
+ // Set by window manager indicating the layer and all its children are
+ // in a different orientation than the display. The hint suggests that
+ // the graphic producers should receive a transform hint as if the
+ // display was in this orientation. When the display changes to match
+ // the layer orientation, the graphic producer may not need to allocate
+ // a buffer of a different size. -1 means the transform hint is not set,
+ // otherwise the value will be a valid ui::Rotation.
+ ui::Transform::RotationFlags fixedTransformHint;
};
struct ComposerState {
- sp<ISurfaceComposerClient> client;
layer_state_t state;
status_t write(Parcel& output) const;
status_t read(const Parcel& input);
@@ -214,15 +247,6 @@
struct DisplayState {
enum {
- eOrientationDefault = 0,
- eOrientation90 = 1,
- eOrientation180 = 2,
- eOrientation270 = 3,
- eOrientationUnchanged = 4,
- eOrientationSwapMask = 0x01
- };
-
- enum {
eSurfaceChanged = 0x01,
eLayerStackChanged = 0x02,
eDisplayProjectionChanged = 0x04,
@@ -248,7 +272,7 @@
// 0, layers will be scaled by a factor of 2 and translated by (20, 10).
// When orientation is 1, layers will be additionally rotated by 90
// degrees around the origin clockwise and translated by (W, 0).
- uint32_t orientation;
+ ui::Rotation orientation = ui::ROTATION_0;
Rect viewport;
Rect frame;
@@ -259,12 +283,6 @@
};
struct InputWindowCommands {
- struct TransferTouchFocusCommand {
- sp<IBinder> fromToken;
- sp<IBinder> toToken;
- };
-
- std::vector<TransferTouchFocusCommand> transferTouchFocusCommands;
bool syncInputWindows{false};
void merge(const InputWindowCommands& other);
@@ -274,8 +292,6 @@
};
static inline int compare_type(const ComposerState& lhs, const ComposerState& rhs) {
- if (lhs.client < rhs.client) return -1;
- if (lhs.client > rhs.client) return 1;
if (lhs.state.surface < rhs.state.surface) return -1;
if (lhs.state.surface > rhs.state.surface) return 1;
return 0;
@@ -285,6 +301,12 @@
return compare_type(lhs.token, rhs.token);
}
+// Returns true if the frameRate and compatibility are valid values, false
+// othwerise. If either of the params are invalid, an error log is printed, and
+// functionName is added to the log to indicate which function call failed.
+// functionName can be null.
+bool ValidateFrameRate(float frameRate, int8_t compatibility, const char* functionName);
+
}; // namespace android
#endif // ANDROID_SF_LAYER_STATE_H
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index a5641b0..49c83da 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -21,16 +21,15 @@
#include <gui/HdrMetadata.h>
#include <gui/IGraphicBufferProducer.h>
#include <gui/IProducerListener.h>
-
+#include <system/window.h>
#include <ui/ANativeObjectBase.h>
#include <ui/GraphicTypes.h>
#include <ui/Region.h>
-
#include <utils/Condition.h>
#include <utils/Mutex.h>
#include <utils/RefBase.h>
-#include <system/window.h>
+#include <shared_mutex>
namespace android {
@@ -179,8 +178,7 @@
status_t getUniqueId(uint64_t* outId) const;
status_t getConsumerUsage(uint64_t* outUsage) const;
- // Returns the CLOCK_MONOTONIC start time of the last dequeueBuffer call
- nsecs_t getLastDequeueStartTime() const;
+ status_t setFrameRate(float frameRate, int8_t compatibility);
protected:
virtual ~Surface();
@@ -205,6 +203,14 @@
ANativeWindowBuffer* buffer, int fenceFd);
static int hook_setSwapInterval(ANativeWindow* window, int interval);
+ static int cancelBufferInternal(ANativeWindow* window, ANativeWindowBuffer* buffer,
+ int fenceFd);
+ static int dequeueBufferInternal(ANativeWindow* window, ANativeWindowBuffer** buffer,
+ int* fenceFd);
+ static int performInternal(ANativeWindow* window, int operation, va_list args);
+ static int queueBufferInternal(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd);
+ static int queryInternal(const ANativeWindow* window, int what, int* value);
+
static int hook_cancelBuffer_DEPRECATED(ANativeWindow* window,
ANativeWindowBuffer* buffer);
static int hook_dequeueBuffer_DEPRECATED(ANativeWindow* window,
@@ -246,6 +252,18 @@
int dispatchGetWideColorSupport(va_list args);
int dispatchGetHdrSupport(va_list args);
int dispatchGetConsumerUsage64(va_list args);
+ int dispatchSetAutoPrerotation(va_list args);
+ int dispatchGetLastDequeueStartTime(va_list args);
+ int dispatchSetDequeueTimeout(va_list args);
+ int dispatchGetLastDequeueDuration(va_list args);
+ int dispatchGetLastQueueDuration(va_list args);
+ int dispatchSetFrameRate(va_list args);
+ int dispatchAddCancelInterceptor(va_list args);
+ int dispatchAddDequeueInterceptor(va_list args);
+ int dispatchAddPerformInterceptor(va_list args);
+ int dispatchAddQueueInterceptor(va_list args);
+ int dispatchAddQueryInterceptor(va_list args);
+ int dispatchGetLastQueuedBuffer(va_list args);
bool transformToDisplayInverse();
protected:
@@ -281,6 +299,7 @@
virtual int setAsyncMode(bool async);
virtual int setSharedBufferMode(bool sharedBufferMode);
virtual int setAutoRefresh(bool autoRefresh);
+ virtual int setAutoPrerotation(bool autoPrerotation);
virtual int setBuffersDimensions(uint32_t width, uint32_t height);
virtual int lock(ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds);
virtual int unlockAndPost();
@@ -450,6 +469,20 @@
// member variables are accessed.
mutable Mutex mMutex;
+ // mInterceptorMutex is the mutex guarding interceptors.
+ mutable std::shared_mutex mInterceptorMutex;
+
+ ANativeWindow_cancelBufferInterceptor mCancelInterceptor = nullptr;
+ void* mCancelInterceptorData = nullptr;
+ ANativeWindow_dequeueBufferInterceptor mDequeueInterceptor = nullptr;
+ void* mDequeueInterceptorData = nullptr;
+ ANativeWindow_performInterceptor mPerformInterceptor = nullptr;
+ void* mPerformInterceptorData = nullptr;
+ ANativeWindow_queueBufferInterceptor mQueueInterceptor = nullptr;
+ void* mQueueInterceptorData = nullptr;
+ ANativeWindow_queryInterceptor mQueryInterceptor = nullptr;
+ void* mQueryInterceptorData = nullptr;
+
// must be used from the lock/unlock thread
sp<GraphicBuffer> mLockedBuffer;
sp<GraphicBuffer> mPostedBuffer;
@@ -474,6 +507,7 @@
// Caches the values that have been passed to the producer.
bool mSharedBufferMode;
bool mAutoRefresh;
+ bool mAutoPrerotation;
// If in shared buffer mode and auto refresh is enabled, store the shared
// buffer slot and return it for all calls to queue/dequeue without going
@@ -506,6 +540,7 @@
bool mReportRemovedBuffers = false;
std::vector<sp<GraphicBuffer>> mRemovedBuffers;
+ int mMaxBufferCount;
sp<IProducerListener> mListenerProxy;
status_t getAndFlushBuffersFromSlots(const std::vector<int32_t>& slots,
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 9d96485..adcb898 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_GUI_SURFACE_COMPOSER_CLIENT_H
-#define ANDROID_GUI_SURFACE_COMPOSER_CLIENT_H
+#pragma once
#include <stdint.h>
#include <sys/types.h>
@@ -35,6 +34,7 @@
#include <ui/FrameStats.h>
#include <ui/GraphicTypes.h>
#include <ui/PixelFormat.h>
+#include <ui/Rotation.h>
#include <gui/CpuConsumer.h>
#include <gui/ISurfaceComposer.h>
@@ -45,25 +45,31 @@
namespace android {
-// ---------------------------------------------------------------------------
-
-struct DisplayInfo;
class HdrCapabilities;
class ISurfaceComposerClient;
class IGraphicBufferProducer;
class IRegionSamplingListener;
class Region;
-// ---------------------------------------------------------------------------
-
struct SurfaceControlStats {
- SurfaceControlStats(const sp<SurfaceControl>& sc, nsecs_t time,
- const sp<Fence>& prevReleaseFence)
- : surfaceControl(sc), acquireTime(time), previousReleaseFence(prevReleaseFence) {}
+ SurfaceControlStats(const sp<SurfaceControl>& sc, nsecs_t latchTime, nsecs_t acquireTime,
+ const sp<Fence>& presentFence, const sp<Fence>& prevReleaseFence,
+ uint32_t hint, FrameEventHistoryStats eventStats)
+ : surfaceControl(sc),
+ latchTime(latchTime),
+ acquireTime(acquireTime),
+ presentFence(presentFence),
+ previousReleaseFence(prevReleaseFence),
+ transformHint(hint),
+ frameEventStats(eventStats) {}
sp<SurfaceControl> surfaceControl;
+ nsecs_t latchTime = -1;
nsecs_t acquireTime = -1;
+ sp<Fence> presentFence;
sp<Fence> previousReleaseFence;
+ uint32_t transformHint = 0;
+ FrameEventHistoryStats frameEventStats;
};
using TransactionCompletedCallbackTakesContext =
@@ -97,33 +103,34 @@
status_t linkToComposerDeath(const sp<IBinder::DeathRecipient>& recipient,
void* cookie = nullptr, uint32_t flags = 0);
- // Get a list of supported configurations for a given display
- static status_t getDisplayConfigs(const sp<IBinder>& display,
- Vector<DisplayInfo>* configs);
+ // Get transactional state of given display.
+ static status_t getDisplayState(const sp<IBinder>& display, ui::DisplayState*);
- // Get the DisplayInfo for the currently-active configuration
- static status_t getDisplayInfo(const sp<IBinder>& display,
- DisplayInfo* info);
+ // Get immutable information about given physical display.
+ static status_t getDisplayInfo(const sp<IBinder>& display, DisplayInfo*);
- // Get the index of the current active configuration (relative to the list
- // returned by getDisplayInfo)
+ // Get configurations supported by given physical display.
+ static status_t getDisplayConfigs(const sp<IBinder>& display, Vector<DisplayConfig>*);
+
+ // Get the ID of the active DisplayConfig, as getDisplayConfigs index.
static int getActiveConfig(const sp<IBinder>& display);
- // Set a new active configuration using an index relative to the list
- // returned by getDisplayInfo
- static status_t setActiveConfig(const sp<IBinder>& display, int id);
+ // Shorthand for getDisplayConfigs element at getActiveConfig index.
+ static status_t getActiveDisplayConfig(const sp<IBinder>& display, DisplayConfig*);
- // Sets the allowed display configurations to be used.
- // The allowedConfigs in a vector of indexes corresponding to the configurations
- // returned from getDisplayConfigs().
- static status_t setAllowedDisplayConfigs(const sp<IBinder>& displayToken,
- const std::vector<int32_t>& allowedConfigs);
-
- // Returns the allowed display configurations currently set.
- // The allowedConfigs in a vector of indexes corresponding to the configurations
- // returned from getDisplayConfigs().
- static status_t getAllowedDisplayConfigs(const sp<IBinder>& displayToken,
- std::vector<int32_t>* outAllowedConfigs);
+ // Sets the refresh rate boundaries for the display.
+ static status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
+ int32_t defaultConfig, float primaryRefreshRateMin,
+ float primaryRefreshRateMax,
+ float appRequestRefreshRateMin,
+ float appRequestRefreshRateMax);
+ // Gets the refresh rate boundaries for the display.
+ static status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
+ int32_t* outDefaultConfig,
+ float* outPrimaryRefreshRateMin,
+ float* outPrimaryRefreshRateMax,
+ float* outAppRequestRefreshRateMin,
+ float* outAppRequestRefreshRateMax);
// Gets the list of supported color modes for the given display
static status_t getDisplayColorModes(const sp<IBinder>& display,
@@ -140,6 +147,21 @@
static status_t setActiveColorMode(const sp<IBinder>& display,
ui::ColorMode colorMode);
+ // Reports whether the connected display supports Auto Low Latency Mode
+ static bool getAutoLowLatencyModeSupport(const sp<IBinder>& display);
+
+ // Switches on/off Auto Low Latency Mode on the connected display. This should only be
+ // called if the connected display supports Auto Low Latency Mode as reported by
+ // #getAutoLowLatencyModeSupport
+ static void setAutoLowLatencyMode(const sp<IBinder>& display, bool on);
+
+ // Reports whether the connected display supports Game content type
+ static bool getGameContentTypeSupport(const sp<IBinder>& display);
+
+ // Turns Game mode on/off on the connected display. This should only be called
+ // if the display supports Game content type, as reported by #getGameContentTypeSupport
+ static void setGameContentType(const sp<IBinder>& display, bool on);
+
/* Triggers screen on/off or low power mode and waits for it to complete */
static void setDisplayPowerMode(const sp<IBinder>& display, int mode);
@@ -160,13 +182,6 @@
static bool getProtectedContentSupport();
/**
- * Called from SurfaceControl d'tor to 'destroy' the surface (or rather, reparent it
- * to null), but without needing an sp<SurfaceControl> to avoid infinite ressurection.
- */
- static void doDropReferenceTransaction(const sp<IBinder>& handle,
- const sp<ISurfaceComposerClient>& client);
-
- /**
* Uncaches a buffer in ISurfaceComposer. It must be uncached via a transaction so that it is
* in order with other transactions that use buffers.
*/
@@ -211,6 +226,27 @@
*/
static status_t notifyPowerHint(int32_t hintId);
+ /*
+ * Sets the global configuration for all the shadows drawn by SurfaceFlinger. Shadow follows
+ * material design guidelines.
+ *
+ * ambientColor
+ * Color to the ambient shadow. The alpha is premultiplied.
+ *
+ * spotColor
+ * Color to the spot shadow. The alpha is premultiplied. The position of the spot shadow
+ * depends on the light position.
+ *
+ * lightPosY/lightPosZ
+ * Position of the light used to cast the spot shadow. The X value is always the display
+ * width / 2.
+ *
+ * lightRadius
+ * Radius of the light casting the shadow.
+ */
+ static status_t setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor,
+ float lightPosY, float lightPosZ, float lightRadius);
+
// ------------------------------------------------------------------------
// surface creation / destruction
@@ -223,18 +259,18 @@
PixelFormat format, // pixel-format desired
uint32_t flags = 0, // usage flags
SurfaceControl* parent = nullptr, // parent
- LayerMetadata metadata = LayerMetadata() // metadata
- );
+ LayerMetadata metadata = LayerMetadata(), // metadata
+ uint32_t* outTransformHint = nullptr);
status_t createSurfaceChecked(const String8& name, // name of the surface
uint32_t w, // width in pixel
uint32_t h, // height in pixel
PixelFormat format, // pixel-format desired
sp<SurfaceControl>* outSurface,
- uint32_t flags = 0, // usage flags
- SurfaceControl* parent = nullptr, // parent
- LayerMetadata metadata = LayerMetadata() // metadata
- );
+ uint32_t flags = 0, // usage flags
+ SurfaceControl* parent = nullptr, // parent
+ LayerMetadata metadata = LayerMetadata(), // metadata
+ uint32_t* outTransformHint = nullptr);
//! Create a surface
sp<SurfaceControl> createWithSurfaceParent(const String8& name, // name of the surface
@@ -243,8 +279,19 @@
PixelFormat format, // pixel-format desired
uint32_t flags = 0, // usage flags
Surface* parent = nullptr, // parent
- LayerMetadata metadata = LayerMetadata() // metadata
- );
+ LayerMetadata metadata = LayerMetadata(), // metadata
+ uint32_t* outTransformHint = nullptr);
+
+ // Creates a mirrored hierarchy for the mirrorFromSurface. This returns a SurfaceControl
+ // which is a parent of the root of the mirrored hierarchy.
+ //
+ // Real Hierarchy Mirror
+ // SC (value that's returned)
+ // |
+ // A A'
+ // | |
+ // B B'
+ sp<SurfaceControl> mirrorSurface(SurfaceControl* mirrorFromSurface);
//! Create a virtual display
static sp<IBinder> createDisplay(const String8& displayName, bool secure);
@@ -270,6 +317,12 @@
}
};
+ struct IBinderHash {
+ std::size_t operator()(const sp<IBinder>& iBinder) const {
+ return std::hash<IBinder*>{}(iBinder.get());
+ }
+ };
+
struct TCLHash {
std::size_t operator()(const sp<ITransactionCompletedListener>& tcl) const {
return std::hash<IBinder*>{}((tcl) ? IInterface::asBinder(tcl).get() : nullptr);
@@ -285,16 +338,19 @@
std::unordered_set<sp<SurfaceControl>, SCHash> surfaceControls;
};
- class Transaction {
- std::unordered_map<sp<SurfaceControl>, ComposerState, SCHash> mComposerStates;
+ class Transaction : public Parcelable {
+ protected:
+ std::unordered_map<sp<IBinder>, ComposerState, IBinderHash> mComposerStates;
SortedVector<DisplayState > mDisplayStates;
std::unordered_map<sp<ITransactionCompletedListener>, CallbackInfo, TCLHash>
mListenerCallbacks;
- uint32_t mForceSynchronous = 0;
- uint32_t mTransactionNestCount = 0;
- bool mAnimation = false;
- bool mEarlyWakeup = false;
+ uint32_t mForceSynchronous = 0;
+ uint32_t mTransactionNestCount = 0;
+ bool mAnimation = false;
+ bool mEarlyWakeup = false;
+ bool mExplicitEarlyWakeupStart = false;
+ bool mExplicitEarlyWakeupEnd = false;
// Indicates that the Transaction contains a buffer that should be cached
bool mContainsBuffer = false;
@@ -314,7 +370,10 @@
InputWindowCommands mInputWindowCommands;
int mStatus = NO_ERROR;
- layer_state_t* getLayerState(const sp<SurfaceControl>& sc);
+ layer_state_t* getLayerState(const sp<IBinder>& surfaceHandle);
+ layer_state_t* getLayerState(const sp<SurfaceControl>& sc) {
+ return getLayerState(sc->getHandle());
+ }
DisplayState& getDisplayState(const sp<IBinder>& token);
void cacheBuffers();
@@ -325,6 +384,15 @@
virtual ~Transaction() = default;
Transaction(Transaction const& other);
+ // 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;
+
+ // Clears the contents of the transaction without applying it.
+ void clear();
+
status_t apply(bool synchronous = false);
// Merge another transaction in to this one, clearing other
// as if it had been applied.
@@ -361,6 +429,8 @@
float dsdx, float dtdx, float dtdy, float dsdy);
Transaction& setCrop_legacy(const sp<SurfaceControl>& sc, const Rect& crop);
Transaction& setCornerRadius(const sp<SurfaceControl>& sc, float cornerRadius);
+ Transaction& setBackgroundBlurRadius(const sp<SurfaceControl>& sc,
+ int backgroundBlurRadius);
Transaction& setLayerStack(const sp<SurfaceControl>& sc, uint32_t layerStack);
Transaction& setMetadata(const sp<SurfaceControl>& sc, uint32_t key, const Parcel& p);
// Defers applying any changes made in this transaction until the Layer
@@ -409,9 +479,15 @@
Transaction& setDesiredPresentTime(nsecs_t desiredPresentTime);
Transaction& setColorSpaceAgnostic(const sp<SurfaceControl>& sc, const bool agnostic);
+ // Sets information about the priority of the frame.
+ Transaction& setFrameRateSelectionPriority(const sp<SurfaceControl>& sc, int32_t priority);
+
Transaction& addTransactionCompletedCallback(
TransactionCompletedCallbackTakesContext callback, void* callbackContext);
+ // ONLY FOR BLAST ADAPTER
+ Transaction& notifyProducerDisconnect(const sp<SurfaceControl>& sc);
+
// Detaches all child surfaces (and their children recursively)
// from their SurfaceControl.
// The child SurfaceControls will not throw exceptions or return errors,
@@ -429,15 +505,8 @@
Transaction& setOverrideScalingMode(const sp<SurfaceControl>& sc,
int32_t overrideScalingMode);
- // If the size changes in this transaction, all geometry updates specified
- // in this transaction will not complete until a buffer of the new size
- // arrives. As some elements normally apply immediately, this enables
- // freezing the total geometry of a surface until a resize is completed.
- Transaction& setGeometryAppliesWithResize(const sp<SurfaceControl>& sc);
-
#ifndef NO_INPUT
Transaction& setInputWindowInfo(const sp<SurfaceControl>& sc, const InputWindowInfo& info);
- Transaction& transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken);
Transaction& syncInputWindows();
#endif
@@ -447,6 +516,18 @@
Transaction& setGeometry(const sp<SurfaceControl>& sc,
const Rect& source, const Rect& dst, int transform);
+ Transaction& setShadowRadius(const sp<SurfaceControl>& sc, float cornerRadius);
+
+ Transaction& setFrameRate(const sp<SurfaceControl>& sc, float frameRate,
+ int8_t compatibility);
+
+ // Set by window manager indicating the layer and all its children are
+ // in a different orientation than the display. The hint suggests that
+ // the graphic producers should receive a transform hint as if the
+ // display was in this orientation. When the display changes to match
+ // the layer orientation, the graphic producer may not need to allocate
+ // a buffer of a different size.
+ Transaction& setFixedTransformHint(const sp<SurfaceControl>& sc, int32_t transformHint);
status_t setDisplaySurface(const sp<IBinder>& token,
const sp<IGraphicBufferProducer>& bufferProducer);
@@ -463,13 +544,13 @@
* mapped to. displayRect is specified post-orientation, that is
* it uses the orientation seen by the end-user.
*/
- void setDisplayProjection(const sp<IBinder>& token,
- uint32_t orientation,
- const Rect& layerStackRect,
- const Rect& displayRect);
+ void setDisplayProjection(const sp<IBinder>& token, ui::Rotation orientation,
+ const Rect& layerStackRect, const Rect& displayRect);
void setDisplaySize(const sp<IBinder>& token, uint32_t width, uint32_t height);
void setAnimationTransaction();
void setEarlyWakeup();
+ void setExplicitEarlyWakeupStart();
+ void setExplicitEarlyWakeupEnd();
};
status_t clearLayerFrameStats(const sp<IBinder>& token) const;
@@ -480,10 +561,8 @@
static status_t getHdrCapabilities(const sp<IBinder>& display,
HdrCapabilities* outCapabilities);
- static void setDisplayProjection(const sp<IBinder>& token,
- uint32_t orientation,
- const Rect& layerStackRect,
- const Rect& displayRect);
+ static void setDisplayProjection(const sp<IBinder>& token, ui::Rotation orientation,
+ const Rect& layerStackRect, const Rect& displayRect);
inline sp<ISurfaceComposerClient> getClient() { return mClient; }
@@ -515,23 +594,23 @@
public:
// if cropping isn't required, callers may pass in a default Rect, e.g.:
// capture(display, producer, Rect(), reqWidth, ...);
- static status_t capture(const sp<IBinder>& display, const ui::Dataspace reqDataSpace,
- const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
+ static status_t capture(const sp<IBinder>& display, ui::Dataspace reqDataSpace,
+ ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform,
- uint32_t rotation, bool captureSecureLayers,
+ ui::Rotation rotation, bool captureSecureLayers,
sp<GraphicBuffer>* outBuffer, bool& outCapturedSecureLayers);
- static status_t capture(const sp<IBinder>& display, const ui::Dataspace reqDataSpace,
- const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
+ static status_t capture(const sp<IBinder>& display, ui::Dataspace reqDataSpace,
+ ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform,
- uint32_t rotation, sp<GraphicBuffer>* outBuffer);
+ ui::Rotation rotation, sp<GraphicBuffer>* outBuffer);
static status_t capture(uint64_t displayOrLayerStack, ui::Dataspace* outDataspace,
sp<GraphicBuffer>* outBuffer);
- static status_t captureLayers(const sp<IBinder>& layerHandle, const ui::Dataspace reqDataSpace,
- const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
+ static status_t captureLayers(const sp<IBinder>& layerHandle, ui::Dataspace reqDataSpace,
+ ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
float frameScale, sp<GraphicBuffer>* outBuffer);
static status_t captureChildLayers(
- const sp<IBinder>& layerHandle, const ui::Dataspace reqDataSpace,
- const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
+ const sp<IBinder>& layerHandle, ui::Dataspace reqDataSpace,
+ ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
const std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>&
excludeHandles,
float frameScale, sp<GraphicBuffer>* outBuffer);
@@ -550,15 +629,10 @@
CallbackId mCallbackIdCounter GUARDED_BY(mMutex) = 1;
- struct IBinderHash {
- std::size_t operator()(const sp<IBinder>& iBinder) const {
- return std::hash<IBinder*>{}(iBinder.get());
- }
- };
-
struct CallbackTranslation {
TransactionCompletedCallback callbackFunction;
- std::unordered_map<sp<IBinder>, sp<SurfaceControl>, IBinderHash> surfaceControls;
+ std::unordered_map<sp<IBinder>, sp<SurfaceControl>, SurfaceComposerClient::IBinderHash>
+ surfaceControls;
};
std::unordered_map<CallbackId, CallbackTranslation> mCallbacks GUARDED_BY(mMutex);
@@ -581,8 +655,4 @@
void onTransactionCompleted(ListenerStats stats) override;
};
-// ---------------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_GUI_SURFACE_COMPOSER_CLIENT_H
+} // namespace android
diff --git a/libs/gui/include/gui/SurfaceControl.h b/libs/gui/include/gui/SurfaceControl.h
index 23bfc02..ac2bbcc 100644
--- a/libs/gui/include/gui/SurfaceControl.h
+++ b/libs/gui/include/gui/SurfaceControl.h
@@ -44,7 +44,7 @@
class SurfaceControl : public RefBase
{
public:
- static sp<SurfaceControl> readFromParcel(Parcel* parcel);
+ static sp<SurfaceControl> readFromParcel(const Parcel* parcel);
void writeToParcel(Parcel* parcel);
static bool isValid(const sp<SurfaceControl>& surface) {
@@ -58,10 +58,6 @@
static bool isSameSurface(
const sp<SurfaceControl>& lhs, const sp<SurfaceControl>& rhs);
- // Release the handles assosciated with the SurfaceControl, without reparenting
- // them off-screen. At the moment if this isn't executed before ~SurfaceControl
- // is called then the destructor will reparent the layer off-screen for you.
- void release();
// Reparent off-screen and release. This is invoked by the destructor.
void destroy();
@@ -81,11 +77,15 @@
status_t getLayerFrameStats(FrameStats* outStats) const;
sp<SurfaceComposerClient> getClient() const;
-
+
+ uint32_t getTransformHint() const;
+
+ void setTransformHint(uint32_t hint);
+
explicit SurfaceControl(const sp<SurfaceControl>& other);
SurfaceControl(const sp<SurfaceComposerClient>& client, const sp<IBinder>& handle,
- const sp<IGraphicBufferProducer>& gbp, bool owned);
+ const sp<IGraphicBufferProducer>& gbp, uint32_t transformHint = 0);
private:
// can't be copied
@@ -105,7 +105,7 @@
sp<IGraphicBufferProducer> mGraphicBufferProducer;
mutable Mutex mLock;
mutable sp<Surface> mSurfaceData;
- bool mOwned;
+ uint32_t mTransformHint;
};
}; // namespace android
diff --git a/libs/gui/include/gui/bufferqueue/1.0/Conversion.h b/libs/gui/include/gui/bufferqueue/1.0/Conversion.h
index 627845c..811dcbe 100644
--- a/libs/gui/include/gui/bufferqueue/1.0/Conversion.h
+++ b/libs/gui/include/gui/bufferqueue/1.0/Conversion.h
@@ -25,8 +25,6 @@
#include <hidl/MQDescriptor.h>
#include <hidl/Status.h>
-#include <binder/Binder.h>
-#include <binder/Status.h>
#include <ui/FenceTime.h>
#include <cutils/native_handle.h>
#include <gui/IGraphicBufferProducer.h>
@@ -127,15 +125,6 @@
*/
/**
- * \brief Convert `Return<void>` to `binder::Status`.
- *
- * \param[in] t The source `Return<void>`.
- * \return The corresponding `binder::Status`.
- */
-// convert: Return<void> -> ::android::binder::Status
-::android::binder::Status toBinderStatus(Return<void> const& t);
-
-/**
* \brief Convert `Return<void>` to `status_t`. This is for legacy binder calls.
*
* \param[in] t The source `Return<void>`.
diff --git a/libs/gui/include/gui/bufferqueue/1.0/H2BProducerListener.h b/libs/gui/include/gui/bufferqueue/1.0/H2BProducerListener.h
index 211fdd5..cda5103 100644
--- a/libs/gui/include/gui/bufferqueue/1.0/H2BProducerListener.h
+++ b/libs/gui/include/gui/bufferqueue/1.0/H2BProducerListener.h
@@ -34,7 +34,12 @@
using BProducerListener = ::android::IProducerListener;
class H2BProducerListener
+#ifndef NO_BINDER
: public H2BConverter<HProducerListener, BnProducerListener> {
+#else
+ : public BProducerListener {
+ sp<HProducerListener> mBase;
+#endif
public:
H2BProducerListener(sp<HProducerListener> const& base);
virtual void onBufferReleased() override;
diff --git a/libs/gui/include/gui/bufferqueue/1.0/WGraphicBufferProducer.h b/libs/gui/include/gui/bufferqueue/1.0/WGraphicBufferProducer.h
index 029dcc0..004d875 100644
--- a/libs/gui/include/gui/bufferqueue/1.0/WGraphicBufferProducer.h
+++ b/libs/gui/include/gui/bufferqueue/1.0/WGraphicBufferProducer.h
@@ -49,15 +49,16 @@
typedef ::android::IGraphicBufferProducer BGraphicBufferProducer;
typedef ::android::IProducerListener BProducerListener;
-using ::android::BnGraphicBufferProducer;
#ifndef LOG
-struct LOG_dummy {
+struct LOG_stub {
template <typename T>
- LOG_dummy& operator<< (const T&) { return *this; }
+ LOG_stub& operator<<(const T&) {
+ return *this;
+ }
};
-#define LOG(x) LOG_dummy()
+#define LOG(x) LOG_stub()
#endif
// Instantiate only if HGraphicBufferProducer is base of BASE.
diff --git a/libs/gui/include/gui/bufferqueue/1.0/WProducerListener.h b/libs/gui/include/gui/bufferqueue/1.0/WProducerListener.h
index 51dff5b..197db26 100644
--- a/libs/gui/include/gui/bufferqueue/1.0/WProducerListener.h
+++ b/libs/gui/include/gui/bufferqueue/1.0/WProducerListener.h
@@ -20,7 +20,6 @@
#include <hidl/MQDescriptor.h>
#include <hidl/Status.h>
-#include <binder/IBinder.h>
#include <gui/IProducerListener.h>
#include <android/hardware/graphics/bufferqueue/1.0/IProducerListener.h>
@@ -55,6 +54,7 @@
LWProducerListener(sp<HProducerListener> const& base);
void onBufferReleased() override;
bool needsReleaseNotify() override;
+ void onBuffersDiscarded(const std::vector<int32_t>& slots) override;
};
} // namespace android
diff --git a/libs/gui/include/gui/bufferqueue/2.0/B2HGraphicBufferProducer.h b/libs/gui/include/gui/bufferqueue/2.0/B2HGraphicBufferProducer.h
index 1c58167..16d054b 100644
--- a/libs/gui/include/gui/bufferqueue/2.0/B2HGraphicBufferProducer.h
+++ b/libs/gui/include/gui/bufferqueue/2.0/B2HGraphicBufferProducer.h
@@ -45,6 +45,7 @@
using ::android::hardware::hidl_vec;
using ::android::hardware::graphics::common::V1_2::HardwareBuffer;
+struct Obituary;
class B2HGraphicBufferProducer : public HGraphicBufferProducer {
public:
@@ -108,6 +109,7 @@
protected:
sp<BGraphicBufferProducer> mBase;
+ sp<Obituary> mObituary;
};
diff --git a/libs/gui/include/gui/bufferqueue/2.0/H2BProducerListener.h b/libs/gui/include/gui/bufferqueue/2.0/H2BProducerListener.h
index 898920b..92650b7 100644
--- a/libs/gui/include/gui/bufferqueue/2.0/H2BProducerListener.h
+++ b/libs/gui/include/gui/bufferqueue/2.0/H2BProducerListener.h
@@ -33,12 +33,20 @@
using BProducerListener = ::android::IProducerListener;
+#ifndef NO_BINDER
class H2BProducerListener
: public H2BConverter<HProducerListener, BnProducerListener> {
+#else
+class H2BProducerListener
+ : public BProducerListener {
+ sp<HProducerListener> mBase;
+
+#endif
public:
H2BProducerListener(sp<HProducerListener> const& base);
virtual void onBufferReleased() override;
virtual bool needsReleaseNotify() override;
+ virtual void onBuffersDiscarded(const std::vector<int32_t>& slots) override;
};
} // namespace utils
diff --git a/libs/gui/include/gui/mock/GraphicBufferConsumer.h b/libs/gui/include/gui/mock/GraphicBufferConsumer.h
new file mode 100644
index 0000000..98f24c2
--- /dev/null
+++ b/libs/gui/include/gui/mock/GraphicBufferConsumer.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2018 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 <gmock/gmock.h>
+
+#include <gui/IGraphicBufferConsumer.h>
+
+#include <utils/RefBase.h>
+
+namespace android {
+namespace mock {
+
+class GraphicBufferConsumer : public BnGraphicBufferConsumer, public virtual android::RefBase {
+public:
+ GraphicBufferConsumer();
+ ~GraphicBufferConsumer() override;
+
+ 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>&));
+ MOCK_METHOD5(releaseBuffer, status_t(int, uint64_t, EGLDisplay, EGLSyncKHR, const sp<Fence>&));
+ MOCK_METHOD2(consumerConnect, status_t(const sp<IConsumerListener>&, bool));
+ MOCK_METHOD0(consumerDisconnect, status_t());
+ MOCK_METHOD1(getReleasedBuffers, status_t(uint64_t*));
+ MOCK_METHOD2(setDefaultBufferSize, status_t(uint32_t, uint32_t));
+ MOCK_METHOD1(setMaxBufferCount, status_t(int));
+ MOCK_METHOD1(setMaxAcquiredBufferCount, status_t(int));
+ MOCK_METHOD1(setConsumerName, status_t(const String8&));
+ MOCK_METHOD1(setDefaultBufferFormat, status_t(PixelFormat));
+ MOCK_METHOD1(setDefaultBufferDataSpace, status_t(android_dataspace));
+ MOCK_METHOD1(setConsumerUsageBits, status_t(uint64_t));
+ MOCK_METHOD1(setConsumerIsProtected, status_t(bool));
+ MOCK_METHOD1(setTransformHint, status_t(uint32_t));
+ MOCK_CONST_METHOD1(getSidebandStream, status_t(sp<NativeHandle>*));
+ MOCK_METHOD2(getOccupancyHistory, status_t(bool, std::vector<OccupancyTracker::Segment>*));
+ MOCK_METHOD0(discardFreeBuffers, status_t());
+ MOCK_CONST_METHOD2(dumpState, status_t(const String8&, String8*));
+};
+
+} // namespace mock
+} // namespace android
diff --git a/libs/gui/include/gui/mock/GraphicBufferProducer.h b/libs/gui/include/gui/mock/GraphicBufferProducer.h
new file mode 100644
index 0000000..c98f39f
--- /dev/null
+++ b/libs/gui/include/gui/mock/GraphicBufferProducer.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2018 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 <gmock/gmock.h>
+
+#include <gui/IGraphicBufferProducer.h>
+
+#include <utils/RefBase.h>
+
+namespace android {
+namespace mock {
+
+class GraphicBufferProducer : public BnGraphicBufferProducer, public virtual android::RefBase {
+public:
+ GraphicBufferProducer();
+ ~GraphicBufferProducer() override;
+
+ MOCK_METHOD2(requestBuffer, status_t(int, sp<GraphicBuffer>*));
+ MOCK_METHOD1(setMaxDequeuedBufferCount, status_t(int));
+ MOCK_METHOD1(setAsyncMode, status_t(bool));
+ MOCK_METHOD8(dequeueBuffer,
+ status_t(int*, sp<Fence>*, uint32_t, uint32_t, PixelFormat, uint64_t, uint64_t*,
+ FrameEventHistoryDelta*));
+ MOCK_METHOD1(detachBuffer, status_t(int));
+ MOCK_METHOD2(detachNextBuffer, status_t(sp<GraphicBuffer>*, sp<Fence>*));
+ MOCK_METHOD2(attachBuffer, status_t(int*, const sp<GraphicBuffer>&));
+ MOCK_METHOD3(queueBuffer, status_t(int, const QueueBufferInput&, QueueBufferOutput*));
+ MOCK_METHOD2(cancelBuffer, status_t(int, const sp<Fence>&));
+ MOCK_METHOD2(query, int(int, int*));
+ MOCK_METHOD4(connect, status_t(const sp<IProducerListener>&, int, bool, QueueBufferOutput*));
+ MOCK_METHOD2(disconnect, status_t(int, DisconnectMode));
+ MOCK_METHOD1(setSidebandStream, status_t(const sp<NativeHandle>&));
+ MOCK_METHOD4(allocateBuffers, void(uint32_t, uint32_t, PixelFormat, uint64_t));
+ MOCK_METHOD1(allowAllocation, status_t(bool));
+ MOCK_METHOD1(setGenerationNumber, status_t(uint32_t));
+ MOCK_CONST_METHOD0(getConsumerName, String8());
+ MOCK_METHOD1(setSharedBufferMode, status_t(bool));
+ MOCK_METHOD1(setAutoRefresh, status_t(bool));
+ MOCK_METHOD1(setDequeueTimeout, status_t(nsecs_t));
+ MOCK_METHOD3(getLastQueuedBuffer, status_t(sp<GraphicBuffer>*, sp<Fence>*, float[16]));
+ MOCK_METHOD1(getFrameTimestamps, void(FrameEventHistoryDelta*));
+ MOCK_CONST_METHOD1(getUniqueId, status_t(uint64_t*));
+ MOCK_CONST_METHOD1(getConsumerUsage, status_t(uint64_t*));
+};
+
+} // namespace mock
+} // namespace android
diff --git a/libs/gui/tests/DummyConsumer.h b/libs/gui/mock/GraphicBufferConsumer.cpp
similarity index 66%
copy from libs/gui/tests/DummyConsumer.h
copy to libs/gui/mock/GraphicBufferConsumer.cpp
index 502bdf9..4a6c081 100644
--- a/libs/gui/tests/DummyConsumer.h
+++ b/libs/gui/mock/GraphicBufferConsumer.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 The Android Open Source Project
+ * Copyright (C) 2018 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.
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-#include <gui/IConsumerListener.h>
+#include <gui/mock/GraphicBufferConsumer.h>
namespace android {
+namespace mock {
-struct DummyConsumer : public BnConsumerListener {
- void onFrameAvailable(const BufferItem& /* item */) override {}
- void onBuffersReleased() override {}
- void onSidebandStreamChanged() override {}
-};
+// Explicit default instantiation is recommended.
+GraphicBufferConsumer::GraphicBufferConsumer() = default;
+GraphicBufferConsumer::~GraphicBufferConsumer() = default;
-} // namespace android
+} // namespace mock
+} // namespace android
\ No newline at end of file
diff --git a/libs/gui/tests/DummyConsumer.h b/libs/gui/mock/GraphicBufferProducer.cpp
similarity index 66%
copy from libs/gui/tests/DummyConsumer.h
copy to libs/gui/mock/GraphicBufferProducer.cpp
index 502bdf9..239a80a 100644
--- a/libs/gui/tests/DummyConsumer.h
+++ b/libs/gui/mock/GraphicBufferProducer.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 The Android Open Source Project
+ * Copyright (C) 2018 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.
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-#include <gui/IConsumerListener.h>
+#include <gui/mock/GraphicBufferProducer.h>
namespace android {
+namespace mock {
-struct DummyConsumer : public BnConsumerListener {
- void onFrameAvailable(const BufferItem& /* item */) override {}
- void onBuffersReleased() override {}
- void onSidebandStreamChanged() override {}
-};
+// Explicit default instantiation is recommended.
+GraphicBufferProducer::GraphicBufferProducer() = default;
+GraphicBufferProducer::~GraphicBufferProducer() = default;
-} // namespace android
+} // namespace mock
+} // namespace android
\ No newline at end of file
diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp
index cbda6b2..a6bcd10 100644
--- a/libs/gui/tests/Android.bp
+++ b/libs/gui/tests/Android.bp
@@ -13,7 +13,8 @@
],
srcs: [
- "BufferItemConsumer_test.cpp",
+ "BLASTBufferQueue_test.cpp",
+ "BufferItemConsumer_test.cpp",
"BufferQueue_test.cpp",
"CpuConsumer_test.cpp",
"EndToEndNativeInputTest.cpp",
@@ -38,6 +39,7 @@
shared_libs: [
"android.hardware.configstore@1.0",
"android.hardware.configstore-utils",
+ "libSurfaceFlingerProp",
"libbase",
"liblog",
"libEGL",
@@ -52,6 +54,8 @@
"libutils",
"libnativewindow"
],
+
+ header_libs: ["libsurfaceflinger_headers"],
}
// Build a separate binary to $(TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE)
diff --git a/libs/gui/tests/AndroidTest.xml b/libs/gui/tests/AndroidTest.xml
index c02e020..5e09fff 100644
--- a/libs/gui/tests/AndroidTest.xml
+++ b/libs/gui/tests/AndroidTest.xml
@@ -18,6 +18,10 @@
<option name="cleanup" value="true" />
<option name="push" value="libgui_test->/data/local/tmp/libgui_test" />
</target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+ <option name="force-skip-system-props" value="true" /> <!-- avoid restarting device -->
+ <option name="screen-always-on" value="on" />
+ </target_preparer>
<option name="test-suite-tag" value="apct" />
<test class="com.android.tradefed.testtype.GTest" >
<option name="native-test-device-path" value="/data/local/tmp" />
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
new file mode 100644
index 0000000..da5bbdd
--- /dev/null
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -0,0 +1,729 @@
+/*
+ * Copyright (C) 2019 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 "BLASTBufferQueue_test"
+
+#include <gui/BLASTBufferQueue.h>
+
+#include <android/hardware/graphics/common/1.2/types.h>
+#include <gui/BufferQueueCore.h>
+#include <gui/BufferQueueProducer.h>
+#include <gui/FrameTimestamps.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/IProducerListener.h>
+#include <gui/SurfaceComposerClient.h>
+#include <private/gui/ComposerService.h>
+#include <ui/DisplayConfig.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/GraphicTypes.h>
+#include <ui/Transform.h>
+
+#include <gtest/gtest.h>
+
+using namespace std::chrono_literals;
+
+namespace android {
+
+using Transaction = SurfaceComposerClient::Transaction;
+using android::hardware::graphics::common::V1_2::BufferUsage;
+
+class BLASTBufferQueueHelper {
+public:
+ BLASTBufferQueueHelper(const sp<SurfaceControl>& sc, int width, int height) {
+ mBlastBufferQueueAdapter = new BLASTBufferQueue(sc, width, height);
+ }
+
+ void update(const sp<SurfaceControl>& sc, int width, int height) {
+ mBlastBufferQueueAdapter->update(sc, width, height);
+ }
+
+ void setNextTransaction(Transaction* next) {
+ mBlastBufferQueueAdapter->setNextTransaction(next);
+ }
+
+ int getWidth() { return mBlastBufferQueueAdapter->mWidth; }
+
+ int getHeight() { return mBlastBufferQueueAdapter->mHeight; }
+
+ Transaction* getNextTransaction() { return mBlastBufferQueueAdapter->mNextTransaction; }
+
+ sp<IGraphicBufferProducer> getIGraphicBufferProducer() {
+ return mBlastBufferQueueAdapter->getIGraphicBufferProducer();
+ }
+
+ const sp<SurfaceControl> getSurfaceControl() {
+ return mBlastBufferQueueAdapter->mSurfaceControl;
+ }
+
+ void waitForCallbacks() {
+ std::unique_lock lock{mBlastBufferQueueAdapter->mMutex};
+ while (mBlastBufferQueueAdapter->mSubmitted.size() > 0) {
+ mBlastBufferQueueAdapter->mCallbackCV.wait(lock);
+ }
+ }
+
+private:
+ sp<BLASTBufferQueue> mBlastBufferQueueAdapter;
+};
+
+class BLASTBufferQueueTest : public ::testing::Test {
+public:
+protected:
+ BLASTBufferQueueTest() {
+ const ::testing::TestInfo* const testInfo =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGV("Begin test: %s.%s", testInfo->test_case_name(), testInfo->name());
+ }
+
+ ~BLASTBufferQueueTest() {
+ const ::testing::TestInfo* const testInfo =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGV("End test: %s.%s", testInfo->test_case_name(), testInfo->name());
+ }
+
+ void SetUp() {
+ mComposer = ComposerService::getComposerService();
+ mClient = new SurfaceComposerClient();
+ mDisplayToken = mClient->getInternalDisplayToken();
+ ASSERT_NE(nullptr, mDisplayToken.get());
+ Transaction t;
+ t.setDisplayLayerStack(mDisplayToken, 0);
+ t.apply();
+ t.clear();
+
+ DisplayConfig config;
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(mDisplayToken, &config));
+ const ui::Size& resolution = config.resolution;
+ mDisplayWidth = resolution.getWidth();
+ mDisplayHeight = resolution.getHeight();
+
+ mSurfaceControl = mClient->createSurface(String8("TestSurface"), mDisplayWidth,
+ mDisplayHeight, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceBufferState,
+ /*parent*/ nullptr);
+ t.setLayerStack(mSurfaceControl, 0)
+ .setLayer(mSurfaceControl, std::numeric_limits<int32_t>::max())
+ .setFrame(mSurfaceControl, Rect(resolution))
+ .show(mSurfaceControl)
+ .setDataspace(mSurfaceControl, ui::Dataspace::V0_SRGB)
+ .apply();
+ }
+
+ void setUpProducer(BLASTBufferQueueHelper adapter, sp<IGraphicBufferProducer>& producer) {
+ auto igbProducer = adapter.getIGraphicBufferProducer();
+ ASSERT_NE(nullptr, igbProducer.get());
+ ASSERT_EQ(NO_ERROR, igbProducer->setMaxDequeuedBufferCount(2));
+ IGraphicBufferProducer::QueueBufferOutput qbOutput;
+ ASSERT_EQ(NO_ERROR,
+ igbProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false,
+ &qbOutput));
+ ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
+ producer = igbProducer;
+ }
+
+ void fillBuffer(uint32_t* bufData, Rect rect, uint32_t stride, uint8_t r, uint8_t g,
+ uint8_t b) {
+ for (uint32_t row = rect.top; row < rect.bottom; row++) {
+ for (uint32_t col = rect.left; col < rect.right; col++) {
+ uint8_t* pixel = (uint8_t*)(bufData + (row * stride) + col);
+ *pixel = r;
+ *(pixel + 1) = g;
+ *(pixel + 2) = b;
+ *(pixel + 3) = 255;
+ }
+ }
+ }
+
+ void fillQuadrants(sp<GraphicBuffer>& buf) {
+ const auto bufWidth = buf->getWidth();
+ const auto bufHeight = buf->getHeight();
+ uint32_t* bufData;
+ buf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_WRITE_OFTEN),
+ reinterpret_cast<void**>(&bufData));
+ fillBuffer(bufData, Rect(0, 0, bufWidth / 2, bufHeight / 2), buf->getStride(), 0, 0, 0);
+ fillBuffer(bufData, Rect(bufWidth / 2, 0, bufWidth, bufHeight / 2), buf->getStride(), 255,
+ 0, 0);
+ fillBuffer(bufData, Rect(bufWidth / 2, bufHeight / 2, bufWidth, bufHeight),
+ buf->getStride(), 0, 255, 0);
+ fillBuffer(bufData, Rect(0, bufHeight / 2, bufWidth / 2, bufHeight), buf->getStride(), 0, 0,
+ 255);
+ buf->unlock();
+ }
+
+ void checkScreenCapture(uint8_t r, uint8_t g, uint8_t b, Rect region, int32_t border = 0,
+ bool outsideRegion = false) {
+ const auto epsilon = 3;
+ const auto width = mScreenCaptureBuf->getWidth();
+ const auto height = mScreenCaptureBuf->getHeight();
+ const auto stride = mScreenCaptureBuf->getStride();
+
+ uint32_t* bufData;
+ mScreenCaptureBuf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_READ_OFTEN),
+ reinterpret_cast<void**>(&bufData));
+
+ for (uint32_t row = 0; row < height; row++) {
+ for (uint32_t col = 0; col < width; col++) {
+ uint8_t* pixel = (uint8_t*)(bufData + (row * stride) + col);
+ bool inRegion;
+ if (!outsideRegion) {
+ inRegion = row >= region.top + border && row < region.bottom - border &&
+ col >= region.left + border && col < region.right - border;
+ } else {
+ inRegion = row >= region.top - border && row < region.bottom + border &&
+ col >= region.left - border && col < region.right + border;
+ }
+ if (!outsideRegion && inRegion) {
+ EXPECT_GE(epsilon, abs(r - *(pixel)));
+ EXPECT_GE(epsilon, abs(g - *(pixel + 1)));
+ EXPECT_GE(epsilon, abs(b - *(pixel + 2)));
+ } else if (outsideRegion && !inRegion) {
+ EXPECT_GE(epsilon, abs(r - *(pixel)));
+ EXPECT_GE(epsilon, abs(g - *(pixel + 1)));
+ EXPECT_GE(epsilon, abs(b - *(pixel + 2)));
+ }
+ }
+ }
+ mScreenCaptureBuf->unlock();
+ ASSERT_EQ(false, ::testing::Test::HasFailure());
+ }
+
+ sp<SurfaceComposerClient> mClient;
+ sp<ISurfaceComposer> mComposer;
+
+ sp<IBinder> mDisplayToken;
+
+ sp<SurfaceControl> mSurfaceControl;
+ sp<GraphicBuffer> mScreenCaptureBuf;
+
+ uint32_t mDisplayWidth;
+ uint32_t mDisplayHeight;
+};
+
+TEST_F(BLASTBufferQueueTest, CreateBLASTBufferQueue) {
+ // create BLASTBufferQueue adapter associated with this surface
+ BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+ ASSERT_EQ(mSurfaceControl, adapter.getSurfaceControl());
+ ASSERT_EQ(mDisplayWidth, adapter.getWidth());
+ ASSERT_EQ(mDisplayHeight, adapter.getHeight());
+ ASSERT_EQ(nullptr, adapter.getNextTransaction());
+}
+
+TEST_F(BLASTBufferQueueTest, Update) {
+ BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+ sp<SurfaceControl> updateSurface =
+ mClient->createSurface(String8("UpdateTest"), mDisplayWidth / 2, mDisplayHeight / 2,
+ PIXEL_FORMAT_RGBA_8888);
+ adapter.update(updateSurface, mDisplayWidth / 2, mDisplayHeight / 2);
+ ASSERT_EQ(updateSurface, adapter.getSurfaceControl());
+ ASSERT_EQ(mDisplayWidth / 2, adapter.getWidth());
+ ASSERT_EQ(mDisplayHeight / 2, adapter.getHeight());
+}
+
+TEST_F(BLASTBufferQueueTest, SetNextTransaction) {
+ BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+ Transaction next;
+ adapter.setNextTransaction(&next);
+ ASSERT_EQ(&next, adapter.getNextTransaction());
+}
+
+TEST_F(BLASTBufferQueueTest, DISABLED_onFrameAvailable_ApplyDesiredPresentTime) {
+ BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+ sp<IGraphicBufferProducer> igbProducer;
+ setUpProducer(adapter, igbProducer);
+
+ int slot;
+ sp<Fence> fence;
+ sp<GraphicBuffer> buf;
+ auto ret = igbProducer->dequeueBuffer(&slot, &fence, mDisplayWidth, mDisplayHeight,
+ PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN,
+ nullptr, nullptr);
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret);
+ ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf));
+
+ nsecs_t desiredPresentTime = systemTime() + nsecs_t(5 * 1e8);
+ IGraphicBufferProducer::QueueBufferOutput qbOutput;
+ IGraphicBufferProducer::QueueBufferInput input(desiredPresentTime, false, HAL_DATASPACE_UNKNOWN,
+ Rect(mDisplayWidth, mDisplayHeight),
+ NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
+ Fence::NO_FENCE);
+ igbProducer->queueBuffer(slot, input, &qbOutput);
+ ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
+
+ adapter.waitForCallbacks();
+ ASSERT_GE(systemTime(), desiredPresentTime);
+}
+
+TEST_F(BLASTBufferQueueTest, onFrameAvailable_Apply) {
+ uint8_t r = 255;
+ uint8_t g = 0;
+ uint8_t b = 0;
+
+ BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+ sp<IGraphicBufferProducer> igbProducer;
+ setUpProducer(adapter, igbProducer);
+
+ int slot;
+ sp<Fence> fence;
+ sp<GraphicBuffer> buf;
+ auto ret = igbProducer->dequeueBuffer(&slot, &fence, mDisplayWidth, mDisplayHeight,
+ PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN,
+ nullptr, nullptr);
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret);
+ ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf));
+
+ uint32_t* bufData;
+ buf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_WRITE_OFTEN),
+ reinterpret_cast<void**>(&bufData));
+ fillBuffer(bufData, Rect(buf->getWidth(), buf->getHeight()), buf->getStride(), r, g, b);
+ buf->unlock();
+
+ IGraphicBufferProducer::QueueBufferOutput qbOutput;
+ IGraphicBufferProducer::QueueBufferInput input(systemTime(), false, HAL_DATASPACE_UNKNOWN,
+ Rect(mDisplayWidth, mDisplayHeight),
+ NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
+ Fence::NO_FENCE);
+ igbProducer->queueBuffer(slot, input, &qbOutput);
+ ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
+
+ adapter.waitForCallbacks();
+
+ // capture screen and verify that it is red
+ bool capturedSecureLayers;
+ ASSERT_EQ(NO_ERROR,
+ mComposer->captureScreen(mDisplayToken, &mScreenCaptureBuf, capturedSecureLayers,
+ ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, Rect(),
+ mDisplayWidth, mDisplayHeight,
+ /*useIdentityTransform*/ false));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
+}
+
+TEST_F(BLASTBufferQueueTest, TripleBuffering) {
+ BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+ sp<IGraphicBufferProducer> igbProducer;
+ setUpProducer(adapter, igbProducer);
+
+ std::vector<std::pair<int, sp<Fence>>> allocated;
+ for (int i = 0; i < 3; i++) {
+ int slot;
+ sp<Fence> fence;
+ sp<GraphicBuffer> buf;
+ auto ret = igbProducer->dequeueBuffer(&slot, &fence, mDisplayWidth, mDisplayHeight,
+ PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN,
+ nullptr, nullptr);
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret);
+ ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf));
+ allocated.push_back({slot, fence});
+ }
+ for (int i = 0; i < allocated.size(); i++) {
+ igbProducer->cancelBuffer(allocated[i].first, allocated[i].second);
+ }
+
+ for (int i = 0; i < 100; i++) {
+ int slot;
+ sp<Fence> fence;
+ sp<GraphicBuffer> buf;
+ auto ret = igbProducer->dequeueBuffer(&slot, &fence, mDisplayWidth, mDisplayHeight,
+ PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN,
+ nullptr, nullptr);
+ ASSERT_EQ(NO_ERROR, ret);
+ IGraphicBufferProducer::QueueBufferOutput qbOutput;
+ IGraphicBufferProducer::QueueBufferInput input(systemTime(), false, HAL_DATASPACE_UNKNOWN,
+ Rect(mDisplayWidth, mDisplayHeight),
+ NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
+ Fence::NO_FENCE);
+ igbProducer->queueBuffer(slot, input, &qbOutput);
+ }
+ adapter.waitForCallbacks();
+}
+
+TEST_F(BLASTBufferQueueTest, SetCrop_Item) {
+ uint8_t r = 255;
+ uint8_t g = 0;
+ uint8_t b = 0;
+
+ BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+ sp<IGraphicBufferProducer> igbProducer;
+ setUpProducer(adapter, igbProducer);
+ int slot;
+ sp<Fence> fence;
+ sp<GraphicBuffer> buf;
+ auto ret = igbProducer->dequeueBuffer(&slot, &fence, mDisplayWidth, mDisplayHeight,
+ PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN,
+ nullptr, nullptr);
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret);
+ ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf));
+
+ uint32_t* bufData;
+ buf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_WRITE_OFTEN),
+ reinterpret_cast<void**>(&bufData));
+ fillBuffer(bufData, Rect(buf->getWidth(), buf->getHeight() / 2), buf->getStride(), r, g, b);
+ buf->unlock();
+
+ IGraphicBufferProducer::QueueBufferOutput qbOutput;
+ IGraphicBufferProducer::QueueBufferInput input(systemTime(), false, HAL_DATASPACE_UNKNOWN,
+ Rect(mDisplayWidth, mDisplayHeight / 2),
+ NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
+ Fence::NO_FENCE);
+ igbProducer->queueBuffer(slot, input, &qbOutput);
+ ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
+
+ adapter.waitForCallbacks();
+ // capture screen and verify that it is red
+ bool capturedSecureLayers;
+ ASSERT_EQ(NO_ERROR,
+ mComposer->captureScreen(mDisplayToken, &mScreenCaptureBuf, capturedSecureLayers,
+ ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, Rect(),
+ mDisplayWidth, mDisplayHeight,
+ /*useIdentityTransform*/ false));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
+}
+
+TEST_F(BLASTBufferQueueTest, SetCrop_ScalingModeScaleCrop) {
+ uint8_t r = 255;
+ uint8_t g = 0;
+ uint8_t b = 0;
+
+ int32_t bufferSideLength =
+ (mDisplayWidth < mDisplayHeight) ? mDisplayWidth / 2 : mDisplayHeight / 2;
+ int32_t finalCropSideLength = bufferSideLength / 2;
+
+ auto bg = mClient->createSurface(String8("BGTest"), 0, 0, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceEffect);
+ ASSERT_NE(nullptr, bg.get());
+ Transaction t;
+ t.setLayerStack(bg, 0)
+ .setCrop_legacy(bg, Rect(0, 0, mDisplayWidth, mDisplayHeight))
+ .setColor(bg, half3{0, 0, 0})
+ .setLayer(bg, 0)
+ .apply();
+
+ BLASTBufferQueueHelper adapter(mSurfaceControl, bufferSideLength, bufferSideLength);
+ sp<IGraphicBufferProducer> igbProducer;
+ setUpProducer(adapter, igbProducer);
+ int slot;
+ sp<Fence> fence;
+ sp<GraphicBuffer> buf;
+ auto ret = igbProducer->dequeueBuffer(&slot, &fence, bufferSideLength, bufferSideLength,
+ PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN,
+ nullptr, nullptr);
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret);
+ ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf));
+
+ uint32_t* bufData;
+ buf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_WRITE_OFTEN),
+ reinterpret_cast<void**>(&bufData));
+ fillBuffer(bufData, Rect(buf->getWidth(), buf->getHeight()), buf->getStride(), 0, 0, 0);
+ fillBuffer(bufData,
+ Rect(finalCropSideLength / 2, 0, buf->getWidth() - finalCropSideLength / 2,
+ buf->getHeight()),
+ buf->getStride(), r, g, b);
+ buf->unlock();
+
+ IGraphicBufferProducer::QueueBufferOutput qbOutput;
+ IGraphicBufferProducer::QueueBufferInput input(systemTime(), false, HAL_DATASPACE_UNKNOWN,
+ Rect(bufferSideLength, finalCropSideLength),
+ NATIVE_WINDOW_SCALING_MODE_SCALE_CROP, 0,
+ Fence::NO_FENCE);
+ igbProducer->queueBuffer(slot, input, &qbOutput);
+ ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
+
+ adapter.waitForCallbacks();
+ // capture screen and verify that it is red
+ bool capturedSecureLayers;
+ ASSERT_EQ(NO_ERROR,
+ mComposer->captureScreen(mDisplayToken, &mScreenCaptureBuf, capturedSecureLayers,
+ ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, Rect(),
+ mDisplayWidth, mDisplayHeight,
+ /*useIdentityTransform*/ false));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(r, g, b,
+ {0, 0, (int32_t)bufferSideLength, (int32_t)bufferSideLength}));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(0, 0, 0,
+ {0, 0, (int32_t)bufferSideLength, (int32_t)bufferSideLength},
+ /*border*/ 0, /*outsideRegion*/ true));
+}
+
+class BLASTBufferQueueTransformTest : public BLASTBufferQueueTest {
+public:
+ void test(uint32_t tr) {
+ BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+ sp<IGraphicBufferProducer> igbProducer;
+ setUpProducer(adapter, igbProducer);
+
+ auto bufWidth = mDisplayWidth;
+ auto bufHeight = mDisplayHeight;
+ int slot;
+ sp<Fence> fence;
+ sp<GraphicBuffer> buf;
+
+ auto ret = igbProducer->dequeueBuffer(&slot, &fence, bufWidth, bufHeight,
+ PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN,
+ nullptr, nullptr);
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret);
+ ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf));
+
+ fillQuadrants(buf);
+
+ IGraphicBufferProducer::QueueBufferOutput qbOutput;
+ IGraphicBufferProducer::QueueBufferInput input(systemTime(), false, HAL_DATASPACE_UNKNOWN,
+ Rect(bufWidth, bufHeight),
+ NATIVE_WINDOW_SCALING_MODE_FREEZE, tr,
+ Fence::NO_FENCE);
+ igbProducer->queueBuffer(slot, input, &qbOutput);
+ ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
+
+ adapter.waitForCallbacks();
+ bool capturedSecureLayers;
+ ASSERT_EQ(NO_ERROR,
+ mComposer->captureScreen(mDisplayToken, &mScreenCaptureBuf, capturedSecureLayers,
+ ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888,
+ Rect(), mDisplayWidth, mDisplayHeight,
+ /*useIdentityTransform*/ false));
+ switch (tr) {
+ case ui::Transform::ROT_0:
+ ASSERT_NO_FATAL_FAILURE(checkScreenCapture(0, 0, 0,
+ {0, 0, (int32_t)mDisplayWidth / 2,
+ (int32_t)mDisplayHeight / 2},
+ 1));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(255, 0, 0,
+ {(int32_t)mDisplayWidth / 2, 0, (int32_t)mDisplayWidth,
+ (int32_t)mDisplayHeight / 2},
+ 1));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(0, 255, 0,
+ {(int32_t)mDisplayWidth / 2, (int32_t)mDisplayHeight / 2,
+ (int32_t)mDisplayWidth, (int32_t)mDisplayHeight},
+ 1));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(0, 0, 255,
+ {0, (int32_t)mDisplayHeight / 2,
+ (int32_t)mDisplayWidth / 2, (int32_t)mDisplayHeight},
+ 1));
+ break;
+ case ui::Transform::FLIP_H:
+ ASSERT_NO_FATAL_FAILURE(checkScreenCapture(255, 0, 0,
+ {0, 0, (int32_t)mDisplayWidth / 2,
+ (int32_t)mDisplayHeight / 2},
+ 1));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(0, 0, 0,
+ {(int32_t)mDisplayWidth / 2, 0, (int32_t)mDisplayWidth,
+ (int32_t)mDisplayHeight / 2},
+ 1));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(0, 0, 255,
+ {(int32_t)mDisplayWidth / 2, (int32_t)mDisplayHeight / 2,
+ (int32_t)mDisplayWidth, (int32_t)mDisplayHeight},
+ 1));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(0, 255, 0,
+ {0, (int32_t)mDisplayHeight / 2,
+ (int32_t)mDisplayWidth / 2, (int32_t)mDisplayHeight},
+ 1));
+ break;
+ case ui::Transform::FLIP_V:
+ ASSERT_NO_FATAL_FAILURE(checkScreenCapture(0, 0, 255,
+ {0, 0, (int32_t)mDisplayWidth / 2,
+ (int32_t)mDisplayHeight / 2},
+ 1));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(0, 255, 0,
+ {(int32_t)mDisplayWidth / 2, 0, (int32_t)mDisplayWidth,
+ (int32_t)mDisplayHeight / 2},
+ 1));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(255, 0, 0,
+ {(int32_t)mDisplayWidth / 2, (int32_t)mDisplayHeight / 2,
+ (int32_t)mDisplayWidth, (int32_t)mDisplayHeight},
+ 1));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(0, 0, 0,
+ {0, (int32_t)mDisplayHeight / 2,
+ (int32_t)mDisplayWidth / 2, (int32_t)mDisplayHeight},
+ 1));
+ break;
+ case ui::Transform::ROT_90:
+ ASSERT_NO_FATAL_FAILURE(checkScreenCapture(0, 0, 255,
+ {0, 0, (int32_t)mDisplayWidth / 2,
+ (int32_t)mDisplayHeight / 2},
+ 1));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(0, 0, 0,
+ {(int32_t)mDisplayWidth / 2, 0, (int32_t)mDisplayWidth,
+ (int32_t)mDisplayHeight / 2},
+ 1));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(255, 0, 0,
+ {(int32_t)mDisplayWidth / 2, (int32_t)mDisplayHeight / 2,
+ (int32_t)mDisplayWidth, (int32_t)mDisplayHeight},
+ 1));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(0, 255, 0,
+ {0, (int32_t)mDisplayHeight / 2,
+ (int32_t)mDisplayWidth / 2, (int32_t)mDisplayHeight},
+ 1));
+ break;
+ case ui::Transform::ROT_180:
+ ASSERT_NO_FATAL_FAILURE(checkScreenCapture(0, 255, 0,
+ {0, 0, (int32_t)mDisplayWidth / 2,
+ (int32_t)mDisplayHeight / 2},
+ 1));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(0, 0, 255,
+ {(int32_t)mDisplayWidth / 2, 0, (int32_t)mDisplayWidth,
+ (int32_t)mDisplayHeight / 2},
+ 1));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(0, 0, 0,
+ {(int32_t)mDisplayWidth / 2, (int32_t)mDisplayHeight / 2,
+ (int32_t)mDisplayWidth, (int32_t)mDisplayHeight},
+ 1));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(255, 0, 0,
+ {0, (int32_t)mDisplayHeight / 2,
+ (int32_t)mDisplayWidth / 2, (int32_t)mDisplayHeight},
+ 1));
+ break;
+ case ui::Transform::ROT_270:
+ ASSERT_NO_FATAL_FAILURE(checkScreenCapture(255, 0, 0,
+ {0, 0, (int32_t)mDisplayWidth / 2,
+ (int32_t)mDisplayHeight / 2},
+ 1));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(0, 255, 0,
+ {(int32_t)mDisplayWidth / 2, 0, (int32_t)mDisplayWidth,
+ (int32_t)mDisplayHeight / 2},
+ 1));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(0, 0, 255,
+ {(int32_t)mDisplayWidth / 2, (int32_t)mDisplayHeight / 2,
+ (int32_t)mDisplayWidth, (int32_t)mDisplayHeight},
+ 1));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(0, 0, 0,
+ {0, (int32_t)mDisplayHeight / 2,
+ (int32_t)mDisplayWidth / 2, (int32_t)mDisplayHeight},
+ 1));
+ }
+ }
+};
+
+TEST_F(BLASTBufferQueueTransformTest, setTransform_ROT_0) {
+ test(ui::Transform::ROT_0);
+}
+
+TEST_F(BLASTBufferQueueTransformTest, setTransform_FLIP_H) {
+ test(ui::Transform::FLIP_H);
+}
+
+TEST_F(BLASTBufferQueueTransformTest, setTransform_FLIP_V) {
+ test(ui::Transform::FLIP_V);
+}
+
+TEST_F(BLASTBufferQueueTransformTest, setTransform_ROT_90) {
+ test(ui::Transform::ROT_90);
+}
+
+TEST_F(BLASTBufferQueueTransformTest, setTransform_ROT_180) {
+ test(ui::Transform::ROT_180);
+}
+
+TEST_F(BLASTBufferQueueTransformTest, setTransform_ROT_270) {
+ test(ui::Transform::ROT_270);
+}
+
+class BLASTFrameEventHistoryTest : public BLASTBufferQueueTest {
+public:
+ void setUpAndQueueBuffer(const sp<IGraphicBufferProducer>& igbProducer,
+ nsecs_t* requestedPresentTime, nsecs_t* postedTime,
+ IGraphicBufferProducer::QueueBufferOutput* qbOutput,
+ bool getFrameTimestamps) {
+ int slot;
+ sp<Fence> fence;
+ sp<GraphicBuffer> buf;
+ auto ret = igbProducer->dequeueBuffer(&slot, &fence, mDisplayWidth, mDisplayHeight,
+ PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN,
+ nullptr, nullptr);
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret);
+ ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf));
+
+ nsecs_t requestedTime = systemTime();
+ if (requestedPresentTime) *requestedPresentTime = requestedTime;
+ IGraphicBufferProducer::QueueBufferInput input(requestedTime, false, HAL_DATASPACE_UNKNOWN,
+ Rect(mDisplayWidth, mDisplayHeight),
+ NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
+ Fence::NO_FENCE, /*sticky*/ 0,
+ getFrameTimestamps);
+ if (postedTime) *postedTime = systemTime();
+ igbProducer->queueBuffer(slot, input, qbOutput);
+ }
+};
+
+TEST_F(BLASTFrameEventHistoryTest, FrameEventHistory_Basic) {
+ BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+ sp<IGraphicBufferProducer> igbProducer;
+ ProducerFrameEventHistory history;
+ setUpProducer(adapter, igbProducer);
+
+ IGraphicBufferProducer::QueueBufferOutput qbOutput;
+ nsecs_t requestedPresentTimeA = 0;
+ nsecs_t postedTimeA = 0;
+ setUpAndQueueBuffer(igbProducer, &requestedPresentTimeA, &postedTimeA, &qbOutput, true);
+ history.applyDelta(qbOutput.frameTimestamps);
+
+ FrameEvents* events = nullptr;
+ events = history.getFrame(1);
+ ASSERT_NE(nullptr, events);
+ ASSERT_EQ(1, events->frameNumber);
+ ASSERT_EQ(requestedPresentTimeA, events->requestedPresentTime);
+ ASSERT_GE(events->postedTime, postedTimeA);
+
+ adapter.waitForCallbacks();
+
+ // queue another buffer so we query for frame event deltas
+ nsecs_t requestedPresentTimeB = 0;
+ nsecs_t postedTimeB = 0;
+ setUpAndQueueBuffer(igbProducer, &requestedPresentTimeB, &postedTimeB, &qbOutput, true);
+ history.applyDelta(qbOutput.frameTimestamps);
+ events = history.getFrame(1);
+ ASSERT_NE(nullptr, events);
+
+ // frame number, requestedPresentTime, and postTime should not have changed
+ ASSERT_EQ(1, events->frameNumber);
+ ASSERT_EQ(requestedPresentTimeA, events->requestedPresentTime);
+ ASSERT_GE(events->postedTime, postedTimeA);
+
+ ASSERT_GE(events->latchTime, postedTimeA);
+ ASSERT_GE(events->dequeueReadyTime, events->latchTime);
+ ASSERT_NE(nullptr, events->gpuCompositionDoneFence);
+ ASSERT_NE(nullptr, events->displayPresentFence);
+ ASSERT_NE(nullptr, events->releaseFence);
+
+ // we should also have gotten the initial values for the next frame
+ events = history.getFrame(2);
+ ASSERT_NE(nullptr, events);
+ ASSERT_EQ(2, events->frameNumber);
+ ASSERT_EQ(requestedPresentTimeB, events->requestedPresentTime);
+ ASSERT_GE(events->postedTime, postedTimeB);
+
+ // wait for any callbacks that have not been received
+ adapter.waitForCallbacks();
+}
+} // namespace android
diff --git a/libs/gui/tests/BufferItemConsumer_test.cpp b/libs/gui/tests/BufferItemConsumer_test.cpp
index b87cbbd..fc6551c 100644
--- a/libs/gui/tests/BufferItemConsumer_test.cpp
+++ b/libs/gui/tests/BufferItemConsumer_test.cpp
@@ -51,7 +51,7 @@
mBFL = new BufferFreedListener(this);
mBIC->setBufferFreedListener(mBFL);
- sp<IProducerListener> producerListener = new DummyProducerListener();
+ sp<IProducerListener> producerListener = new StubProducerListener();
IGraphicBufferProducer::QueueBufferOutput bufferOutput;
ASSERT_EQ(NO_ERROR,
mProducer->connect(producerListener, NATIVE_WINDOW_API_CPU,
@@ -131,7 +131,7 @@
// Test that detaching buffer from consumer side triggers onBufferFreed.
TEST_F(BufferItemConsumerTest, TriggerBufferFreed_DetachBufferFromConsumer) {
int slot;
- // Producer: generate a dummy buffer.
+ // Producer: generate a placeholder buffer.
DequeueBuffer(&slot);
QueueBuffer(slot);
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index 406f21f..d1208ee 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -17,7 +17,7 @@
#define LOG_TAG "BufferQueue_test"
//#define LOG_NDEBUG 0
-#include "DummyConsumer.h"
+#include "MockConsumer.h"
#include <gui/BufferItem.h>
#include <gui/BufferQueue.h>
@@ -134,8 +134,8 @@
mConsumer = interface_cast<IGraphicBufferConsumer>(binderConsumer);
EXPECT_TRUE(mConsumer != nullptr);
- sp<DummyConsumer> dc(new DummyConsumer);
- ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false));
+ 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));
@@ -169,13 +169,24 @@
ASSERT_EQ(OK, item.mGraphicBuffer->unlock());
}
+TEST_F(BufferQueueTest, GetMaxBufferCountInQueueBufferOutput_Succeeds) {
+ createBufferQueue();
+ sp<MockConsumer> mc(new MockConsumer);
+ mConsumer->consumerConnect(mc, false);
+ int bufferCount = 50;
+ mConsumer->setMaxBufferCount(bufferCount);
+
+ IGraphicBufferProducer::QueueBufferOutput output;
+ mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false, &output);
+ ASSERT_EQ(output.maxBufferCount, bufferCount);
+}
+
TEST_F(BufferQueueTest, AcquireBuffer_ExceedsMaxAcquireCount_Fails) {
createBufferQueue();
- sp<DummyConsumer> dc(new DummyConsumer);
- mConsumer->consumerConnect(dc, false);
+ sp<MockConsumer> mc(new MockConsumer);
+ mConsumer->consumerConnect(mc, false);
IGraphicBufferProducer::QueueBufferOutput qbo;
- mProducer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, false,
- &qbo);
+ mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false, &qbo);
mProducer->setMaxDequeuedBufferCount(3);
int slot;
@@ -207,15 +218,14 @@
TEST_F(BufferQueueTest, SetMaxAcquiredBufferCountWithIllegalValues_ReturnsError) {
createBufferQueue();
- sp<DummyConsumer> dc(new DummyConsumer);
- mConsumer->consumerConnect(dc, false);
+ sp<MockConsumer> mc(new MockConsumer);
+ mConsumer->consumerConnect(mc, false);
EXPECT_EQ(OK, mConsumer->setMaxBufferCount(10));
EXPECT_EQ(BAD_VALUE, mConsumer->setMaxAcquiredBufferCount(10));
IGraphicBufferProducer::QueueBufferOutput qbo;
- mProducer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, false,
- &qbo);
+ mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false, &qbo);
mProducer->setMaxDequeuedBufferCount(3);
int minBufferCount;
@@ -251,12 +261,11 @@
TEST_F(BufferQueueTest, SetMaxAcquiredBufferCountWithLegalValues_Succeeds) {
createBufferQueue();
- sp<DummyConsumer> dc(new DummyConsumer);
- mConsumer->consumerConnect(dc, false);
+ sp<MockConsumer> mc(new MockConsumer);
+ mConsumer->consumerConnect(mc, false);
IGraphicBufferProducer::QueueBufferOutput qbo;
- mProducer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, false,
- &qbo);
+ mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false, &qbo);
mProducer->setMaxDequeuedBufferCount(2);
int minBufferCount;
@@ -298,8 +307,8 @@
TEST_F(BufferQueueTest, SetMaxBufferCountWithLegalValues_Succeeds) {
createBufferQueue();
- sp<DummyConsumer> dc(new DummyConsumer);
- mConsumer->consumerConnect(dc, false);
+ sp<MockConsumer> mc(new MockConsumer);
+ mConsumer->consumerConnect(mc, false);
// Test shared buffer mode
EXPECT_EQ(OK, mConsumer->setMaxAcquiredBufferCount(1));
@@ -307,8 +316,8 @@
TEST_F(BufferQueueTest, SetMaxBufferCountWithIllegalValues_ReturnsError) {
createBufferQueue();
- sp<DummyConsumer> dc(new DummyConsumer);
- mConsumer->consumerConnect(dc, false);
+ sp<MockConsumer> mc(new MockConsumer);
+ mConsumer->consumerConnect(mc, false);
EXPECT_EQ(BAD_VALUE, mConsumer->setMaxBufferCount(0));
EXPECT_EQ(BAD_VALUE, mConsumer->setMaxBufferCount(
@@ -320,11 +329,11 @@
TEST_F(BufferQueueTest, DetachAndReattachOnProducerSide) {
createBufferQueue();
- sp<DummyConsumer> dc(new DummyConsumer);
- ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false));
+ sp<MockConsumer> mc(new MockConsumer);
+ ASSERT_EQ(OK, mConsumer->consumerConnect(mc, false));
IGraphicBufferProducer::QueueBufferOutput output;
- ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
- NATIVE_WINDOW_API_CPU, false, &output));
+ ASSERT_EQ(OK,
+ mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false, &output));
ASSERT_EQ(BAD_VALUE, mProducer->detachBuffer(-1)); // Index too low
ASSERT_EQ(BAD_VALUE, mProducer->detachBuffer(
@@ -374,11 +383,11 @@
TEST_F(BufferQueueTest, DetachAndReattachOnConsumerSide) {
createBufferQueue();
- sp<DummyConsumer> dc(new DummyConsumer);
- ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false));
+ sp<MockConsumer> mc(new MockConsumer);
+ ASSERT_EQ(OK, mConsumer->consumerConnect(mc, false));
IGraphicBufferProducer::QueueBufferOutput output;
- ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
- NATIVE_WINDOW_API_CPU, false, &output));
+ ASSERT_EQ(OK,
+ mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false, &output));
int slot;
sp<Fence> fence;
@@ -433,11 +442,11 @@
TEST_F(BufferQueueTest, MoveFromConsumerToProducer) {
createBufferQueue();
- sp<DummyConsumer> dc(new DummyConsumer);
- ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false));
+ sp<MockConsumer> mc(new MockConsumer);
+ ASSERT_EQ(OK, mConsumer->consumerConnect(mc, false));
IGraphicBufferProducer::QueueBufferOutput output;
- ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
- NATIVE_WINDOW_API_CPU, false, &output));
+ ASSERT_EQ(OK,
+ mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false, &output));
int slot;
sp<Fence> fence;
@@ -476,11 +485,11 @@
TEST_F(BufferQueueTest, TestDisallowingAllocation) {
createBufferQueue();
- sp<DummyConsumer> dc(new DummyConsumer);
- ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true));
+ sp<MockConsumer> mc(new MockConsumer);
+ ASSERT_EQ(OK, mConsumer->consumerConnect(mc, true));
IGraphicBufferProducer::QueueBufferOutput output;
- ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
- NATIVE_WINDOW_API_CPU, true, &output));
+ ASSERT_EQ(OK,
+ mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, true, &output));
static const uint32_t WIDTH = 320;
static const uint32_t HEIGHT = 240;
@@ -514,11 +523,11 @@
TEST_F(BufferQueueTest, TestGenerationNumbers) {
createBufferQueue();
- sp<DummyConsumer> dc(new DummyConsumer);
- ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true));
+ sp<MockConsumer> mc(new MockConsumer);
+ ASSERT_EQ(OK, mConsumer->consumerConnect(mc, true));
IGraphicBufferProducer::QueueBufferOutput output;
- ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
- NATIVE_WINDOW_API_CPU, true, &output));
+ ASSERT_EQ(OK,
+ mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, true, &output));
ASSERT_EQ(OK, mProducer->setGenerationNumber(1));
@@ -556,11 +565,11 @@
TEST_F(BufferQueueTest, TestSharedBufferModeWithoutAutoRefresh) {
createBufferQueue();
- sp<DummyConsumer> dc(new DummyConsumer);
- ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true));
+ sp<MockConsumer> mc(new MockConsumer);
+ ASSERT_EQ(OK, mConsumer->consumerConnect(mc, true));
IGraphicBufferProducer::QueueBufferOutput output;
- ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
- NATIVE_WINDOW_API_CPU, true, &output));
+ ASSERT_EQ(OK,
+ mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, true, &output));
ASSERT_EQ(OK, mProducer->setSharedBufferMode(true));
@@ -606,11 +615,11 @@
TEST_F(BufferQueueTest, TestSharedBufferModeWithAutoRefresh) {
createBufferQueue();
- sp<DummyConsumer> dc(new DummyConsumer);
- ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true));
+ sp<MockConsumer> mc(new MockConsumer);
+ ASSERT_EQ(OK, mConsumer->consumerConnect(mc, true));
IGraphicBufferProducer::QueueBufferOutput output;
- ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
- NATIVE_WINDOW_API_CPU, true, &output));
+ ASSERT_EQ(OK,
+ mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, true, &output));
ASSERT_EQ(OK, mProducer->setSharedBufferMode(true));
ASSERT_EQ(OK, mProducer->setAutoRefresh(true));
@@ -675,11 +684,11 @@
TEST_F(BufferQueueTest, TestSharedBufferModeUsingAlreadyDequeuedBuffer) {
createBufferQueue();
- sp<DummyConsumer> dc(new DummyConsumer);
- ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true));
+ sp<MockConsumer> mc(new MockConsumer);
+ ASSERT_EQ(OK, mConsumer->consumerConnect(mc, true));
IGraphicBufferProducer::QueueBufferOutput output;
- ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
- NATIVE_WINDOW_API_CPU, true, &output));
+ ASSERT_EQ(OK,
+ mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, true, &output));
// Dequeue a buffer
int sharedSlot;
@@ -726,11 +735,11 @@
TEST_F(BufferQueueTest, TestTimeouts) {
createBufferQueue();
- sp<DummyConsumer> dc(new DummyConsumer);
- ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true));
+ sp<MockConsumer> mc(new MockConsumer);
+ ASSERT_EQ(OK, mConsumer->consumerConnect(mc, true));
IGraphicBufferProducer::QueueBufferOutput output;
- ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
- NATIVE_WINDOW_API_CPU, true, &output));
+ ASSERT_EQ(OK,
+ mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, true, &output));
// Fill up the queue. Since the controlledByApp flags are set to true, this
// queue should be in non-blocking mode, and we should be recycling the same
@@ -788,11 +797,11 @@
TEST_F(BufferQueueTest, CanAttachWhileDisallowingAllocation) {
createBufferQueue();
- sp<DummyConsumer> dc(new DummyConsumer);
- ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true));
+ sp<MockConsumer> mc(new MockConsumer);
+ ASSERT_EQ(OK, mConsumer->consumerConnect(mc, true));
IGraphicBufferProducer::QueueBufferOutput output;
- ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
- NATIVE_WINDOW_API_CPU, true, &output));
+ ASSERT_EQ(OK,
+ mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, true, &output));
int slot = BufferQueue::INVALID_BUFFER_SLOT;
sp<Fence> sourceFence;
@@ -810,11 +819,11 @@
TEST_F(BufferQueueTest, CanRetrieveLastQueuedBuffer) {
createBufferQueue();
- sp<DummyConsumer> dc(new DummyConsumer);
- ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false));
+ sp<MockConsumer> mc(new MockConsumer);
+ ASSERT_EQ(OK, mConsumer->consumerConnect(mc, false));
IGraphicBufferProducer::QueueBufferOutput output;
- ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
- NATIVE_WINDOW_API_CPU, false, &output));
+ ASSERT_EQ(OK,
+ mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false, &output));
// Dequeue and queue the first buffer, storing the handle
int slot = BufferQueue::INVALID_BUFFER_SLOT;
@@ -864,11 +873,11 @@
TEST_F(BufferQueueTest, TestOccupancyHistory) {
createBufferQueue();
- sp<DummyConsumer> dc(new DummyConsumer);
- ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false));
+ sp<MockConsumer> mc(new MockConsumer);
+ ASSERT_EQ(OK, mConsumer->consumerConnect(mc, false));
IGraphicBufferProducer::QueueBufferOutput output;
- ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
- NATIVE_WINDOW_API_CPU, false, &output));
+ ASSERT_EQ(OK,
+ mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false, &output));
int slot = BufferQueue::INVALID_BUFFER_SLOT;
sp<Fence> fence = Fence::NO_FENCE;
@@ -1018,8 +1027,8 @@
TEST_F(BufferQueueTest, TestDiscardFreeBuffers) {
createBufferQueue();
- sp<DummyConsumer> dc(new DummyConsumer);
- ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false));
+ sp<MockConsumer> mc(new MockConsumer);
+ ASSERT_EQ(OK, mConsumer->consumerConnect(mc, false));
IGraphicBufferProducer::QueueBufferOutput output;
sp<BufferDiscardedListener> pl(new BufferDiscardedListener);
ASSERT_EQ(OK, mProducer->connect(pl,
@@ -1103,11 +1112,11 @@
TEST_F(BufferQueueTest, TestBufferReplacedInQueueBuffer) {
createBufferQueue();
- sp<DummyConsumer> dc(new DummyConsumer);
- ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true));
+ sp<MockConsumer> mc(new MockConsumer);
+ ASSERT_EQ(OK, mConsumer->consumerConnect(mc, true));
IGraphicBufferProducer::QueueBufferOutput output;
- ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
- NATIVE_WINDOW_API_CPU, true, &output));
+ ASSERT_EQ(OK,
+ mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, true, &output));
ASSERT_EQ(OK, mConsumer->setMaxAcquiredBufferCount(1));
int slot = BufferQueue::INVALID_BUFFER_SLOT;
@@ -1144,12 +1153,11 @@
TEST_F(BufferQueueTest, TestStaleBufferHandleSentAfterDisconnect) {
createBufferQueue();
- sp<DummyConsumer> dc(new DummyConsumer);
- ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true));
+ sp<MockConsumer> mc(new MockConsumer);
+ ASSERT_EQ(OK, mConsumer->consumerConnect(mc, true));
IGraphicBufferProducer::QueueBufferOutput output;
- sp<IProducerListener> dummyListener(new DummyProducerListener);
- ASSERT_EQ(OK, mProducer->connect(dummyListener, NATIVE_WINDOW_API_CPU,
- true, &output));
+ sp<IProducerListener> fakeListener(new StubProducerListener);
+ ASSERT_EQ(OK, mProducer->connect(fakeListener, NATIVE_WINDOW_API_CPU, true, &output));
int slot = BufferQueue::INVALID_BUFFER_SLOT;
sp<Fence> fence = Fence::NO_FENCE;
@@ -1203,15 +1211,13 @@
TEST_F(BufferQueueTest, TestProducerConnectDisconnect) {
createBufferQueue();
- sp<DummyConsumer> dc(new DummyConsumer);
- ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true));
+ sp<MockConsumer> mc(new MockConsumer);
+ ASSERT_EQ(OK, mConsumer->consumerConnect(mc, true));
IGraphicBufferProducer::QueueBufferOutput output;
- sp<IProducerListener> dummyListener(new DummyProducerListener);
+ sp<IProducerListener> fakeListener(new StubProducerListener);
ASSERT_EQ(NO_INIT, mProducer->disconnect(NATIVE_WINDOW_API_CPU));
- ASSERT_EQ(OK, mProducer->connect(
- dummyListener, NATIVE_WINDOW_API_CPU, true, &output));
- ASSERT_EQ(BAD_VALUE, mProducer->connect(
- dummyListener, NATIVE_WINDOW_API_MEDIA, true, &output));
+ ASSERT_EQ(OK, mProducer->connect(fakeListener, NATIVE_WINDOW_API_CPU, true, &output));
+ ASSERT_EQ(BAD_VALUE, mProducer->connect(fakeListener, NATIVE_WINDOW_API_MEDIA, true, &output));
ASSERT_EQ(BAD_VALUE, mProducer->disconnect(NATIVE_WINDOW_API_MEDIA));
ASSERT_EQ(OK, mProducer->disconnect(NATIVE_WINDOW_API_CPU));
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index 03b9cd7..b1d3ecb 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -41,7 +41,7 @@
#include <input/InputTransport.h>
#include <input/Input.h>
-#include <ui/DisplayInfo.h>
+#include <ui/DisplayConfig.h>
#include <ui/Rect.h>
#include <ui/Region.h>
@@ -69,7 +69,6 @@
mSurfaceControl = sc;
InputChannel::openInputChannelPair("testchannels", mServerChannel, mClientChannel);
- mServerChannel->setToken(new BBinder());
mInputFlinger = getInputFlinger();
mInputFlinger->registerInputChannel(mServerChannel);
@@ -83,7 +82,8 @@
int width, int height) {
sp<SurfaceControl> surfaceControl =
scc->createSurface(String8("Test Surface"), 0 /* bufHeight */, 0 /* bufWidth */,
- PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceColor);
+ PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceEffect);
return std::make_unique<InputSurface>(surfaceControl, width, height);
}
@@ -104,6 +104,15 @@
return std::make_unique<InputSurface>(surfaceControl, width, height);
}
+ static std::unique_ptr<InputSurface> makeCursorInputSurface(
+ const sp<SurfaceComposerClient> &scc, int width, int height) {
+ sp<SurfaceControl> surfaceControl =
+ scc->createSurface(String8("Test Cursor Surface"), 0 /* bufHeight */,
+ 0 /* bufWidth */, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eCursorWindow);
+ return std::make_unique<InputSurface>(surfaceControl, width, height);
+ }
+
InputEvent* consumeEvent() {
waitForEventAvailable();
@@ -113,24 +122,35 @@
if (consumed != OK) {
return nullptr;
}
- mInputConsumer->sendFinishedSignal(seqId, true);
+ status_t status = mInputConsumer->sendFinishedSignal(seqId, true);
+ EXPECT_EQ(OK, status) << "Could not send finished signal";
return ev;
}
+ void assertFocusChange(bool hasFocus) {
+ InputEvent *ev = consumeEvent();
+ ASSERT_NE(ev, nullptr);
+ ASSERT_EQ(AINPUT_EVENT_TYPE_FOCUS, ev->getType());
+ FocusEvent *focusEvent = static_cast<FocusEvent *>(ev);
+ EXPECT_EQ(hasFocus, focusEvent->getHasFocus());
+ }
+
void expectTap(int x, int y) {
InputEvent* ev = consumeEvent();
- EXPECT_TRUE(ev != nullptr);
- EXPECT_TRUE(ev->getType() == AINPUT_EVENT_TYPE_MOTION);
+ ASSERT_NE(ev, nullptr);
+ ASSERT_EQ(AINPUT_EVENT_TYPE_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_EQ(0, mev->getFlags() & VERIFIED_MOTION_EVENT_FLAGS);
ev = consumeEvent();
- EXPECT_TRUE(ev != nullptr);
- EXPECT_TRUE(ev->getType() == AINPUT_EVENT_TYPE_MOTION);
+ ASSERT_NE(ev, nullptr);
+ ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, ev->getType());
mev = static_cast<MotionEvent*>(ev);
EXPECT_EQ(AMOTION_EVENT_ACTION_UP, mev->getAction());
+ EXPECT_EQ(0, mev->getFlags() & VERIFIED_MOTION_EVENT_FLAGS);
}
~InputSurface() {
@@ -165,11 +185,11 @@
}
void populateInputInfo(int width, int height) {
- mInputInfo.token = mServerChannel->getToken();
+ mInputInfo.token = mServerChannel->getConnectionToken();
mInputInfo.name = "Test info";
mInputInfo.layoutParamsFlags = InputWindowInfo::FLAG_NOT_TOUCH_MODAL;
mInputInfo.layoutParamsType = InputWindowInfo::TYPE_BASE_APPLICATION;
- mInputInfo.dispatchingTimeout = 100000;
+ mInputInfo.dispatchingTimeout = seconds_to_nanoseconds(5);
mInputInfo.globalScaleFactor = 1.0;
mInputInfo.canReceiveKeys = true;
mInputInfo.hasFocus = true;
@@ -187,7 +207,7 @@
InputApplicationInfo aInfo;
aInfo.token = new BBinder();
aInfo.name = "Test app info";
- aInfo.dispatchingTimeout = 100000;
+ aInfo.dispatchingTimeout = seconds_to_nanoseconds(5);
mInputInfo.applicationInfo = aInfo;
}
@@ -213,15 +233,15 @@
ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
const auto display = mComposerClient->getInternalDisplayToken();
- ASSERT_FALSE(display == nullptr);
+ ASSERT_NE(display, nullptr);
- DisplayInfo info;
- ASSERT_EQ(NO_ERROR, mComposerClient->getDisplayInfo(display, &info));
+ DisplayConfig config;
+ ASSERT_EQ(NO_ERROR, mComposerClient->getActiveDisplayConfig(display, &config));
// After a new buffer is queued, SurfaceFlinger is notified and will
// latch the new buffer on next vsync. Let's heuristically wait for 3
// vsyncs.
- mBufferPostDelay = int32_t(1e6 / info.fps) * 3;
+ mBufferPostDelay = static_cast<int32_t>(1e6 / config.refreshRate) * 3;
}
void TearDown() {
@@ -260,18 +280,28 @@
TEST_F(InputSurfacesTest, can_receive_input) {
std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
surface->showAt(100, 100);
+ surface->assertFocusChange(true);
injectTap(101, 101);
- EXPECT_TRUE(surface->consumeEvent() != nullptr);
+ EXPECT_NE(surface->consumeEvent(), nullptr);
}
+/**
+ * Set up two surfaces side-by-side. Tap each surface.
+ * Next, swap the positions of the two surfaces. Inject tap into the two
+ * original locations. Ensure that the tap is received by the surfaces in the
+ * reverse order.
+ */
TEST_F(InputSurfacesTest, input_respects_positioning) {
std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
surface->showAt(100, 100);
+ surface->assertFocusChange(true);
std::unique_ptr<InputSurface> surface2 = makeSurface(100, 100);
surface2->showAt(200, 200);
+ surface->assertFocusChange(false);
+ surface2->assertFocusChange(true);
injectTap(201, 201);
surface2->expectTap(1, 1);
@@ -298,11 +328,16 @@
std::unique_ptr<InputSurface> surface2 = makeSurface(100, 100);
surface->showAt(10, 10);
+ surface->assertFocusChange(true);
surface2->showAt(10, 10);
+ surface->assertFocusChange(false);
+ surface2->assertFocusChange(true);
surface->doTransaction([](auto &t, auto &sc) {
t.setLayer(sc, LAYER_BASE + 1);
});
+ surface2->assertFocusChange(false);
+ surface->assertFocusChange(true);
injectTap(11, 11);
surface->expectTap(1, 1);
@@ -310,6 +345,8 @@
surface2->doTransaction([](auto &t, auto &sc) {
t.setLayer(sc, LAYER_BASE + 1);
});
+ surface2->assertFocusChange(true);
+ surface->assertFocusChange(false);
injectTap(11, 11);
surface2->expectTap(1, 1);
@@ -317,6 +354,8 @@
surface2->doTransaction([](auto &t, auto &sc) {
t.hide(sc);
});
+ surface2->assertFocusChange(false);
+ surface->assertFocusChange(true);
injectTap(11, 11);
surface->expectTap(1, 1);
@@ -329,9 +368,12 @@
std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
bgSurface->showAt(100, 100);
+ bgSurface->assertFocusChange(true);
fgSurface->mInputInfo.surfaceInset = 5;
fgSurface->showAt(100, 100);
+ fgSurface->assertFocusChange(true);
+ bgSurface->assertFocusChange(false);
injectTap(106, 106);
fgSurface->expectTap(1, 1);
@@ -345,9 +387,12 @@
std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100);
std::unique_ptr<InputSurface> childSurface = makeSurface(100, 100);
parentSurface->showAt(100, 100);
+ parentSurface->assertFocusChange(true);
childSurface->mInputInfo.surfaceInset = 10;
childSurface->showAt(100, 100);
+ childSurface->assertFocusChange(true);
+ parentSurface->assertFocusChange(false);
childSurface->doTransaction([&](auto &t, auto &sc) {
t.setPosition(sc, -5, -5);
@@ -366,9 +411,12 @@
std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
bgSurface->showAt(100, 100);
+ bgSurface->assertFocusChange(true);
fgSurface->mInputInfo.surfaceInset = 5;
fgSurface->showAt(100, 100);
+ bgSurface->assertFocusChange(false);
+ fgSurface->assertFocusChange(true);
fgSurface->doTransaction([&](auto &t, auto &sc) { t.setMatrix(sc, 2.0, 0, 0, 4.0); });
@@ -385,6 +433,7 @@
// In case we pass the very big inset without any checking.
fgSurface->mInputInfo.surfaceInset = INT32_MAX;
fgSurface->showAt(100, 100);
+ fgSurface->assertFocusChange(true);
fgSurface->doTransaction([&](auto &t, auto &sc) { t.setMatrix(sc, 2.0, 0, 0, 2.0); });
@@ -401,6 +450,7 @@
t.setTransparentRegionHint(sc, transparentRegion);
});
surface->showAt(100, 100);
+ surface->assertFocusChange(true);
injectTap(101, 101);
surface->expectTap(1, 1);
}
@@ -415,7 +465,10 @@
InputSurface::makeBufferInputSurface(mComposerClient, 100, 100);
bgSurface->showAt(10, 10);
+ bgSurface->assertFocusChange(true);
bufferSurface->showAt(10, 10);
+ bgSurface->assertFocusChange(false);
+ bufferSurface->assertFocusChange(true);
injectTap(11, 11);
bufferSurface->expectTap(1, 1);
@@ -432,7 +485,10 @@
postBuffer(bufferSurface->mSurfaceControl);
bgSurface->showAt(10, 10);
+ bgSurface->assertFocusChange(true);
bufferSurface->showAt(10, 10);
+ bufferSurface->assertFocusChange(true);
+ bgSurface->assertFocusChange(false);
injectTap(11, 11);
bufferSurface->expectTap(1, 1);
@@ -448,7 +504,10 @@
std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
bgSurface->showAt(10, 10);
+ bgSurface->assertFocusChange(true);
fgSurface->showAt(10, 10);
+ bgSurface->assertFocusChange(false);
+ fgSurface->assertFocusChange(true);
injectTap(11, 11);
fgSurface->expectTap(1, 1);
@@ -465,12 +524,17 @@
InputSurface::makeContainerInputSurface(mComposerClient, 100, 100);
bgSurface->showAt(10, 10);
+ bgSurface->assertFocusChange(true);
containerSurface->showAt(10, 10);
+ bgSurface->assertFocusChange(false);
+ containerSurface->assertFocusChange(true);
injectTap(11, 11);
containerSurface->expectTap(1, 1);
containerSurface->doTransaction([](auto &t, auto &sc) { t.hide(sc); });
+ containerSurface->assertFocusChange(false);
+ bgSurface->assertFocusChange(true);
injectTap(11, 11);
bgSurface->expectTap(1, 1);
@@ -479,9 +543,23 @@
TEST_F(InputSurfacesTest, input_respects_outscreen) {
std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
surface->showAt(-1, -1);
+ surface->assertFocusChange(true);
injectTap(0, 0);
surface->expectTap(1, 1);
}
+
+TEST_F(InputSurfacesTest, input_ignores_cursor_layer) {
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ std::unique_ptr<InputSurface> cursorSurface =
+ InputSurface::makeCursorInputSurface(mComposerClient, 10, 10);
+
+ surface->showAt(10, 10);
+ surface->assertFocusChange(true);
+ cursorSurface->showAt(10, 10);
+
+ injectTap(11, 11);
+ surface->expectTap(1, 1);
+}
}
}
diff --git a/libs/gui/tests/IGraphicBufferProducer_test.cpp b/libs/gui/tests/IGraphicBufferProducer_test.cpp
index aef7aed..15bd32d 100644
--- a/libs/gui/tests/IGraphicBufferProducer_test.cpp
+++ b/libs/gui/tests/IGraphicBufferProducer_test.cpp
@@ -17,7 +17,7 @@
#define LOG_TAG "IGraphicBufferProducer_test"
//#define LOG_NDEBUG 0
-#include "DummyConsumer.h"
+#include "MockConsumer.h"
#include <gtest/gtest.h>
@@ -89,7 +89,7 @@
ALOGV("Begin test: %s.%s", testInfo->test_case_name(),
testInfo->name());
- mDC = new DummyConsumer;
+ mMC = new MockConsumer;
switch (GetParam()) {
case USE_BUFFER_QUEUE_PRODUCER: {
@@ -114,7 +114,7 @@
}
// Must connect consumer before producer connects will succeed.
- ASSERT_OK(mConsumer->consumerConnect(mDC, /*controlledByApp*/false));
+ ASSERT_OK(mConsumer->consumerConnect(mMC, /*controlledByApp*/ false));
}
virtual void TearDown() {
@@ -249,7 +249,7 @@
}
private: // hide from test body
- sp<DummyConsumer> mDC;
+ sp<MockConsumer> mMC;
protected: // accessible from test body
sp<IGraphicBufferProducer> mProducer;
@@ -543,7 +543,6 @@
// Should now be able to dequeue up to minBuffers times
DequeueBufferResult result;
for (int i = 0; i < minBuffers; ++i) {
-
EXPECT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
(dequeueBuffer(DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
TEST_PRODUCER_USAGE_BITS, &result)))
diff --git a/libs/gui/tests/Malicious.cpp b/libs/gui/tests/Malicious.cpp
index acd4297..58d7cc6 100644
--- a/libs/gui/tests/Malicious.cpp
+++ b/libs/gui/tests/Malicious.cpp
@@ -129,7 +129,7 @@
int32_t mExpectedSlot = 0;
};
-class DummyListener : public BnConsumerListener {
+class FakeListener : public BnConsumerListener {
public:
void onFrameAvailable(const BufferItem&) override {}
void onBuffersReleased() override {}
@@ -140,7 +140,7 @@
sp<IGraphicBufferProducer> producer;
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer);
- sp<IConsumerListener> listener = new DummyListener;
+ sp<IConsumerListener> listener = new FakeListener;
consumer->consumerConnect(listener, false);
sp<MaliciousBQP> malicious = new MaliciousBQP(producer);
diff --git a/libs/gui/tests/DummyConsumer.h b/libs/gui/tests/MockConsumer.h
similarity index 94%
rename from libs/gui/tests/DummyConsumer.h
rename to libs/gui/tests/MockConsumer.h
index 502bdf9..4a6c51c 100644
--- a/libs/gui/tests/DummyConsumer.h
+++ b/libs/gui/tests/MockConsumer.h
@@ -18,7 +18,7 @@
namespace android {
-struct DummyConsumer : public BnConsumerListener {
+struct MockConsumer : public BnConsumerListener {
void onFrameAvailable(const BufferItem& /* item */) override {}
void onBuffersReleased() override {}
void onSidebandStreamChanged() override {}
diff --git a/libs/gui/tests/RegionSampling_test.cpp b/libs/gui/tests/RegionSampling_test.cpp
index d33ecfb..6746b0a 100644
--- a/libs/gui/tests/RegionSampling_test.cpp
+++ b/libs/gui/tests/RegionSampling_test.cpp
@@ -183,7 +183,7 @@
mBackgroundLayer =
mSurfaceComposerClient->createSurface(String8("Background RegionSamplingTest"), 0,
0, PIXEL_FORMAT_RGBA_8888,
- ISurfaceComposerClient::eFXSurfaceColor);
+ ISurfaceComposerClient::eFXSurfaceEffect);
uint32_t layerPositionBottom = 0x7E000000;
SurfaceComposerClient::Transaction{}
.setLayer(mBackgroundLayer, layerPositionBottom)
@@ -240,6 +240,19 @@
float const luma_gray = 0.50;
};
+TEST_F(RegionSamplingTest, invalidLayerHandle_doesNotCrash) {
+ sp<ISurfaceComposer> composer = ComposerService::getComposerService();
+ sp<Listener> listener = new Listener();
+ const Rect sampleArea{100, 100, 200, 200};
+ // Passing in composer service as the layer handle should not crash, we'll
+ // treat it as a layer that no longer exists and silently allow sampling to
+ // occur.
+ status_t status = composer->addRegionSamplingListener(sampleArea,
+ IInterface::asBinder(composer), listener);
+ ASSERT_EQ(NO_ERROR, status);
+ composer->removeRegionSamplingListener(listener);
+}
+
TEST_F(RegionSamplingTest, DISABLED_CollectsLuma) {
fill_render(rgba_green);
@@ -297,4 +310,70 @@
composer->removeRegionSamplingListener(grayListener);
}
+TEST_F(RegionSamplingTest, DISABLED_TestIfInvalidInputParameters) {
+ sp<ISurfaceComposer> composer = ComposerService::getComposerService();
+ sp<Listener> listener = new Listener();
+ const Rect sampleArea{100, 100, 200, 200};
+ // Invalid input sampleArea
+ EXPECT_EQ(BAD_VALUE,
+ composer->addRegionSamplingListener(Rect::INVALID_RECT, mTopLayer->getHandle(),
+ listener));
+ listener->reset();
+ // Invalid input binder
+ EXPECT_EQ(NO_ERROR, composer->addRegionSamplingListener(sampleArea, NULL, listener));
+ // Invalid input listener
+ EXPECT_EQ(BAD_VALUE,
+ composer->addRegionSamplingListener(sampleArea, mTopLayer->getHandle(), NULL));
+ EXPECT_EQ(BAD_VALUE, composer->removeRegionSamplingListener(NULL));
+ // remove the listener
+ composer->removeRegionSamplingListener(listener);
+}
+
+TEST_F(RegionSamplingTest, DISABLED_TestCallbackAfterRemoveListener) {
+ fill_render(rgba_green);
+ sp<ISurfaceComposer> composer = ComposerService::getComposerService();
+ sp<Listener> listener = new Listener();
+ const Rect sampleArea{100, 100, 200, 200};
+ composer->addRegionSamplingListener(sampleArea, mTopLayer->getHandle(), listener);
+ fill_render(rgba_green);
+
+ EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received";
+ EXPECT_NEAR(listener->luma(), luma_green, error_margin);
+
+ listener->reset();
+ composer->removeRegionSamplingListener(listener);
+ fill_render(rgba_green);
+ EXPECT_FALSE(listener->wait_event(100ms))
+ << "callback should stop after remove the region sampling listener";
+}
+
+TEST_F(RegionSamplingTest, DISABLED_CollectsLumaFromMovingLayer) {
+ sp<ISurfaceComposer> composer = ComposerService::getComposerService();
+ sp<Listener> listener = new Listener();
+ Rect sampleArea{100, 100, 200, 200};
+
+ // Test: listener in (100, 100). See layer before move, no layer after move.
+ fill_render(rgba_blue);
+ composer->addRegionSamplingListener(sampleArea, mTopLayer->getHandle(), listener);
+ EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received";
+ EXPECT_NEAR(listener->luma(), luma_blue, error_margin);
+ listener->reset();
+ SurfaceComposerClient::Transaction{}.setPosition(mContentLayer, 600, 600).apply();
+ EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received";
+ EXPECT_NEAR(listener->luma(), luma_gray, error_margin);
+ composer->removeRegionSamplingListener(listener);
+
+ // Test: listener offset to (600, 600). No layer before move, see layer after move.
+ fill_render(rgba_green);
+ sampleArea.offsetTo(600, 600);
+ composer->addRegionSamplingListener(sampleArea, mTopLayer->getHandle(), listener);
+ EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received";
+ EXPECT_NEAR(listener->luma(), luma_gray, error_margin);
+ listener->reset();
+ SurfaceComposerClient::Transaction{}.setPosition(mContentLayer, 600, 600).apply();
+ EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received";
+ EXPECT_NEAR(listener->luma(), luma_green, error_margin);
+ composer->removeRegionSamplingListener(listener);
+}
+
} // namespace android::test
diff --git a/libs/gui/tests/SamplingDemo.cpp b/libs/gui/tests/SamplingDemo.cpp
index 9891587..5c1bebb 100644
--- a/libs/gui/tests/SamplingDemo.cpp
+++ b/libs/gui/tests/SamplingDemo.cpp
@@ -39,7 +39,7 @@
sp<SurfaceComposerClient> client = new SurfaceComposerClient;
mButton = client->createSurface(String8(name), 0, 0, PIXEL_FORMAT_RGBA_8888,
- ISurfaceComposerClient::eFXSurfaceColor);
+ ISurfaceComposerClient::eFXSurfaceEffect);
const int32_t width = samplingArea.getWidth();
const int32_t height = samplingArea.getHeight();
@@ -55,7 +55,7 @@
.apply();
mButtonBlend = client->createSurface(String8(name) + "Blend", 0, 0, PIXEL_FORMAT_RGBA_8888,
- ISurfaceComposerClient::eFXSurfaceColor);
+ ISurfaceComposerClient::eFXSurfaceEffect);
SurfaceComposerClient::Transaction{}
.setLayer(mButtonBlend, 0x7ffffffe)
@@ -73,7 +73,7 @@
if (HIGHLIGHT_SAMPLING_AREA) {
mSamplingArea =
client->createSurface(String8("SamplingArea"), 0, 0, PIXEL_FORMAT_RGBA_8888,
- ISurfaceComposerClient::eFXSurfaceColor);
+ ISurfaceComposerClient::eFXSurfaceEffect);
SurfaceComposerClient::Transaction{}
.setLayer(mSamplingArea, 0x7ffffffd)
diff --git a/libs/gui/tests/StreamSplitter_test.cpp b/libs/gui/tests/StreamSplitter_test.cpp
index ad6e051..b65cdda 100644
--- a/libs/gui/tests/StreamSplitter_test.cpp
+++ b/libs/gui/tests/StreamSplitter_test.cpp
@@ -48,7 +48,7 @@
}
};
-struct DummyListener : public BnConsumerListener {
+struct FakeListener : public BnConsumerListener {
virtual void onFrameAvailable(const BufferItem& /* item */) {}
virtual void onBuffersReleased() {}
virtual void onSidebandStreamChanged() {}
@@ -64,7 +64,7 @@
sp<IGraphicBufferProducer> outputProducer;
sp<IGraphicBufferConsumer> outputConsumer;
BufferQueue::createBufferQueue(&outputProducer, &outputConsumer);
- ASSERT_EQ(OK, outputConsumer->consumerConnect(new DummyListener, false));
+ ASSERT_EQ(OK, outputConsumer->consumerConnect(new FakeListener, false));
sp<StreamSplitter> splitter;
status_t status = StreamSplitter::createSplitter(inputConsumer, &splitter);
@@ -75,8 +75,9 @@
ASSERT_EQ(OK, outputProducer->allowAllocation(false));
IGraphicBufferProducer::QueueBufferOutput qbOutput;
- ASSERT_EQ(OK, inputProducer->connect(new DummyProducerListener,
- NATIVE_WINDOW_API_CPU, false, &qbOutput));
+ ASSERT_EQ(OK,
+ inputProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false,
+ &qbOutput));
int slot;
sp<Fence> fence;
@@ -132,8 +133,7 @@
for (int output = 0; output < NUM_OUTPUTS; ++output) {
BufferQueue::createBufferQueue(&outputProducers[output],
&outputConsumers[output]);
- ASSERT_EQ(OK, outputConsumers[output]->consumerConnect(
- new DummyListener, false));
+ ASSERT_EQ(OK, outputConsumers[output]->consumerConnect(new FakeListener, false));
}
sp<StreamSplitter> splitter;
@@ -147,8 +147,9 @@
}
IGraphicBufferProducer::QueueBufferOutput qbOutput;
- ASSERT_EQ(OK, inputProducer->connect(new DummyProducerListener,
- NATIVE_WINDOW_API_CPU, false, &qbOutput));
+ ASSERT_EQ(OK,
+ inputProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false,
+ &qbOutput));
int slot;
sp<Fence> fence;
@@ -203,7 +204,7 @@
sp<IGraphicBufferProducer> outputProducer;
sp<IGraphicBufferConsumer> outputConsumer;
BufferQueue::createBufferQueue(&outputProducer, &outputConsumer);
- ASSERT_EQ(OK, outputConsumer->consumerConnect(new DummyListener, false));
+ ASSERT_EQ(OK, outputConsumer->consumerConnect(new FakeListener, false));
sp<StreamSplitter> splitter;
status_t status = StreamSplitter::createSplitter(inputConsumer, &splitter);
@@ -211,8 +212,9 @@
ASSERT_EQ(OK, splitter->addOutput(outputProducer));
IGraphicBufferProducer::QueueBufferOutput qbOutput;
- ASSERT_EQ(OK, inputProducer->connect(new DummyProducerListener,
- NATIVE_WINDOW_API_CPU, false, &qbOutput));
+ ASSERT_EQ(OK,
+ inputProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false,
+ &qbOutput));
int slot;
sp<Fence> fence;
diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp
index 65e09f2..c7458a3 100644
--- a/libs/gui/tests/SurfaceTextureClient_test.cpp
+++ b/libs/gui/tests/SurfaceTextureClient_test.cpp
@@ -28,8 +28,6 @@
#include <utils/Log.h>
#include <utils/Thread.h>
-extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
-
namespace android {
class SurfaceTextureClientTest : public ::testing::Test {
@@ -56,7 +54,7 @@
mANW = mSTC;
// We need a valid GL context so we can test updateTexImage()
- // This initializes EGL and create a dummy GL context with a
+ // This initializes EGL and create a GL context placeholder with a
// pbuffer render target.
mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
ASSERT_EQ(EGL_SUCCESS, eglGetError());
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index a851687..592913c 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -14,22 +14,23 @@
* limitations under the License.
*/
-#include "DummyConsumer.h"
+#include "MockConsumer.h"
#include <gtest/gtest.h>
+#include <SurfaceFlingerProperties.h>
#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
#include <binder/ProcessState.h>
#include <configstore/Utils.h>
-#include <cutils/properties.h>
-#include <inttypes.h>
#include <gui/BufferItemConsumer.h>
#include <gui/IDisplayEventConnection.h>
#include <gui/IProducerListener.h>
#include <gui/ISurfaceComposer.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
+#include <inttypes.h>
#include <private/gui/ComposerService.h>
+#include <ui/BufferQueueDefs.h>
#include <ui/Rect.h>
#include <utils/String8.h>
@@ -46,23 +47,20 @@
using Transaction = SurfaceComposerClient::Transaction;
-static bool hasWideColorDisplay =
- getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::hasWideColorDisplay>(false);
+static bool hasWideColorDisplay = android::sysprop::has_wide_color_display(false);
-static bool hasHdrDisplay =
- getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::hasHDRDisplay>(false);
+static bool hasHdrDisplay = android::sysprop::has_HDR_display(false);
class FakeSurfaceComposer;
class FakeProducerFrameEventHistory;
static constexpr uint64_t NO_FRAME_INDEX = std::numeric_limits<uint64_t>::max();
-class DummySurfaceListener : public SurfaceListener {
+class FakeSurfaceListener : public SurfaceListener {
public:
- DummySurfaceListener(bool enableReleasedCb = false) :
- mEnableReleaseCb(enableReleasedCb),
- mBuffersReleased(0) {}
- virtual ~DummySurfaceListener() = default;
+ FakeSurfaceListener(bool enableReleasedCb = false)
+ : mEnableReleaseCb(enableReleasedCb), mBuffersReleased(0) {}
+ virtual ~FakeSurfaceListener() = default;
virtual void onBufferReleased() {
mBuffersReleased++;
@@ -125,15 +123,15 @@
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer);
- sp<DummyConsumer> dummyConsumer(new DummyConsumer);
- consumer->consumerConnect(dummyConsumer, false);
+ sp<MockConsumer> mockConsumer(new MockConsumer);
+ consumer->consumerConnect(mockConsumer, false);
consumer->setConsumerName(String8("TestConsumer"));
sp<Surface> surface = new Surface(producer);
sp<ANativeWindow> window(surface);
- sp<DummySurfaceListener> listener;
+ sp<FakeSurfaceListener> listener;
if (hasSurfaceListener) {
- listener = new DummySurfaceListener(enableReleasedCb);
+ listener = new FakeSurfaceListener(enableReleasedCb);
}
ASSERT_EQ(OK, surface->connect(
NATIVE_WINDOW_API_CPU,
@@ -382,8 +380,8 @@
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer);
- sp<DummyConsumer> dummyConsumer(new DummyConsumer);
- consumer->consumerConnect(dummyConsumer, false);
+ sp<MockConsumer> mockConsumer(new MockConsumer);
+ consumer->consumerConnect(mockConsumer, false);
consumer->setConsumerName(String8("TestConsumer"));
sp<Surface> surface = new Surface(producer);
@@ -398,8 +396,8 @@
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer);
- sp<DummyConsumer> dummyConsumer(new DummyConsumer);
- consumer->consumerConnect(dummyConsumer, false);
+ sp<MockConsumer> mockConsumer(new MockConsumer);
+ consumer->consumerConnect(mockConsumer, false);
consumer->setConsumerName(String8("TestConsumer"));
sp<Surface> surface = new Surface(producer);
@@ -429,8 +427,8 @@
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer);
- sp<DummyConsumer> dummyConsumer(new DummyConsumer);
- consumer->consumerConnect(dummyConsumer, false);
+ sp<MockConsumer> mockConsumer(new MockConsumer);
+ consumer->consumerConnect(mockConsumer, false);
consumer->setConsumerName(String8("TestConsumer"));
sp<Surface> surface = new Surface(producer);
@@ -453,8 +451,8 @@
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer);
- sp<DummyConsumer> dummyConsumer(new DummyConsumer);
- consumer->consumerConnect(dummyConsumer, false);
+ sp<MockConsumer> mockConsumer(new MockConsumer);
+ consumer->consumerConnect(mockConsumer, false);
consumer->setConsumerName(String8("TestConsumer"));
sp<Surface> surface = new Surface(producer);
@@ -498,8 +496,8 @@
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer);
- sp<DummyConsumer> dummyConsumer(new DummyConsumer);
- consumer->consumerConnect(dummyConsumer, false);
+ sp<MockConsumer> mockConsumer(new MockConsumer);
+ consumer->consumerConnect(mockConsumer, false);
consumer->setConsumerName(String8("TestConsumer"));
sp<Surface> surface = new Surface(producer);
@@ -524,13 +522,13 @@
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer);
- sp<DummyConsumer> dummyConsumer(new DummyConsumer);
- consumer->consumerConnect(dummyConsumer, false);
+ sp<MockConsumer> mockConsumer(new MockConsumer);
+ consumer->consumerConnect(mockConsumer, false);
consumer->setConsumerName(String8("TestConsumer"));
sp<Surface> surface = new Surface(producer);
sp<ANativeWindow> window(surface);
- sp<DummyProducerListener> listener = new DummyProducerListener();
+ sp<StubProducerListener> listener = new StubProducerListener();
ASSERT_EQ(OK, surface->connect(
NATIVE_WINDOW_API_CPU,
/*listener*/listener,
@@ -617,7 +615,7 @@
anw->dequeueBuffer(anw.get(), &buffer, &fenceFd);
nsecs_t after = systemTime(CLOCK_MONOTONIC);
- nsecs_t lastDequeueTime = mSurface->getLastDequeueStartTime();
+ nsecs_t lastDequeueTime = ANativeWindow_getLastDequeueStartTime(anw.get());
ASSERT_LE(before, lastDequeueTime);
ASSERT_GE(after, lastDequeueTime);
}
@@ -688,6 +686,7 @@
const sp<IBinder>& /*applyToken*/,
const InputWindowCommands& /*inputWindowCommands*/,
int64_t /*desiredPresentTime*/, const client_cache_t& /*cachedBuffer*/,
+ bool /*hasListenerCallbacks*/,
const std::vector<ListenerCallbacks>& /*listenerCallbacks*/) override {
}
@@ -717,15 +716,18 @@
}
void setPowerMode(const sp<IBinder>& /*display*/, int /*mode*/) override {}
- status_t getDisplayConfigs(const sp<IBinder>& /*display*/,
- Vector<DisplayInfo>* /*configs*/) override { return NO_ERROR; }
+ status_t getDisplayInfo(const sp<IBinder>& /*display*/, DisplayInfo*) override {
+ return NO_ERROR;
+ }
+ status_t getDisplayConfigs(const sp<IBinder>& /*display*/, Vector<DisplayConfig>*) override {
+ return NO_ERROR;
+ }
+ status_t getDisplayState(const sp<IBinder>& /*display*/, ui::DisplayState*) override {
+ return NO_ERROR;
+ }
status_t getDisplayStats(const sp<IBinder>& /*display*/,
DisplayStatInfo* /*stats*/) override { return NO_ERROR; }
int getActiveConfig(const sp<IBinder>& /*display*/) override { return 0; }
- status_t setActiveConfig(const sp<IBinder>& /*display*/, int /*id*/)
- override {
- return NO_ERROR;
- }
status_t getDisplayColorModes(const sp<IBinder>& /*display*/,
Vector<ColorMode>* /*outColorModes*/) override {
return NO_ERROR;
@@ -741,21 +743,30 @@
status_t setActiveColorMode(const sp<IBinder>& /*display*/,
ColorMode /*colorMode*/) override { return NO_ERROR; }
status_t captureScreen(const sp<IBinder>& /*display*/, sp<GraphicBuffer>* /*outBuffer*/,
- bool& /* outCapturedSecureLayers */,
- const ui::Dataspace /*reqDataspace*/,
- const ui::PixelFormat /*reqPixelFormat*/, Rect /*sourceCrop*/,
+ bool& /*outCapturedSecureLayers*/, ui::Dataspace /*reqDataspace*/,
+ ui::PixelFormat /*reqPixelFormat*/, const Rect& /*sourceCrop*/,
uint32_t /*reqWidth*/, uint32_t /*reqHeight*/,
- bool /*useIdentityTransform*/, Rotation /*rotation*/,
+ bool /*useIdentityTransform*/, ui::Rotation,
bool /*captureSecureLayers*/) override {
return NO_ERROR;
}
+ status_t getAutoLowLatencyModeSupport(const sp<IBinder>& /*display*/,
+ bool* /*outSupport*/) const override {
+ return NO_ERROR;
+ }
+ void setAutoLowLatencyMode(const sp<IBinder>& /*display*/, bool /*on*/) override {}
+ status_t getGameContentTypeSupport(const sp<IBinder>& /*display*/,
+ bool* /*outSupport*/) const override {
+ return NO_ERROR;
+ }
+ void setGameContentType(const sp<IBinder>& /*display*/, bool /*on*/) override {}
status_t captureScreen(uint64_t /*displayOrLayerStack*/, ui::Dataspace* /*outDataspace*/,
sp<GraphicBuffer>* /*outBuffer*/) override {
return NO_ERROR;
}
virtual status_t captureLayers(
const sp<IBinder>& /*parentHandle*/, sp<GraphicBuffer>* /*outBuffer*/,
- const ui::Dataspace /*reqDataspace*/, const ui::PixelFormat /*reqPixelFormat*/,
+ ui::Dataspace /*reqDataspace*/, ui::PixelFormat /*reqPixelFormat*/,
const Rect& /*sourceCrop*/,
const std::unordered_set<sp<IBinder>,
ISurfaceComposer::SpHash<IBinder>>& /*excludeHandles*/,
@@ -774,7 +785,7 @@
return NO_ERROR;
}
status_t injectVSync(nsecs_t /*when*/) override { return NO_ERROR; }
- status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* /*layers*/) const override {
+ status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* /*layers*/) override {
return NO_ERROR;
}
status_t getCompositionPreference(
@@ -791,7 +802,7 @@
}
status_t setDisplayContentSamplingEnabled(const sp<IBinder>& /*display*/, bool /*enable*/,
uint8_t /*componentMask*/,
- uint64_t /*maxFrames*/) const override {
+ uint64_t /*maxFrames*/) override {
return NO_ERROR;
}
status_t getDisplayedContentSample(const sp<IBinder>& /*display*/, uint64_t /*maxFrames*/,
@@ -809,7 +820,7 @@
return NO_ERROR;
}
status_t setDisplayBrightness(const sp<IBinder>& /*displayToken*/,
- float /*brightness*/) const override {
+ float /*brightness*/) override {
return NO_ERROR;
}
@@ -822,16 +833,37 @@
const sp<IRegionSamplingListener>& /*listener*/) override {
return NO_ERROR;
}
- status_t setAllowedDisplayConfigs(const sp<IBinder>& /*displayToken*/,
- const std::vector<int32_t>& /*allowedConfigs*/) override {
+ status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& /*displayToken*/,
+ int32_t /*defaultConfig*/,
+ float /*primaryRefreshRateMin*/,
+ float /*primaryRefreshRateMax*/,
+ float /*appRequestRefreshRateMin*/,
+ float /*appRequestRefreshRateMax*/) {
return NO_ERROR;
}
- status_t getAllowedDisplayConfigs(const sp<IBinder>& /*displayToken*/,
- std::vector<int32_t>* /*outAllowedConfigs*/) override {
+ status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& /*displayToken*/,
+ int32_t* /*outDefaultConfig*/,
+ float* /*outPrimaryRefreshRateMin*/,
+ float* /*outPrimaryRefreshRateMax*/,
+ float* /*outAppRequestRefreshRateMin*/,
+ float* /*outAppRequestRefreshRateMax*/) override {
return NO_ERROR;
- }
+ };
status_t notifyPowerHint(int32_t /*hintId*/) override { return NO_ERROR; }
+ status_t setGlobalShadowSettings(const half4& /*ambientColor*/, const half4& /*spotColor*/,
+ float /*lightPosY*/, float /*lightPosZ*/,
+ float /*lightRadius*/) override {
+ return NO_ERROR;
+ }
+
+ status_t setFrameRate(const sp<IGraphicBufferProducer>& /*surface*/, float /*frameRate*/,
+ int8_t /*compatibility*/) override {
+ return NO_ERROR;
+ }
+
+ status_t acquireFrameRateFlexibilityToken(sp<IBinder>* /*outToken*/) { return NO_ERROR; }
+
protected:
IBinder* onAsBinder() override { return nullptr; }
@@ -1872,4 +1904,99 @@
EXPECT_EQ(-1, outDisplayPresentTime);
}
+TEST_F(SurfaceTest, DequeueWithConsumerDrivenSize) {
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&producer, &consumer);
+
+ sp<MockConsumer> mockConsumer(new MockConsumer);
+ consumer->consumerConnect(mockConsumer, false);
+ consumer->setDefaultBufferSize(10, 10);
+
+ sp<Surface> surface = new Surface(producer);
+ sp<ANativeWindow> window(surface);
+ native_window_api_connect(window.get(), NATIVE_WINDOW_API_CPU);
+ native_window_set_buffers_dimensions(window.get(), 0, 0);
+
+ int fence;
+ ANativeWindowBuffer* buffer;
+
+ // Buffer size is driven by the consumer
+ ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffer, &fence));
+ EXPECT_EQ(10, buffer->width);
+ EXPECT_EQ(10, buffer->height);
+ ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffer, fence));
+
+ // Buffer size is driven by the consumer
+ consumer->setDefaultBufferSize(10, 20);
+ ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffer, &fence));
+ EXPECT_EQ(10, buffer->width);
+ EXPECT_EQ(20, buffer->height);
+ ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffer, fence));
+
+ // Transform hint isn't synced to producer before queueBuffer or connect
+ consumer->setTransformHint(NATIVE_WINDOW_TRANSFORM_ROT_270);
+ ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffer, &fence));
+ EXPECT_EQ(10, buffer->width);
+ EXPECT_EQ(20, buffer->height);
+ ASSERT_EQ(NO_ERROR, window->queueBuffer(window.get(), buffer, fence));
+
+ // Transform hint is synced to producer but no auto prerotation
+ consumer->setTransformHint(NATIVE_WINDOW_TRANSFORM_ROT_270);
+ ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffer, &fence));
+ EXPECT_EQ(10, buffer->width);
+ EXPECT_EQ(20, buffer->height);
+ ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffer, fence));
+
+ // Prerotation is driven by the consumer with the transform hint used by producer
+ native_window_set_auto_prerotation(window.get(), true);
+ ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffer, &fence));
+ EXPECT_EQ(20, buffer->width);
+ EXPECT_EQ(10, buffer->height);
+ ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffer, fence));
+
+ // Turn off auto prerotaton
+ native_window_set_auto_prerotation(window.get(), false);
+ ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffer, &fence));
+ EXPECT_EQ(10, buffer->width);
+ EXPECT_EQ(20, buffer->height);
+ ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffer, fence));
+
+ // Test auto prerotation bit is disabled after disconnect
+ native_window_set_auto_prerotation(window.get(), true);
+ native_window_api_disconnect(window.get(), NATIVE_WINDOW_API_CPU);
+ native_window_api_connect(window.get(), NATIVE_WINDOW_API_CPU);
+ consumer->setTransformHint(NATIVE_WINDOW_TRANSFORM_ROT_270);
+ native_window_set_buffers_dimensions(window.get(), 0, 0);
+ ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffer, &fence));
+ EXPECT_EQ(10, buffer->width);
+ EXPECT_EQ(20, buffer->height);
+ ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffer, fence));
+}
+
+TEST_F(SurfaceTest, DefaultMaxBufferCountSetAndUpdated) {
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&producer, &consumer);
+
+ sp<MockConsumer> mockConsumer(new MockConsumer);
+ consumer->consumerConnect(mockConsumer, false);
+
+ sp<Surface> surface = new Surface(producer);
+ sp<ANativeWindow> window(surface);
+
+ int count = -1;
+ ASSERT_EQ(NO_ERROR, window->query(window.get(), NATIVE_WINDOW_MAX_BUFFER_COUNT, &count));
+ EXPECT_EQ(BufferQueueDefs::NUM_BUFFER_SLOTS, count);
+
+ consumer->setMaxBufferCount(10);
+ ASSERT_EQ(NO_ERROR, native_window_api_connect(window.get(), NATIVE_WINDOW_API_CPU));
+ EXPECT_EQ(NO_ERROR, window->query(window.get(), NATIVE_WINDOW_MAX_BUFFER_COUNT, &count));
+ EXPECT_EQ(10, count);
+
+ ASSERT_EQ(NO_ERROR, native_window_api_disconnect(window.get(), NATIVE_WINDOW_API_CPU));
+ ASSERT_EQ(NO_ERROR, window->query(window.get(), NATIVE_WINDOW_MAX_BUFFER_COUNT, &count));
+ EXPECT_EQ(BufferQueueDefs::NUM_BUFFER_SLOTS, count);
+}
+
} // namespace android
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 6132b1c..7037680 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -51,6 +51,7 @@
"InputTransport.cpp",
"InputWindow.cpp",
"ISetInputWindowsListener.cpp",
+ "LatencyStatistics.cpp",
"VelocityControl.cpp",
"VelocityTracker.cpp",
],
@@ -58,7 +59,7 @@
shared_libs: [
"libutils",
"libbinder",
- "libui"
+ "libui",
],
sanitize: {
diff --git a/libs/input/IInputFlinger.cpp b/libs/input/IInputFlinger.cpp
index de3a23d..8ec5165 100644
--- a/libs/input/IInputFlinger.cpp
+++ b/libs/input/IInputFlinger.cpp
@@ -82,15 +82,13 @@
}
case REGISTER_INPUT_CHANNEL_TRANSACTION: {
CHECK_INTERFACE(IInputFlinger, data, reply);
- sp<InputChannel> channel = new InputChannel();
- channel->read(data);
+ sp<InputChannel> channel = InputChannel::read(data);
registerInputChannel(channel);
break;
}
case UNREGISTER_INPUT_CHANNEL_TRANSACTION: {
CHECK_INTERFACE(IInputFlinger, data, reply);
- sp<InputChannel> channel = new InputChannel();
- channel->read(data);
+ sp<InputChannel> channel = InputChannel::read(data);
unregisterInputChannel(channel);
break;
}
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 9fd25f9..31aa685 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -17,14 +17,17 @@
#define LOG_TAG "Input"
//#define LOG_NDEBUG 0
-#include <math.h>
+#include <cutils/compiler.h>
#include <limits.h>
+#include <string.h>
#include <input/Input.h>
+#include <input/InputDevice.h>
#include <input/InputEventLabels.h>
#ifdef __ANDROID__
#include <binder/Parcel.h>
+#include <sys/random.h>
#endif
namespace android {
@@ -40,18 +43,93 @@
}
}
+// --- IdGenerator ---
+IdGenerator::IdGenerator(Source source) : mSource(source) {}
+
+int32_t IdGenerator::nextId() const {
+ constexpr uint32_t SEQUENCE_NUMBER_MASK = ~SOURCE_MASK;
+ int32_t id = 0;
+
+// Avoid building against syscall getrandom(2) on host, which will fail build on Mac. Host doesn't
+// use sequence number so just always return mSource.
+#ifdef __ANDROID__
+ constexpr size_t BUF_LEN = sizeof(id);
+ size_t totalBytes = 0;
+ while (totalBytes < BUF_LEN) {
+ ssize_t bytes = TEMP_FAILURE_RETRY(getrandom(&id, BUF_LEN, GRND_NONBLOCK));
+ if (CC_UNLIKELY(bytes < 0)) {
+ ALOGW("Failed to fill in random number for sequence number: %s.", strerror(errno));
+ id = 0;
+ break;
+ }
+ totalBytes += bytes;
+ }
+#endif // __ANDROID__
+
+ return (id & SEQUENCE_NUMBER_MASK) | static_cast<int32_t>(mSource);
+}
+
// --- InputEvent ---
-void InputEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId) {
+const char* inputEventTypeToString(int32_t type) {
+ switch (type) {
+ case AINPUT_EVENT_TYPE_KEY: {
+ return "KEY";
+ }
+ case AINPUT_EVENT_TYPE_MOTION: {
+ return "MOTION";
+ }
+ case AINPUT_EVENT_TYPE_FOCUS: {
+ return "FOCUS";
+ }
+ }
+ return "UNKNOWN";
+}
+
+VerifiedKeyEvent verifiedKeyEventFromKeyEvent(const KeyEvent& event) {
+ return {{VerifiedInputEvent::Type::KEY, event.getDeviceId(), event.getEventTime(),
+ event.getSource(), event.getDisplayId()},
+ event.getAction(),
+ event.getDownTime(),
+ event.getFlags() & VERIFIED_KEY_EVENT_FLAGS,
+ event.getKeyCode(),
+ event.getScanCode(),
+ event.getMetaState(),
+ event.getRepeatCount()};
+}
+
+VerifiedMotionEvent verifiedMotionEventFromMotionEvent(const MotionEvent& event) {
+ return {{VerifiedInputEvent::Type::MOTION, event.getDeviceId(), event.getEventTime(),
+ event.getSource(), event.getDisplayId()},
+ event.getRawX(0),
+ event.getRawY(0),
+ event.getActionMasked(),
+ event.getDownTime(),
+ event.getFlags() & VERIFIED_MOTION_EVENT_FLAGS,
+ event.getMetaState(),
+ event.getButtonState()};
+}
+
+void InputEvent::initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId,
+ std::array<uint8_t, 32> hmac) {
+ mId = id;
mDeviceId = deviceId;
mSource = source;
mDisplayId = displayId;
+ mHmac = hmac;
}
void InputEvent::initialize(const InputEvent& from) {
+ mId = from.mId;
mDeviceId = from.mDeviceId;
mSource = from.mSource;
mDisplayId = from.mDisplayId;
+ mHmac = from.mHmac;
+}
+
+int32_t InputEvent::nextId() {
+ static IdGenerator idGen(IdGenerator::Source::OTHER);
+ return idGen.nextId();
}
// --- KeyEvent ---
@@ -64,19 +142,11 @@
return getKeyCodeByLabel(label);
}
-void KeyEvent::initialize(
- int32_t deviceId,
- int32_t source,
- int32_t displayId,
- int32_t action,
- int32_t flags,
- int32_t keyCode,
- int32_t scanCode,
- int32_t metaState,
- int32_t repeatCount,
- nsecs_t downTime,
- nsecs_t eventTime) {
- InputEvent::initialize(deviceId, source, displayId);
+void KeyEvent::initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId,
+ std::array<uint8_t, 32> hmac, int32_t action, int32_t flags,
+ int32_t keyCode, int32_t scanCode, int32_t metaState, int32_t repeatCount,
+ nsecs_t downTime, nsecs_t eventTime) {
+ InputEvent::initialize(id, deviceId, source, displayId, hmac);
mAction = action;
mFlags = flags;
mKeyCode = keyCode;
@@ -99,6 +169,18 @@
mEventTime = from.mEventTime;
}
+const char* KeyEvent::actionToString(int32_t action) {
+ // Convert KeyEvent action to string
+ switch (action) {
+ case AKEY_EVENT_ACTION_DOWN:
+ return "DOWN";
+ case AKEY_EVENT_ACTION_UP:
+ return "UP";
+ case AKEY_EVENT_ACTION_MULTIPLE:
+ return "MULTIPLE";
+ }
+ return "UNKNOWN";
+}
// --- PointerCoords ---
@@ -235,27 +317,16 @@
// --- MotionEvent ---
-void MotionEvent::initialize(
- int32_t deviceId,
- int32_t source,
- int32_t displayId,
- int32_t action,
- int32_t actionButton,
- int32_t flags,
- int32_t edgeFlags,
- int32_t metaState,
- int32_t buttonState,
- MotionClassification classification,
- float xOffset,
- float yOffset,
- float xPrecision,
- float yPrecision,
- nsecs_t downTime,
- nsecs_t eventTime,
- size_t pointerCount,
- const PointerProperties* pointerProperties,
- const PointerCoords* pointerCoords) {
- InputEvent::initialize(deviceId, source, displayId);
+void MotionEvent::initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId,
+ std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton,
+ int32_t flags, int32_t edgeFlags, int32_t metaState,
+ int32_t buttonState, MotionClassification classification, float xScale,
+ float yScale, float xOffset, float yOffset, float xPrecision,
+ float yPrecision, float rawXCursorPosition, float rawYCursorPosition,
+ nsecs_t downTime, nsecs_t eventTime, size_t pointerCount,
+ const PointerProperties* pointerProperties,
+ const PointerCoords* pointerCoords) {
+ InputEvent::initialize(id, deviceId, source, displayId, hmac);
mAction = action;
mActionButton = actionButton;
mFlags = flags;
@@ -263,10 +334,14 @@
mMetaState = metaState;
mButtonState = buttonState;
mClassification = classification;
+ mXScale = xScale;
+ mYScale = yScale;
mXOffset = xOffset;
mYOffset = yOffset;
mXPrecision = xPrecision;
mYPrecision = yPrecision;
+ mRawXCursorPosition = rawXCursorPosition;
+ mRawYCursorPosition = rawYCursorPosition;
mDownTime = downTime;
mPointerProperties.clear();
mPointerProperties.appendArray(pointerProperties, pointerCount);
@@ -276,7 +351,8 @@
}
void MotionEvent::copyFrom(const MotionEvent* other, bool keepHistory) {
- InputEvent::initialize(other->mDeviceId, other->mSource, other->mDisplayId);
+ InputEvent::initialize(other->mId, other->mDeviceId, other->mSource, other->mDisplayId,
+ other->mHmac);
mAction = other->mAction;
mActionButton = other->mActionButton;
mFlags = other->mFlags;
@@ -284,10 +360,14 @@
mMetaState = other->mMetaState;
mButtonState = other->mButtonState;
mClassification = other->mClassification;
+ mXScale = other->mXScale;
+ mYScale = other->mYScale;
mXOffset = other->mXOffset;
mYOffset = other->mYOffset;
mXPrecision = other->mXPrecision;
mYPrecision = other->mYPrecision;
+ mRawXCursorPosition = other->mRawXCursorPosition;
+ mRawYCursorPosition = other->mRawYCursorPosition;
mDownTime = other->mDownTime;
mPointerProperties = other->mPointerProperties;
@@ -312,6 +392,21 @@
mSamplePointerCoords.appendArray(pointerCoords, getPointerCount());
}
+float MotionEvent::getXCursorPosition() const {
+ const float rawX = getRawXCursorPosition();
+ return rawX * mXScale + mXOffset;
+}
+
+float MotionEvent::getYCursorPosition() const {
+ const float rawY = getRawYCursorPosition();
+ return rawY * mYScale + mYOffset;
+}
+
+void MotionEvent::setCursorPosition(float x, float y) {
+ mRawXCursorPosition = (x - mXOffset) / mXScale;
+ mRawYCursorPosition = (y - mYOffset) / mYScale;
+}
+
const PointerCoords* MotionEvent::getRawPointerCoords(size_t pointerIndex) const {
return &mSamplePointerCoords[getHistorySize() * getPointerCount() + pointerIndex];
}
@@ -324,9 +419,9 @@
float value = getRawPointerCoords(pointerIndex)->getAxisValue(axis);
switch (axis) {
case AMOTION_EVENT_AXIS_X:
- return value + mXOffset;
+ return value * mXScale + mXOffset;
case AMOTION_EVENT_AXIS_Y:
- return value + mYOffset;
+ return value * mYScale + mYOffset;
}
return value;
}
@@ -346,9 +441,9 @@
float value = getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis);
switch (axis) {
case AMOTION_EVENT_AXIS_X:
- return value + mXOffset;
+ return value * mXScale + mXOffset;
case AMOTION_EVENT_AXIS_Y:
- return value + mYOffset;
+ return value * mYScale + mYOffset;
}
return value;
}
@@ -420,26 +515,35 @@
float oldXOffset = mXOffset;
float oldYOffset = mYOffset;
float newX, newY;
- float rawX = getRawX(0);
- float rawY = getRawY(0);
- transformPoint(matrix, rawX + oldXOffset, rawY + oldYOffset, &newX, &newY);
- mXOffset = newX - rawX;
- mYOffset = newY - rawY;
+ float scaledRawX = getRawX(0) * mXScale;
+ float scaledRawY = getRawY(0) * mYScale;
+ transformPoint(matrix, scaledRawX + oldXOffset, scaledRawY + oldYOffset, &newX, &newY);
+ mXOffset = newX - scaledRawX;
+ mYOffset = newY - scaledRawY;
// Determine how the origin is transformed by the matrix so that we
// can transform orientation vectors.
float originX, originY;
transformPoint(matrix, 0, 0, &originX, &originY);
+ // Apply the transformation to cursor position.
+ if (isValidCursorPosition(mRawXCursorPosition, mRawYCursorPosition)) {
+ float x = mRawXCursorPosition * mXScale + oldXOffset;
+ float y = mRawYCursorPosition * mYScale + oldYOffset;
+ transformPoint(matrix, x, y, &x, &y);
+ mRawXCursorPosition = (x - mXOffset) / mXScale;
+ mRawYCursorPosition = (y - mYOffset) / mYScale;
+ }
+
// Apply the transformation to all samples.
size_t numSamples = mSamplePointerCoords.size();
for (size_t i = 0; i < numSamples; i++) {
PointerCoords& c = mSamplePointerCoords.editItemAt(i);
- float x = c.getAxisValue(AMOTION_EVENT_AXIS_X) + oldXOffset;
- float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y) + oldYOffset;
+ float x = c.getAxisValue(AMOTION_EVENT_AXIS_X) * mXScale + oldXOffset;
+ float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y) * mYScale + oldYOffset;
transformPoint(matrix, x, y, &x, &y);
- c.setAxisValue(AMOTION_EVENT_AXIS_X, x - mXOffset);
- c.setAxisValue(AMOTION_EVENT_AXIS_Y, y - mYOffset);
+ c.setAxisValue(AMOTION_EVENT_AXIS_X, (x - mXOffset) / mXScale);
+ c.setAxisValue(AMOTION_EVENT_AXIS_Y, (y - mYOffset) / mYScale);
float orientation = c.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION);
c.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION,
@@ -456,9 +560,16 @@
return BAD_VALUE;
}
+ mId = parcel->readInt32();
mDeviceId = parcel->readInt32();
- mSource = parcel->readInt32();
+ mSource = parcel->readUint32();
mDisplayId = parcel->readInt32();
+ std::vector<uint8_t> hmac;
+ status_t result = parcel->readByteVector(&hmac);
+ if (result != OK || hmac.size() != 32) {
+ return BAD_VALUE;
+ }
+ std::move(hmac.begin(), hmac.begin() + hmac.size(), mHmac.begin());
mAction = parcel->readInt32();
mActionButton = parcel->readInt32();
mFlags = parcel->readInt32();
@@ -466,10 +577,14 @@
mMetaState = parcel->readInt32();
mButtonState = parcel->readInt32();
mClassification = static_cast<MotionClassification>(parcel->readByte());
+ mXScale = parcel->readFloat();
+ mYScale = parcel->readFloat();
mXOffset = parcel->readFloat();
mYOffset = parcel->readFloat();
mXPrecision = parcel->readFloat();
mYPrecision = parcel->readFloat();
+ mRawXCursorPosition = parcel->readFloat();
+ mRawYCursorPosition = parcel->readFloat();
mDownTime = parcel->readInt64();
mPointerProperties.clear();
@@ -507,9 +622,12 @@
parcel->writeInt32(pointerCount);
parcel->writeInt32(sampleCount);
+ parcel->writeInt32(mId);
parcel->writeInt32(mDeviceId);
- parcel->writeInt32(mSource);
+ parcel->writeUint32(mSource);
parcel->writeInt32(mDisplayId);
+ std::vector<uint8_t> hmac(mHmac.begin(), mHmac.end());
+ parcel->writeByteVector(hmac);
parcel->writeInt32(mAction);
parcel->writeInt32(mActionButton);
parcel->writeInt32(mFlags);
@@ -517,10 +635,14 @@
parcel->writeInt32(mMetaState);
parcel->writeInt32(mButtonState);
parcel->writeByte(static_cast<int8_t>(mClassification));
+ parcel->writeFloat(mXScale);
+ parcel->writeFloat(mYScale);
parcel->writeFloat(mXOffset);
parcel->writeFloat(mYOffset);
parcel->writeFloat(mXPrecision);
parcel->writeFloat(mYPrecision);
+ parcel->writeFloat(mRawXCursorPosition);
+ parcel->writeFloat(mRawYCursorPosition);
parcel->writeInt64(mDownTime);
for (size_t i = 0; i < pointerCount; i++) {
@@ -543,7 +665,7 @@
}
#endif
-bool MotionEvent::isTouchEvent(int32_t source, int32_t action) {
+bool MotionEvent::isTouchEvent(uint32_t source, int32_t action) {
if (source & AINPUT_SOURCE_CLASS_POINTER) {
// Specifically excludes HOVER_MOVE and SCROLL.
switch (action & AMOTION_EVENT_ACTION_MASK) {
@@ -568,6 +690,39 @@
return getAxisByLabel(label);
}
+const char* MotionEvent::actionToString(int32_t action) {
+ // Convert MotionEvent action to string
+ switch (action & AMOTION_EVENT_ACTION_MASK) {
+ case AMOTION_EVENT_ACTION_DOWN:
+ return "DOWN";
+ case AMOTION_EVENT_ACTION_MOVE:
+ return "MOVE";
+ case AMOTION_EVENT_ACTION_UP:
+ return "UP";
+ case AMOTION_EVENT_ACTION_CANCEL:
+ return "CANCEL";
+ case AMOTION_EVENT_ACTION_POINTER_DOWN:
+ return "POINTER_DOWN";
+ case AMOTION_EVENT_ACTION_POINTER_UP:
+ return "POINTER_UP";
+ }
+ return "UNKNOWN";
+}
+
+// --- FocusEvent ---
+
+void FocusEvent::initialize(int32_t id, bool hasFocus, bool inTouchMode) {
+ InputEvent::initialize(id, ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, AINPUT_SOURCE_UNKNOWN,
+ ADISPLAY_ID_NONE, INVALID_HMAC);
+ mHasFocus = hasFocus;
+ mInTouchMode = inTouchMode;
+}
+
+void FocusEvent::initialize(const FocusEvent& from) {
+ InputEvent::initialize(from);
+ mHasFocus = from.mHasFocus;
+ mInTouchMode = from.mInTouchMode;
+}
// --- PooledInputEventFactory ---
@@ -576,43 +731,52 @@
}
PooledInputEventFactory::~PooledInputEventFactory() {
- for (size_t i = 0; i < mKeyEventPool.size(); i++) {
- delete mKeyEventPool.itemAt(i);
- }
- for (size_t i = 0; i < mMotionEventPool.size(); i++) {
- delete mMotionEventPool.itemAt(i);
- }
}
KeyEvent* PooledInputEventFactory::createKeyEvent() {
- if (!mKeyEventPool.isEmpty()) {
- KeyEvent* event = mKeyEventPool.top();
- mKeyEventPool.pop();
- return event;
+ if (mKeyEventPool.empty()) {
+ return new KeyEvent();
}
- return new KeyEvent();
+ KeyEvent* event = mKeyEventPool.front().release();
+ mKeyEventPool.pop();
+ return event;
}
MotionEvent* PooledInputEventFactory::createMotionEvent() {
- if (!mMotionEventPool.isEmpty()) {
- MotionEvent* event = mMotionEventPool.top();
- mMotionEventPool.pop();
- return event;
+ if (mMotionEventPool.empty()) {
+ return new MotionEvent();
}
- return new MotionEvent();
+ MotionEvent* event = mMotionEventPool.front().release();
+ mMotionEventPool.pop();
+ return event;
+}
+
+FocusEvent* PooledInputEventFactory::createFocusEvent() {
+ if (mFocusEventPool.empty()) {
+ return new FocusEvent();
+ }
+ FocusEvent* event = mFocusEventPool.front().release();
+ mFocusEventPool.pop();
+ return event;
}
void PooledInputEventFactory::recycle(InputEvent* event) {
switch (event->getType()) {
case AINPUT_EVENT_TYPE_KEY:
if (mKeyEventPool.size() < mMaxPoolSize) {
- mKeyEventPool.push(static_cast<KeyEvent*>(event));
+ mKeyEventPool.push(std::unique_ptr<KeyEvent>(static_cast<KeyEvent*>(event)));
return;
}
break;
case AINPUT_EVENT_TYPE_MOTION:
if (mMotionEventPool.size() < mMaxPoolSize) {
- mMotionEventPool.push(static_cast<MotionEvent*>(event));
+ mMotionEventPool.push(std::unique_ptr<MotionEvent>(static_cast<MotionEvent*>(event)));
+ return;
+ }
+ break;
+ case AINPUT_EVENT_TYPE_FOCUS:
+ if (mFocusEventPool.size() < mMaxPoolSize) {
+ mFocusEventPool.push(std::unique_ptr<FocusEvent>(static_cast<FocusEvent*>(event)));
return;
}
break;
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index d02cb8e..11af23e 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -11,10 +11,10 @@
#define DEBUG_CHANNEL_MESSAGES 0
// Log debug messages whenever InputChannel objects are created/destroyed
-#define DEBUG_CHANNEL_LIFECYCLE 0
+static constexpr bool DEBUG_CHANNEL_LIFECYCLE = false;
// Log debug messages about transport actions
-#define DEBUG_TRANSPORT_ACTIONS 0
+static constexpr bool DEBUG_TRANSPORT_ACTIONS = false;
// Log debug messages about touch event resampling
#define DEBUG_RESAMPLING 0
@@ -88,18 +88,23 @@
return (source & AINPUT_SOURCE_CLASS_POINTER) == AINPUT_SOURCE_CLASS_POINTER;
}
+inline static const char* toString(bool value) {
+ return value ? "true" : "false";
+}
+
// --- InputMessage ---
bool InputMessage::isValid(size_t actualSize) const {
if (size() == actualSize) {
switch (header.type) {
- case TYPE_KEY:
- return true;
- case TYPE_MOTION:
- return body.motion.pointerCount > 0
- && body.motion.pointerCount <= MAX_POINTERS;
- case TYPE_FINISHED:
- return true;
+ case Type::KEY:
+ return true;
+ case Type::MOTION:
+ return body.motion.pointerCount > 0 && body.motion.pointerCount <= MAX_POINTERS;
+ case Type::FINISHED:
+ return true;
+ case Type::FOCUS:
+ return true;
}
}
return false;
@@ -107,12 +112,14 @@
size_t InputMessage::size() const {
switch (header.type) {
- case TYPE_KEY:
- return sizeof(Header) + body.key.size();
- case TYPE_MOTION:
- return sizeof(Header) + body.motion.size();
- case TYPE_FINISHED:
- return sizeof(Header) + body.finished.size();
+ case Type::KEY:
+ return sizeof(Header) + body.key.size();
+ case Type::MOTION:
+ return sizeof(Header) + body.motion.size();
+ case Type::FINISHED:
+ return sizeof(Header) + body.finished.size();
+ case Type::FOCUS:
+ return sizeof(Header) + body.focus.size();
}
return sizeof(Header);
}
@@ -129,9 +136,11 @@
// Write the body
switch(header.type) {
- case InputMessage::TYPE_KEY: {
+ case InputMessage::Type::KEY: {
// uint32_t seq
msg->body.key.seq = body.key.seq;
+ // int32_t eventId
+ msg->body.key.eventId = body.key.eventId;
// nsecs_t eventTime
msg->body.key.eventTime = body.key.eventTime;
// int32_t deviceId
@@ -140,6 +149,8 @@
msg->body.key.source = body.key.source;
// int32_t displayId
msg->body.key.displayId = body.key.displayId;
+ // std::array<uint8_t, 32> hmac
+ msg->body.key.hmac = body.key.hmac;
// int32_t action
msg->body.key.action = body.key.action;
// int32_t flags
@@ -156,9 +167,11 @@
msg->body.key.downTime = body.key.downTime;
break;
}
- case InputMessage::TYPE_MOTION: {
+ case InputMessage::Type::MOTION: {
// uint32_t seq
msg->body.motion.seq = body.motion.seq;
+ // int32_t eventId
+ msg->body.motion.eventId = body.motion.eventId;
// nsecs_t eventTime
msg->body.motion.eventTime = body.motion.eventTime;
// int32_t deviceId
@@ -167,6 +180,8 @@
msg->body.motion.source = body.motion.source;
// int32_t displayId
msg->body.motion.displayId = body.motion.displayId;
+ // std::array<uint8_t, 32> hmac
+ msg->body.motion.hmac = body.motion.hmac;
// int32_t action
msg->body.motion.action = body.motion.action;
// int32_t actionButton
@@ -183,6 +198,10 @@
msg->body.motion.edgeFlags = body.motion.edgeFlags;
// nsecs_t downTime
msg->body.motion.downTime = body.motion.downTime;
+ // float xScale
+ msg->body.motion.xScale = body.motion.xScale;
+ // float yScale
+ msg->body.motion.yScale = body.motion.yScale;
// float xOffset
msg->body.motion.xOffset = body.motion.xOffset;
// float yOffset
@@ -191,6 +210,10 @@
msg->body.motion.xPrecision = body.motion.xPrecision;
// float yPrecision
msg->body.motion.yPrecision = body.motion.yPrecision;
+ // float xCursorPosition
+ msg->body.motion.xCursorPosition = body.motion.xCursorPosition;
+ // float yCursorPosition
+ msg->body.motion.yCursorPosition = body.motion.yCursorPosition;
// uint32_t pointerCount
msg->body.motion.pointerCount = body.motion.pointerCount;
//struct Pointer pointers[MAX_POINTERS]
@@ -208,13 +231,16 @@
}
break;
}
- case InputMessage::TYPE_FINISHED: {
+ case InputMessage::Type::FINISHED: {
msg->body.finished.seq = body.finished.seq;
msg->body.finished.handled = body.finished.handled;
break;
}
- default: {
- LOG_FATAL("Unexpected message type %i", header.type);
+ case InputMessage::Type::FOCUS: {
+ msg->body.focus.seq = body.focus.seq;
+ msg->body.focus.eventId = body.focus.eventId;
+ msg->body.focus.hasFocus = body.focus.hasFocus;
+ msg->body.focus.inTouchMode = body.focus.inTouchMode;
break;
}
}
@@ -222,34 +248,27 @@
// --- InputChannel ---
-InputChannel::InputChannel(const std::string& name, int fd) :
- mName(name) {
-#if DEBUG_CHANNEL_LIFECYCLE
- ALOGD("Input channel constructed: name='%s', fd=%d",
- mName.c_str(), fd);
-#endif
+sp<InputChannel> InputChannel::create(const std::string& name, android::base::unique_fd fd,
+ sp<IBinder> token) {
+ const int result = fcntl(fd, F_SETFL, O_NONBLOCK);
+ if (result != 0) {
+ LOG_ALWAYS_FATAL("channel '%s' ~ Could not make socket non-blocking: %s", name.c_str(),
+ strerror(errno));
+ return nullptr;
+ }
+ return new InputChannel(name, std::move(fd), token);
+}
- setFd(fd);
+InputChannel::InputChannel(const std::string& name, android::base::unique_fd fd, sp<IBinder> token)
+ : mName(name), mFd(std::move(fd)), mToken(token) {
+ if (DEBUG_CHANNEL_LIFECYCLE) {
+ ALOGD("Input channel constructed: name='%s', fd=%d", mName.c_str(), mFd.get());
+ }
}
InputChannel::~InputChannel() {
-#if DEBUG_CHANNEL_LIFECYCLE
- ALOGD("Input channel destroyed: name='%s', fd=%d",
- mName.c_str(), mFd);
-#endif
-
- ::close(mFd);
-}
-
-void InputChannel::setFd(int fd) {
- if (mFd > 0) {
- ::close(mFd);
- }
- mFd = fd;
- if (mFd > 0) {
- int result = fcntl(mFd, F_SETFL, O_NONBLOCK);
- LOG_ALWAYS_FATAL_IF(result != 0, "channel '%s' ~ Could not make socket "
- "non-blocking. errno=%d", mName.c_str(), errno);
+ if (DEBUG_CHANNEL_LIFECYCLE) {
+ ALOGD("Input channel destroyed: name='%s', fd=%d", mName.c_str(), mFd.get());
}
}
@@ -271,13 +290,15 @@
setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
- std::string serverChannelName = name;
- serverChannelName += " (server)";
- outServerChannel = new InputChannel(serverChannelName, sockets[0]);
+ sp<IBinder> token = new BBinder();
- std::string clientChannelName = name;
- clientChannelName += " (client)";
- outClientChannel = new InputChannel(clientChannelName, sockets[1]);
+ std::string serverChannelName = name + " (server)";
+ android::base::unique_fd serverFd(sockets[0]);
+ outServerChannel = InputChannel::create(serverChannelName, std::move(serverFd), token);
+
+ std::string clientChannelName = name + " (client)";
+ android::base::unique_fd clientFd(sockets[1]);
+ outClientChannel = InputChannel::create(clientChannelName, std::move(clientFd), token);
return OK;
}
@@ -287,14 +308,14 @@
msg->getSanitizedCopy(&cleanMsg);
ssize_t nWrite;
do {
- nWrite = ::send(mFd, &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
+ nWrite = ::send(mFd.get(), &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
} while (nWrite == -1 && errno == EINTR);
if (nWrite < 0) {
int error = errno;
#if DEBUG_CHANNEL_MESSAGES
- ALOGD("channel '%s' ~ error sending message of type %d, errno=%d", mName.c_str(),
- msg->header.type, error);
+ ALOGD("channel '%s' ~ error sending message of type %d, %s", mName.c_str(),
+ msg->header.type, strerror(error));
#endif
if (error == EAGAIN || error == EWOULDBLOCK) {
return WOULD_BLOCK;
@@ -322,7 +343,7 @@
status_t InputChannel::receiveMessage(InputMessage* msg) {
ssize_t nRead;
do {
- nRead = ::recv(mFd, msg, sizeof(InputMessage), MSG_DONTWAIT);
+ nRead = ::recv(mFd.get(), msg, sizeof(InputMessage), MSG_DONTWAIT);
} while (nRead == -1 && errno == EINTR);
if (nRead < 0) {
@@ -360,52 +381,53 @@
}
sp<InputChannel> InputChannel::dup() const {
- int fd = ::dup(getFd());
- return fd >= 0 ? new InputChannel(getName(), fd) : nullptr;
+ android::base::unique_fd newFd(::dup(getFd()));
+ if (!newFd.ok()) {
+ ALOGE("Could not duplicate fd %i for channel %s: %s", getFd(), mName.c_str(),
+ strerror(errno));
+ const bool hitFdLimit = errno == EMFILE || errno == ENFILE;
+ // If this process is out of file descriptors, then throwing that might end up exploding
+ // on the other side of a binder call, which isn't really helpful.
+ // Better to just crash here and hope that the FD leak is slow.
+ // Other failures could be client errors, so we still propagate those back to the caller.
+ LOG_ALWAYS_FATAL_IF(hitFdLimit, "Too many open files, could not duplicate input channel %s",
+ getName().c_str());
+ return nullptr;
+ }
+ return InputChannel::create(mName, std::move(newFd), mToken);
}
-
status_t InputChannel::write(Parcel& out) const {
- status_t s = out.writeString8(String8(getName().c_str()));
-
+ status_t s = out.writeCString(getName().c_str());
if (s != OK) {
return s;
}
+
s = out.writeStrongBinder(mToken);
if (s != OK) {
return s;
}
- s = out.writeDupFileDescriptor(getFd());
-
+ s = out.writeUniqueFileDescriptor(mFd);
return s;
}
-status_t InputChannel::read(const Parcel& from) {
- mName = from.readString8();
- mToken = from.readStrongBinder();
-
- int rawFd = from.readFileDescriptor();
- setFd(::dup(rawFd));
-
- if (mFd < 0) {
- return BAD_VALUE;
+sp<InputChannel> InputChannel::read(const Parcel& from) {
+ std::string name = from.readCString();
+ sp<IBinder> token = from.readStrongBinder();
+ android::base::unique_fd rawFd;
+ status_t fdResult = from.readUniqueFileDescriptor(&rawFd);
+ if (fdResult != OK) {
+ return nullptr;
}
- return OK;
+ return InputChannel::create(name, std::move(rawFd), token);
}
-sp<IBinder> InputChannel::getToken() const {
+sp<IBinder> InputChannel::getConnectionToken() const {
return mToken;
}
-void InputChannel::setToken(const sp<IBinder>& token) {
- if (mToken != nullptr) {
- ALOGE("Assigning InputChannel (%s) a second handle?", mName.c_str());
- }
- mToken = token;
-}
-
// --- InputPublisher ---
InputPublisher::InputPublisher(const sp<InputChannel>& channel) :
@@ -415,32 +437,24 @@
InputPublisher::~InputPublisher() {
}
-status_t InputPublisher::publishKeyEvent(
- uint32_t seq,
- int32_t deviceId,
- int32_t source,
- int32_t displayId,
- int32_t action,
- int32_t flags,
- int32_t keyCode,
- int32_t scanCode,
- int32_t metaState,
- int32_t repeatCount,
- nsecs_t downTime,
- nsecs_t eventTime) {
+status_t InputPublisher::publishKeyEvent(uint32_t seq, int32_t eventId, int32_t deviceId,
+ int32_t source, int32_t displayId,
+ std::array<uint8_t, 32> hmac, int32_t action,
+ int32_t flags, int32_t keyCode, int32_t scanCode,
+ int32_t metaState, int32_t repeatCount, nsecs_t downTime,
+ nsecs_t eventTime) {
if (ATRACE_ENABLED()) {
std::string message = StringPrintf("publishKeyEvent(inputChannel=%s, keyCode=%" PRId32 ")",
mChannel->getName().c_str(), keyCode);
ATRACE_NAME(message.c_str());
}
-#if DEBUG_TRANSPORT_ACTIONS
- ALOGD("channel '%s' publisher ~ publishKeyEvent: seq=%u, deviceId=%d, source=0x%x, "
- "action=0x%x, flags=0x%x, keyCode=%d, scanCode=%d, metaState=0x%x, repeatCount=%d,"
- "downTime=%" PRId64 ", eventTime=%" PRId64,
- mChannel->getName().c_str(), seq,
- deviceId, source, action, flags, keyCode, scanCode, metaState, repeatCount,
- downTime, eventTime);
-#endif
+ if (DEBUG_TRANSPORT_ACTIONS) {
+ ALOGD("channel '%s' publisher ~ publishKeyEvent: seq=%u, deviceId=%d, source=0x%x, "
+ "action=0x%x, flags=0x%x, keyCode=%d, scanCode=%d, metaState=0x%x, repeatCount=%d,"
+ "downTime=%" PRId64 ", eventTime=%" PRId64,
+ mChannel->getName().c_str(), seq, deviceId, source, action, flags, keyCode, scanCode,
+ metaState, repeatCount, downTime, eventTime);
+ }
if (!seq) {
ALOGE("Attempted to publish a key event with sequence number 0.");
@@ -448,11 +462,13 @@
}
InputMessage msg;
- msg.header.type = InputMessage::TYPE_KEY;
+ msg.header.type = InputMessage::Type::KEY;
msg.body.key.seq = seq;
+ msg.body.key.eventId = eventId;
msg.body.key.deviceId = deviceId;
msg.body.key.source = source;
msg.body.key.displayId = displayId;
+ msg.body.key.hmac = std::move(hmac);
msg.body.key.action = action;
msg.body.key.flags = flags;
msg.body.key.keyCode = keyCode;
@@ -465,44 +481,32 @@
}
status_t InputPublisher::publishMotionEvent(
- uint32_t seq,
- int32_t deviceId,
- int32_t source,
- int32_t displayId,
- int32_t action,
- int32_t actionButton,
- int32_t flags,
- int32_t edgeFlags,
- int32_t metaState,
- int32_t buttonState,
- MotionClassification classification,
- float xOffset,
- float yOffset,
- float xPrecision,
- float yPrecision,
- nsecs_t downTime,
- nsecs_t eventTime,
- uint32_t pointerCount,
- const PointerProperties* pointerProperties,
- const PointerCoords* pointerCoords) {
+ uint32_t seq, int32_t eventId, int32_t deviceId, int32_t source, int32_t displayId,
+ std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton, int32_t flags,
+ int32_t edgeFlags, int32_t metaState, int32_t buttonState,
+ MotionClassification classification, float xScale, float yScale, float xOffset,
+ float yOffset, float xPrecision, float yPrecision, float xCursorPosition,
+ float yCursorPosition, nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount,
+ const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) {
if (ATRACE_ENABLED()) {
std::string message = StringPrintf(
"publishMotionEvent(inputChannel=%s, action=%" PRId32 ")",
mChannel->getName().c_str(), action);
ATRACE_NAME(message.c_str());
}
-#if DEBUG_TRANSPORT_ACTIONS
- ALOGD("channel '%s' publisher ~ publishMotionEvent: seq=%u, deviceId=%d, source=0x%x, "
- "displayId=%" PRId32 ", "
- "action=0x%x, actionButton=0x%08x, flags=0x%x, edgeFlags=0x%x, "
- "metaState=0x%x, buttonState=0x%x, classification=%s, xOffset=%f, yOffset=%f, "
- "xPrecision=%f, yPrecision=%f, downTime=%" PRId64 ", eventTime=%" PRId64 ", "
- "pointerCount=%" PRIu32,
- mChannel->getName().c_str(), seq,
- deviceId, source, displayId, action, actionButton, flags, edgeFlags, metaState,
- buttonState, motionClassificationToString(classification),
- xOffset, yOffset, xPrecision, yPrecision, downTime, eventTime, pointerCount);
-#endif
+ if (DEBUG_TRANSPORT_ACTIONS) {
+ ALOGD("channel '%s' publisher ~ publishMotionEvent: seq=%u, deviceId=%d, source=0x%x, "
+ "displayId=%" PRId32 ", "
+ "action=0x%x, actionButton=0x%08x, flags=0x%x, edgeFlags=0x%x, "
+ "metaState=0x%x, buttonState=0x%x, classification=%s, xScale=%.1f, yScale=%.1f, "
+ "xOffset=%.1f, yOffset=%.1f, "
+ "xPrecision=%f, yPrecision=%f, downTime=%" PRId64 ", eventTime=%" PRId64 ", "
+ "pointerCount=%" PRIu32,
+ mChannel->getName().c_str(), seq, deviceId, source, displayId, action, actionButton,
+ flags, edgeFlags, metaState, buttonState,
+ motionClassificationToString(classification), xScale, yScale, xOffset, yOffset,
+ xPrecision, yPrecision, downTime, eventTime, pointerCount);
+ }
if (!seq) {
ALOGE("Attempted to publish a motion event with sequence number 0.");
@@ -516,11 +520,13 @@
}
InputMessage msg;
- msg.header.type = InputMessage::TYPE_MOTION;
+ msg.header.type = InputMessage::Type::MOTION;
msg.body.motion.seq = seq;
+ msg.body.motion.eventId = eventId;
msg.body.motion.deviceId = deviceId;
msg.body.motion.source = source;
msg.body.motion.displayId = displayId;
+ msg.body.motion.hmac = std::move(hmac);
msg.body.motion.action = action;
msg.body.motion.actionButton = actionButton;
msg.body.motion.flags = flags;
@@ -528,10 +534,14 @@
msg.body.motion.metaState = metaState;
msg.body.motion.buttonState = buttonState;
msg.body.motion.classification = classification;
+ msg.body.motion.xScale = xScale;
+ msg.body.motion.yScale = yScale;
msg.body.motion.xOffset = xOffset;
msg.body.motion.yOffset = yOffset;
msg.body.motion.xPrecision = xPrecision;
msg.body.motion.yPrecision = yPrecision;
+ msg.body.motion.xCursorPosition = xCursorPosition;
+ msg.body.motion.yCursorPosition = yCursorPosition;
msg.body.motion.downTime = downTime;
msg.body.motion.eventTime = eventTime;
msg.body.motion.pointerCount = pointerCount;
@@ -539,14 +549,33 @@
msg.body.motion.pointers[i].properties.copyFrom(pointerProperties[i]);
msg.body.motion.pointers[i].coords.copyFrom(pointerCoords[i]);
}
+
+ return mChannel->sendMessage(&msg);
+}
+
+status_t InputPublisher::publishFocusEvent(uint32_t seq, int32_t eventId, bool hasFocus,
+ bool inTouchMode) {
+ if (ATRACE_ENABLED()) {
+ std::string message =
+ StringPrintf("publishFocusEvent(inputChannel=%s, hasFocus=%s, inTouchMode=%s)",
+ mChannel->getName().c_str(), toString(hasFocus),
+ toString(inTouchMode));
+ ATRACE_NAME(message.c_str());
+ }
+
+ InputMessage msg;
+ msg.header.type = InputMessage::Type::FOCUS;
+ msg.body.focus.seq = seq;
+ msg.body.focus.eventId = eventId;
+ msg.body.focus.hasFocus = hasFocus ? 1 : 0;
+ msg.body.focus.inTouchMode = inTouchMode ? 1 : 0;
return mChannel->sendMessage(&msg);
}
status_t InputPublisher::receiveFinishedSignal(uint32_t* outSeq, bool* outHandled) {
-#if DEBUG_TRANSPORT_ACTIONS
- ALOGD("channel '%s' publisher ~ receiveFinishedSignal",
- mChannel->getName().c_str());
-#endif
+ if (DEBUG_TRANSPORT_ACTIONS) {
+ ALOGD("channel '%s' publisher ~ receiveFinishedSignal", mChannel->getName().c_str());
+ }
InputMessage msg;
status_t result = mChannel->receiveMessage(&msg);
@@ -555,13 +584,13 @@
*outHandled = false;
return result;
}
- if (msg.header.type != InputMessage::TYPE_FINISHED) {
+ if (msg.header.type != InputMessage::Type::FINISHED) {
ALOGE("channel '%s' publisher ~ Received unexpected message of type %d from consumer",
mChannel->getName().c_str(), msg.header.type);
return UNKNOWN_ERROR;
}
*outSeq = msg.body.finished.seq;
- *outHandled = msg.body.finished.handled;
+ *outHandled = msg.body.finished.handled == 1;
return OK;
}
@@ -579,12 +608,12 @@
return property_get_bool(PROPERTY_RESAMPLING_ENABLED, true);
}
-status_t InputConsumer::consume(InputEventFactoryInterface* factory,
- bool consumeBatches, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) {
-#if DEBUG_TRANSPORT_ACTIONS
- ALOGD("channel '%s' consumer ~ consume: consumeBatches=%s, frameTime=%" PRId64,
- mChannel->getName().c_str(), consumeBatches ? "true" : "false", frameTime);
-#endif
+status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consumeBatches,
+ nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) {
+ if (DEBUG_TRANSPORT_ACTIONS) {
+ ALOGD("channel '%s' consumer ~ consume: consumeBatches=%s, frameTime=%" PRId64,
+ mChannel->getName().c_str(), toString(consumeBatches), frameTime);
+ }
*outSeq = 0;
*outEvent = nullptr;
@@ -604,10 +633,10 @@
if (consumeBatches || result != WOULD_BLOCK) {
result = consumeBatch(factory, frameTime, outSeq, outEvent);
if (*outEvent) {
-#if DEBUG_TRANSPORT_ACTIONS
- ALOGD("channel '%s' consumer ~ consumed batch event, seq=%u",
- mChannel->getName().c_str(), *outSeq);
-#endif
+ if (DEBUG_TRANSPORT_ACTIONS) {
+ ALOGD("channel '%s' consumer ~ consumed batch event, seq=%u",
+ mChannel->getName().c_str(), *outSeq);
+ }
break;
}
}
@@ -616,92 +645,103 @@
}
switch (mMsg.header.type) {
- case InputMessage::TYPE_KEY: {
- KeyEvent* keyEvent = factory->createKeyEvent();
- if (!keyEvent) return NO_MEMORY;
+ case InputMessage::Type::KEY: {
+ KeyEvent* keyEvent = factory->createKeyEvent();
+ if (!keyEvent) return NO_MEMORY;
- initializeKeyEvent(keyEvent, &mMsg);
- *outSeq = mMsg.body.key.seq;
- *outEvent = keyEvent;
-#if DEBUG_TRANSPORT_ACTIONS
- ALOGD("channel '%s' consumer ~ consumed key event, seq=%u",
- mChannel->getName().c_str(), *outSeq);
-#endif
- break;
- }
-
- case InputMessage::TYPE_MOTION: {
- ssize_t batchIndex = findBatch(mMsg.body.motion.deviceId, mMsg.body.motion.source);
- if (batchIndex >= 0) {
- Batch& batch = mBatches.editItemAt(batchIndex);
- if (canAddSample(batch, &mMsg)) {
- batch.samples.push(mMsg);
-#if DEBUG_TRANSPORT_ACTIONS
- ALOGD("channel '%s' consumer ~ appended to batch event",
- mChannel->getName().c_str());
-#endif
- break;
- } else if (isPointerEvent(mMsg.body.motion.source) &&
- mMsg.body.motion.action == AMOTION_EVENT_ACTION_CANCEL) {
- // No need to process events that we are going to cancel anyways
- const size_t count = batch.samples.size();
- for (size_t i = 0; i < count; i++) {
- const InputMessage& msg = batch.samples.itemAt(i);
- sendFinishedSignal(msg.body.motion.seq, false);
- }
- batch.samples.removeItemsAt(0, count);
- mBatches.removeAt(batchIndex);
- } else {
- // We cannot append to the batch in progress, so we need to consume
- // the previous batch right now and defer the new message until later.
- mMsgDeferred = true;
- status_t result = consumeSamples(factory,
- batch, batch.samples.size(), outSeq, outEvent);
- mBatches.removeAt(batchIndex);
- if (result) {
- return result;
- }
-#if DEBUG_TRANSPORT_ACTIONS
- ALOGD("channel '%s' consumer ~ consumed batch event and "
- "deferred current event, seq=%u",
- mChannel->getName().c_str(), *outSeq);
-#endif
- break;
+ initializeKeyEvent(keyEvent, &mMsg);
+ *outSeq = mMsg.body.key.seq;
+ *outEvent = keyEvent;
+ if (DEBUG_TRANSPORT_ACTIONS) {
+ ALOGD("channel '%s' consumer ~ consumed key event, seq=%u",
+ mChannel->getName().c_str(), *outSeq);
}
+ break;
}
- // Start a new batch if needed.
- if (mMsg.body.motion.action == AMOTION_EVENT_ACTION_MOVE
- || mMsg.body.motion.action == AMOTION_EVENT_ACTION_HOVER_MOVE) {
- mBatches.push();
- Batch& batch = mBatches.editTop();
- batch.samples.push(mMsg);
-#if DEBUG_TRANSPORT_ACTIONS
- ALOGD("channel '%s' consumer ~ started batch event",
- mChannel->getName().c_str());
-#endif
+ case InputMessage::Type::MOTION: {
+ ssize_t batchIndex = findBatch(mMsg.body.motion.deviceId, mMsg.body.motion.source);
+ if (batchIndex >= 0) {
+ Batch& batch = mBatches.editItemAt(batchIndex);
+ if (canAddSample(batch, &mMsg)) {
+ batch.samples.push(mMsg);
+ if (DEBUG_TRANSPORT_ACTIONS) {
+ ALOGD("channel '%s' consumer ~ appended to batch event",
+ mChannel->getName().c_str());
+ }
+ break;
+ } else if (isPointerEvent(mMsg.body.motion.source) &&
+ mMsg.body.motion.action == AMOTION_EVENT_ACTION_CANCEL) {
+ // No need to process events that we are going to cancel anyways
+ const size_t count = batch.samples.size();
+ for (size_t i = 0; i < count; i++) {
+ const InputMessage& msg = batch.samples.itemAt(i);
+ sendFinishedSignal(msg.body.motion.seq, false);
+ }
+ batch.samples.removeItemsAt(0, count);
+ mBatches.removeAt(batchIndex);
+ } else {
+ // We cannot append to the batch in progress, so we need to consume
+ // the previous batch right now and defer the new message until later.
+ mMsgDeferred = true;
+ status_t result = consumeSamples(factory, batch, batch.samples.size(),
+ outSeq, outEvent);
+ mBatches.removeAt(batchIndex);
+ if (result) {
+ return result;
+ }
+ if (DEBUG_TRANSPORT_ACTIONS) {
+ ALOGD("channel '%s' consumer ~ consumed batch event and "
+ "deferred current event, seq=%u",
+ mChannel->getName().c_str(), *outSeq);
+ }
+ break;
+ }
+ }
+
+ // Start a new batch if needed.
+ if (mMsg.body.motion.action == AMOTION_EVENT_ACTION_MOVE ||
+ mMsg.body.motion.action == AMOTION_EVENT_ACTION_HOVER_MOVE) {
+ mBatches.push();
+ Batch& batch = mBatches.editTop();
+ batch.samples.push(mMsg);
+ if (DEBUG_TRANSPORT_ACTIONS) {
+ ALOGD("channel '%s' consumer ~ started batch event",
+ mChannel->getName().c_str());
+ }
+ break;
+ }
+
+ MotionEvent* motionEvent = factory->createMotionEvent();
+ if (!motionEvent) return NO_MEMORY;
+
+ updateTouchState(mMsg);
+ initializeMotionEvent(motionEvent, &mMsg);
+ *outSeq = mMsg.body.motion.seq;
+ *outEvent = motionEvent;
+
+ if (DEBUG_TRANSPORT_ACTIONS) {
+ ALOGD("channel '%s' consumer ~ consumed motion event, seq=%u",
+ mChannel->getName().c_str(), *outSeq);
+ }
break;
}
- MotionEvent* motionEvent = factory->createMotionEvent();
- if (! motionEvent) return NO_MEMORY;
+ case InputMessage::Type::FINISHED: {
+ LOG_ALWAYS_FATAL("Consumed a FINISHED message, which should never be seen by "
+ "InputConsumer!");
+ break;
+ }
- updateTouchState(mMsg);
- initializeMotionEvent(motionEvent, &mMsg);
- *outSeq = mMsg.body.motion.seq;
- *outEvent = motionEvent;
+ case InputMessage::Type::FOCUS: {
+ FocusEvent* focusEvent = factory->createFocusEvent();
+ if (!focusEvent) return NO_MEMORY;
-#if DEBUG_TRANSPORT_ACTIONS
- ALOGD("channel '%s' consumer ~ consumed motion event, seq=%u",
- mChannel->getName().c_str(), *outSeq);
-#endif
- break;
- }
-
- default:
- ALOGE("channel '%s' consumer ~ Received unexpected message of type %d",
- mChannel->getName().c_str(), mMsg.header.type);
- return UNKNOWN_ERROR;
+ initializeFocusEvent(focusEvent, &mMsg);
+ *outSeq = mMsg.body.focus.seq;
+ *outEvent = focusEvent;
+ break;
+ }
}
}
return OK;
@@ -1026,10 +1066,10 @@
}
status_t InputConsumer::sendFinishedSignal(uint32_t seq, bool handled) {
-#if DEBUG_TRANSPORT_ACTIONS
- ALOGD("channel '%s' consumer ~ sendFinishedSignal: seq=%u, handled=%s",
- mChannel->getName().c_str(), seq, handled ? "true" : "false");
-#endif
+ if (DEBUG_TRANSPORT_ACTIONS) {
+ ALOGD("channel '%s' consumer ~ sendFinishedSignal: seq=%u, handled=%s",
+ mChannel->getName().c_str(), seq, toString(handled));
+ }
if (!seq) {
ALOGE("Attempted to send a finished signal with sequence number 0.");
@@ -1076,9 +1116,9 @@
status_t InputConsumer::sendUnchainedFinishedSignal(uint32_t seq, bool handled) {
InputMessage msg;
- msg.header.type = InputMessage::TYPE_FINISHED;
+ msg.header.type = InputMessage::Type::FINISHED;
msg.body.finished.seq = seq;
- msg.body.finished.handled = handled;
+ msg.body.finished.handled = handled ? 1 : 0;
return mChannel->sendMessage(&msg);
}
@@ -1090,6 +1130,16 @@
return !mBatches.isEmpty();
}
+int32_t InputConsumer::getPendingBatchSource() const {
+ if (mBatches.isEmpty()) {
+ return AINPUT_SOURCE_CLASS_NONE;
+ }
+
+ const Batch& batch = mBatches.itemAt(0);
+ const InputMessage& head = batch.samples.itemAt(0);
+ return head.body.motion.source;
+}
+
ssize_t InputConsumer::findBatch(int32_t deviceId, int32_t source) const {
for (size_t i = 0; i < mBatches.size(); i++) {
const Batch& batch = mBatches.itemAt(i);
@@ -1112,18 +1162,16 @@
}
void InputConsumer::initializeKeyEvent(KeyEvent* event, const InputMessage* msg) {
- event->initialize(
- msg->body.key.deviceId,
- msg->body.key.source,
- msg->body.key.displayId,
- msg->body.key.action,
- msg->body.key.flags,
- msg->body.key.keyCode,
- msg->body.key.scanCode,
- msg->body.key.metaState,
- msg->body.key.repeatCount,
- msg->body.key.downTime,
- msg->body.key.eventTime);
+ event->initialize(msg->body.key.eventId, msg->body.key.deviceId, msg->body.key.source,
+ msg->body.key.displayId, msg->body.key.hmac, msg->body.key.action,
+ msg->body.key.flags, msg->body.key.keyCode, msg->body.key.scanCode,
+ msg->body.key.metaState, msg->body.key.repeatCount, msg->body.key.downTime,
+ msg->body.key.eventTime);
+}
+
+void InputConsumer::initializeFocusEvent(FocusEvent* event, const InputMessage* msg) {
+ event->initialize(msg->body.focus.eventId, msg->body.focus.hasFocus == 1,
+ msg->body.focus.inTouchMode == 1);
}
void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage* msg) {
@@ -1135,26 +1183,16 @@
pointerCoords[i].copyFrom(msg->body.motion.pointers[i].coords);
}
- event->initialize(
- msg->body.motion.deviceId,
- msg->body.motion.source,
- msg->body.motion.displayId,
- msg->body.motion.action,
- msg->body.motion.actionButton,
- msg->body.motion.flags,
- msg->body.motion.edgeFlags,
- msg->body.motion.metaState,
- msg->body.motion.buttonState,
- msg->body.motion.classification,
- msg->body.motion.xOffset,
- msg->body.motion.yOffset,
- msg->body.motion.xPrecision,
- msg->body.motion.yPrecision,
- msg->body.motion.downTime,
- msg->body.motion.eventTime,
- pointerCount,
- pointerProperties,
- pointerCoords);
+ event->initialize(msg->body.motion.eventId, msg->body.motion.deviceId, msg->body.motion.source,
+ msg->body.motion.displayId, msg->body.motion.hmac, msg->body.motion.action,
+ msg->body.motion.actionButton, msg->body.motion.flags,
+ msg->body.motion.edgeFlags, msg->body.motion.metaState,
+ msg->body.motion.buttonState, msg->body.motion.classification,
+ msg->body.motion.xScale, msg->body.motion.yScale, msg->body.motion.xOffset,
+ msg->body.motion.yOffset, msg->body.motion.xPrecision,
+ msg->body.motion.yPrecision, msg->body.motion.xCursorPosition,
+ msg->body.motion.yCursorPosition, msg->body.motion.downTime,
+ msg->body.motion.eventTime, pointerCount, pointerProperties, pointerCoords);
}
void InputConsumer::addSample(MotionEvent* event, const InputMessage* msg) {
diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp
index ec28757..85a2015 100644
--- a/libs/input/InputWindow.cpp
+++ b/libs/input/InputWindow.cpp
@@ -42,17 +42,18 @@
&& y >= frameTop && y < frameBottom;
}
+// TODO(b/155781676): Remove and replace call points with trustedOverlay when that is ready.
bool InputWindowInfo::isTrustedOverlay() const {
- return layoutParamsType == TYPE_INPUT_METHOD
- || layoutParamsType == TYPE_INPUT_METHOD_DIALOG
- || layoutParamsType == TYPE_MAGNIFICATION_OVERLAY
- || layoutParamsType == TYPE_STATUS_BAR
- || layoutParamsType == TYPE_NAVIGATION_BAR
- || layoutParamsType == TYPE_NAVIGATION_BAR_PANEL
- || layoutParamsType == TYPE_SECURE_SYSTEM_OVERLAY
- || layoutParamsType == TYPE_DOCK_DIVIDER
- || layoutParamsType == TYPE_ACCESSIBILITY_OVERLAY
- || layoutParamsType == TYPE_INPUT_CONSUMER;
+ return layoutParamsType == TYPE_INPUT_METHOD || layoutParamsType == TYPE_INPUT_METHOD_DIALOG ||
+ layoutParamsType == TYPE_MAGNIFICATION_OVERLAY || layoutParamsType == TYPE_STATUS_BAR ||
+ layoutParamsType == TYPE_NOTIFICATION_SHADE ||
+ layoutParamsType == TYPE_NAVIGATION_BAR ||
+ layoutParamsType == TYPE_NAVIGATION_BAR_PANEL ||
+ layoutParamsType == TYPE_SECURE_SYSTEM_OVERLAY ||
+ layoutParamsType == TYPE_DOCK_DIVIDER ||
+ layoutParamsType == TYPE_ACCESSIBILITY_OVERLAY ||
+ layoutParamsType == TYPE_INPUT_CONSUMER ||
+ layoutParamsType == TYPE_TRUSTED_APPLICATION_OVERLAY;
}
bool InputWindowInfo::supportsSplitTouch() const {
@@ -65,7 +66,7 @@
}
status_t InputWindowInfo::write(Parcel& output) const {
- if (token == nullptr) {
+ if (name.empty()) {
output.writeInt32(0);
return OK;
}
@@ -73,6 +74,7 @@
status_t s = output.writeStrongBinder(token);
if (s != OK) return s;
+ output.writeInt32(id);
output.writeString8(String8(name.c_str()));
output.writeInt32(layoutParamsFlags);
output.writeInt32(layoutParamsType);
@@ -90,7 +92,6 @@
output.writeBool(hasFocus);
output.writeBool(hasWallpaper);
output.writeBool(paused);
- output.writeInt32(layer);
output.writeInt32(ownerPid);
output.writeInt32(ownerUid);
output.writeInt32(inputFeatures);
@@ -110,12 +111,8 @@
return ret;
}
- sp<IBinder> token = from.readStrongBinder();
- if (token == nullptr) {
- return ret;
- }
-
- ret.token = token;
+ ret.token = from.readStrongBinder();
+ ret.id = from.readInt32();
ret.name = from.readString8().c_str();
ret.layoutParamsFlags = from.readInt32();
ret.layoutParamsType = from.readInt32();
@@ -133,7 +130,6 @@
ret.hasFocus = from.readBool();
ret.hasWallpaper = from.readBool();
ret.paused = from.readBool();
- ret.layer = from.readInt32();
ret.ownerPid = from.readInt32();
ret.ownerUid = from.readInt32();
ret.inputFeatures = from.readInt32();
diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp
index e189d20..cb68165 100644
--- a/libs/input/KeyCharacterMap.cpp
+++ b/libs/input/KeyCharacterMap.cpp
@@ -487,9 +487,9 @@
int32_t deviceId, int32_t keyCode, int32_t metaState, bool down, nsecs_t time) {
outEvents.push();
KeyEvent& event = outEvents.editTop();
- event.initialize(deviceId, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE,
- down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
- 0, keyCode, 0, metaState, 0, time, time);
+ event.initialize(InputEvent::nextId(), deviceId, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE,
+ INVALID_HMAC, down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, 0, keyCode,
+ 0, metaState, 0, time, time);
}
void KeyCharacterMap::addMetaKeys(Vector<KeyEvent>& outEvents,
diff --git a/libs/input/Keyboard.cpp b/libs/input/Keyboard.cpp
index 0c22bfe..56900c1 100644
--- a/libs/input/Keyboard.cpp
+++ b/libs/input/Keyboard.cpp
@@ -38,29 +38,29 @@
KeyMap::~KeyMap() {
}
-status_t KeyMap::load(const InputDeviceIdentifier& deviceIdenfifier,
+status_t KeyMap::load(const InputDeviceIdentifier& deviceIdentifier,
const PropertyMap* deviceConfiguration) {
// Use the configured key layout if available.
if (deviceConfiguration) {
String8 keyLayoutName;
if (deviceConfiguration->tryGetProperty(String8("keyboard.layout"),
keyLayoutName)) {
- status_t status = loadKeyLayout(deviceIdenfifier, keyLayoutName.c_str());
+ status_t status = loadKeyLayout(deviceIdentifier, keyLayoutName.c_str());
if (status == NAME_NOT_FOUND) {
ALOGE("Configuration for keyboard device '%s' requested keyboard layout '%s' but "
"it was not found.",
- deviceIdenfifier.name.c_str(), keyLayoutName.string());
+ deviceIdentifier.name.c_str(), keyLayoutName.string());
}
}
String8 keyCharacterMapName;
if (deviceConfiguration->tryGetProperty(String8("keyboard.characterMap"),
keyCharacterMapName)) {
- status_t status = loadKeyCharacterMap(deviceIdenfifier, keyCharacterMapName.c_str());
+ status_t status = loadKeyCharacterMap(deviceIdentifier, keyCharacterMapName.c_str());
if (status == NAME_NOT_FOUND) {
ALOGE("Configuration for keyboard device '%s' requested keyboard character "
"map '%s' but it was not found.",
- deviceIdenfifier.name.c_str(), keyLayoutName.string());
+ deviceIdentifier.name.c_str(), keyCharacterMapName.string());
}
}
@@ -70,25 +70,25 @@
}
// Try searching by device identifier.
- if (probeKeyMap(deviceIdenfifier, "")) {
+ if (probeKeyMap(deviceIdentifier, "")) {
return OK;
}
// Fall back on the Generic key map.
// TODO Apply some additional heuristics here to figure out what kind of
// generic key map to use (US English, etc.) for typical external keyboards.
- if (probeKeyMap(deviceIdenfifier, "Generic")) {
+ if (probeKeyMap(deviceIdentifier, "Generic")) {
return OK;
}
// Try the Virtual key map as a last resort.
- if (probeKeyMap(deviceIdenfifier, "Virtual")) {
+ if (probeKeyMap(deviceIdentifier, "Virtual")) {
return OK;
}
// Give up!
ALOGE("Could not determine key map for device '%s' and no default key maps were found!",
- deviceIdenfifier.name.c_str());
+ deviceIdentifier.name.c_str());
return NAME_NOT_FOUND;
}
diff --git a/libs/input/LatencyStatistics.cpp b/libs/input/LatencyStatistics.cpp
new file mode 100644
index 0000000..394da22
--- /dev/null
+++ b/libs/input/LatencyStatistics.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2019 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/LatencyStatistics.h>
+
+#include <android-base/chrono_utils.h>
+
+#include <cmath>
+#include <limits>
+
+namespace android {
+
+LatencyStatistics::LatencyStatistics(std::chrono::seconds period) : mReportPeriod(period) {
+ reset();
+}
+
+/**
+ * Add a raw value to the statistics
+ */
+void LatencyStatistics::addValue(float value) {
+ if (value < mMin) {
+ mMin = value;
+ }
+ if (value > mMax) {
+ mMax = value;
+ }
+ mSum += value;
+ mSum2 += value * value;
+ mCount++;
+}
+
+/**
+ * Get the mean. Should not be called if no samples have been added.
+ */
+float LatencyStatistics::getMean() {
+ return mSum / mCount;
+}
+
+/**
+ * Get the standard deviation. Should not be called if no samples have been added.
+ */
+float LatencyStatistics::getStDev() {
+ float mean = getMean();
+ return sqrt(mSum2 / mCount - mean * mean);
+}
+
+float LatencyStatistics::getMin() {
+ return mMin;
+}
+
+float LatencyStatistics::getMax() {
+ return mMax;
+}
+
+size_t LatencyStatistics::getCount() {
+ return mCount;
+}
+
+/**
+ * Reset internal state. The variable 'when' is the time when the data collection started.
+ * Call this to start a new data collection window.
+ */
+void LatencyStatistics::reset() {
+ mMax = std::numeric_limits<float>::lowest();
+ mMin = std::numeric_limits<float>::max();
+ mSum = 0;
+ mSum2 = 0;
+ mCount = 0;
+ mLastReportTime = std::chrono::steady_clock::now();
+}
+
+bool LatencyStatistics::shouldReport() {
+ std::chrono::duration timeSinceReport = std::chrono::steady_clock::now() - mLastReportTime;
+ return mCount != 0 && timeSinceReport >= mReportPeriod;
+}
+
+} // namespace android
diff --git a/libs/input/TouchVideoFrame.cpp b/libs/input/TouchVideoFrame.cpp
index ce76e3f..c62e098 100644
--- a/libs/input/TouchVideoFrame.cpp
+++ b/libs/input/TouchVideoFrame.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <input/DisplayViewport.h>
#include <input/TouchVideoFrame.h>
namespace android {
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index ade931e..3b57146 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -2,19 +2,21 @@
cc_test {
name: "libinput_tests",
srcs: [
+ "IdGenerator_test.cpp",
"InputChannel_test.cpp",
"InputDevice_test.cpp",
"InputEvent_test.cpp",
"InputPublisherAndConsumer_test.cpp",
"InputWindow_test.cpp",
+ "LatencyStatistics_test.cpp",
"TouchVideoFrame_test.cpp",
"VelocityTracker_test.cpp",
+ "VerifiedInputEvent_test.cpp",
],
cflags: [
"-Wall",
"-Wextra",
"-Werror",
- "-Wno-unused-variable",
],
shared_libs: [
"libinput",
diff --git a/libs/input/tests/IdGenerator_test.cpp b/libs/input/tests/IdGenerator_test.cpp
new file mode 100644
index 0000000..f7fc3c0
--- /dev/null
+++ b/libs/input/tests/IdGenerator_test.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 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 <gtest/gtest.h>
+#include <input/Input.h>
+#include <ios>
+#include <memory>
+#include <unordered_set>
+
+namespace android::test {
+
+class IdGeneratorTest : public testing::TestWithParam<IdGenerator::Source> {
+protected:
+ void SetUp() override { mGenerator.reset(new IdGenerator(GetParam())); }
+
+ std::unique_ptr<IdGenerator> mGenerator;
+};
+
+TEST_P(IdGeneratorTest, GenerateRandomNumber) {
+ for (int i = 0; i < 500; ++i) {
+ mGenerator->nextId();
+ }
+}
+
+TEST_P(IdGeneratorTest, GenerateRandomNumberWithProperFlag) {
+ for (int i = 0; i < 500; ++i) {
+ int32_t id = mGenerator->nextId();
+ IdGenerator::Source source = IdGenerator::getSource(id);
+ EXPECT_EQ(source, GetParam())
+ << std::hex << "Generator generated a value with wrong source. Value: 0x" << id
+ << " Source: 0x" << static_cast<int32_t>(source);
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(SourceInstantiation, IdGeneratorTest,
+ testing::Values(IdGenerator::Source::INPUT_READER,
+ IdGenerator::Source::INPUT_DISPATCHER,
+ IdGenerator::Source::OTHER));
+} // namespace android::test
diff --git a/libs/input/tests/InputChannel_test.cpp b/libs/input/tests/InputChannel_test.cpp
index f1675c0..ada275d 100644
--- a/libs/input/tests/InputChannel_test.cpp
+++ b/libs/input/tests/InputChannel_test.cpp
@@ -22,11 +22,12 @@
#include <time.h>
#include <errno.h>
+#include <binder/Binder.h>
#include <gtest/gtest.h>
#include <input/InputTransport.h>
-#include <utils/Timers.h>
#include <utils/StopWatch.h>
#include <utils/StrongPointer.h>
+#include <utils/Timers.h>
namespace android {
@@ -43,20 +44,27 @@
// of a pipe and to check for EPIPE on the other end after the channel is destroyed.
Pipe pipe;
- sp<InputChannel> inputChannel = new InputChannel("channel name", pipe.sendFd);
+ android::base::unique_fd sendFd(pipe.sendFd);
+ sp<InputChannel> inputChannel =
+ InputChannel::create("channel name", std::move(sendFd), new BBinder());
+
+ EXPECT_NE(inputChannel, nullptr) << "channel should be successfully created";
EXPECT_STREQ("channel name", inputChannel->getName().c_str())
<< "channel should have provided name";
- EXPECT_EQ(pipe.sendFd, inputChannel->getFd())
- << "channel should have provided fd";
+ EXPECT_NE(-1, inputChannel->getFd()) << "channel should have valid fd";
- inputChannel.clear(); // destroys input channel
+ // InputChannel should be the owner of the file descriptor now
+ ASSERT_FALSE(sendFd.ok());
+}
- EXPECT_EQ(-EPIPE, pipe.readSignal())
- << "channel should have closed fd when destroyed";
+TEST_F(InputChannelTest, SetAndGetToken) {
+ Pipe pipe;
+ sp<IBinder> token = new BBinder();
+ sp<InputChannel> channel =
+ InputChannel::create("test channel", android::base::unique_fd(pipe.sendFd), token);
- // clean up fds of Pipe endpoints that were closed so we don't try to close them again
- pipe.sendFd = -1;
+ EXPECT_EQ(token, channel->getConnectionToken());
}
TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) {
@@ -77,7 +85,7 @@
// Server->Client communication
InputMessage serverMsg;
memset(&serverMsg, 0, sizeof(InputMessage));
- serverMsg.header.type = InputMessage::TYPE_KEY;
+ serverMsg.header.type = InputMessage::Type::KEY;
serverMsg.body.key.action = AKEY_EVENT_ACTION_DOWN;
EXPECT_EQ(OK, serverChannel->sendMessage(&serverMsg))
<< "server channel should be able to send message to client channel";
@@ -93,7 +101,7 @@
// Client->Server communication
InputMessage clientReply;
memset(&clientReply, 0, sizeof(InputMessage));
- clientReply.header.type = InputMessage::TYPE_FINISHED;
+ clientReply.header.type = InputMessage::Type::FINISHED;
clientReply.body.finished.seq = 0x11223344;
clientReply.body.finished.handled = true;
EXPECT_EQ(OK, clientChannel->sendMessage(&clientReply))
@@ -152,7 +160,7 @@
serverChannel.clear(); // close server channel
InputMessage msg;
- msg.header.type = InputMessage::TYPE_KEY;
+ msg.header.type = InputMessage::Type::KEY;
EXPECT_EQ(DEAD_OBJECT, clientChannel->sendMessage(&msg))
<< "sendMessage should have returned DEAD_OBJECT";
}
@@ -171,7 +179,7 @@
};
InputMessage serverMsg = {}, clientMsg;
- serverMsg.header.type = InputMessage::TYPE_MOTION;
+ serverMsg.header.type = InputMessage::Type::MOTION;
serverMsg.body.motion.seq = 1;
serverMsg.body.motion.pointerCount = 1;
diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp
index 2b75c82..553dc4c 100644
--- a/libs/input/tests/InputEvent_test.cpp
+++ b/libs/input/tests/InputEvent_test.cpp
@@ -26,7 +26,12 @@
// Default display id.
static constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT;
-class BaseTest : public testing::Test { };
+class BaseTest : public testing::Test {
+protected:
+ static constexpr std::array<uint8_t, 32> HMAC = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+ 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
+};
// --- PointerCoordsTest ---
@@ -41,7 +46,6 @@
}
TEST_F(PointerCoordsTest, AxisValues) {
- float* valuePtr;
PointerCoords coords;
coords.clear();
@@ -176,16 +180,19 @@
KeyEvent event;
// Initialize and get properties.
- const nsecs_t ARBITRARY_DOWN_TIME = 1;
- const nsecs_t ARBITRARY_EVENT_TIME = 2;
- event.initialize(2, AINPUT_SOURCE_GAMEPAD, DISPLAY_ID, AKEY_EVENT_ACTION_DOWN,
- AKEY_EVENT_FLAG_FROM_SYSTEM, AKEYCODE_BUTTON_X, 121,
- AMETA_ALT_ON, 1, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME);
+ constexpr nsecs_t ARBITRARY_DOWN_TIME = 1;
+ constexpr nsecs_t ARBITRARY_EVENT_TIME = 2;
+ const int32_t id = InputEvent::nextId();
+ event.initialize(id, 2, AINPUT_SOURCE_GAMEPAD, DISPLAY_ID, HMAC, AKEY_EVENT_ACTION_DOWN,
+ AKEY_EVENT_FLAG_FROM_SYSTEM, AKEYCODE_BUTTON_X, 121, AMETA_ALT_ON, 1,
+ ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME);
+ ASSERT_EQ(id, event.getId());
ASSERT_EQ(AINPUT_EVENT_TYPE_KEY, event.getType());
ASSERT_EQ(2, event.getDeviceId());
- ASSERT_EQ(static_cast<int>(AINPUT_SOURCE_GAMEPAD), event.getSource());
+ ASSERT_EQ(AINPUT_SOURCE_GAMEPAD, event.getSource());
ASSERT_EQ(DISPLAY_ID, event.getDisplayId());
+ EXPECT_EQ(HMAC, event.getHmac());
ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, event.getAction());
ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, event.getFlags());
ASSERT_EQ(AKEYCODE_BUTTON_X, event.getKeyCode());
@@ -197,7 +204,7 @@
// Set source.
event.setSource(AINPUT_SOURCE_JOYSTICK);
- ASSERT_EQ(static_cast<int>(AINPUT_SOURCE_JOYSTICK), event.getSource());
+ ASSERT_EQ(AINPUT_SOURCE_JOYSTICK, event.getSource());
// Set display id.
constexpr int32_t newDisplayId = 2;
@@ -210,21 +217,23 @@
class MotionEventTest : public BaseTest {
protected:
- static const nsecs_t ARBITRARY_DOWN_TIME;
- static const nsecs_t ARBITRARY_EVENT_TIME;
- static const float X_OFFSET;
- static const float Y_OFFSET;
+ static constexpr nsecs_t ARBITRARY_DOWN_TIME = 1;
+ static constexpr nsecs_t ARBITRARY_EVENT_TIME = 2;
+ static constexpr float X_SCALE = 2.0;
+ static constexpr float Y_SCALE = 3.0;
+ static constexpr float X_OFFSET = 1;
+ static constexpr float Y_OFFSET = 1.1;
+
+ int32_t mId;
void initializeEventWithHistory(MotionEvent* event);
void assertEqualsEventWithHistory(const MotionEvent* event);
};
-const nsecs_t MotionEventTest::ARBITRARY_DOWN_TIME = 1;
-const nsecs_t MotionEventTest::ARBITRARY_EVENT_TIME = 2;
-const float MotionEventTest::X_OFFSET = 1.0f;
-const float MotionEventTest::Y_OFFSET = 1.1f;
void MotionEventTest::initializeEventWithHistory(MotionEvent* event) {
+ mId = InputEvent::nextId();
+
PointerProperties pointerProperties[2];
pointerProperties[0].clear();
pointerProperties[0].id = 1;
@@ -254,12 +263,13 @@
pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 26);
pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 27);
pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 28);
- event->initialize(2, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, AMOTION_EVENT_ACTION_MOVE, 0,
- AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED,
- AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY,
- MotionClassification::NONE, X_OFFSET, Y_OFFSET, 2.0f, 2.1f,
- ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME,
- 2, pointerProperties, pointerCoords);
+ event->initialize(mId, 2, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, HMAC,
+ AMOTION_EVENT_ACTION_MOVE, 0, AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED,
+ AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY,
+ MotionClassification::NONE, X_SCALE, Y_SCALE, X_OFFSET, Y_OFFSET, 2.0f, 2.1f,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 2, pointerProperties,
+ pointerCoords);
pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 110);
pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 111);
@@ -304,16 +314,20 @@
void MotionEventTest::assertEqualsEventWithHistory(const MotionEvent* event) {
// Check properties.
+ ASSERT_EQ(mId, event->getId());
ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType());
ASSERT_EQ(2, event->getDeviceId());
- ASSERT_EQ(static_cast<int>(AINPUT_SOURCE_TOUCHSCREEN), event->getSource());
+ ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, event->getSource());
ASSERT_EQ(DISPLAY_ID, event->getDisplayId());
+ EXPECT_EQ(HMAC, event->getHmac());
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, event->getAction());
ASSERT_EQ(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, event->getFlags());
ASSERT_EQ(AMOTION_EVENT_EDGE_FLAG_TOP, event->getEdgeFlags());
ASSERT_EQ(AMETA_ALT_ON, event->getMetaState());
ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, event->getButtonState());
ASSERT_EQ(MotionClassification::NONE, event->getClassification());
+ EXPECT_EQ(X_SCALE, event->getXScale());
+ EXPECT_EQ(Y_SCALE, event->getYScale());
ASSERT_EQ(X_OFFSET, event->getXOffset());
ASSERT_EQ(Y_OFFSET, event->getYOffset());
ASSERT_EQ(2.0f, event->getXPrecision());
@@ -367,19 +381,19 @@
ASSERT_EQ(211, event->getRawY(0));
ASSERT_EQ(221, event->getRawY(1));
- ASSERT_EQ(X_OFFSET + 10, event->getHistoricalX(0, 0));
- ASSERT_EQ(X_OFFSET + 20, event->getHistoricalX(1, 0));
- ASSERT_EQ(X_OFFSET + 110, event->getHistoricalX(0, 1));
- ASSERT_EQ(X_OFFSET + 120, event->getHistoricalX(1, 1));
- ASSERT_EQ(X_OFFSET + 210, event->getX(0));
- ASSERT_EQ(X_OFFSET + 220, event->getX(1));
+ ASSERT_EQ(X_OFFSET + 10 * X_SCALE, event->getHistoricalX(0, 0));
+ ASSERT_EQ(X_OFFSET + 20 * X_SCALE, event->getHistoricalX(1, 0));
+ ASSERT_EQ(X_OFFSET + 110 * X_SCALE, event->getHistoricalX(0, 1));
+ ASSERT_EQ(X_OFFSET + 120 * X_SCALE, event->getHistoricalX(1, 1));
+ ASSERT_EQ(X_OFFSET + 210 * X_SCALE, event->getX(0));
+ ASSERT_EQ(X_OFFSET + 220 * X_SCALE, event->getX(1));
- ASSERT_EQ(Y_OFFSET + 11, event->getHistoricalY(0, 0));
- ASSERT_EQ(Y_OFFSET + 21, event->getHistoricalY(1, 0));
- ASSERT_EQ(Y_OFFSET + 111, event->getHistoricalY(0, 1));
- ASSERT_EQ(Y_OFFSET + 121, event->getHistoricalY(1, 1));
- ASSERT_EQ(Y_OFFSET + 211, event->getY(0));
- ASSERT_EQ(Y_OFFSET + 221, event->getY(1));
+ ASSERT_EQ(Y_OFFSET + 11 * Y_SCALE, event->getHistoricalY(0, 0));
+ ASSERT_EQ(Y_OFFSET + 21 * Y_SCALE, event->getHistoricalY(1, 0));
+ ASSERT_EQ(Y_OFFSET + 111 * Y_SCALE, event->getHistoricalY(0, 1));
+ ASSERT_EQ(Y_OFFSET + 121 * Y_SCALE, event->getHistoricalY(1, 1));
+ ASSERT_EQ(Y_OFFSET + 211 * Y_SCALE, event->getY(0));
+ ASSERT_EQ(Y_OFFSET + 221 * Y_SCALE, event->getY(1));
ASSERT_EQ(12, event->getHistoricalPressure(0, 0));
ASSERT_EQ(22, event->getHistoricalPressure(1, 0));
@@ -440,7 +454,7 @@
// Set source.
event.setSource(AINPUT_SOURCE_JOYSTICK);
- ASSERT_EQ(static_cast<int>(AINPUT_SOURCE_JOYSTICK), event.getSource());
+ ASSERT_EQ(AINPUT_SOURCE_JOYSTICK, event.getSource());
// Set displayId.
constexpr int32_t newDisplayId = 2;
@@ -505,8 +519,8 @@
ASSERT_EQ(210 * 2, event.getRawX(0));
ASSERT_EQ(211 * 2, event.getRawY(0));
- ASSERT_EQ((X_OFFSET + 210) * 2, event.getX(0));
- ASSERT_EQ((Y_OFFSET + 211) * 2, event.getY(0));
+ ASSERT_EQ((X_OFFSET + 210 * X_SCALE) * 2, event.getX(0));
+ ASSERT_EQ((Y_OFFSET + 211 * Y_SCALE) * 2, event.getY(0));
ASSERT_EQ(212, event.getPressure(0));
ASSERT_EQ(213, event.getSize(0));
ASSERT_EQ(214 * 2, event.getTouchMajor(0));
@@ -570,11 +584,13 @@
pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, angle);
}
MotionEvent event;
- event.initialize(0 /*deviceId*/, AINPUT_SOURCE_UNKNOWN, DISPLAY_ID, AMOTION_EVENT_ACTION_MOVE,
- 0 /*actionButton*/, 0 /*flags*/, AMOTION_EVENT_EDGE_FLAG_NONE,
- AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE,
- 0 /*xOffset*/, 0 /*yOffset*/, 0 /*xPrecision*/, 0 /*yPrecision*/,
- 0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords);
+ event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_UNKNOWN, DISPLAY_ID,
+ INVALID_HMAC, AMOTION_EVENT_ACTION_MOVE, 0 /*actionButton*/, 0 /*flags*/,
+ AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/,
+ MotionClassification::NONE, 1 /*xScale*/, 1 /*yScale*/, 0 /*xOffset*/,
+ 0 /*yOffset*/, 0 /*xPrecision*/, 0 /*yPrecision*/,
+ 3 + RADIUS /*xCursorPosition*/, 2 /*yCursorPosition*/, 0 /*downTime*/,
+ 0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords);
float originalRawX = 0 + 3;
float originalRawY = -RADIUS + 2;
@@ -602,6 +618,14 @@
ASSERT_NEAR(tanf(angle), tanf(event.getOrientation(i)), 0.1);
}
+ // Check cursor positions. The original cursor position is at (3 + RADIUS, 2), where the center
+ // of the circle is (3, 2), so the cursor position is to the right of the center of the circle.
+ // The choice of triangular functions in this test defines the angle of rotation clockwise
+ // relative to the y-axis. Therefore the cursor position's angle is 90 degrees. Here we swap the
+ // triangular function so that we don't have to add the 90 degrees.
+ ASSERT_NEAR(cosf(PI_180 * ROTATION) * RADIUS, event.getXCursorPosition(), 0.001);
+ ASSERT_NEAR(sinf(PI_180 * ROTATION) * RADIUS, event.getYCursorPosition(), 0.001);
+
// Applying the transformation should preserve the raw X and Y of the first point.
ASSERT_NEAR(originalRawX, event.getRawX(0), 0.001);
ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001);
@@ -625,12 +649,47 @@
}
for (MotionClassification classification : classifications) {
- event.initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID,
- AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0,
- classification, 0, 0, 0, 0, 0 /*downTime*/, 0 /*eventTime*/,
- pointerCount, pointerProperties, pointerCoords);
+ event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN,
+ DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0,
+ AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, classification, 1 /*xScale*/,
+ 1 /*yScale*/, 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, 0 /*downTime*/, 0 /*eventTime*/,
+ pointerCount, pointerProperties, pointerCoords);
ASSERT_EQ(classification, event.getClassification());
}
}
+TEST_F(MotionEventTest, Initialize_SetsCursorPosition) {
+ MotionEvent event;
+ constexpr size_t pointerCount = 1;
+ PointerProperties pointerProperties[pointerCount];
+ PointerCoords pointerCoords[pointerCount];
+ for (size_t i = 0; i < pointerCount; i++) {
+ pointerProperties[i].clear();
+ pointerProperties[i].id = i;
+ pointerCoords[i].clear();
+ }
+
+ event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_MOUSE, DISPLAY_ID,
+ INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE,
+ AMETA_NONE, 0, MotionClassification::NONE, 1 /*xScale*/, 1 /*yScale*/, 0, 0, 0,
+ 0, 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, 0 /*downTime*/,
+ 0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords);
+ event.offsetLocation(20, 60);
+ ASSERT_EQ(280, event.getRawXCursorPosition());
+ ASSERT_EQ(540, event.getRawYCursorPosition());
+ ASSERT_EQ(300, event.getXCursorPosition());
+ ASSERT_EQ(600, event.getYCursorPosition());
+}
+
+TEST_F(MotionEventTest, SetCursorPosition) {
+ MotionEvent event;
+ initializeEventWithHistory(&event);
+ event.setSource(AINPUT_SOURCE_MOUSE);
+
+ event.setCursorPosition(3, 4);
+ ASSERT_EQ(3, event.getXCursorPosition());
+ ASSERT_EQ(4, event.getYCursorPosition());
+}
+
} // namespace android
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp
index f2cd1be..8e2eec8 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -38,6 +38,7 @@
virtual void SetUp() {
status_t result = InputChannel::openInputChannelPair("channel name",
serverChannel, clientChannel);
+ ASSERT_EQ(OK, result);
mPublisher = new InputPublisher(serverChannel);
mConsumer = new InputConsumer(clientChannel);
@@ -60,6 +61,7 @@
void PublishAndConsumeKeyEvent();
void PublishAndConsumeMotionEvent();
+ void PublishAndConsumeFocusEvent();
};
TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) {
@@ -71,9 +73,13 @@
status_t status;
constexpr uint32_t seq = 15;
+ int32_t eventId = InputEvent::nextId();
constexpr int32_t deviceId = 1;
- constexpr int32_t source = AINPUT_SOURCE_KEYBOARD;
+ constexpr uint32_t source = AINPUT_SOURCE_KEYBOARD;
constexpr int32_t displayId = ADISPLAY_ID_DEFAULT;
+ constexpr std::array<uint8_t, 32> hmac = {31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21,
+ 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10,
+ 9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
constexpr int32_t action = AKEY_EVENT_ACTION_DOWN;
constexpr int32_t flags = AKEY_EVENT_FLAG_FROM_SYSTEM;
constexpr int32_t keyCode = AKEYCODE_ENTER;
@@ -83,8 +89,9 @@
constexpr nsecs_t downTime = 3;
constexpr nsecs_t eventTime = 4;
- status = mPublisher->publishKeyEvent(seq, deviceId, source, displayId, action, flags,
- keyCode, scanCode, metaState, repeatCount, downTime, eventTime);
+ status = mPublisher->publishKeyEvent(seq, eventId, deviceId, source, displayId, hmac, action,
+ flags, keyCode, scanCode, metaState, repeatCount, downTime,
+ eventTime);
ASSERT_EQ(OK, status)
<< "publisher publishKeyEvent should return OK";
@@ -101,9 +108,11 @@
KeyEvent* keyEvent = static_cast<KeyEvent*>(event);
EXPECT_EQ(seq, consumeSeq);
+ EXPECT_EQ(eventId, keyEvent->getId());
EXPECT_EQ(deviceId, keyEvent->getDeviceId());
EXPECT_EQ(source, keyEvent->getSource());
EXPECT_EQ(displayId, keyEvent->getDisplayId());
+ EXPECT_EQ(hmac, keyEvent->getHmac());
EXPECT_EQ(action, keyEvent->getAction());
EXPECT_EQ(flags, keyEvent->getFlags());
EXPECT_EQ(keyCode, keyEvent->getKeyCode());
@@ -132,9 +141,13 @@
status_t status;
constexpr uint32_t seq = 15;
+ int32_t eventId = InputEvent::nextId();
constexpr int32_t deviceId = 1;
- constexpr int32_t source = AINPUT_SOURCE_TOUCHSCREEN;
+ constexpr uint32_t source = AINPUT_SOURCE_TOUCHSCREEN;
constexpr int32_t displayId = ADISPLAY_ID_DEFAULT;
+ constexpr std::array<uint8_t, 32> hmac = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+ 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
constexpr int32_t action = AMOTION_EVENT_ACTION_MOVE;
constexpr int32_t actionButton = 0;
constexpr int32_t flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
@@ -142,10 +155,14 @@
constexpr int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON;
constexpr int32_t buttonState = AMOTION_EVENT_BUTTON_PRIMARY;
constexpr MotionClassification classification = MotionClassification::AMBIGUOUS_GESTURE;
+ constexpr float xScale = 2;
+ constexpr float yScale = 3;
constexpr float xOffset = -10;
constexpr float yOffset = -20;
constexpr float xPrecision = 0.25;
constexpr float yPrecision = 0.5;
+ constexpr float xCursorPosition = 1.3;
+ constexpr float yCursorPosition = 50.6;
constexpr nsecs_t downTime = 3;
constexpr size_t pointerCount = 3;
constexpr nsecs_t eventTime = 4;
@@ -168,10 +185,12 @@
pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 3.5 * i);
}
- status = mPublisher->publishMotionEvent(seq, deviceId, source, displayId, action, actionButton,
- flags, edgeFlags, metaState, buttonState, classification,
- xOffset, yOffset, xPrecision, yPrecision, downTime, eventTime, pointerCount,
- pointerProperties, pointerCoords);
+ status = mPublisher->publishMotionEvent(seq, eventId, deviceId, source, displayId, hmac, action,
+ actionButton, flags, edgeFlags, metaState, buttonState,
+ classification, xScale, yScale, xOffset, yOffset,
+ xPrecision, yPrecision, xCursorPosition,
+ yCursorPosition, downTime, eventTime, pointerCount,
+ pointerProperties, pointerCoords);
ASSERT_EQ(OK, status)
<< "publisher publishMotionEvent should return OK";
@@ -188,17 +207,27 @@
MotionEvent* motionEvent = static_cast<MotionEvent*>(event);
EXPECT_EQ(seq, consumeSeq);
+ EXPECT_EQ(eventId, motionEvent->getId());
EXPECT_EQ(deviceId, motionEvent->getDeviceId());
EXPECT_EQ(source, motionEvent->getSource());
EXPECT_EQ(displayId, motionEvent->getDisplayId());
+ EXPECT_EQ(hmac, motionEvent->getHmac());
EXPECT_EQ(action, motionEvent->getAction());
EXPECT_EQ(flags, motionEvent->getFlags());
EXPECT_EQ(edgeFlags, motionEvent->getEdgeFlags());
EXPECT_EQ(metaState, motionEvent->getMetaState());
EXPECT_EQ(buttonState, motionEvent->getButtonState());
EXPECT_EQ(classification, motionEvent->getClassification());
+ EXPECT_EQ(xScale, motionEvent->getXScale());
+ EXPECT_EQ(yScale, motionEvent->getYScale());
+ EXPECT_EQ(xOffset, motionEvent->getXOffset());
+ EXPECT_EQ(yOffset, motionEvent->getYOffset());
EXPECT_EQ(xPrecision, motionEvent->getXPrecision());
EXPECT_EQ(yPrecision, motionEvent->getYPrecision());
+ EXPECT_EQ(xCursorPosition, motionEvent->getRawXCursorPosition());
+ EXPECT_EQ(yCursorPosition, motionEvent->getRawYCursorPosition());
+ EXPECT_EQ(xCursorPosition * xScale + xOffset, motionEvent->getXCursorPosition());
+ EXPECT_EQ(yCursorPosition * yScale + yOffset, motionEvent->getYCursorPosition());
EXPECT_EQ(downTime, motionEvent->getDownTime());
EXPECT_EQ(eventTime, motionEvent->getEventTime());
EXPECT_EQ(pointerCount, motionEvent->getPointerCount());
@@ -213,10 +242,10 @@
motionEvent->getRawX(i));
EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
motionEvent->getRawY(i));
- EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X) + xOffset,
- motionEvent->getX(i));
- EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y) + yOffset,
- motionEvent->getY(i));
+ EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X) * xScale + xOffset,
+ motionEvent->getX(i));
+ EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y) * yScale + yOffset,
+ motionEvent->getY(i));
EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
motionEvent->getPressure(i));
EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
@@ -248,6 +277,45 @@
<< "publisher receiveFinishedSignal should have set handled to consumer's reply";
}
+void InputPublisherAndConsumerTest::PublishAndConsumeFocusEvent() {
+ status_t status;
+
+ constexpr uint32_t seq = 15;
+ int32_t eventId = InputEvent::nextId();
+ constexpr bool hasFocus = true;
+ constexpr bool inTouchMode = true;
+
+ status = mPublisher->publishFocusEvent(seq, eventId, hasFocus, inTouchMode);
+ ASSERT_EQ(OK, status) << "publisher publishKeyEvent should return OK";
+
+ uint32_t consumeSeq;
+ InputEvent* event;
+ status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event);
+ ASSERT_EQ(OK, status) << "consumer consume should return OK";
+
+ ASSERT_TRUE(event != nullptr) << "consumer should have returned non-NULL event";
+ ASSERT_EQ(AINPUT_EVENT_TYPE_FOCUS, event->getType())
+ << "consumer should have returned a focus event";
+
+ FocusEvent* focusEvent = static_cast<FocusEvent*>(event);
+ EXPECT_EQ(seq, consumeSeq);
+ EXPECT_EQ(eventId, focusEvent->getId());
+ EXPECT_EQ(hasFocus, focusEvent->getHasFocus());
+ EXPECT_EQ(inTouchMode, focusEvent->getInTouchMode());
+
+ status = mConsumer->sendFinishedSignal(seq, true);
+ ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK";
+
+ uint32_t finishedSeq = 0;
+ bool handled = false;
+ status = mPublisher->receiveFinishedSignal(&finishedSeq, &handled);
+ ASSERT_EQ(OK, status) << "publisher receiveFinishedSignal should return OK";
+ ASSERT_EQ(seq, finishedSeq)
+ << "publisher receiveFinishedSignal should have returned the original sequence number";
+ ASSERT_TRUE(handled)
+ << "publisher receiveFinishedSignal should have set handled to consumer's reply";
+}
+
TEST_F(InputPublisherAndConsumerTest, PublishKeyEvent_EndToEnd) {
ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
}
@@ -256,6 +324,10 @@
ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
}
+TEST_F(InputPublisherAndConsumerTest, PublishFocusEvent_EndToEnd) {
+ ASSERT_NO_FATAL_FAILURE(PublishAndConsumeFocusEvent());
+}
+
TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenSequenceNumberIsZero_ReturnsError) {
status_t status;
const size_t pointerCount = 1;
@@ -266,9 +338,12 @@
pointerCoords[i].clear();
}
- status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- MotionClassification::NONE, 0, 0, 0, 0, 0, 0,
- pointerCount, pointerProperties, pointerCoords);
+ status = mPublisher->publishMotionEvent(0, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
+ 0, 0, 0, MotionClassification::NONE, 1 /* xScale */,
+ 1 /* yScale */, 0, 0, 0, 0,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0,
+ pointerCount, pointerProperties, pointerCoords);
ASSERT_EQ(BAD_VALUE, status)
<< "publisher publishMotionEvent should return BAD_VALUE";
}
@@ -279,9 +354,12 @@
PointerProperties pointerProperties[pointerCount];
PointerCoords pointerCoords[pointerCount];
- status = mPublisher->publishMotionEvent(1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- MotionClassification::NONE, 0, 0, 0, 0, 0, 0,
- pointerCount, pointerProperties, pointerCoords);
+ status = mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
+ 0, 0, 0, MotionClassification::NONE, 1 /* xScale */,
+ 1 /* yScale */, 0, 0, 0, 0,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0,
+ pointerCount, pointerProperties, pointerCoords);
ASSERT_EQ(BAD_VALUE, status)
<< "publisher publishMotionEvent should return BAD_VALUE";
}
@@ -297,9 +375,12 @@
pointerCoords[i].clear();
}
- status = mPublisher->publishMotionEvent(1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- MotionClassification::NONE, 0, 0, 0, 0, 0, 0,
- pointerCount, pointerProperties, pointerCoords);
+ status = mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
+ 0, 0, 0, MotionClassification::NONE, 1 /* xScale */,
+ 1 /* yScale */, 0, 0, 0, 0,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0,
+ pointerCount, pointerProperties, pointerCoords);
ASSERT_EQ(BAD_VALUE, status)
<< "publisher publishMotionEvent should return BAD_VALUE";
}
@@ -308,6 +389,7 @@
ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
+ ASSERT_NO_FATAL_FAILURE(PublishAndConsumeFocusEvent());
ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
}
diff --git a/libs/input/tests/InputWindow_test.cpp b/libs/input/tests/InputWindow_test.cpp
index 6db18ab..d1cb527 100644
--- a/libs/input/tests/InputWindow_test.cpp
+++ b/libs/input/tests/InputWindow_test.cpp
@@ -40,6 +40,7 @@
sp<IBinder> touchableRegionCropHandle = new BBinder();
InputWindowInfo i;
i.token = new BBinder();
+ i.id = 1;
i.name = "Foobar";
i.layoutParamsFlags = 7;
i.layoutParamsType = 39;
@@ -57,7 +58,6 @@
i.hasFocus = false;
i.hasWallpaper = false;
i.paused = false;
- i.layer = 7;
i.ownerPid = 19;
i.ownerUid = 24;
i.inputFeatures = 29;
@@ -72,6 +72,7 @@
p.setDataPosition(0);
InputWindowInfo i2 = InputWindowInfo::read(p);
ASSERT_EQ(i.token, i2.token);
+ ASSERT_EQ(i.id, i2.id);
ASSERT_EQ(i.name, i2.name);
ASSERT_EQ(i.layoutParamsFlags, i2.layoutParamsFlags);
ASSERT_EQ(i.layoutParamsType, i2.layoutParamsType);
@@ -89,7 +90,6 @@
ASSERT_EQ(i.hasFocus, i2.hasFocus);
ASSERT_EQ(i.hasWallpaper, i2.hasWallpaper);
ASSERT_EQ(i.paused, i2.paused);
- ASSERT_EQ(i.layer, i2.layer);
ASSERT_EQ(i.ownerPid, i2.ownerPid);
ASSERT_EQ(i.ownerUid, i2.ownerUid);
ASSERT_EQ(i.inputFeatures, i2.inputFeatures);
diff --git a/libs/input/tests/LatencyStatistics_test.cpp b/libs/input/tests/LatencyStatistics_test.cpp
new file mode 100644
index 0000000..eb12d4e
--- /dev/null
+++ b/libs/input/tests/LatencyStatistics_test.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2019 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 <input/LatencyStatistics.h>
+#include <cmath>
+#include <limits>
+#include <thread>
+
+namespace android {
+namespace test {
+
+TEST(LatencyStatisticsTest, ResetStats) {
+ LatencyStatistics stats{5min};
+ stats.addValue(5.0);
+ stats.addValue(19.3);
+ stats.addValue(20);
+ stats.reset();
+
+ ASSERT_EQ(stats.getCount(), 0u);
+ ASSERT_EQ(std::isnan(stats.getStDev()), true);
+ ASSERT_EQ(std::isnan(stats.getMean()), true);
+}
+
+TEST(LatencyStatisticsTest, AddStatsValue) {
+ LatencyStatistics stats{5min};
+ stats.addValue(5.0);
+
+ ASSERT_EQ(stats.getMin(), 5.0);
+ ASSERT_EQ(stats.getMax(), 5.0);
+ ASSERT_EQ(stats.getCount(), 1u);
+ ASSERT_EQ(stats.getMean(), 5.0);
+ ASSERT_EQ(stats.getStDev(), 0.0);
+}
+
+TEST(LatencyStatisticsTest, AddMultipleStatsValue) {
+ LatencyStatistics stats{5min};
+ stats.addValue(4.0);
+ stats.addValue(6.0);
+ stats.addValue(8.0);
+ stats.addValue(10.0);
+
+ float stdev = stats.getStDev();
+
+ ASSERT_EQ(stats.getMin(), 4.0);
+ ASSERT_EQ(stats.getMax(), 10.0);
+ ASSERT_EQ(stats.getCount(), 4u);
+ ASSERT_EQ(stats.getMean(), 7.0);
+ ASSERT_EQ(stdev * stdev, 5.0);
+}
+
+TEST(LatencyStatisticsTest, ShouldReportStats) {
+ LatencyStatistics stats{0min};
+ stats.addValue(5.0);
+
+ std::this_thread::sleep_for(1us);
+
+ ASSERT_EQ(stats.shouldReport(), true);
+}
+
+} // namespace test
+} // namespace android
\ No newline at end of file
diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp
index 62023fb..1fe7bb9 100644
--- a/libs/input/tests/StructLayout_test.cpp
+++ b/libs/input/tests/StructLayout_test.cpp
@@ -35,40 +35,100 @@
CHECK_OFFSET(InputMessage, body, 8);
CHECK_OFFSET(InputMessage::Body::Key, seq, 0);
+ CHECK_OFFSET(InputMessage::Body::Key, eventId, 4);
CHECK_OFFSET(InputMessage::Body::Key, eventTime, 8);
CHECK_OFFSET(InputMessage::Body::Key, deviceId, 16);
CHECK_OFFSET(InputMessage::Body::Key, source, 20);
CHECK_OFFSET(InputMessage::Body::Key, displayId, 24);
- CHECK_OFFSET(InputMessage::Body::Key, action, 28);
- CHECK_OFFSET(InputMessage::Body::Key, flags, 32);
- CHECK_OFFSET(InputMessage::Body::Key, keyCode, 36);
- CHECK_OFFSET(InputMessage::Body::Key, scanCode, 40);
- CHECK_OFFSET(InputMessage::Body::Key, metaState, 44);
- CHECK_OFFSET(InputMessage::Body::Key, repeatCount, 48);
- CHECK_OFFSET(InputMessage::Body::Key, downTime, 56);
+ CHECK_OFFSET(InputMessage::Body::Key, hmac, 28);
+ CHECK_OFFSET(InputMessage::Body::Key, action, 60);
+ CHECK_OFFSET(InputMessage::Body::Key, flags, 64);
+ CHECK_OFFSET(InputMessage::Body::Key, keyCode, 68);
+ CHECK_OFFSET(InputMessage::Body::Key, scanCode, 72);
+ CHECK_OFFSET(InputMessage::Body::Key, metaState, 76);
+ CHECK_OFFSET(InputMessage::Body::Key, repeatCount, 80);
+ CHECK_OFFSET(InputMessage::Body::Key, downTime, 88);
CHECK_OFFSET(InputMessage::Body::Motion, seq, 0);
+ CHECK_OFFSET(InputMessage::Body::Motion, eventId, 4);
CHECK_OFFSET(InputMessage::Body::Motion, eventTime, 8);
CHECK_OFFSET(InputMessage::Body::Motion, deviceId, 16);
CHECK_OFFSET(InputMessage::Body::Motion, source, 20);
CHECK_OFFSET(InputMessage::Body::Motion, displayId, 24);
- CHECK_OFFSET(InputMessage::Body::Motion, action, 28);
- CHECK_OFFSET(InputMessage::Body::Motion, actionButton, 32);
- CHECK_OFFSET(InputMessage::Body::Motion, flags, 36);
- CHECK_OFFSET(InputMessage::Body::Motion, metaState, 40);
- CHECK_OFFSET(InputMessage::Body::Motion, buttonState, 44);
- CHECK_OFFSET(InputMessage::Body::Motion, classification, 48);
- CHECK_OFFSET(InputMessage::Body::Motion, edgeFlags, 52);
- CHECK_OFFSET(InputMessage::Body::Motion, downTime, 56);
- CHECK_OFFSET(InputMessage::Body::Motion, xOffset, 64);
- CHECK_OFFSET(InputMessage::Body::Motion, yOffset, 68);
- CHECK_OFFSET(InputMessage::Body::Motion, xPrecision, 72);
- CHECK_OFFSET(InputMessage::Body::Motion, yPrecision, 76);
- CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 80);
- CHECK_OFFSET(InputMessage::Body::Motion, pointers, 88);
+ CHECK_OFFSET(InputMessage::Body::Motion, hmac, 28);
+ CHECK_OFFSET(InputMessage::Body::Motion, action, 60);
+ CHECK_OFFSET(InputMessage::Body::Motion, actionButton, 64);
+ CHECK_OFFSET(InputMessage::Body::Motion, flags, 68);
+ CHECK_OFFSET(InputMessage::Body::Motion, metaState, 72);
+ CHECK_OFFSET(InputMessage::Body::Motion, buttonState, 76);
+ CHECK_OFFSET(InputMessage::Body::Motion, classification, 80);
+ CHECK_OFFSET(InputMessage::Body::Motion, edgeFlags, 84);
+ CHECK_OFFSET(InputMessage::Body::Motion, downTime, 88);
+ CHECK_OFFSET(InputMessage::Body::Motion, xScale, 96);
+ CHECK_OFFSET(InputMessage::Body::Motion, yScale, 100);
+ CHECK_OFFSET(InputMessage::Body::Motion, xOffset, 104);
+ CHECK_OFFSET(InputMessage::Body::Motion, yOffset, 108);
+ CHECK_OFFSET(InputMessage::Body::Motion, xPrecision, 112);
+ CHECK_OFFSET(InputMessage::Body::Motion, yPrecision, 116);
+ CHECK_OFFSET(InputMessage::Body::Motion, xCursorPosition, 120);
+ CHECK_OFFSET(InputMessage::Body::Motion, yCursorPosition, 124);
+ CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 128);
+ CHECK_OFFSET(InputMessage::Body::Motion, pointers, 136);
+
+ CHECK_OFFSET(InputMessage::Body::Focus, seq, 0);
+ CHECK_OFFSET(InputMessage::Body::Focus, eventId, 4);
+ CHECK_OFFSET(InputMessage::Body::Focus, hasFocus, 12);
+ CHECK_OFFSET(InputMessage::Body::Focus, inTouchMode, 14);
CHECK_OFFSET(InputMessage::Body::Finished, seq, 0);
CHECK_OFFSET(InputMessage::Body::Finished, handled, 4);
}
+void TestHeaderSize() {
+ static_assert(sizeof(InputMessage::Header) == 8);
+}
+
+/**
+ * We cannot use the Body::size() method here because it is not static for
+ * the Motion type, where "pointerCount" variable affects the size and can change at runtime.
+ */
+void TestBodySize() {
+ static_assert(sizeof(InputMessage::Body::Key) == 96);
+ static_assert(sizeof(InputMessage::Body::Motion) ==
+ offsetof(InputMessage::Body::Motion, pointers) +
+ sizeof(InputMessage::Body::Motion::Pointer) * MAX_POINTERS);
+ static_assert(sizeof(InputMessage::Body::Finished) == 8);
+ static_assert(sizeof(InputMessage::Body::Focus) == 16);
+}
+
+// --- VerifiedInputEvent ---
+// Ensure that VerifiedInputEvent, VerifiedKeyEvent, VerifiedMotionEvent are packed.
+// We will treat them as byte collections when signing them. There should not be any uninitialized
+// data in-between fields. Otherwise, the padded data will affect the hmac value and verifications
+// will fail.
+
+void TestVerifiedEventSize() {
+ // VerifiedInputEvent
+ constexpr size_t VERIFIED_INPUT_EVENT_SIZE = sizeof(VerifiedInputEvent::type) +
+ sizeof(VerifiedInputEvent::deviceId) + sizeof(VerifiedInputEvent::eventTimeNanos) +
+ sizeof(VerifiedInputEvent::source) + sizeof(VerifiedInputEvent::displayId);
+ static_assert(sizeof(VerifiedInputEvent) == VERIFIED_INPUT_EVENT_SIZE);
+
+ // VerifiedKeyEvent
+ constexpr size_t VERIFIED_KEY_EVENT_SIZE = VERIFIED_INPUT_EVENT_SIZE +
+ sizeof(VerifiedKeyEvent::action) + sizeof(VerifiedKeyEvent::downTimeNanos) +
+ sizeof(VerifiedKeyEvent::flags) + sizeof(VerifiedKeyEvent::keyCode) +
+ sizeof(VerifiedKeyEvent::scanCode) + sizeof(VerifiedKeyEvent::metaState) +
+ sizeof(VerifiedKeyEvent::repeatCount);
+ static_assert(sizeof(VerifiedKeyEvent) == VERIFIED_KEY_EVENT_SIZE);
+
+ // VerifiedMotionEvent
+ constexpr size_t VERIFIED_MOTION_EVENT_SIZE = VERIFIED_INPUT_EVENT_SIZE +
+ sizeof(VerifiedMotionEvent::rawX) + sizeof(VerifiedMotionEvent::rawY) +
+ sizeof(VerifiedMotionEvent::actionMasked) + sizeof(VerifiedMotionEvent::downTimeNanos) +
+ sizeof(VerifiedMotionEvent::flags) + sizeof(VerifiedMotionEvent::metaState) +
+ sizeof(VerifiedMotionEvent::buttonState);
+ static_assert(sizeof(VerifiedMotionEvent) == VERIFIED_MOTION_EVENT_SIZE);
+}
+
} // namespace android
diff --git a/libs/input/tests/TouchVideoFrame_test.cpp b/libs/input/tests/TouchVideoFrame_test.cpp
index fe06cad..654b236 100644
--- a/libs/input/tests/TouchVideoFrame_test.cpp
+++ b/libs/input/tests/TouchVideoFrame_test.cpp
@@ -16,6 +16,7 @@
#include <gtest/gtest.h>
+#include <input/DisplayViewport.h>
#include <input/TouchVideoFrame.h>
namespace android {
diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp
index 368446f..bf452c0 100644
--- a/libs/input/tests/VelocityTracker_test.cpp
+++ b/libs/input/tests/VelocityTracker_test.cpp
@@ -176,12 +176,14 @@
EXPECT_EQ(pointerIndex, pointerCount);
MotionEvent event;
- event.initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID,
- action, 0 /*actionButton*/, 0 /*flags*/,
- AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/,
- MotionClassification::NONE,
- 0 /*xOffset*/, 0 /*yOffset*/, 0 /*xPrecision*/, 0 /*yPrecision*/,
- 0 /*downTime*/, entry.eventTime.count(), pointerCount, properties, coords);
+ event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN,
+ DISPLAY_ID, INVALID_HMAC, action, 0 /*actionButton*/, 0 /*flags*/,
+ AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/,
+ MotionClassification::NONE, 1 /*xScale*/, 1 /*yScale*/, 0 /*xOffset*/,
+ 0 /*yOffset*/, 0 /*xPrecision*/, 0 /*yPrecision*/,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, 0 /*downTime*/,
+ entry.eventTime.count(), pointerCount, properties, coords);
events.emplace_back(event);
}
diff --git a/libs/input/tests/VerifiedInputEvent_test.cpp b/libs/input/tests/VerifiedInputEvent_test.cpp
new file mode 100644
index 0000000..4e8e840
--- /dev/null
+++ b/libs/input/tests/VerifiedInputEvent_test.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 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 <gtest/gtest.h>
+#include <input/Input.h>
+
+namespace android {
+
+static KeyEvent getKeyEventWithFlags(int32_t flags) {
+ KeyEvent event;
+ event.initialize(InputEvent::nextId(), 2 /*deviceId*/, AINPUT_SOURCE_GAMEPAD,
+ ADISPLAY_ID_DEFAULT, INVALID_HMAC, AKEY_EVENT_ACTION_DOWN, flags,
+ AKEYCODE_BUTTON_X, 121 /*scanCode*/, AMETA_ALT_ON, 1 /*repeatCount*/,
+ 1000 /*downTime*/, 2000 /*eventTime*/);
+ return event;
+}
+
+static MotionEvent getMotionEventWithFlags(int32_t flags) {
+ MotionEvent event;
+ constexpr size_t pointerCount = 1;
+ PointerProperties pointerProperties[pointerCount];
+ PointerCoords pointerCoords[pointerCount];
+ for (size_t i = 0; i < pointerCount; i++) {
+ pointerProperties[i].clear();
+ pointerProperties[i].id = i;
+ pointerCoords[i].clear();
+ }
+
+ event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_DEFAULT,
+ INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0 /*actionButton*/, flags,
+ AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/,
+ MotionClassification::NONE, 2 /*xScale*/, 3 /*yScale*/, 4 /*xOffset*/,
+ 5 /*yOffset*/, 0.1 /*xPrecision*/, 0.2 /*yPrecision*/, 280 /*xCursorPosition*/,
+ 540 /*yCursorPosition*/, 100 /*downTime*/, 200 /*eventTime*/, pointerCount,
+ pointerProperties, pointerCoords);
+ return event;
+}
+
+TEST(VerifiedKeyEventTest, ConvertKeyEventToVerifiedKeyEvent) {
+ KeyEvent event = getKeyEventWithFlags(0);
+ VerifiedKeyEvent verified = verifiedKeyEventFromKeyEvent(event);
+
+ ASSERT_EQ(VerifiedInputEvent::Type::KEY, verified.type);
+
+ ASSERT_EQ(event.getDeviceId(), verified.deviceId);
+ ASSERT_EQ(event.getEventTime(), verified.eventTimeNanos);
+ ASSERT_EQ(event.getSource(), verified.source);
+ ASSERT_EQ(event.getDisplayId(), verified.displayId);
+
+ ASSERT_EQ(event.getAction(), verified.action);
+ ASSERT_EQ(event.getDownTime(), verified.downTimeNanos);
+ ASSERT_EQ(event.getFlags() & VERIFIED_KEY_EVENT_FLAGS, verified.flags);
+ ASSERT_EQ(event.getKeyCode(), verified.keyCode);
+ ASSERT_EQ(event.getScanCode(), verified.scanCode);
+ ASSERT_EQ(event.getMetaState(), verified.metaState);
+ ASSERT_EQ(event.getRepeatCount(), verified.repeatCount);
+}
+
+TEST(VerifiedKeyEventTest, VerifiedKeyEventContainsOnlyVerifiedFlags) {
+ KeyEvent event = getKeyEventWithFlags(AKEY_EVENT_FLAG_CANCELED | AKEY_EVENT_FLAG_FALLBACK);
+ VerifiedKeyEvent verified = verifiedKeyEventFromKeyEvent(event);
+ ASSERT_EQ(AKEY_EVENT_FLAG_CANCELED, verified.flags);
+}
+
+TEST(VerifiedKeyEventTest, VerifiedKeyEventDoesNotContainUnverifiedFlags) {
+ KeyEvent event = getKeyEventWithFlags(AKEY_EVENT_FLAG_EDITOR_ACTION);
+ VerifiedKeyEvent verified = verifiedKeyEventFromKeyEvent(event);
+ ASSERT_EQ(0, verified.flags);
+}
+
+TEST(VerifiedMotionEventTest, ConvertMotionEventToVerifiedMotionEvent) {
+ MotionEvent event = getMotionEventWithFlags(0);
+ VerifiedMotionEvent verified = verifiedMotionEventFromMotionEvent(event);
+
+ ASSERT_EQ(VerifiedInputEvent::Type::MOTION, verified.type);
+
+ ASSERT_EQ(event.getDeviceId(), verified.deviceId);
+ ASSERT_EQ(event.getEventTime(), verified.eventTimeNanos);
+ ASSERT_EQ(event.getSource(), verified.source);
+ ASSERT_EQ(event.getDisplayId(), verified.displayId);
+
+ ASSERT_EQ(event.getRawX(0), verified.rawX);
+ ASSERT_EQ(event.getRawY(0), verified.rawY);
+ ASSERT_EQ(event.getAction(), verified.actionMasked);
+ ASSERT_EQ(event.getDownTime(), verified.downTimeNanos);
+ ASSERT_EQ(event.getFlags() & VERIFIED_MOTION_EVENT_FLAGS, verified.flags);
+ ASSERT_EQ(event.getMetaState(), verified.metaState);
+ ASSERT_EQ(event.getButtonState(), verified.buttonState);
+}
+
+TEST(VerifiedMotionEventTest, VerifiedMotionEventContainsOnlyVerifiedFlags) {
+ MotionEvent event = getMotionEventWithFlags(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED |
+ AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE);
+ VerifiedMotionEvent verified = verifiedMotionEventFromMotionEvent(event);
+ ASSERT_EQ(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, verified.flags);
+}
+
+TEST(VerifiedMotionEventTest, VerifiedMotionEventDoesNotContainUnverifiedFlags) {
+ MotionEvent event = getMotionEventWithFlags(AMOTION_EVENT_FLAG_TAINTED);
+ VerifiedMotionEvent verified = verifiedMotionEventFromMotionEvent(event);
+ ASSERT_EQ(0, verified.flags);
+}
+
+} // namespace android
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
new file mode 100644
index 0000000..e458b2e
--- /dev/null
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -0,0 +1,555 @@
+/*
+ * Copyright (C) 2015 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 "Choreographer"
+//#define LOG_NDEBUG 0
+
+#include <android-base/thread_annotations.h>
+#include <gui/DisplayEventDispatcher.h>
+#include <gui/ISurfaceComposer.h>
+#include <gui/SurfaceComposerClient.h>
+#include <nativehelper/JNIHelp.h>
+#include <private/android/choreographer.h>
+#include <utils/Looper.h>
+#include <utils/Timers.h>
+
+#include <cinttypes>
+#include <mutex>
+#include <optional>
+#include <queue>
+#include <thread>
+
+namespace {
+struct {
+ // Global JVM that is provided by zygote
+ JavaVM* jvm = nullptr;
+ struct {
+ jclass clazz;
+ jmethodID getInstance;
+ jmethodID registerNativeChoreographerForRefreshRateCallbacks;
+ jmethodID unregisterNativeChoreographerForRefreshRateCallbacks;
+ } displayManagerGlobal;
+} gJni;
+
+// Gets the JNIEnv* for this thread, and performs one-off initialization if we
+// have never retrieved a JNIEnv* pointer before.
+JNIEnv* getJniEnv() {
+ if (gJni.jvm == nullptr) {
+ ALOGW("AChoreographer: No JVM provided!");
+ return nullptr;
+ }
+
+ JNIEnv* env = nullptr;
+ if (gJni.jvm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK) {
+ ALOGD("Attaching thread to JVM for AChoreographer");
+ JavaVMAttachArgs args = {JNI_VERSION_1_4, "AChoreographer_env", NULL};
+ jint attachResult = gJni.jvm->AttachCurrentThreadAsDaemon(&env, (void*)&args);
+ if (attachResult != JNI_OK) {
+ ALOGE("Unable to attach thread. Error: %d", attachResult);
+ return nullptr;
+ }
+ }
+ if (env == nullptr) {
+ ALOGW("AChoreographer: No JNI env available!");
+ }
+ return env;
+}
+
+inline const char* toString(bool value) {
+ return value ? "true" : "false";
+}
+} // namespace
+
+namespace android {
+
+struct FrameCallback {
+ AChoreographer_frameCallback callback;
+ AChoreographer_frameCallback64 callback64;
+ void* data;
+ nsecs_t dueTime;
+
+ inline bool operator<(const FrameCallback& rhs) const {
+ // Note that this is intentionally flipped because we want callbacks due sooner to be at
+ // the head of the queue
+ return dueTime > rhs.dueTime;
+ }
+};
+
+struct RefreshRateCallback {
+ AChoreographer_refreshRateCallback callback;
+ void* data;
+ bool firstCallbackFired = false;
+};
+
+class Choreographer;
+
+struct {
+ std::mutex lock;
+ std::vector<Choreographer*> ptrs GUARDED_BY(lock);
+ bool registeredToDisplayManager GUARDED_BY(lock) = false;
+
+ std::atomic<nsecs_t> mLastKnownVsync = -1;
+} gChoreographers;
+
+class Choreographer : public DisplayEventDispatcher, public MessageHandler {
+public:
+ explicit Choreographer(const sp<Looper>& looper) EXCLUDES(gChoreographers.lock);
+ void postFrameCallbackDelayed(AChoreographer_frameCallback cb,
+ AChoreographer_frameCallback64 cb64, void* data, nsecs_t delay);
+ void registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data)
+ EXCLUDES(gChoreographers.lock);
+ void unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data);
+ // Drains the queue of pending vsync periods and dispatches refresh rate
+ // updates to callbacks.
+ // The assumption is that this method is only called on a single
+ // processing thread, either by looper or by AChoreographer_handleEvents
+ void handleRefreshRateUpdates();
+ void scheduleLatestConfigRequest();
+
+ enum {
+ MSG_SCHEDULE_CALLBACKS = 0,
+ MSG_SCHEDULE_VSYNC = 1,
+ MSG_HANDLE_REFRESH_RATE_UPDATES = 2,
+ };
+ virtual void handleMessage(const Message& message) override;
+
+ static Choreographer* getForThread();
+ virtual ~Choreographer() override EXCLUDES(gChoreographers.lock);
+
+private:
+ Choreographer(const Choreographer&) = delete;
+
+ void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) override;
+ void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override;
+ void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId, int32_t configId,
+ nsecs_t vsyncPeriod) override;
+
+ void scheduleCallbacks();
+
+ std::mutex mLock;
+ // Protected by mLock
+ std::priority_queue<FrameCallback> mFrameCallbacks;
+ std::vector<RefreshRateCallback> mRefreshRateCallbacks;
+
+ nsecs_t mLatestVsyncPeriod = -1;
+
+ const sp<Looper> mLooper;
+ const std::thread::id mThreadId;
+};
+
+static thread_local Choreographer* gChoreographer;
+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);
+ status_t result = gChoreographer->initialize();
+ if (result != OK) {
+ ALOGW("Failed to initialize");
+ return nullptr;
+ }
+ }
+ return gChoreographer;
+}
+
+Choreographer::Choreographer(const sp<Looper>& looper)
+ : DisplayEventDispatcher(looper, ISurfaceComposer::VsyncSource::eVsyncSourceApp,
+ ISurfaceComposer::ConfigChanged::eConfigChangedDispatch),
+ mLooper(looper),
+ mThreadId(std::this_thread::get_id()) {
+ std::lock_guard<std::mutex> _l(gChoreographers.lock);
+ gChoreographers.ptrs.push_back(this);
+}
+
+Choreographer::~Choreographer() {
+ std::lock_guard<std::mutex> _l(gChoreographers.lock);
+ gChoreographers.ptrs.erase(std::remove_if(gChoreographers.ptrs.begin(),
+ gChoreographers.ptrs.end(),
+ [=](Choreographer* c) { return c == this; }),
+ gChoreographers.ptrs.end());
+ // Only poke DisplayManagerGlobal to unregister if we previously registered
+ // callbacks.
+ if (gChoreographers.ptrs.empty() && gChoreographers.registeredToDisplayManager) {
+ JNIEnv* env = getJniEnv();
+ if (env == nullptr) {
+ ALOGW("JNI environment is unavailable, skipping choreographer cleanup");
+ return;
+ }
+ jobject dmg = env->CallStaticObjectMethod(gJni.displayManagerGlobal.clazz,
+ gJni.displayManagerGlobal.getInstance);
+ if (dmg == nullptr) {
+ ALOGW("DMS is not initialized yet, skipping choreographer cleanup");
+ } else {
+ env->CallVoidMethod(dmg,
+ gJni.displayManagerGlobal
+ .unregisterNativeChoreographerForRefreshRateCallbacks);
+ env->DeleteLocalRef(dmg);
+ }
+ }
+}
+
+void Choreographer::postFrameCallbackDelayed(
+ AChoreographer_frameCallback cb, AChoreographer_frameCallback64 cb64, void* data, nsecs_t delay) {
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ FrameCallback callback{cb, cb64, data, now + delay};
+ {
+ std::lock_guard<std::mutex> _l{mLock};
+ mFrameCallbacks.push(callback);
+ }
+ if (callback.dueTime <= now) {
+ if (std::this_thread::get_id() != mThreadId) {
+ if (mLooper != nullptr) {
+ Message m{MSG_SCHEDULE_VSYNC};
+ mLooper->sendMessage(this, m);
+ } else {
+ scheduleVsync();
+ }
+ } else {
+ scheduleVsync();
+ }
+ } else {
+ if (mLooper != nullptr) {
+ Message m{MSG_SCHEDULE_CALLBACKS};
+ mLooper->sendMessageDelayed(delay, this, m);
+ } else {
+ scheduleCallbacks();
+ }
+ }
+}
+
+void Choreographer::registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data) {
+ std::lock_guard<std::mutex> _l{mLock};
+ for (const auto& callback : mRefreshRateCallbacks) {
+ // Don't re-add callbacks.
+ if (cb == callback.callback && data == callback.data) {
+ return;
+ }
+ }
+ mRefreshRateCallbacks.emplace_back(
+ RefreshRateCallback{.callback = cb, .data = data, .firstCallbackFired = false});
+ bool needsRegistration = false;
+ {
+ std::lock_guard<std::mutex> _l2(gChoreographers.lock);
+ needsRegistration = !gChoreographers.registeredToDisplayManager;
+ }
+ if (needsRegistration) {
+ JNIEnv* env = getJniEnv();
+ if (env == nullptr) {
+ ALOGW("JNI environment is unavailable, skipping registration");
+ return;
+ }
+ jobject dmg = env->CallStaticObjectMethod(gJni.displayManagerGlobal.clazz,
+ gJni.displayManagerGlobal.getInstance);
+ if (dmg == nullptr) {
+ ALOGW("DMS is not initialized yet: skipping registration");
+ return;
+ } else {
+ env->CallVoidMethod(dmg,
+ gJni.displayManagerGlobal
+ .registerNativeChoreographerForRefreshRateCallbacks,
+ reinterpret_cast<int64_t>(this));
+ env->DeleteLocalRef(dmg);
+ {
+ std::lock_guard<std::mutex> _l2(gChoreographers.lock);
+ gChoreographers.registeredToDisplayManager = true;
+ }
+ }
+ } else {
+ scheduleLatestConfigRequest();
+ }
+}
+
+void Choreographer::unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb,
+ void* data) {
+ std::lock_guard<std::mutex> _l{mLock};
+ mRefreshRateCallbacks.erase(std::remove_if(mRefreshRateCallbacks.begin(),
+ mRefreshRateCallbacks.end(),
+ [&](const RefreshRateCallback& callback) {
+ return cb == callback.callback &&
+ data == callback.data;
+ }),
+ mRefreshRateCallbacks.end());
+}
+
+void Choreographer::scheduleLatestConfigRequest() {
+ if (mLooper != nullptr) {
+ Message m{MSG_HANDLE_REFRESH_RATE_UPDATES};
+ mLooper->sendMessage(this, m);
+ } else {
+ // If the looper thread is detached from Choreographer, then refresh rate
+ // changes will be handled in AChoreographer_handlePendingEvents, so we
+ // need to redispatch a config from SF
+ requestLatestConfig();
+ }
+}
+
+void Choreographer::scheduleCallbacks() {
+ const nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ nsecs_t dueTime;
+ {
+ std::lock_guard<std::mutex> _l{mLock};
+ // If there are no pending callbacks then don't schedule a vsync
+ if (mFrameCallbacks.empty()) {
+ return;
+ }
+ dueTime = mFrameCallbacks.top().dueTime;
+ }
+
+ if (dueTime <= now) {
+ ALOGV("choreographer %p ~ scheduling vsync", this);
+ scheduleVsync();
+ return;
+ }
+}
+
+void Choreographer::handleRefreshRateUpdates() {
+ std::vector<RefreshRateCallback> callbacks{};
+ const nsecs_t pendingPeriod = gChoreographers.mLastKnownVsync.load();
+ const nsecs_t lastPeriod = mLatestVsyncPeriod;
+ if (pendingPeriod > 0) {
+ mLatestVsyncPeriod = pendingPeriod;
+ }
+ {
+ std::lock_guard<std::mutex> _l{mLock};
+ for (auto& cb : mRefreshRateCallbacks) {
+ callbacks.push_back(cb);
+ cb.firstCallbackFired = true;
+ }
+ }
+
+ for (auto& cb : callbacks) {
+ if (!cb.firstCallbackFired || (pendingPeriod > 0 && pendingPeriod != lastPeriod)) {
+ cb.callback(pendingPeriod, cb.data);
+ }
+ }
+}
+
+// TODO(b/74619554): The PhysicalDisplayId is ignored because SF only emits VSYNC events for the
+// internal display and DisplayEventReceiver::requestNextVsync only allows requesting VSYNC for
+// the internal display implicitly.
+void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t) {
+ std::vector<FrameCallback> callbacks{};
+ {
+ std::lock_guard<std::mutex> _l{mLock};
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ while (!mFrameCallbacks.empty() && mFrameCallbacks.top().dueTime < now) {
+ callbacks.push_back(mFrameCallbacks.top());
+ mFrameCallbacks.pop();
+ }
+ }
+ for (const auto& cb : callbacks) {
+ if (cb.callback64 != nullptr) {
+ cb.callback64(timestamp, cb.data);
+ } else if (cb.callback != nullptr) {
+ cb.callback(timestamp, cb.data);
+ }
+ }
+}
+
+void Choreographer::dispatchHotplug(nsecs_t, PhysicalDisplayId displayId, bool connected) {
+ ALOGV("choreographer %p ~ received hotplug event (displayId=%"
+ ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", connected=%s), ignoring.",
+ this, displayId, toString(connected));
+}
+
+// TODO(b/74619554): The PhysicalDisplayId is ignored because currently
+// Choreographer only supports dispatching VSYNC events for the internal
+// display, so as such Choreographer does not support the notion of multiple
+// displays. When multi-display choreographer is properly supported, then
+// PhysicalDisplayId should no longer be ignored.
+void Choreographer::dispatchConfigChanged(nsecs_t, PhysicalDisplayId displayId, int32_t configId,
+ nsecs_t vsyncPeriod) {
+ ALOGV("choreographer %p ~ received config change event "
+ "(displayId=%" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", configId=%d).",
+ this, displayId, configId);
+
+ const nsecs_t lastPeriod = mLatestVsyncPeriod;
+ std::vector<RefreshRateCallback> callbacks{};
+ {
+ std::lock_guard<std::mutex> _l{mLock};
+ for (auto& cb : mRefreshRateCallbacks) {
+ callbacks.push_back(cb);
+ cb.firstCallbackFired = true;
+ }
+ }
+
+ for (auto& cb : callbacks) {
+ if (!cb.firstCallbackFired || (vsyncPeriod > 0 && vsyncPeriod != lastPeriod)) {
+ cb.callback(vsyncPeriod, cb.data);
+ }
+ }
+
+ mLatestVsyncPeriod = vsyncPeriod;
+}
+
+void Choreographer::handleMessage(const Message& message) {
+ switch (message.what) {
+ case MSG_SCHEDULE_CALLBACKS:
+ scheduleCallbacks();
+ break;
+ case MSG_SCHEDULE_VSYNC:
+ scheduleVsync();
+ break;
+ case MSG_HANDLE_REFRESH_RATE_UPDATES:
+ handleRefreshRateUpdates();
+ break;
+ }
+}
+
+} // namespace android
+using namespace android;
+
+static inline Choreographer* AChoreographer_to_Choreographer(AChoreographer* choreographer) {
+ return reinterpret_cast<Choreographer*>(choreographer);
+}
+
+// Glue for private C api
+namespace android {
+void AChoreographer_signalRefreshRateCallbacks(nsecs_t vsyncPeriod) EXCLUDES(gChoreographers.lock) {
+ std::lock_guard<std::mutex> _l(gChoreographers.lock);
+ gChoreographers.mLastKnownVsync.store(vsyncPeriod);
+ for (auto c : gChoreographers.ptrs) {
+ c->scheduleLatestConfigRequest();
+ }
+}
+
+void AChoreographer_initJVM(JNIEnv* env) {
+ env->GetJavaVM(&gJni.jvm);
+ // Now we need to find the java classes.
+ jclass dmgClass = env->FindClass("android/hardware/display/DisplayManagerGlobal");
+ gJni.displayManagerGlobal.clazz = static_cast<jclass>(env->NewGlobalRef(dmgClass));
+ gJni.displayManagerGlobal.getInstance =
+ env->GetStaticMethodID(dmgClass, "getInstance",
+ "()Landroid/hardware/display/DisplayManagerGlobal;");
+ gJni.displayManagerGlobal.registerNativeChoreographerForRefreshRateCallbacks =
+ env->GetMethodID(dmgClass, "registerNativeChoreographerForRefreshRateCallbacks", "()V");
+ gJni.displayManagerGlobal.unregisterNativeChoreographerForRefreshRateCallbacks =
+ env->GetMethodID(dmgClass, "unregisterNativeChoreographerForRefreshRateCallbacks",
+ "()V");
+}
+
+AChoreographer* AChoreographer_routeGetInstance() {
+ return AChoreographer_getInstance();
+}
+void AChoreographer_routePostFrameCallback(AChoreographer* choreographer,
+ AChoreographer_frameCallback callback, void* data) {
+ return AChoreographer_postFrameCallback(choreographer, callback, data);
+}
+void AChoreographer_routePostFrameCallbackDelayed(AChoreographer* choreographer,
+ AChoreographer_frameCallback callback, void* data,
+ long delayMillis) {
+ return AChoreographer_postFrameCallbackDelayed(choreographer, callback, data, delayMillis);
+}
+void AChoreographer_routePostFrameCallback64(AChoreographer* choreographer,
+ AChoreographer_frameCallback64 callback, void* data) {
+ return AChoreographer_postFrameCallback64(choreographer, callback, data);
+}
+void AChoreographer_routePostFrameCallbackDelayed64(AChoreographer* choreographer,
+ AChoreographer_frameCallback64 callback,
+ void* data, uint32_t delayMillis) {
+ return AChoreographer_postFrameCallbackDelayed64(choreographer, callback, data, delayMillis);
+}
+void AChoreographer_routeRegisterRefreshRateCallback(AChoreographer* choreographer,
+ AChoreographer_refreshRateCallback callback,
+ void* data) {
+ return AChoreographer_registerRefreshRateCallback(choreographer, callback, data);
+}
+void AChoreographer_routeUnregisterRefreshRateCallback(AChoreographer* choreographer,
+ AChoreographer_refreshRateCallback callback,
+ void* data) {
+ return AChoreographer_unregisterRefreshRateCallback(choreographer, callback, data);
+}
+
+} // namespace android
+
+/* Glue for the NDK interface */
+
+static inline const Choreographer* AChoreographer_to_Choreographer(
+ const AChoreographer* choreographer) {
+ return reinterpret_cast<const Choreographer*>(choreographer);
+}
+
+static inline AChoreographer* Choreographer_to_AChoreographer(Choreographer* choreographer) {
+ return reinterpret_cast<AChoreographer*>(choreographer);
+}
+
+AChoreographer* AChoreographer_getInstance() {
+ return Choreographer_to_AChoreographer(Choreographer::getForThread());
+}
+
+void AChoreographer_postFrameCallback(AChoreographer* choreographer,
+ AChoreographer_frameCallback callback, void* data) {
+ AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed(
+ callback, nullptr, data, 0);
+}
+void AChoreographer_postFrameCallbackDelayed(AChoreographer* choreographer,
+ AChoreographer_frameCallback callback, void* data, long delayMillis) {
+ AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed(
+ callback, nullptr, data, ms2ns(delayMillis));
+}
+void AChoreographer_postFrameCallback64(AChoreographer* choreographer,
+ AChoreographer_frameCallback64 callback, void* data) {
+ AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed(
+ nullptr, callback, data, 0);
+}
+void AChoreographer_postFrameCallbackDelayed64(AChoreographer* choreographer,
+ AChoreographer_frameCallback64 callback, void* data, uint32_t delayMillis) {
+ AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed(
+ nullptr, callback, data, ms2ns(delayMillis));
+}
+void AChoreographer_registerRefreshRateCallback(AChoreographer* choreographer,
+ AChoreographer_refreshRateCallback callback,
+ void* data) {
+ AChoreographer_to_Choreographer(choreographer)->registerRefreshRateCallback(callback, data);
+}
+void AChoreographer_unregisterRefreshRateCallback(AChoreographer* choreographer,
+ AChoreographer_refreshRateCallback callback,
+ void* data) {
+ AChoreographer_to_Choreographer(choreographer)->unregisterRefreshRateCallback(callback, data);
+}
+
+AChoreographer* AChoreographer_create() {
+ Choreographer* choreographer = new Choreographer(nullptr);
+ status_t result = choreographer->initialize();
+ if (result != OK) {
+ ALOGW("Failed to initialize");
+ return nullptr;
+ }
+ return Choreographer_to_AChoreographer(choreographer);
+}
+
+void AChoreographer_destroy(AChoreographer* choreographer) {
+ if (choreographer == nullptr) {
+ return;
+ }
+
+ delete AChoreographer_to_Choreographer(choreographer);
+}
+
+int AChoreographer_getFd(const AChoreographer* choreographer) {
+ return AChoreographer_to_Choreographer(choreographer)->getFd();
+}
+
+void AChoreographer_handlePendingEvents(AChoreographer* choreographer, void* data) {
+ // Pass dummy fd and events args to handleEvent, since the underlying
+ // DisplayEventDispatcher doesn't need them outside of validating that a
+ // Looper instance didn't break, but these args circumvent those checks.
+ Choreographer* impl = AChoreographer_to_Choreographer(choreographer);
+ impl->handleEvent(-1, Looper::EVENT_INPUT, data);
+}
diff --git a/libs/nativedisplay/ADisplay.cpp b/libs/nativedisplay/ADisplay.cpp
new file mode 100644
index 0000000..277635c
--- /dev/null
+++ b/libs/nativedisplay/ADisplay.cpp
@@ -0,0 +1,307 @@
+/*
+ * Copyright 2019 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 <apex/display.h>
+#include <gui/SurfaceComposerClient.h>
+#include <ui/DisplayConfig.h>
+#include <ui/DisplayInfo.h>
+#include <ui/GraphicTypes.h>
+#include <ui/PixelFormat.h>
+
+#include <algorithm>
+#include <optional>
+#include <type_traits>
+#include <vector>
+
+namespace android::display::impl {
+
+/**
+ * Implementation of ADisplayConfig
+ */
+struct DisplayConfigImpl {
+ /**
+ * The width in pixels of the display configuration.
+ */
+ int32_t width{0};
+
+ /**
+ * The height in pixels of the display configuration.
+ */
+
+ int32_t height{0};
+
+ /**
+ * The display density.
+ */
+ float density{0};
+
+ /**
+ * The refresh rate of the display configuration, in frames per second.
+ */
+ float fps{0.0};
+
+ /**
+ * The vsync offset at which surfaceflinger runs, in nanoseconds.
+ */
+ int64_t sfOffset{0};
+
+ /**
+ * The vsync offset at which applications run, in nanoseconds.
+ */
+ int64_t appOffset{0};
+};
+
+// DisplayConfigImpl allocation is not managed through C++ memory apis, so
+// preventing calling the destructor here.
+static_assert(std::is_trivially_destructible<DisplayConfigImpl>::value);
+
+/**
+ * Implementation of ADisplay
+ */
+struct DisplayImpl {
+ /**
+ * A physical display ID, unique to this display.
+ */
+ PhysicalDisplayId id;
+
+ /**
+ * The type of the display, i.e. whether it is an internal or external
+ * display.
+ */
+ ADisplayType type;
+
+ /**
+ * The preferred WCG dataspace
+ */
+ ADataSpace wcgDataspace;
+
+ /**
+ * The preferred WCG pixel format
+ */
+ AHardwareBuffer_Format wcgPixelFormat;
+
+ /**
+ * Number of supported configs
+ */
+ size_t numConfigs;
+
+ /**
+ * Set of supported configs by this display.
+ */
+ DisplayConfigImpl* configs;
+};
+
+// DisplayImpl allocation is not managed through C++ memory apis, so
+// preventing calling the destructor here.
+static_assert(std::is_trivially_destructible<DisplayImpl>::value);
+
+} // namespace android::display::impl
+
+using namespace android;
+using namespace android::display::impl;
+
+#define CHECK_NOT_NULL(name) \
+ LOG_ALWAYS_FATAL_IF(name == nullptr, "nullptr passed as " #name " argument");
+
+namespace {
+
+sp<IBinder> getToken(ADisplay* display) {
+ DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display);
+ return SurfaceComposerClient::getPhysicalDisplayToken(impl->id);
+}
+
+} // namespace
+
+namespace android {
+
+int ADisplay_acquirePhysicalDisplays(ADisplay*** outDisplays) {
+ const std::vector<PhysicalDisplayId> ids = SurfaceComposerClient::getPhysicalDisplayIds();
+ const size_t size = ids.size();
+ if (size == 0) {
+ return NO_INIT;
+ }
+
+ std::vector<DisplayConfigImpl> configsPerDisplay[size];
+ int numConfigs = 0;
+ for (int i = 0; i < size; ++i) {
+ const sp<IBinder> token = SurfaceComposerClient::getPhysicalDisplayToken(ids[i]);
+
+ DisplayInfo info;
+ if (const status_t status = SurfaceComposerClient::getDisplayInfo(token, &info);
+ status != OK) {
+ return status;
+ }
+
+ Vector<DisplayConfig> configs;
+ if (const status_t status = SurfaceComposerClient::getDisplayConfigs(token, &configs);
+ status != OK) {
+ return status;
+ }
+ if (configs.empty()) {
+ return NO_INIT;
+ }
+
+ numConfigs += configs.size();
+ configsPerDisplay[i].reserve(configs.size());
+ for (int j = 0; j < configs.size(); ++j) {
+ const DisplayConfig& config = configs[j];
+ configsPerDisplay[i].emplace_back(
+ DisplayConfigImpl{config.resolution.getWidth(), config.resolution.getHeight(),
+ info.density, config.refreshRate, config.sfVsyncOffset,
+ config.appVsyncOffset});
+ }
+ }
+
+ const std::optional<PhysicalDisplayId> internalId =
+ SurfaceComposerClient::getInternalDisplayId();
+ ui::Dataspace defaultDataspace;
+ ui::PixelFormat defaultPixelFormat;
+ ui::Dataspace wcgDataspace;
+ ui::PixelFormat wcgPixelFormat;
+
+ const status_t status =
+ SurfaceComposerClient::getCompositionPreference(&defaultDataspace, &defaultPixelFormat,
+ &wcgDataspace, &wcgPixelFormat);
+ if (status != NO_ERROR) {
+ return status;
+ }
+
+ // Here we allocate all our required memory in one block. The layout is as
+ // follows:
+ // ------------------------------------------------------------
+ // | DisplayImpl pointers | DisplayImpls | DisplayConfigImpls |
+ // ------------------------------------------------------------
+ //
+ // The caller will be given a DisplayImpl** which points to the beginning of
+ // the block of DisplayImpl pointers.
+ // Each DisplayImpl* points to a DisplayImpl in the second block.
+ // Each DisplayImpl contains a DisplayConfigImpl*, which points to a
+ // contiguous block of DisplayConfigImpls specific to that display.
+ DisplayImpl** const impls = reinterpret_cast<DisplayImpl**>(
+ malloc((sizeof(DisplayImpl) + sizeof(DisplayImpl*)) * size +
+ sizeof(DisplayConfigImpl) * numConfigs));
+ DisplayImpl* const displayData = reinterpret_cast<DisplayImpl*>(impls + size);
+ DisplayConfigImpl* configData = reinterpret_cast<DisplayConfigImpl*>(displayData + size);
+
+ for (size_t i = 0; i < size; ++i) {
+ const PhysicalDisplayId id = ids[i];
+ const ADisplayType type = (internalId == id) ? ADisplayType::DISPLAY_TYPE_INTERNAL
+ : ADisplayType::DISPLAY_TYPE_EXTERNAL;
+ const std::vector<DisplayConfigImpl>& configs = configsPerDisplay[i];
+ memcpy(configData, configs.data(), sizeof(DisplayConfigImpl) * configs.size());
+
+ displayData[i] = DisplayImpl{id,
+ type,
+ static_cast<ADataSpace>(wcgDataspace),
+ static_cast<AHardwareBuffer_Format>(wcgPixelFormat),
+ configs.size(),
+ configData};
+ impls[i] = displayData + i;
+ // Advance the configData pointer so that future configs are written to
+ // the correct display.
+ configData += configs.size();
+ }
+
+ *outDisplays = reinterpret_cast<ADisplay**>(impls);
+ return size;
+}
+
+void ADisplay_release(ADisplay** displays) {
+ if (displays == nullptr) {
+ return;
+ }
+ free(displays);
+}
+
+float ADisplay_getMaxSupportedFps(ADisplay* display) {
+ CHECK_NOT_NULL(display);
+ DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display);
+ float maxFps = 0.0;
+ for (int i = 0; i < impl->numConfigs; ++i) {
+ maxFps = std::max(maxFps, impl->configs[i].fps);
+ }
+ return maxFps;
+}
+
+ADisplayType ADisplay_getDisplayType(ADisplay* display) {
+ CHECK_NOT_NULL(display);
+
+ return reinterpret_cast<DisplayImpl*>(display)->type;
+}
+
+void ADisplay_getPreferredWideColorFormat(ADisplay* display, ADataSpace* outDataspace,
+ AHardwareBuffer_Format* outPixelFormat) {
+ CHECK_NOT_NULL(display);
+ CHECK_NOT_NULL(outDataspace);
+ CHECK_NOT_NULL(outPixelFormat);
+
+ DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display);
+ *outDataspace = impl->wcgDataspace;
+ *outPixelFormat = impl->wcgPixelFormat;
+}
+
+int ADisplay_getCurrentConfig(ADisplay* display, ADisplayConfig** outConfig) {
+ CHECK_NOT_NULL(display);
+
+ sp<IBinder> token = getToken(display);
+ const int index = SurfaceComposerClient::getActiveConfig(token);
+ if (index < 0) {
+ return index;
+ }
+
+ DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display);
+
+ *outConfig = reinterpret_cast<ADisplayConfig*>(impl->configs + index);
+ return OK;
+}
+
+float ADisplayConfig_getDensity(ADisplayConfig* config) {
+ CHECK_NOT_NULL(config);
+
+ return reinterpret_cast<DisplayConfigImpl*>(config)->density;
+}
+
+int32_t ADisplayConfig_getWidth(ADisplayConfig* config) {
+ CHECK_NOT_NULL(config);
+
+ return reinterpret_cast<DisplayConfigImpl*>(config)->width;
+}
+
+int32_t ADisplayConfig_getHeight(ADisplayConfig* config) {
+ CHECK_NOT_NULL(config);
+
+ return reinterpret_cast<DisplayConfigImpl*>(config)->height;
+}
+
+float ADisplayConfig_getFps(ADisplayConfig* config) {
+ CHECK_NOT_NULL(config);
+
+ return reinterpret_cast<DisplayConfigImpl*>(config)->fps;
+}
+
+int64_t ADisplayConfig_getCompositorOffsetNanos(ADisplayConfig* config) {
+ CHECK_NOT_NULL(config);
+
+ return reinterpret_cast<DisplayConfigImpl*>(config)->sfOffset;
+}
+
+int64_t ADisplayConfig_getAppVsyncOffsetNanos(ADisplayConfig* config) {
+ CHECK_NOT_NULL(config);
+
+ return reinterpret_cast<DisplayConfigImpl*>(config)->appOffset;
+}
+
+} // namespace android
diff --git a/libs/nativedisplay/Android.bp b/libs/nativedisplay/Android.bp
new file mode 100644
index 0000000..f56b3a2
--- /dev/null
+++ b/libs/nativedisplay/Android.bp
@@ -0,0 +1,67 @@
+// Copyright 2019 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.
+
+cc_library_headers {
+ name: "libnativedisplay_headers",
+ export_include_dirs: ["include",],
+}
+
+cc_library_shared {
+ name: "libnativedisplay",
+ export_include_dirs: [
+ "include",
+ "include-private",
+ ],
+
+ clang: true,
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-enum-compare",
+ "-Wno-unused-function",
+ ],
+
+ version_script: "libnativedisplay.map.txt",
+
+ srcs: [
+ "AChoreographer.cpp",
+ "ADisplay.cpp",
+ "surfacetexture/surface_texture.cpp",
+ "surfacetexture/SurfaceTexture.cpp",
+ "surfacetexture/ImageConsumer.cpp",
+ "surfacetexture/EGLConsumer.cpp",
+ ],
+
+ shared_libs: [
+ "libgui",
+ "liblog",
+ "libnativewindow",
+ "libui",
+ "libutils",
+ "libcutils",
+ "libEGL",
+ "libGLESv2",
+ "libnativehelper",
+ ],
+
+ export_shared_lib_headers: [
+ "libnativehelper",
+ ],
+
+ header_libs: [
+ "libnativedisplay_headers",
+ ],
+
+}
diff --git a/libs/nativedisplay/MODULE_LICENSE_APACHE2 b/libs/nativedisplay/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libs/nativedisplay/MODULE_LICENSE_APACHE2
diff --git a/libs/nativedisplay/NOTICE b/libs/nativedisplay/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/libs/nativedisplay/NOTICE
@@ -0,0 +1,190 @@
+
+ Copyright (c) 2005-2008, 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.
+
+ 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.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
diff --git a/libs/nativedisplay/include-private/private/android/choreographer.h b/libs/nativedisplay/include-private/private/android/choreographer.h
new file mode 100644
index 0000000..2164930
--- /dev/null
+++ b/libs/nativedisplay/include-private/private/android/choreographer.h
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <apex/choreographer.h>
+#include <inttypes.h>
+#include <nativehelper/JNIHelp.h>
+
+namespace android {
+
+// Registers the global JVM for AChoreographer
+void AChoreographer_initJVM(JNIEnv* env);
+
+// Signals all AChoregorapher* instances that a new vsync period is available
+// for consumption by callbacks.
+void AChoreographer_signalRefreshRateCallbacks(int64_t vsyncPeriod);
+
+// Trampoline functions allowing libandroid.so to define the NDK symbols without including
+// the entirety of libnativedisplay as a whole static lib. As libnativedisplay
+// maintains global state, libnativedisplay can never be directly statically
+// linked so that global state won't be duplicated. This way libandroid.so can
+// reroute the NDK methods into the implementations defined by libnativedisplay
+AChoreographer* AChoreographer_routeGetInstance();
+void AChoreographer_routePostFrameCallback(AChoreographer* choreographer,
+ AChoreographer_frameCallback callback, void* data);
+void AChoreographer_routePostFrameCallbackDelayed(AChoreographer* choreographer,
+ AChoreographer_frameCallback callback, void* data,
+ long delayMillis);
+void AChoreographer_routePostFrameCallback64(AChoreographer* choreographer,
+ AChoreographer_frameCallback64 callback, void* data);
+void AChoreographer_routePostFrameCallbackDelayed64(AChoreographer* choreographer,
+ AChoreographer_frameCallback64 callback,
+ void* data, uint32_t delayMillis);
+void AChoreographer_routeRegisterRefreshRateCallback(AChoreographer* choreographer,
+ AChoreographer_refreshRateCallback callback,
+ void* data);
+void AChoreographer_routeUnregisterRefreshRateCallback(AChoreographer* choreographer,
+ AChoreographer_refreshRateCallback callback,
+ void* data);
+
+} // namespace android
diff --git a/libs/nativedisplay/include/apex/choreographer.h b/libs/nativedisplay/include/apex/choreographer.h
new file mode 100644
index 0000000..683abc4
--- /dev/null
+++ b/libs/nativedisplay/include/apex/choreographer.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2019 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/choreographer.h>
+#include <inttypes.h>
+
+__BEGIN_DECLS
+
+/**
+ * Creates an instance of AChoreographer.
+ *
+ * The key differences between this method and AChoreographer_getInstance are:
+ * 1. The returned AChoreographer instance is not a thread-local, and
+ * 2. This method does not require an existing ALooper attached to the thread.
+ */
+AChoreographer* AChoreographer_create();
+
+/**
+ * Destroys a choreographer instance created from AChoreographer_create.
+ */
+void AChoreographer_destroy(AChoreographer* choreographer);
+
+/**
+ * Returns the underlying file descriptor associated with this choreographer
+ * instance.
+ *
+ * The caller can listen to the file descriptor to respond to any AChoreographer
+ * events. One such way is registering the file descriptor to a Looper instance,
+ * although this is not a requirement.
+ */
+int AChoreographer_getFd(const AChoreographer* choreographer);
+
+/**
+ * Provides a callback to handle all pending events emitted by this
+ * choreographer instance. Specifically, this delegates to the callbacks
+ * previously registered to choreographer.
+ *
+ * If the associated file descriptor is attached to a Looper instance, then the
+ * callback attached to that Looper is expected to handle exceptional Looper
+ * events.
+ */
+void AChoreographer_handlePendingEvents(AChoreographer* choreographer, void* data);
+
+__END_DECLS
diff --git a/libs/nativedisplay/include/apex/display.h b/libs/nativedisplay/include/apex/display.h
new file mode 100644
index 0000000..a7eaf87
--- /dev/null
+++ b/libs/nativedisplay/include/apex/display.h
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2019 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/data_space.h>
+#include <android/hardware_buffer.h>
+#include <inttypes.h>
+
+// TODO: the intention of these apis is to be stable - hence they are defined in
+// an apex directory. But because they don't yet need to be stable, hold off on
+// making them stable until a Mainline module needs them.
+// __BEGIN_DECLS
+
+namespace android {
+
+/**
+ * Opaque handle for a native display
+ */
+typedef struct ADisplay ADisplay;
+
+/**
+ * Enum describing the possible types of a display
+ */
+enum ADisplayType {
+ /**
+ * A display that is the internal, or "primary" display for a device.
+ */
+ DISPLAY_TYPE_INTERNAL = 0,
+
+ /**
+ * A display that is externally connected for a device.
+ */
+ DISPLAY_TYPE_EXTERNAL = 1,
+};
+
+/**
+ * Opaque handle for display metadata
+ */
+typedef struct ADisplayConfig ADisplayConfig;
+
+/**
+ * Acquires a list of display handles. Memory is allocated for the list and is
+ * owned by the caller. The caller is responsible for freeing this memory by
+ * calling ADisplayList_release.
+ *
+ * Returns the size of the returned list on success.
+ * Returns -errno on error.
+ */
+int ADisplay_acquirePhysicalDisplays(ADisplay*** outDisplays);
+
+/**
+ * Releases a list of display handles created by
+ * ADisplayList_acquirePhysicalDisplays.
+ */
+void ADisplay_release(ADisplay** displays);
+
+/**
+ * Queries the maximum supported fps for the given display.
+ */
+float ADisplay_getMaxSupportedFps(ADisplay* display);
+
+/**
+ * Queries the display's type.
+ */
+ADisplayType ADisplay_getDisplayType(ADisplay* display);
+
+/**
+ * Queries the display's preferred WCG format
+ */
+void ADisplay_getPreferredWideColorFormat(ADisplay* display, ADataSpace* outDataspace,
+ AHardwareBuffer_Format* outPixelFormat);
+
+/**
+ * Gets the current display configuration for the given display.
+ *
+ * Memory is *not* allocated for the caller. As such, the returned output
+ * configuration's lifetime will not be longer than the ADisplay* passed to this
+ * function - if ADisplay_release is called destroying the ADisplay object then
+ * it is invalid to access the ADisplayConfig returned here.
+ *
+ * Note that the current display configuration can change. Listening to updates
+ * to the current display configuration should be done via Choreographer. If
+ * such an update is observed, then this method should be recalled to get the
+ * new current configuration.
+ *
+ * Returns OK on success, -errno on failure.
+ */
+int ADisplay_getCurrentConfig(ADisplay* display, ADisplayConfig** outConfig);
+
+/**
+ * Queries the density for a given display configuration.
+ */
+float ADisplayConfig_getDensity(ADisplayConfig* config);
+
+/**
+ * Queries the width in pixels for a given display configuration.
+ */
+int32_t ADisplayConfig_getWidth(ADisplayConfig* config);
+
+/**
+ * Queries the height in pixels for a given display configuration.
+ */
+int32_t ADisplayConfig_getHeight(ADisplayConfig* config);
+
+/**
+ * Queries the display refresh rate for a given display configuration.
+ */
+float ADisplayConfig_getFps(ADisplayConfig* config);
+
+/**
+ * Queries the vsync offset from which the system compositor is scheduled to
+ * run. If a vsync occurs at time T, and the compositor runs at time T + S, then
+ * this returns S in nanoseconds.
+ */
+int64_t ADisplayConfig_getCompositorOffsetNanos(ADisplayConfig* config);
+
+/**
+ * Queries the vsync offset from which applications are scheduled to run. If a
+ * vsync occurs at time T, and applications run at time T + S, then this returns
+ * S in nanoseconds.
+ */
+int64_t ADisplayConfig_getAppVsyncOffsetNanos(ADisplayConfig* config);
+
+} // namespace android
+// __END_DECLS
diff --git a/libs/nativedisplay/include/surfacetexture/EGLConsumer.h b/libs/nativedisplay/include/surfacetexture/EGLConsumer.h
new file mode 100644
index 0000000..444722b
--- /dev/null
+++ b/libs/nativedisplay/include/surfacetexture/EGLConsumer.h
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2018 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 <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <gui/BufferQueueDefs.h>
+#include <ui/FenceTime.h>
+#include <ui/GraphicBuffer.h>
+#include <utils/Mutex.h>
+
+namespace android {
+
+class SurfaceTexture;
+
+/*
+ * EGLConsumer implements the parts of SurfaceTexture that deal with
+ * textures attached to an GL context.
+ */
+class EGLConsumer {
+public:
+ EGLConsumer();
+
+ /**
+ * updateTexImage acquires the most recently queued buffer, and sets the
+ * image contents of the target texture to it.
+ *
+ * This call may only be made while the OpenGL ES context to which the
+ * target texture belongs is bound to the calling thread.
+ *
+ * This calls doGLFenceWait to ensure proper synchronization.
+ */
+ status_t updateTexImage(SurfaceTexture& st);
+
+ /*
+ * releaseTexImage releases the texture acquired in updateTexImage().
+ * This is intended to be used in single buffer mode.
+ *
+ * This call may only be made while the OpenGL ES context to which the
+ * target texture belongs is bound to the calling thread.
+ */
+ status_t releaseTexImage(SurfaceTexture& st);
+
+ /**
+ * detachFromContext detaches the EGLConsumer from the calling thread's
+ * current OpenGL ES context. This context must be the same as the context
+ * that was current for previous calls to updateTexImage.
+ *
+ * Detaching a EGLConsumer from an OpenGL ES context will result in the
+ * deletion of the OpenGL ES texture object into which the images were being
+ * streamed. After a EGLConsumer has been detached from the OpenGL ES
+ * context calls to updateTexImage will fail returning INVALID_OPERATION
+ * until the EGLConsumer is attached to a new OpenGL ES context using the
+ * attachToContext method.
+ */
+ status_t detachFromContext(SurfaceTexture& st);
+
+ /**
+ * attachToContext attaches a EGLConsumer that is currently in the
+ * 'detached' state to the current OpenGL ES context. A EGLConsumer is
+ * in the 'detached' state iff detachFromContext has successfully been
+ * called and no calls to attachToContext have succeeded since the last
+ * detachFromContext call. Calls to attachToContext made on a
+ * EGLConsumer that is not in the 'detached' state will result in an
+ * INVALID_OPERATION error.
+ *
+ * The tex argument specifies the OpenGL ES texture object name in the
+ * new context into which the image contents will be streamed. A successful
+ * call to attachToContext will result in this texture object being bound to
+ * the texture target and populated with the image contents that were
+ * current at the time of the last call to detachFromContext.
+ */
+ status_t attachToContext(uint32_t tex, SurfaceTexture& st);
+
+ /**
+ * onAcquireBufferLocked amends the ConsumerBase method to update the
+ * mEglSlots array in addition to the ConsumerBase behavior.
+ */
+ void onAcquireBufferLocked(BufferItem* item, SurfaceTexture& st);
+
+ /**
+ * onReleaseBufferLocked amends the ConsumerBase method to update the
+ * mEglSlots array in addition to the ConsumerBase.
+ */
+ void onReleaseBufferLocked(int slot);
+
+ /**
+ * onFreeBufferLocked frees up the given buffer slot. If the slot has been
+ * initialized this will release the reference to the GraphicBuffer in that
+ * slot and destroy the EGLImage in that slot. Otherwise it has no effect.
+ */
+ void onFreeBufferLocked(int slotIndex);
+
+ /**
+ * onAbandonLocked amends the ConsumerBase method to clear
+ * mCurrentTextureImage in addition to the ConsumerBase behavior.
+ */
+ void onAbandonLocked();
+
+protected:
+ struct PendingRelease {
+ PendingRelease()
+ : isPending(false),
+ currentTexture(-1),
+ graphicBuffer(),
+ display(nullptr),
+ fence(nullptr) {}
+
+ bool isPending;
+ int currentTexture;
+ sp<GraphicBuffer> graphicBuffer;
+ EGLDisplay display;
+ EGLSyncKHR fence;
+ };
+
+ /**
+ * This releases the buffer in the slot referenced by mCurrentTexture,
+ * then updates state to refer to the BufferItem, which must be a
+ * newly-acquired buffer. If pendingRelease is not null, the parameters
+ * which would have been passed to releaseBufferLocked upon the successful
+ * completion of the method will instead be returned to the caller, so that
+ * it may call releaseBufferLocked itself later.
+ */
+ status_t updateAndReleaseLocked(const BufferItem& item, PendingRelease* pendingRelease,
+ SurfaceTexture& st);
+
+ /**
+ * Binds mTexName and the current buffer to mTexTarget. Uses
+ * mCurrentTexture if it's set, mCurrentTextureImage if not. If the
+ * bind succeeds, this calls doGLFenceWait.
+ */
+ status_t bindTextureImageLocked(SurfaceTexture& st);
+
+ /**
+ * Gets the current EGLDisplay and EGLContext values, and compares them
+ * to mEglDisplay and mEglContext. If the fields have been previously
+ * set, the values must match; if not, the fields are set to the current
+ * values.
+ * The contextCheck argument is used to ensure that a GL context is
+ * properly set; when set to false, the check is not performed.
+ */
+ status_t checkAndUpdateEglStateLocked(SurfaceTexture& st, bool contextCheck = false);
+
+ /**
+ * EglImage is a utility class for tracking and creating EGLImageKHRs. There
+ * is primarily just one image per slot, but there is also special cases:
+ * - For releaseTexImage, we use a debug image (mReleasedTexImage)
+ * - After freeBuffer, we must still keep the current image/buffer
+ * Reference counting EGLImages lets us handle all these cases easily while
+ * also only creating new EGLImages from buffers when required.
+ */
+ class EglImage : public LightRefBase<EglImage> {
+ public:
+ EglImage(sp<GraphicBuffer> graphicBuffer);
+
+ /**
+ * createIfNeeded creates an EGLImage if required (we haven't created
+ * one yet, or the EGLDisplay or crop-rect has changed).
+ */
+ status_t createIfNeeded(EGLDisplay display, bool forceCreate = false);
+
+ /**
+ * This calls glEGLImageTargetTexture2DOES to bind the image to the
+ * texture in the specified texture target.
+ */
+ void bindToTextureTarget(uint32_t texTarget);
+
+ const sp<GraphicBuffer>& graphicBuffer() { return mGraphicBuffer; }
+ const native_handle* graphicBufferHandle() {
+ return mGraphicBuffer == nullptr ? nullptr : mGraphicBuffer->handle;
+ }
+
+ private:
+ // Only allow instantiation using ref counting.
+ friend class LightRefBase<EglImage>;
+ virtual ~EglImage();
+
+ // createImage creates a new EGLImage from a GraphicBuffer.
+ EGLImageKHR createImage(EGLDisplay dpy, const sp<GraphicBuffer>& graphicBuffer);
+
+ // Disallow copying
+ EglImage(const EglImage& rhs);
+ void operator=(const EglImage& rhs);
+
+ // mGraphicBuffer is the buffer that was used to create this image.
+ sp<GraphicBuffer> mGraphicBuffer;
+
+ // mEglImage is the EGLImage created from mGraphicBuffer.
+ EGLImageKHR mEglImage;
+
+ // mEGLDisplay is the EGLDisplay that was used to create mEglImage.
+ EGLDisplay mEglDisplay;
+
+ // mCropRect is the crop rectangle passed to EGL when mEglImage
+ // was created.
+ Rect mCropRect;
+ };
+
+ /**
+ * doGLFenceWaitLocked inserts a wait command into the OpenGL ES command
+ * stream to ensure that it is safe for future OpenGL ES commands to
+ * access the current texture buffer.
+ */
+ status_t doGLFenceWaitLocked(SurfaceTexture& st) const;
+
+ /**
+ * syncForReleaseLocked performs the synchronization needed to release the
+ * current slot from an OpenGL ES context. If needed it will set the
+ * current slot's fence to guard against a producer accessing the buffer
+ * before the outstanding accesses have completed.
+ */
+ status_t syncForReleaseLocked(EGLDisplay dpy, SurfaceTexture& st);
+
+ /**
+ * returns a graphic buffer used when the texture image has been released
+ */
+ static sp<GraphicBuffer> getDebugTexImageBuffer();
+
+ /**
+ * The default consumer usage flags that EGLConsumer always sets on its
+ * BufferQueue instance; these will be OR:d with any additional flags passed
+ * from the EGLConsumer user. In particular, EGLConsumer will always
+ * consume buffers as hardware textures.
+ */
+ static const uint64_t DEFAULT_USAGE_FLAGS = GraphicBuffer::USAGE_HW_TEXTURE;
+
+ /**
+ * mCurrentTextureImage is the EglImage/buffer of the current texture. It's
+ * possible that this buffer is not associated with any buffer slot, so we
+ * must track it separately in order to support the getCurrentBuffer method.
+ */
+ sp<EglImage> mCurrentTextureImage;
+
+ /**
+ * EGLSlot contains the information and object references that
+ * EGLConsumer maintains about a BufferQueue buffer slot.
+ */
+ struct EglSlot {
+ EglSlot() : mEglFence(EGL_NO_SYNC_KHR) {}
+
+ /**
+ * mEglImage is the EGLImage created from mGraphicBuffer.
+ */
+ sp<EglImage> mEglImage;
+
+ /**
+ * 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;
+ };
+
+ /**
+ * mEglDisplay is the EGLDisplay with which this EGLConsumer is currently
+ * associated. It is intialized to EGL_NO_DISPLAY and gets set to the
+ * current display when updateTexImage is called for the first time and when
+ * attachToContext is called.
+ */
+ EGLDisplay mEglDisplay;
+
+ /**
+ * mEglContext is the OpenGL ES context with which this EGLConsumer is
+ * currently associated. It is initialized to EGL_NO_CONTEXT and gets set
+ * to the current GL context when updateTexImage is called for the first
+ * time and when attachToContext is called.
+ */
+ EGLContext mEglContext;
+
+ /**
+ * mEGLSlots stores the buffers that have been allocated by the BufferQueue
+ * for each buffer slot. It is initialized to null pointers, and gets
+ * filled in with the result of BufferQueue::acquire when the
+ * client dequeues a buffer from a
+ * 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.
+ */
+ EglSlot mEglSlots[BufferQueueDefs::NUM_BUFFER_SLOTS];
+
+ /**
+ * protects static initialization
+ */
+ static Mutex sStaticInitLock;
+
+ /**
+ * mReleasedTexImageBuffer is a dummy buffer used when in single buffer
+ * mode and releaseTexImage() has been called
+ */
+ static sp<GraphicBuffer> sReleasedTexImageBuffer;
+ sp<EglImage> mReleasedTexImage;
+};
+
+} // namespace android
diff --git a/libs/nativedisplay/include/surfacetexture/ImageConsumer.h b/libs/nativedisplay/include/surfacetexture/ImageConsumer.h
new file mode 100644
index 0000000..35ae3d2
--- /dev/null
+++ b/libs/nativedisplay/include/surfacetexture/ImageConsumer.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2019 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 <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <cutils/compiler.h>
+#include <gui/BufferItem.h>
+#include <gui/BufferQueueDefs.h>
+#include <sys/cdefs.h>
+#include <system/graphics.h>
+
+namespace android {
+
+class SurfaceTexture;
+class DequeueBufferCallbacks;
+
+/*
+ * ImageConsumer implements the parts of SurfaceTexture that deal with
+ * images consumed by HWUI view system.
+ */
+class ImageConsumer {
+public:
+ typedef status_t (*SurfaceTexture_createReleaseFence)(bool useFenceSync, EGLSyncKHR* eglFence,
+ EGLDisplay* display, int* releaseFence,
+ void* fencePassThroughHandle);
+
+ typedef status_t (*SurfaceTexture_fenceWait)(int fence, void* fencePassThroughHandle);
+
+ sp<GraphicBuffer> dequeueBuffer(int* outSlotid, android_dataspace* outDataspace,
+ bool* outQueueEmpty, SurfaceTexture& cb,
+ SurfaceTexture_createReleaseFence createFence,
+ SurfaceTexture_fenceWait fenceWait,
+ void* fencePassThroughHandle);
+
+ /**
+ * onReleaseBufferLocked amends the ConsumerBase method to update the
+ * mImageSlots array in addition to the ConsumerBase.
+ */
+ void onReleaseBufferLocked(int slot);
+
+private:
+ /**
+ * ImageSlot contains the information and object references that
+ * ImageConsumer maintains about a BufferQueue buffer slot.
+ */
+ class ImageSlot {
+ public:
+ ImageSlot() : mEglFence(EGL_NO_SYNC_KHR) {}
+
+ inline EGLSyncKHR& eglFence() { return mEglFence; }
+
+ private:
+ /**
+ * mEglFence is the EGL sync object that must signal before the buffer
+ * associated with this buffer slot may be dequeued.
+ */
+ EGLSyncKHR mEglFence;
+ };
+
+ /**
+ * ImageConsumer stores the SkImages that have been allocated by the BufferQueue
+ * for each buffer slot. It is initialized to null pointers, and gets
+ * filled in with the result of BufferQueue::acquire when the
+ * client dequeues a buffer from a
+ * 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.
+ */
+ ImageSlot mImageSlots[BufferQueueDefs::NUM_BUFFER_SLOTS];
+};
+
+} /* namespace android */
diff --git a/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h b/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h
new file mode 100644
index 0000000..6eaa84e
--- /dev/null
+++ b/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h
@@ -0,0 +1,472 @@
+/*
+ * Copyright 2019 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/hardware_buffer.h>
+#include <gui/BufferQueueDefs.h>
+#include <gui/ConsumerBase.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <sys/cdefs.h>
+#include <system/graphics.h>
+#include <ui/FenceTime.h>
+#include <ui/GraphicBuffer.h>
+#include <utils/Mutex.h>
+#include <utils/String8.h>
+
+#include "EGLConsumer.h"
+#include "ImageConsumer.h"
+
+namespace android {
+
+/*
+ * SurfaceTexture consumes buffers of graphics data from a BufferQueue,
+ * and makes them available to HWUI render thread as a SkImage and to
+ * an application GL render thread as an OpenGL texture.
+ *
+ * When attached to an application GL render thread, a typical usage
+ * pattern is to set up the SurfaceTexture with the
+ * desired options, and call updateTexImage() when a new frame is desired.
+ * If a new frame is available, the texture will be updated. If not,
+ * the previous contents are retained.
+ *
+ * When attached to a HWUI render thread, the TextureView implementation
+ * calls dequeueBuffer, which either pulls a new buffer or returns the
+ * last cached buffer if BufferQueue is empty.
+ * When attached to HWUI render thread, SurfaceTexture is compatible to
+ * both Vulkan and GL drawing pipelines.
+ */
+class ANDROID_API SurfaceTexture : public ConsumerBase {
+public:
+ /**
+ * Callback function needed by dequeueBuffer. It creates a fence,
+ * that is signalled, when the previous buffer is no longer in use by HWUI
+ * and can be used by written by the producer.
+ */
+ typedef status_t (*SurfaceTexture_createReleaseFence)(bool useFenceSync, EGLSyncKHR* eglFence,
+ EGLDisplay* display, int* releaseFence,
+ void* passThroughHandle);
+
+ /**
+ * Callback function needed by dequeueBuffer. It waits for the new buffer
+ * fence to signal, before issuing any draw commands.
+ */
+ typedef status_t (*SurfaceTexture_fenceWait)(int fence, void* passThroughHandle);
+
+ enum { TEXTURE_EXTERNAL = 0x8D65 }; // GL_TEXTURE_EXTERNAL_OES
+ typedef ConsumerBase::FrameAvailableListener FrameAvailableListener;
+
+ /**
+ * SurfaceTexture constructs a new SurfaceTexture object. If the constructor
+ * with the tex parameter is used, tex indicates the name of the OpenGL ES
+ * texture to which images are to be streamed. texTarget specifies the
+ * OpenGL ES texture target to which the texture will be bound in
+ * updateTexImage. useFenceSync specifies whether fences should be used to
+ * synchronize access to buffers if that behavior is enabled at
+ * compile-time.
+ *
+ * A SurfaceTexture may be detached from one OpenGL ES context and then
+ * attached to a different context using the detachFromContext and
+ * attachToContext methods, respectively. The intention of these methods is
+ * purely to allow a SurfaceTexture to be transferred from one consumer
+ * context to another. If such a transfer is not needed there is no
+ * requirement that either of these methods be called.
+ *
+ * If the constructor with the tex parameter is used, the SurfaceTexture is
+ * created in a state where it is considered attached to an OpenGL ES
+ * context for the purposes of the attachToContext and detachFromContext
+ * methods. However, despite being considered "attached" to a context, the
+ * specific OpenGL ES context doesn't get latched until the first call to
+ * updateTexImage. After that point, all calls to updateTexImage must be
+ * made with the same OpenGL ES context current.
+ *
+ * If the constructor without the tex parameter is used, the SurfaceTexture
+ * is created in a detached state, and attachToContext must be called before
+ * calls to updateTexImage.
+ */
+ SurfaceTexture(const sp<IGraphicBufferConsumer>& bq, uint32_t tex, uint32_t textureTarget,
+ bool useFenceSync, bool isControlledByApp);
+
+ SurfaceTexture(const sp<IGraphicBufferConsumer>& bq, uint32_t textureTarget, bool useFenceSync,
+ bool isControlledByApp);
+
+ /**
+ * updateTexImage acquires the most recently queued buffer, and sets the
+ * image contents of the target texture to it.
+ *
+ * This call may only be made while the OpenGL ES context to which the
+ * target texture belongs is bound to the calling thread.
+ *
+ * This calls doGLFenceWait to ensure proper synchronization.
+ */
+ status_t updateTexImage();
+
+ /**
+ * releaseTexImage releases the texture acquired in updateTexImage().
+ * This is intended to be used in single buffer mode.
+ *
+ * This call may only be made while the OpenGL ES context to which the
+ * target texture belongs is bound to the calling thread.
+ */
+ status_t releaseTexImage();
+
+ /**
+ * getTransformMatrix retrieves the 4x4 texture coordinate transform matrix
+ * associated with the texture image set by the most recent call to
+ * updateTexImage.
+ *
+ * This transform matrix maps 2D homogeneous texture coordinates of the form
+ * (s, t, 0, 1) with s and t in the inclusive range [0, 1] to the texture
+ * coordinate that should be used to sample that location from the texture.
+ * Sampling the texture outside of the range of this transform is undefined.
+ *
+ * This transform is necessary to compensate for transforms that the stream
+ * content producer may implicitly apply to the content. By forcing users of
+ * a SurfaceTexture to apply this transform we avoid performing an extra
+ * copy of the data that would be needed to hide the transform from the
+ * user.
+ *
+ * The matrix is stored in column-major order so that it may be passed
+ * directly to OpenGL ES via the glLoadMatrixf or glUniformMatrix4fv
+ * functions.
+ */
+ void getTransformMatrix(float mtx[16]);
+
+ /**
+ * Computes the transform matrix documented by getTransformMatrix
+ * from the BufferItem sub parts.
+ */
+ static void computeTransformMatrix(float outTransform[16], const sp<GraphicBuffer>& buf,
+ const Rect& cropRect, uint32_t transform, bool filtering);
+
+ /**
+ * Scale the crop down horizontally or vertically such that it has the
+ * same aspect ratio as the buffer does.
+ */
+ static Rect scaleDownCrop(const Rect& crop, uint32_t bufferWidth, uint32_t bufferHeight);
+
+ /**
+ * getTimestamp retrieves the timestamp associated with the texture image
+ * set by the most recent call to updateTexImage.
+ *
+ * The timestamp is in nanoseconds, and is monotonically increasing. Its
+ * other semantics (zero point, etc) are source-dependent and should be
+ * documented by the source.
+ */
+ int64_t getTimestamp();
+
+ /**
+ * getDataSpace retrieves the DataSpace associated with the texture image
+ * set by the most recent call to updateTexImage.
+ */
+ android_dataspace getCurrentDataSpace();
+
+ /**
+ * getFrameNumber retrieves the frame number associated with the texture
+ * image set by the most recent call to updateTexImage.
+ *
+ * The frame number is an incrementing counter set to 0 at the creation of
+ * the BufferQueue associated with this consumer.
+ */
+ uint64_t getFrameNumber();
+
+ /**
+ * setDefaultBufferSize is used to set the size of buffers returned by
+ * requestBuffers when a with and height of zero is requested.
+ * A call to setDefaultBufferSize() may trigger requestBuffers() to
+ * be called from the client.
+ * The width and height parameters must be no greater than the minimum of
+ * GL_MAX_VIEWPORT_DIMS and GL_MAX_TEXTURE_SIZE (see: glGetIntegerv).
+ * An error due to invalid dimensions might not be reported until
+ * updateTexImage() is called.
+ */
+ status_t setDefaultBufferSize(uint32_t width, uint32_t height);
+
+ /**
+ * setFilteringEnabled sets whether the transform matrix should be computed
+ * for use with bilinear filtering.
+ */
+ void setFilteringEnabled(bool enabled);
+
+ /**
+ * getCurrentTextureTarget returns the texture target of the current
+ * texture as returned by updateTexImage().
+ */
+ uint32_t getCurrentTextureTarget() const;
+
+ /**
+ * getCurrentCrop returns the cropping rectangle of the current buffer.
+ */
+ Rect getCurrentCrop() const;
+
+ /**
+ * getCurrentTransform returns the transform of the current buffer.
+ */
+ uint32_t getCurrentTransform() const;
+
+ /**
+ * getCurrentScalingMode returns the scaling mode of the current buffer.
+ */
+ uint32_t getCurrentScalingMode() const;
+
+ /**
+ * getCurrentFence returns the fence indicating when the current buffer is
+ * ready to be read from.
+ */
+ sp<Fence> getCurrentFence() const;
+
+ /**
+ * getCurrentFence returns the FenceTime indicating when the current
+ * buffer is ready to be read from.
+ */
+ std::shared_ptr<FenceTime> getCurrentFenceTime() const;
+
+ /**
+ * setConsumerUsageBits overrides the ConsumerBase method to OR
+ * DEFAULT_USAGE_FLAGS to usage.
+ */
+ status_t setConsumerUsageBits(uint64_t usage);
+
+ /**
+ * detachFromContext detaches the SurfaceTexture from the calling thread's
+ * current OpenGL ES context. This context must be the same as the context
+ * that was current for previous calls to updateTexImage.
+ *
+ * Detaching a SurfaceTexture from an OpenGL ES context will result in the
+ * deletion of the OpenGL ES texture object into which the images were being
+ * streamed. After a SurfaceTexture has been detached from the OpenGL ES
+ * context calls to updateTexImage will fail returning INVALID_OPERATION
+ * until the SurfaceTexture is attached to a new OpenGL ES context using the
+ * attachToContext method.
+ */
+ status_t detachFromContext();
+
+ /**
+ * attachToContext attaches a SurfaceTexture that is currently in the
+ * 'detached' state to the current OpenGL ES context. A SurfaceTexture is
+ * in the 'detached' state iff detachFromContext has successfully been
+ * called and no calls to attachToContext have succeeded since the last
+ * detachFromContext call. Calls to attachToContext made on a
+ * SurfaceTexture that is not in the 'detached' state will result in an
+ * INVALID_OPERATION error.
+ *
+ * The tex argument specifies the OpenGL ES texture object name in the
+ * new context into which the image contents will be streamed. A successful
+ * call to attachToContext will result in this texture object being bound to
+ * the texture target and populated with the image contents that were
+ * current at the time of the last call to detachFromContext.
+ */
+ status_t attachToContext(uint32_t tex);
+
+ sp<GraphicBuffer> dequeueBuffer(int* outSlotid, android_dataspace* outDataspace,
+ float* outTransformMatrix, bool* outQueueEmpty,
+ SurfaceTexture_createReleaseFence createFence,
+ SurfaceTexture_fenceWait fenceWait,
+ void* fencePassThroughHandle);
+
+ /**
+ * takeConsumerOwnership attaches a SurfaceTexture that is currently in the
+ * 'detached' state to a consumer context (usually HWUI RenderThread).
+ */
+ void takeConsumerOwnership();
+
+ /**
+ * releaseConsumerOwnership detaches a SurfaceTexture from a consumer
+ * context (usually HWUI RenderThread).
+ */
+ void releaseConsumerOwnership();
+
+protected:
+ /**
+ * abandonLocked overrides the ConsumerBase method to clear
+ * mCurrentTextureImage in addition to the ConsumerBase behavior.
+ */
+ virtual void abandonLocked();
+
+ /**
+ * dumpLocked overrides the ConsumerBase method to dump SurfaceTexture-
+ * specific info in addition to the ConsumerBase behavior.
+ */
+ virtual void dumpLocked(String8& result, const char* prefix) const override;
+
+ /**
+ * acquireBufferLocked overrides the ConsumerBase method to update the
+ * mEglSlots array in addition to the ConsumerBase behavior.
+ */
+ virtual status_t acquireBufferLocked(BufferItem* item, nsecs_t presentWhen,
+ uint64_t maxFrameNumber = 0) override;
+
+ /**
+ * releaseBufferLocked overrides the ConsumerBase method to update the
+ * mEglSlots array in addition to the ConsumerBase.
+ */
+ virtual status_t releaseBufferLocked(int slot, const sp<GraphicBuffer> graphicBuffer,
+ EGLDisplay display, EGLSyncKHR eglFence) override;
+
+ /**
+ * freeBufferLocked frees up the given buffer slot. If the slot has been
+ * initialized this will release the reference to the GraphicBuffer in that
+ * slot and destroy the EGLImage in that slot. Otherwise it has no effect.
+ *
+ * This method must be called with mMutex locked.
+ */
+ virtual void freeBufferLocked(int slotIndex);
+
+ /**
+ * computeCurrentTransformMatrixLocked computes the transform matrix for the
+ * current texture. It uses mCurrentTransform and the current GraphicBuffer
+ * to compute this matrix and stores it in mCurrentTransformMatrix.
+ * mCurrentTextureImage must not be NULL.
+ */
+ void computeCurrentTransformMatrixLocked();
+
+ /**
+ * The default consumer usage flags that SurfaceTexture always sets on its
+ * BufferQueue instance; these will be OR:d with any additional flags passed
+ * from the SurfaceTexture user. In particular, SurfaceTexture will always
+ * consume buffers as hardware textures.
+ */
+ static const uint64_t DEFAULT_USAGE_FLAGS = GraphicBuffer::USAGE_HW_TEXTURE;
+
+ /**
+ * mCurrentCrop is the crop rectangle that applies to the current texture.
+ * It gets set each time updateTexImage is called.
+ */
+ Rect mCurrentCrop;
+
+ /**
+ * mCurrentTransform is the transform identifier for the current texture. It
+ * gets set each time updateTexImage is called.
+ */
+ uint32_t mCurrentTransform;
+
+ /**
+ * mCurrentScalingMode is the scaling mode for the current texture. It gets
+ * set each time updateTexImage is called.
+ */
+ uint32_t mCurrentScalingMode;
+
+ /**
+ * mCurrentFence is the fence received from BufferQueue in updateTexImage.
+ */
+ sp<Fence> mCurrentFence;
+
+ /**
+ * The FenceTime wrapper around mCurrentFence.
+ */
+ std::shared_ptr<FenceTime> mCurrentFenceTime{FenceTime::NO_FENCE};
+
+ /**
+ * mCurrentTransformMatrix is the transform matrix for the current texture.
+ * It gets computed by computeTransformMatrix each time updateTexImage is
+ * called.
+ */
+ float mCurrentTransformMatrix[16];
+
+ /**
+ * mCurrentTimestamp is the timestamp for the current texture. It
+ * gets set each time updateTexImage is called.
+ */
+ int64_t mCurrentTimestamp;
+
+ /**
+ * mCurrentDataSpace is the dataspace for the current texture. It
+ * gets set each time updateTexImage is called.
+ */
+ android_dataspace mCurrentDataSpace;
+
+ /**
+ * mCurrentFrameNumber is the frame counter for the current texture.
+ * It gets set each time updateTexImage is called.
+ */
+ uint64_t mCurrentFrameNumber;
+
+ uint32_t mDefaultWidth, mDefaultHeight;
+
+ /**
+ * mFilteringEnabled indicates whether the transform matrix is computed for
+ * use with bilinear filtering. It defaults to true and is changed by
+ * setFilteringEnabled().
+ */
+ bool mFilteringEnabled;
+
+ /**
+ * mTexName is the name of the OpenGL texture to which streamed images will
+ * be bound when updateTexImage is called. It is set at construction time
+ * and can be changed with a call to attachToContext.
+ */
+ uint32_t mTexName;
+
+ /**
+ * mUseFenceSync indicates whether creation of the EGL_KHR_fence_sync
+ * extension should be used to prevent buffers from being dequeued before
+ * it's safe for them to be written. It gets set at construction time and
+ * never changes.
+ */
+ const bool mUseFenceSync;
+
+ /**
+ * mTexTarget is the GL texture target with which the GL texture object is
+ * associated. It is set in the constructor and never changed. It is
+ * almost always GL_TEXTURE_EXTERNAL_OES except for one use case in Android
+ * Browser. In that case it is set to GL_TEXTURE_2D to allow
+ * glCopyTexSubImage to read from the texture. This is a hack to work
+ * around a GL driver limitation on the number of FBO attachments, which the
+ * browser's tile cache exceeds.
+ */
+ const uint32_t mTexTarget;
+
+ /**
+ * 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,
+ * however, that a value of INVALID_BUFFER_SLOT does not necessarily mean
+ * that no buffer is bound to the texture. A call to setBufferCount will
+ * reset mCurrentTexture to INVALID_BUFFER_SLOT.
+ */
+ int mCurrentTexture;
+
+ enum class OpMode { detached, attachedToConsumer, attachedToGL };
+ /**
+ * mOpMode indicates whether the SurfaceTexture is currently attached to
+ * an OpenGL ES context or the consumer context. For legacy reasons, this
+ * is initialized to, "attachedToGL" indicating that the SurfaceTexture is
+ * considered to be attached to whatever GL context is current at the time
+ * of the first updateTexImage call.
+ * It is set to "detached" by detachFromContext, and then set to
+ * "attachedToGL" again by attachToContext.
+ * takeConsumerOwnership/releaseConsumerOwnership are used to attach/detach
+ * from a consumer context - usually HWUI RenderThread.
+ */
+ OpMode mOpMode;
+
+ /**
+ * mEGLConsumer has SurfaceTexture logic used when attached to GL context.
+ */
+ EGLConsumer mEGLConsumer;
+
+ /**
+ * mImageConsumer has SurfaceTexture logic used when attached to a consumer
+ * context (usually HWUI RenderThread).
+ */
+ ImageConsumer mImageConsumer;
+
+ friend class ImageConsumer;
+ friend class EGLConsumer;
+};
+
+// ----------------------------------------------------------------------------
+} // namespace android
diff --git a/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h b/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h
new file mode 100644
index 0000000..f371667
--- /dev/null
+++ b/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2019 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.
+ */
+
+#ifndef _ANDROID_GRAPHICS_SURFACE_TEXTURE_PLATFORM_H
+#define _ANDROID_GRAPHICS_SURFACE_TEXTURE_PLATFORM_H
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <nativehelper/JNIHelp.h>
+#include <system/graphics.h>
+
+// This file provides a facade API on top of SurfaceTexture, which avoids using
+// C++ types. This is still a C++ unstable API though. Ideally features here
+// will be exposed via public NDK API and this file will be deleted.
+
+struct ASurfaceTexture;
+
+namespace android {
+
+// Trampoline functions allowing libandroid.so to define the NDK symbols without including
+// the entirety of libnativedisplay as a whole static lib. As libnativedisplay
+// maintains global state, libnativedisplay can never be directly statically
+// linked so that global state won't be duplicated. This way libandroid.so can
+// reroute the NDK methods into the implementations defined by libnativedisplay
+ANativeWindow* ASurfaceTexture_routeAcquireANativeWindow(ASurfaceTexture* st);
+int ASurfaceTexture_routeAttachToGLContext(ASurfaceTexture* st, uint32_t texName);
+int ASurfaceTexture_routeDetachFromGLContext(ASurfaceTexture* st);
+void ASurfaceTexture_routeRelease(ASurfaceTexture* st);
+int ASurfaceTexture_routeUpdateTexImage(ASurfaceTexture* st);
+void ASurfaceTexture_routeGetTransformMatrix(ASurfaceTexture* st, float mtx[16]);
+int64_t ASurfaceTexture_routeGetTimestamp(ASurfaceTexture* st);
+ASurfaceTexture* ASurfaceTexture_routeFromSurfaceTexture(JNIEnv* env, jobject surfacetexture);
+
+/**
+ * ASurfaceTexture_getCurrentTextureTarget returns the texture target of the
+ * current texture.
+ */
+unsigned int ASurfaceTexture_getCurrentTextureTarget(ASurfaceTexture* st);
+
+/**
+ * ASurfaceTexture_takeConsumerOwnership attaches an ASurfaceTexture that is
+ * currently in the 'detached' state to a consumer context.
+ */
+void ASurfaceTexture_takeConsumerOwnership(ASurfaceTexture* st);
+
+/**
+ * ASurfaceTexture_releaseConsumerOwnership detaches a SurfaceTexture from
+ * a consumer context.
+ */
+void ASurfaceTexture_releaseConsumerOwnership(ASurfaceTexture* st);
+
+/**
+ * Callback function needed by ASurfaceTexture_dequeueBuffer. It creates a
+ * fence that is signalled when the previous buffer is no longer in use by the
+ * consumer (usually HWUI RenderThread) and can be written to by the producer.
+ */
+typedef int (*ASurfaceTexture_createReleaseFence)(bool useFenceSync, EGLSyncKHR* eglFence,
+ EGLDisplay* display, int* releaseFence,
+ void* fencePassThroughHandle);
+
+/**
+ * Callback function needed by ASurfaceTexture_dequeueBuffer. It waits for the
+ * new buffer fence to signal before issuing any draw commands.
+ */
+typedef int (*ASurfaceTexture_fenceWait)(int fence, void* fencePassThroughHandle);
+
+/**
+ * ASurfaceTexture_dequeueBuffer returns the next available AHardwareBuffer.
+ * The caller gets ownership of the buffer and need to release it with
+ * AHardwareBuffer_release.
+ */
+AHardwareBuffer* ASurfaceTexture_dequeueBuffer(ASurfaceTexture* st, int* outSlotid,
+ android_dataspace* outDataspace,
+ float* outTransformMatrix, bool* outNewContent,
+ ASurfaceTexture_createReleaseFence createFence,
+ ASurfaceTexture_fenceWait fenceWait,
+ void* fencePassThroughHandle);
+
+} // namespace android
+
+#endif // _ANDROID_GRAPHICS_SURFACE_TEXTURE_PLATFORM_H
diff --git a/libs/nativedisplay/libnativedisplay.map.txt b/libs/nativedisplay/libnativedisplay.map.txt
new file mode 100644
index 0000000..fc59431
--- /dev/null
+++ b/libs/nativedisplay/libnativedisplay.map.txt
@@ -0,0 +1,64 @@
+LIBNATIVEDISPLAY {
+ global:
+ AChoreographer_getInstance; # apex # introduced=30
+ AChoreographer_postFrameCallback; # apex # introduced=30
+ AChoreographer_postFrameCallbackDelayed; # apex # introduced=30
+ AChoreographer_postFrameCallback64; # apex # introduced=30
+ AChoreographer_postFrameCallbackDelayed64; # apex # introduced=30
+ AChoreographer_registerRefreshRateCallback; # apex # introduced=30
+ AChoreographer_unregisterRefreshRateCallback; # apex # introduced=30
+ AChoreographer_create; # apex # introduced=30
+ AChoreographer_destroy; # apex # introduced=30
+ AChoreographer_getFd; # apex # introduced=30
+ AChoreographer_handlePendingEvents; # apex # introduced=30
+ ASurfaceTexture_fromSurfaceTexture; # apex # introduced=30
+ ASurfaceTexture_release; # apex # introduced=30
+ local:
+ *;
+};
+
+LIBNATIVEDISPLAY_PLATFORM {
+ global:
+ extern "C++" {
+ android::AChoreographer_initJVM*;
+ android::AChoreographer_routeGetInstance*;
+ android::AChoreographer_routePostFrameCallback*;
+ android::AChoreographer_routePostFrameCallbackDelayed*;
+ android::AChoreographer_routePostFrameCallback64*;
+ android::AChoreographer_routePostFrameCallbackDelayed64*;
+ android::AChoreographer_routeRegisterRefreshRateCallback*;
+ android::AChoreographer_routeUnregisterRefreshRateCallback*;
+ android::AChoreographer_signalRefreshRateCallbacks*;
+ android::ADisplay_acquirePhysicalDisplays*;
+ android::ADisplay_release*;
+ android::ADisplay_getMaxSupportedFps*;
+ android::ADisplay_getDisplayType*;
+ android::ADisplay_getPreferredWideColorFormat*;
+ android::ADisplay_getCurrentConfig*;
+ android::ADisplayConfig_getDensity*;
+ android::ADisplayConfig_getWidth*;
+ android::ADisplayConfig_getHeight*;
+ android::ADisplayConfig_getFps*;
+ android::ADisplayConfig_getCompositorOffsetNanos*;
+ android::ADisplayConfig_getAppVsyncOffsetNanos*;
+ android::ASurfaceTexture_getCurrentTextureTarget*;
+ android::ASurfaceTexture_takeConsumerOwnership*;
+ android::ASurfaceTexture_releaseConsumerOwnership*;
+ android::ASurfaceTexture_dequeueBuffer*;
+ android::ASurfaceTexture_routeAcquireANativeWindow*;
+ android::ASurfaceTexture_routeAttachToGLContext*;
+ android::ASurfaceTexture_routeDetachFromGLContext*;
+ android::ASurfaceTexture_routeGetTimestamp*;
+ android::ASurfaceTexture_routeGetTransformMatrix*;
+ android::ASurfaceTexture_routeUpdateTexImage*;
+ android::ASurfaceTexture_routeFromSurfaceTexture*;
+ android::ASurfaceTexture_routeRelease*;
+ android::SurfaceTexture*;
+ };
+ ASurfaceTexture_acquireANativeWindow;
+ ASurfaceTexture_attachToGLContext;
+ ASurfaceTexture_detachFromGLContext;
+ ASurfaceTexture_getTimestamp;
+ ASurfaceTexture_getTransformMatrix;
+ ASurfaceTexture_updateTexImage;
+} LIBNATIVEDISPLAY;
diff --git a/libs/nativedisplay/surfacetexture/EGLConsumer.cpp b/libs/nativedisplay/surfacetexture/EGLConsumer.cpp
new file mode 100644
index 0000000..2f31888
--- /dev/null
+++ b/libs/nativedisplay/surfacetexture/EGLConsumer.cpp
@@ -0,0 +1,674 @@
+/*
+ * Copyright (C) 2018 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 GL_GLEXT_PROTOTYPES
+#define EGL_EGLEXT_PROTOTYPES
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <cutils/compiler.h>
+#include <gui/BufferItem.h>
+#include <gui/BufferQueue.h>
+#include <surfacetexture/EGLConsumer.h>
+#include <surfacetexture/SurfaceTexture.h>
+#include <inttypes.h>
+#include <private/gui/SyncFeatures.h>
+#include <utils/Log.h>
+#include <utils/String8.h>
+#include <utils/Trace.h>
+
+#define PROT_CONTENT_EXT_STR "EGL_EXT_protected_content"
+#define EGL_PROTECTED_CONTENT_EXT 0x32C0
+
+namespace android {
+
+// Macros for including the SurfaceTexture name in log messages
+#define EGC_LOGV(x, ...) ALOGV("[%s] " x, st.mName.string(), ##__VA_ARGS__)
+#define EGC_LOGD(x, ...) ALOGD("[%s] " x, st.mName.string(), ##__VA_ARGS__)
+#define EGC_LOGW(x, ...) ALOGW("[%s] " x, st.mName.string(), ##__VA_ARGS__)
+#define EGC_LOGE(x, ...) ALOGE("[%s] " x, st.mName.string(), ##__VA_ARGS__)
+
+static const struct {
+ uint32_t width, height;
+ char const* bits;
+} kDebugData = {15, 12,
+ "_______________"
+ "_______________"
+ "_____XX_XX_____"
+ "__X_X_____X_X__"
+ "__X_XXXXXXX_X__"
+ "__XXXXXXXXXXX__"
+ "___XX_XXX_XX___"
+ "____XXXXXXX____"
+ "_____X___X_____"
+ "____X_____X____"
+ "_______________"
+ "_______________"};
+
+Mutex EGLConsumer::sStaticInitLock;
+sp<GraphicBuffer> EGLConsumer::sReleasedTexImageBuffer;
+
+static bool hasEglProtectedContentImpl() {
+ EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ const char* exts = eglQueryString(dpy, EGL_EXTENSIONS);
+ size_t cropExtLen = strlen(PROT_CONTENT_EXT_STR);
+ size_t extsLen = strlen(exts);
+ bool equal = !strcmp(PROT_CONTENT_EXT_STR, exts);
+ bool atStart = !strncmp(PROT_CONTENT_EXT_STR " ", exts, cropExtLen + 1);
+ bool atEnd = (cropExtLen + 1) < extsLen &&
+ !strcmp(" " PROT_CONTENT_EXT_STR, exts + extsLen - (cropExtLen + 1));
+ bool inMiddle = strstr(exts, " " PROT_CONTENT_EXT_STR " ");
+ return equal || atStart || atEnd || inMiddle;
+}
+
+static bool hasEglProtectedContent() {
+ // Only compute whether the extension is present once the first time this
+ // function is called.
+ static bool hasIt = hasEglProtectedContentImpl();
+ return hasIt;
+}
+
+EGLConsumer::EGLConsumer() : mEglDisplay(EGL_NO_DISPLAY), mEglContext(EGL_NO_CONTEXT) {}
+
+status_t EGLConsumer::updateTexImage(SurfaceTexture& st) {
+ // Make sure the EGL state is the same as in previous calls.
+ status_t err = checkAndUpdateEglStateLocked(st);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ BufferItem item;
+
+ // Acquire the next buffer.
+ // In asynchronous mode the list is guaranteed to be one buffer
+ // deep, while in synchronous mode we use the oldest buffer.
+ err = st.acquireBufferLocked(&item, 0);
+ if (err != NO_ERROR) {
+ if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
+ // We always bind the texture even if we don't update its contents.
+ EGC_LOGV("updateTexImage: no buffers were available");
+ glBindTexture(st.mTexTarget, st.mTexName);
+ err = NO_ERROR;
+ } else {
+ EGC_LOGE("updateTexImage: acquire failed: %s (%d)", strerror(-err), err);
+ }
+ return err;
+ }
+
+ // Release the previous buffer.
+ err = updateAndReleaseLocked(item, nullptr, st);
+ if (err != NO_ERROR) {
+ // We always bind the texture.
+ glBindTexture(st.mTexTarget, st.mTexName);
+ return err;
+ }
+
+ // Bind the new buffer to the GL texture, and wait until it's ready.
+ return bindTextureImageLocked(st);
+}
+
+status_t EGLConsumer::releaseTexImage(SurfaceTexture& st) {
+ // Make sure the EGL state is the same as in previous calls.
+ status_t err = NO_ERROR;
+
+ // if we're detached, no need to validate EGL's state -- we won't use it.
+ if (st.mOpMode == SurfaceTexture::OpMode::attachedToGL) {
+ err = checkAndUpdateEglStateLocked(st, true);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ }
+
+ // Update the EGLConsumer state.
+ int buf = st.mCurrentTexture;
+ if (buf != BufferQueue::INVALID_BUFFER_SLOT) {
+ EGC_LOGV("releaseTexImage: (slot=%d, mOpMode=%d)", buf, (int)st.mOpMode);
+
+ // if we're detached, we just use the fence that was created in
+ // detachFromContext() so... basically, nothing more to do here.
+ if (st.mOpMode == SurfaceTexture::OpMode::attachedToGL) {
+ // Do whatever sync ops we need to do before releasing the slot.
+ err = syncForReleaseLocked(mEglDisplay, st);
+ if (err != NO_ERROR) {
+ EGC_LOGE("syncForReleaseLocked failed (slot=%d), err=%d", buf, err);
+ return err;
+ }
+ }
+
+ err = st.releaseBufferLocked(buf, st.mSlots[buf].mGraphicBuffer, mEglDisplay,
+ EGL_NO_SYNC_KHR);
+ if (err < NO_ERROR) {
+ EGC_LOGE("releaseTexImage: failed to release buffer: %s (%d)", strerror(-err), err);
+ return err;
+ }
+
+ if (mReleasedTexImage == nullptr) {
+ mReleasedTexImage = new EglImage(getDebugTexImageBuffer());
+ }
+
+ st.mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT;
+ mCurrentTextureImage = mReleasedTexImage;
+ st.mCurrentCrop.makeInvalid();
+ st.mCurrentTransform = 0;
+ st.mCurrentTimestamp = 0;
+ st.mCurrentDataSpace = HAL_DATASPACE_UNKNOWN;
+ st.mCurrentFence = Fence::NO_FENCE;
+ st.mCurrentFenceTime = FenceTime::NO_FENCE;
+
+ // detached, don't touch the texture (and we may not even have an
+ // EGLDisplay here.
+ if (st.mOpMode == SurfaceTexture::OpMode::attachedToGL) {
+ // This binds a dummy buffer (mReleasedTexImage).
+ status_t result = bindTextureImageLocked(st);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ }
+ }
+
+ return NO_ERROR;
+}
+
+sp<GraphicBuffer> EGLConsumer::getDebugTexImageBuffer() {
+ Mutex::Autolock _l(sStaticInitLock);
+ 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,
+ GraphicBuffer::USAGE_SW_WRITE_RARELY,
+ "[EGLConsumer debug texture]");
+ uint32_t* bits;
+ buffer->lock(GraphicBuffer::USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&bits));
+ uint32_t stride = buffer->getStride();
+ uint32_t height = buffer->getHeight();
+ memset(bits, 0, stride * height * 4);
+ for (uint32_t y = 0; y < kDebugData.height; y++) {
+ for (uint32_t x = 0; x < kDebugData.width; x++) {
+ bits[x] = (kDebugData.bits[y + kDebugData.width + x] == 'X') ? 0xFF000000
+ : 0xFFFFFFFF;
+ }
+ bits += stride;
+ }
+ buffer->unlock();
+ sReleasedTexImageBuffer = buffer;
+ }
+ return sReleasedTexImageBuffer;
+}
+
+void EGLConsumer::onAcquireBufferLocked(BufferItem* item, SurfaceTexture& st) {
+ // If item->mGraphicBuffer is not null, this buffer has not been acquired
+ // before, so any prior EglImage created is using a stale buffer. This
+ // replaces any old EglImage with a new one (using the new buffer).
+ int slot = item->mSlot;
+ if (item->mGraphicBuffer != nullptr || mEglSlots[slot].mEglImage.get() == nullptr) {
+ mEglSlots[slot].mEglImage = new EglImage(st.mSlots[slot].mGraphicBuffer);
+ }
+}
+
+void EGLConsumer::onReleaseBufferLocked(int buf) {
+ mEglSlots[buf].mEglFence = EGL_NO_SYNC_KHR;
+}
+
+status_t EGLConsumer::updateAndReleaseLocked(const BufferItem& item, PendingRelease* pendingRelease,
+ SurfaceTexture& st) {
+ status_t err = NO_ERROR;
+
+ int slot = item.mSlot;
+
+ if (st.mOpMode != SurfaceTexture::OpMode::attachedToGL) {
+ EGC_LOGE("updateAndRelease: EGLConsumer is not attached to an OpenGL "
+ "ES context");
+ st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR);
+ return INVALID_OPERATION;
+ }
+
+ // Confirm state.
+ err = checkAndUpdateEglStateLocked(st);
+ if (err != NO_ERROR) {
+ st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR);
+ return err;
+ }
+
+ // Ensure we have a valid EglImageKHR for the slot, creating an EglImage
+ // if nessessary, for the gralloc buffer currently in the slot in
+ // ConsumerBase.
+ // We may have to do this even when item.mGraphicBuffer == NULL (which
+ // means the buffer was previously acquired).
+ err = mEglSlots[slot].mEglImage->createIfNeeded(mEglDisplay);
+ if (err != NO_ERROR) {
+ EGC_LOGW("updateAndRelease: unable to createImage on display=%p slot=%d", mEglDisplay,
+ slot);
+ st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR);
+ return UNKNOWN_ERROR;
+ }
+
+ // Do whatever sync ops we need to do before releasing the old slot.
+ if (slot != st.mCurrentTexture) {
+ err = syncForReleaseLocked(mEglDisplay, st);
+ if (err != NO_ERROR) {
+ // Release the buffer we just acquired. It's not safe to
+ // release the old buffer, so instead we just drop the new frame.
+ // As we are still under lock since acquireBuffer, it is safe to
+ // release by slot.
+ st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay,
+ EGL_NO_SYNC_KHR);
+ return err;
+ }
+ }
+
+ EGC_LOGV("updateAndRelease: (slot=%d buf=%p) -> (slot=%d buf=%p)", st.mCurrentTexture,
+ mCurrentTextureImage != nullptr ? mCurrentTextureImage->graphicBufferHandle()
+ : nullptr,
+ slot, st.mSlots[slot].mGraphicBuffer->handle);
+
+ // Hang onto the pointer so that it isn't freed in the call to
+ // releaseBufferLocked() if we're in shared buffer mode and both buffers are
+ // the same.
+ sp<EglImage> nextTextureImage = mEglSlots[slot].mEglImage;
+
+ // release old buffer
+ if (st.mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
+ if (pendingRelease == nullptr) {
+ status_t status =
+ st.releaseBufferLocked(st.mCurrentTexture,
+ mCurrentTextureImage->graphicBuffer(), mEglDisplay,
+ mEglSlots[st.mCurrentTexture].mEglFence);
+ if (status < NO_ERROR) {
+ EGC_LOGE("updateAndRelease: failed to release buffer: %s (%d)", strerror(-status),
+ status);
+ err = status;
+ // keep going, with error raised [?]
+ }
+ } else {
+ pendingRelease->currentTexture = st.mCurrentTexture;
+ pendingRelease->graphicBuffer = mCurrentTextureImage->graphicBuffer();
+ pendingRelease->display = mEglDisplay;
+ pendingRelease->fence = mEglSlots[st.mCurrentTexture].mEglFence;
+ pendingRelease->isPending = true;
+ }
+ }
+
+ // Update the EGLConsumer state.
+ st.mCurrentTexture = slot;
+ mCurrentTextureImage = nextTextureImage;
+ st.mCurrentCrop = item.mCrop;
+ st.mCurrentTransform = item.mTransform;
+ st.mCurrentScalingMode = item.mScalingMode;
+ st.mCurrentTimestamp = item.mTimestamp;
+ st.mCurrentDataSpace = item.mDataSpace;
+ st.mCurrentFence = item.mFence;
+ st.mCurrentFenceTime = item.mFenceTime;
+ st.mCurrentFrameNumber = item.mFrameNumber;
+
+ st.computeCurrentTransformMatrixLocked();
+
+ return err;
+}
+
+status_t EGLConsumer::bindTextureImageLocked(SurfaceTexture& st) {
+ if (mEglDisplay == EGL_NO_DISPLAY) {
+ ALOGE("bindTextureImage: invalid display");
+ return INVALID_OPERATION;
+ }
+
+ GLenum error;
+ while ((error = glGetError()) != GL_NO_ERROR) {
+ EGC_LOGW("bindTextureImage: clearing GL error: %#04x", error);
+ }
+
+ glBindTexture(st.mTexTarget, st.mTexName);
+ if (st.mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT && mCurrentTextureImage == nullptr) {
+ EGC_LOGE("bindTextureImage: no currently-bound texture");
+ return NO_INIT;
+ }
+
+ status_t err = mCurrentTextureImage->createIfNeeded(mEglDisplay);
+ if (err != NO_ERROR) {
+ EGC_LOGW("bindTextureImage: can't create image on display=%p slot=%d", mEglDisplay,
+ st.mCurrentTexture);
+ return UNKNOWN_ERROR;
+ }
+ mCurrentTextureImage->bindToTextureTarget(st.mTexTarget);
+
+ // In the rare case that the display is terminated and then initialized
+ // again, we can't detect that the display changed (it didn't), but the
+ // image is invalid. In this case, repeat the exact same steps while
+ // forcing the creation of a new image.
+ if ((error = glGetError()) != GL_NO_ERROR) {
+ glBindTexture(st.mTexTarget, st.mTexName);
+ status_t result = mCurrentTextureImage->createIfNeeded(mEglDisplay, true);
+ if (result != NO_ERROR) {
+ EGC_LOGW("bindTextureImage: can't create image on display=%p slot=%d", mEglDisplay,
+ st.mCurrentTexture);
+ return UNKNOWN_ERROR;
+ }
+ mCurrentTextureImage->bindToTextureTarget(st.mTexTarget);
+ if ((error = glGetError()) != GL_NO_ERROR) {
+ EGC_LOGE("bindTextureImage: error binding external image: %#04x", error);
+ return UNKNOWN_ERROR;
+ }
+ }
+
+ // Wait for the new buffer to be ready.
+ return doGLFenceWaitLocked(st);
+}
+
+status_t EGLConsumer::checkAndUpdateEglStateLocked(SurfaceTexture& st, bool contextCheck) {
+ EGLDisplay dpy = eglGetCurrentDisplay();
+ EGLContext ctx = eglGetCurrentContext();
+
+ if (!contextCheck) {
+ // if this is the first time we're called, mEglDisplay/mEglContext have
+ // never been set, so don't error out (below).
+ if (mEglDisplay == EGL_NO_DISPLAY) {
+ mEglDisplay = dpy;
+ }
+ if (mEglContext == EGL_NO_CONTEXT) {
+ mEglContext = ctx;
+ }
+ }
+
+ if (mEglDisplay != dpy || dpy == EGL_NO_DISPLAY) {
+ EGC_LOGE("checkAndUpdateEglState: invalid current EGLDisplay");
+ return INVALID_OPERATION;
+ }
+
+ if (mEglContext != ctx || ctx == EGL_NO_CONTEXT) {
+ EGC_LOGE("checkAndUpdateEglState: invalid current EGLContext");
+ return INVALID_OPERATION;
+ }
+
+ mEglDisplay = dpy;
+ mEglContext = ctx;
+ return NO_ERROR;
+}
+
+status_t EGLConsumer::detachFromContext(SurfaceTexture& st) {
+ EGLDisplay dpy = eglGetCurrentDisplay();
+ EGLContext ctx = eglGetCurrentContext();
+
+ if (mEglDisplay != dpy && mEglDisplay != EGL_NO_DISPLAY) {
+ EGC_LOGE("detachFromContext: invalid current EGLDisplay");
+ return INVALID_OPERATION;
+ }
+
+ if (mEglContext != ctx && mEglContext != EGL_NO_CONTEXT) {
+ EGC_LOGE("detachFromContext: invalid current EGLContext");
+ return INVALID_OPERATION;
+ }
+
+ if (dpy != EGL_NO_DISPLAY && ctx != EGL_NO_CONTEXT) {
+ status_t err = syncForReleaseLocked(dpy, st);
+ if (err != OK) {
+ return err;
+ }
+
+ glDeleteTextures(1, &st.mTexName);
+ }
+
+ mEglDisplay = EGL_NO_DISPLAY;
+ mEglContext = EGL_NO_CONTEXT;
+
+ return OK;
+}
+
+status_t EGLConsumer::attachToContext(uint32_t tex, SurfaceTexture& st) {
+ // Initialize mCurrentTextureImage if there is a current buffer from past
+ // attached state.
+ int slot = st.mCurrentTexture;
+ if (slot != BufferItem::INVALID_BUFFER_SLOT) {
+ if (!mEglSlots[slot].mEglImage.get()) {
+ mEglSlots[slot].mEglImage = new EglImage(st.mSlots[slot].mGraphicBuffer);
+ }
+ mCurrentTextureImage = mEglSlots[slot].mEglImage;
+ }
+
+ EGLDisplay dpy = eglGetCurrentDisplay();
+ EGLContext ctx = eglGetCurrentContext();
+
+ if (dpy == EGL_NO_DISPLAY) {
+ EGC_LOGE("attachToContext: invalid current EGLDisplay");
+ return INVALID_OPERATION;
+ }
+
+ if (ctx == EGL_NO_CONTEXT) {
+ EGC_LOGE("attachToContext: invalid current EGLContext");
+ return INVALID_OPERATION;
+ }
+
+ // We need to bind the texture regardless of whether there's a current
+ // buffer.
+ glBindTexture(st.mTexTarget, GLuint(tex));
+
+ mEglDisplay = dpy;
+ mEglContext = ctx;
+ st.mTexName = tex;
+ st.mOpMode = SurfaceTexture::OpMode::attachedToGL;
+
+ if (mCurrentTextureImage != nullptr) {
+ // This may wait for a buffer a second time. This is likely required if
+ // this is a different context, since otherwise the wait could be skipped
+ // by bouncing through another context. For the same context the extra
+ // wait is redundant.
+ status_t err = bindTextureImageLocked(st);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ }
+
+ return OK;
+}
+
+status_t EGLConsumer::syncForReleaseLocked(EGLDisplay dpy, SurfaceTexture& st) {
+ EGC_LOGV("syncForReleaseLocked");
+
+ if (st.mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
+ if (SyncFeatures::getInstance().useNativeFenceSync()) {
+ EGLSyncKHR sync = eglCreateSyncKHR(dpy, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr);
+ if (sync == EGL_NO_SYNC_KHR) {
+ EGC_LOGE("syncForReleaseLocked: error creating EGL fence: %#x", eglGetError());
+ return UNKNOWN_ERROR;
+ }
+ glFlush();
+ int fenceFd = eglDupNativeFenceFDANDROID(dpy, sync);
+ eglDestroySyncKHR(dpy, sync);
+ if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) {
+ EGC_LOGE("syncForReleaseLocked: error dup'ing native fence "
+ "fd: %#x",
+ eglGetError());
+ return UNKNOWN_ERROR;
+ }
+ sp<Fence> fence(new Fence(fenceFd));
+ status_t err = st.addReleaseFenceLocked(st.mCurrentTexture,
+ mCurrentTextureImage->graphicBuffer(), fence);
+ if (err != OK) {
+ EGC_LOGE("syncForReleaseLocked: error adding release fence: "
+ "%s (%d)",
+ strerror(-err), err);
+ return err;
+ }
+ } else if (st.mUseFenceSync && SyncFeatures::getInstance().useFenceSync()) {
+ EGLSyncKHR fence = mEglSlots[st.mCurrentTexture].mEglFence;
+ if (fence != EGL_NO_SYNC_KHR) {
+ // There is already a fence for the current slot. We need to
+ // wait on that before replacing it with another fence to
+ // ensure that all outstanding buffer accesses have completed
+ // before the producer accesses it.
+ EGLint result = eglClientWaitSyncKHR(dpy, fence, 0, 1000000000);
+ if (result == EGL_FALSE) {
+ EGC_LOGE("syncForReleaseLocked: error waiting for previous "
+ "fence: %#x",
+ eglGetError());
+ return UNKNOWN_ERROR;
+ } else if (result == EGL_TIMEOUT_EXPIRED_KHR) {
+ EGC_LOGE("syncForReleaseLocked: timeout waiting for previous "
+ "fence");
+ return TIMED_OUT;
+ }
+ eglDestroySyncKHR(dpy, fence);
+ }
+
+ // Create a fence for the outstanding accesses in the current
+ // OpenGL ES context.
+ fence = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, nullptr);
+ if (fence == EGL_NO_SYNC_KHR) {
+ EGC_LOGE("syncForReleaseLocked: error creating fence: %#x", eglGetError());
+ return UNKNOWN_ERROR;
+ }
+ glFlush();
+ mEglSlots[st.mCurrentTexture].mEglFence = fence;
+ }
+ }
+
+ return OK;
+}
+
+status_t EGLConsumer::doGLFenceWaitLocked(SurfaceTexture& st) const {
+ EGLDisplay dpy = eglGetCurrentDisplay();
+ EGLContext ctx = eglGetCurrentContext();
+
+ if (mEglDisplay != dpy || mEglDisplay == EGL_NO_DISPLAY) {
+ EGC_LOGE("doGLFenceWait: invalid current EGLDisplay");
+ return INVALID_OPERATION;
+ }
+
+ if (mEglContext != ctx || mEglContext == EGL_NO_CONTEXT) {
+ EGC_LOGE("doGLFenceWait: invalid current EGLContext");
+ return INVALID_OPERATION;
+ }
+
+ if (st.mCurrentFence->isValid()) {
+ if (SyncFeatures::getInstance().useWaitSync() &&
+ SyncFeatures::getInstance().useNativeFenceSync()) {
+ // Create an EGLSyncKHR from the current fence.
+ int fenceFd = st.mCurrentFence->dup();
+ if (fenceFd == -1) {
+ EGC_LOGE("doGLFenceWait: error dup'ing fence fd: %d", errno);
+ return -errno;
+ }
+ EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd, EGL_NONE};
+ EGLSyncKHR sync = eglCreateSyncKHR(dpy, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs);
+ if (sync == EGL_NO_SYNC_KHR) {
+ close(fenceFd);
+ EGC_LOGE("doGLFenceWait: error creating EGL fence: %#x", eglGetError());
+ return UNKNOWN_ERROR;
+ }
+
+ // XXX: The spec draft is inconsistent as to whether this should
+ // return an EGLint or void. Ignore the return value for now, as
+ // it's not strictly needed.
+ eglWaitSyncKHR(dpy, sync, 0);
+ EGLint eglErr = eglGetError();
+ eglDestroySyncKHR(dpy, sync);
+ if (eglErr != EGL_SUCCESS) {
+ EGC_LOGE("doGLFenceWait: error waiting for EGL fence: %#x", eglErr);
+ return UNKNOWN_ERROR;
+ }
+ } else {
+ status_t err = st.mCurrentFence->waitForever("EGLConsumer::doGLFenceWaitLocked");
+ if (err != NO_ERROR) {
+ EGC_LOGE("doGLFenceWait: error waiting for fence: %d", err);
+ return err;
+ }
+ }
+ }
+
+ return NO_ERROR;
+}
+
+void EGLConsumer::onFreeBufferLocked(int slotIndex) {
+ mEglSlots[slotIndex].mEglImage.clear();
+}
+
+void EGLConsumer::onAbandonLocked() {
+ mCurrentTextureImage.clear();
+}
+
+EGLConsumer::EglImage::EglImage(sp<GraphicBuffer> graphicBuffer)
+ : mGraphicBuffer(graphicBuffer), mEglImage(EGL_NO_IMAGE_KHR), mEglDisplay(EGL_NO_DISPLAY) {}
+
+EGLConsumer::EglImage::~EglImage() {
+ if (mEglImage != EGL_NO_IMAGE_KHR) {
+ if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) {
+ ALOGE("~EglImage: eglDestroyImageKHR failed");
+ }
+ eglTerminate(mEglDisplay);
+ }
+}
+
+status_t EGLConsumer::EglImage::createIfNeeded(EGLDisplay eglDisplay, bool forceCreation) {
+ // If there's an image and it's no longer valid, destroy it.
+ bool haveImage = mEglImage != EGL_NO_IMAGE_KHR;
+ bool displayInvalid = mEglDisplay != eglDisplay;
+ if (haveImage && (displayInvalid || forceCreation)) {
+ if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) {
+ ALOGE("createIfNeeded: eglDestroyImageKHR failed");
+ }
+ eglTerminate(mEglDisplay);
+ mEglImage = EGL_NO_IMAGE_KHR;
+ mEglDisplay = EGL_NO_DISPLAY;
+ }
+
+ // If there's no image, create one.
+ if (mEglImage == EGL_NO_IMAGE_KHR) {
+ mEglDisplay = eglDisplay;
+ mEglImage = createImage(mEglDisplay, mGraphicBuffer);
+ }
+
+ // Fail if we can't create a valid image.
+ if (mEglImage == EGL_NO_IMAGE_KHR) {
+ mEglDisplay = EGL_NO_DISPLAY;
+ const sp<GraphicBuffer>& buffer = mGraphicBuffer;
+ ALOGE("Failed to create image. size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d",
+ buffer->getWidth(), buffer->getHeight(), buffer->getStride(), buffer->getUsage(),
+ buffer->getPixelFormat());
+ return UNKNOWN_ERROR;
+ }
+
+ return OK;
+}
+
+void EGLConsumer::EglImage::bindToTextureTarget(uint32_t texTarget) {
+ glEGLImageTargetTexture2DOES(texTarget, static_cast<GLeglImageOES>(mEglImage));
+}
+
+EGLImageKHR EGLConsumer::EglImage::createImage(EGLDisplay dpy,
+ const sp<GraphicBuffer>& graphicBuffer) {
+ EGLClientBuffer cbuf = static_cast<EGLClientBuffer>(graphicBuffer->getNativeBuffer());
+ const bool createProtectedImage =
+ (graphicBuffer->getUsage() & GRALLOC_USAGE_PROTECTED) && hasEglProtectedContent();
+ EGLint attrs[] = {
+ EGL_IMAGE_PRESERVED_KHR,
+ EGL_TRUE,
+ createProtectedImage ? EGL_PROTECTED_CONTENT_EXT : EGL_NONE,
+ createProtectedImage ? EGL_TRUE : EGL_NONE,
+ EGL_NONE,
+ };
+ eglInitialize(dpy, nullptr, nullptr);
+ EGLImageKHR image =
+ eglCreateImageKHR(dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, cbuf, attrs);
+ if (image == EGL_NO_IMAGE_KHR) {
+ EGLint error = eglGetError();
+ ALOGE("error creating EGLImage: %#x", error);
+ eglTerminate(dpy);
+ }
+ return image;
+}
+
+} // namespace android
diff --git a/libs/nativedisplay/surfacetexture/ImageConsumer.cpp b/libs/nativedisplay/surfacetexture/ImageConsumer.cpp
new file mode 100644
index 0000000..16afc68
--- /dev/null
+++ b/libs/nativedisplay/surfacetexture/ImageConsumer.cpp
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2019 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/BufferQueue.h>
+#include <surfacetexture/ImageConsumer.h>
+#include <surfacetexture/SurfaceTexture.h>
+
+// Macro for including the SurfaceTexture name in log messages
+#define IMG_LOGE(x, ...) ALOGE("[%s] " x, st.mName.string(), ##__VA_ARGS__)
+
+namespace android {
+
+void ImageConsumer::onReleaseBufferLocked(int buf) {
+ mImageSlots[buf].eglFence() = EGL_NO_SYNC_KHR;
+}
+
+sp<GraphicBuffer> ImageConsumer::dequeueBuffer(int* outSlotid, android_dataspace* outDataspace,
+ bool* outQueueEmpty, SurfaceTexture& st,
+ SurfaceTexture_createReleaseFence createFence,
+ SurfaceTexture_fenceWait fenceWait,
+ void* fencePassThroughHandle) {
+ BufferItem item;
+ status_t err;
+ err = st.acquireBufferLocked(&item, 0);
+ if (err != OK) {
+ if (err != BufferQueue::NO_BUFFER_AVAILABLE) {
+ IMG_LOGE("Error acquiring buffer: %s (%d)", strerror(err), err);
+ } else {
+ int slot = st.mCurrentTexture;
+ if (slot != BufferItem::INVALID_BUFFER_SLOT) {
+ *outQueueEmpty = true;
+ *outDataspace = st.mCurrentDataSpace;
+ *outSlotid = slot;
+ return st.mSlots[slot].mGraphicBuffer;
+ }
+ }
+ return nullptr;
+ }
+
+ int slot = item.mSlot;
+ if (item.mFence->isValid()) {
+ // Wait on the producer fence for the buffer to be ready.
+ err = fenceWait(item.mFence->get(), fencePassThroughHandle);
+ if (err != OK) {
+ st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY,
+ EGL_NO_SYNC_KHR);
+ return nullptr;
+ }
+ }
+
+ // Release old buffer.
+ if (st.mCurrentTexture != BufferItem::INVALID_BUFFER_SLOT) {
+ // If needed, set the released slot's fence to guard against a producer
+ // accessing the buffer before the outstanding accesses have completed.
+ int releaseFenceId = -1;
+ EGLDisplay display = EGL_NO_DISPLAY;
+ err = createFence(st.mUseFenceSync, &mImageSlots[slot].eglFence(), &display,
+ &releaseFenceId, fencePassThroughHandle);
+ if (OK != err) {
+ st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY,
+ EGL_NO_SYNC_KHR);
+ return nullptr;
+ }
+
+ if (releaseFenceId != -1) {
+ sp<Fence> releaseFence(new Fence(releaseFenceId));
+ status_t err = st.addReleaseFenceLocked(st.mCurrentTexture,
+ st.mSlots[st.mCurrentTexture].mGraphicBuffer,
+ releaseFence);
+ if (err != OK) {
+ IMG_LOGE("dequeueImage: error adding release fence: %s (%d)", strerror(-err), err);
+ st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY,
+ EGL_NO_SYNC_KHR);
+ return nullptr;
+ }
+ }
+
+ // Finally release the old buffer.
+ status_t status =
+ st.releaseBufferLocked(st.mCurrentTexture,
+ st.mSlots[st.mCurrentTexture].mGraphicBuffer, display,
+ mImageSlots[st.mCurrentTexture].eglFence());
+ if (status < NO_ERROR) {
+ IMG_LOGE("dequeueImage: failed to release buffer: %s (%d)", strerror(-status), status);
+ err = status;
+ // Keep going, with error raised.
+ }
+ }
+
+ // Update the state.
+ st.mCurrentTexture = slot;
+ st.mCurrentCrop = item.mCrop;
+ st.mCurrentTransform = item.mTransform;
+ st.mCurrentScalingMode = item.mScalingMode;
+ st.mCurrentTimestamp = item.mTimestamp;
+ st.mCurrentDataSpace = item.mDataSpace;
+ st.mCurrentFence = item.mFence;
+ st.mCurrentFenceTime = item.mFenceTime;
+ st.mCurrentFrameNumber = item.mFrameNumber;
+ st.computeCurrentTransformMatrixLocked();
+
+ *outQueueEmpty = false;
+ *outDataspace = item.mDataSpace;
+ *outSlotid = slot;
+ return st.mSlots[slot].mGraphicBuffer;
+}
+
+} /* namespace android */
diff --git a/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp b/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp
new file mode 100644
index 0000000..62db6d0
--- /dev/null
+++ b/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp
@@ -0,0 +1,490 @@
+/*
+ * Copyright 2019 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 <cutils/compiler.h>
+#include <gui/BufferQueue.h>
+#include <surfacetexture/ImageConsumer.h>
+#include <surfacetexture/SurfaceTexture.h>
+#include <surfacetexture/surface_texture_platform.h>
+#include <math/mat4.h>
+#include <system/window.h>
+#include <utils/Trace.h>
+
+namespace android {
+
+// Macros for including the SurfaceTexture name in log messages
+#define SFT_LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__)
+#define SFT_LOGD(x, ...) ALOGD("[%s] " x, mName.string(), ##__VA_ARGS__)
+#define SFT_LOGW(x, ...) ALOGW("[%s] " x, mName.string(), ##__VA_ARGS__)
+#define SFT_LOGE(x, ...) ALOGE("[%s] " x, mName.string(), ##__VA_ARGS__)
+
+static const mat4 mtxIdentity;
+
+SurfaceTexture::SurfaceTexture(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),
+ mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT),
+ mOpMode(OpMode::attachedToGL) {
+ SFT_LOGV("SurfaceTexture");
+
+ memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), sizeof(mCurrentTransformMatrix));
+
+ mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS);
+}
+
+SurfaceTexture::SurfaceTexture(const sp<IGraphicBufferConsumer>& bq, 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(0),
+ mUseFenceSync(useFenceSync),
+ mTexTarget(texTarget),
+ mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT),
+ mOpMode(OpMode::detached) {
+ SFT_LOGV("SurfaceTexture");
+
+ memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), sizeof(mCurrentTransformMatrix));
+
+ mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS);
+}
+
+status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h) {
+ Mutex::Autolock lock(mMutex);
+ if (mAbandoned) {
+ SFT_LOGE("setDefaultBufferSize: SurfaceTexture is abandoned!");
+ return NO_INIT;
+ }
+ mDefaultWidth = w;
+ mDefaultHeight = h;
+ return mConsumer->setDefaultBufferSize(w, h);
+}
+
+status_t SurfaceTexture::updateTexImage() {
+ ATRACE_CALL();
+ SFT_LOGV("updateTexImage");
+ Mutex::Autolock lock(mMutex);
+
+ if (mAbandoned) {
+ SFT_LOGE("updateTexImage: SurfaceTexture is abandoned!");
+ return NO_INIT;
+ }
+
+ return mEGLConsumer.updateTexImage(*this);
+}
+
+status_t SurfaceTexture::releaseTexImage() {
+ // releaseTexImage can be invoked even when not attached to a GL context.
+ ATRACE_CALL();
+ SFT_LOGV("releaseTexImage");
+ Mutex::Autolock lock(mMutex);
+
+ if (mAbandoned) {
+ SFT_LOGE("releaseTexImage: SurfaceTexture is abandoned!");
+ return NO_INIT;
+ }
+
+ return mEGLConsumer.releaseTexImage(*this);
+}
+
+status_t SurfaceTexture::acquireBufferLocked(BufferItem* item, nsecs_t presentWhen,
+ uint64_t maxFrameNumber) {
+ status_t err = ConsumerBase::acquireBufferLocked(item, presentWhen, maxFrameNumber);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ switch (mOpMode) {
+ case OpMode::attachedToConsumer:
+ break;
+ case OpMode::attachedToGL:
+ mEGLConsumer.onAcquireBufferLocked(item, *this);
+ break;
+ case OpMode::detached:
+ break;
+ }
+
+ return NO_ERROR;
+}
+
+status_t SurfaceTexture::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);
+ // We could be releasing an EGL/Vulkan buffer, even if not currently
+ // attached to a GL context.
+ mImageConsumer.onReleaseBufferLocked(buf);
+ mEGLConsumer.onReleaseBufferLocked(buf);
+ return err;
+}
+
+status_t SurfaceTexture::detachFromContext() {
+ ATRACE_CALL();
+ SFT_LOGV("detachFromContext");
+ Mutex::Autolock lock(mMutex);
+
+ if (mAbandoned) {
+ SFT_LOGE("detachFromContext: abandoned SurfaceTexture");
+ return NO_INIT;
+ }
+
+ if (mOpMode != OpMode::attachedToGL) {
+ SFT_LOGE("detachFromContext: SurfaceTexture is not attached to a GL context");
+ return INVALID_OPERATION;
+ }
+
+ status_t err = mEGLConsumer.detachFromContext(*this);
+ if (err == OK) {
+ mOpMode = OpMode::detached;
+ }
+
+ return err;
+}
+
+status_t SurfaceTexture::attachToContext(uint32_t tex) {
+ ATRACE_CALL();
+ SFT_LOGV("attachToContext");
+ Mutex::Autolock lock(mMutex);
+
+ if (mAbandoned) {
+ SFT_LOGE("attachToContext: abandoned SurfaceTexture");
+ return NO_INIT;
+ }
+
+ if (mOpMode != OpMode::detached) {
+ SFT_LOGE("attachToContext: SurfaceTexture is already attached to a "
+ "context");
+ return INVALID_OPERATION;
+ }
+
+ return mEGLConsumer.attachToContext(tex, *this);
+}
+
+void SurfaceTexture::takeConsumerOwnership() {
+ ATRACE_CALL();
+ Mutex::Autolock _l(mMutex);
+ if (mAbandoned) {
+ SFT_LOGE("attachToView: abandoned SurfaceTexture");
+ return;
+ }
+ if (mOpMode == OpMode::detached) {
+ mOpMode = OpMode::attachedToConsumer;
+
+ if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
+ // release possible EGLConsumer texture cache
+ mEGLConsumer.onFreeBufferLocked(mCurrentTexture);
+ mEGLConsumer.onAbandonLocked();
+ }
+ } else {
+ SFT_LOGE("attachToView: already attached");
+ }
+}
+
+void SurfaceTexture::releaseConsumerOwnership() {
+ ATRACE_CALL();
+ Mutex::Autolock _l(mMutex);
+
+ if (mAbandoned) {
+ SFT_LOGE("detachFromView: abandoned SurfaceTexture");
+ return;
+ }
+
+ if (mOpMode == OpMode::attachedToConsumer) {
+ mOpMode = OpMode::detached;
+ } else {
+ SFT_LOGE("detachFromView: not attached to View");
+ }
+}
+
+uint32_t SurfaceTexture::getCurrentTextureTarget() const {
+ return mTexTarget;
+}
+
+void SurfaceTexture::getTransformMatrix(float mtx[16]) {
+ Mutex::Autolock lock(mMutex);
+ memcpy(mtx, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix));
+}
+
+void SurfaceTexture::setFilteringEnabled(bool enabled) {
+ Mutex::Autolock lock(mMutex);
+ if (mAbandoned) {
+ SFT_LOGE("setFilteringEnabled: SurfaceTexture is abandoned!");
+ return;
+ }
+ bool needsRecompute = mFilteringEnabled != enabled;
+ mFilteringEnabled = enabled;
+
+ if (needsRecompute && mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT) {
+ SFT_LOGD("setFilteringEnabled called with no current item");
+ }
+
+ if (needsRecompute && mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
+ computeCurrentTransformMatrixLocked();
+ }
+}
+
+void SurfaceTexture::computeCurrentTransformMatrixLocked() {
+ SFT_LOGV("computeCurrentTransformMatrixLocked");
+ sp<GraphicBuffer> buf = (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT)
+ ? nullptr
+ : mSlots[mCurrentTexture].mGraphicBuffer;
+ if (buf == nullptr) {
+ SFT_LOGD("computeCurrentTransformMatrixLocked: no current item");
+ }
+ computeTransformMatrix(mCurrentTransformMatrix, buf, mCurrentCrop, mCurrentTransform,
+ mFilteringEnabled);
+}
+
+void SurfaceTexture::computeTransformMatrix(float outTransform[16], const sp<GraphicBuffer>& buf,
+ const Rect& cropRect, uint32_t transform,
+ bool filtering) {
+ // Transform matrices
+ static const mat4 mtxFlipH(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1);
+ static const mat4 mtxFlipV(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1);
+ static const mat4 mtxRot90(0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1);
+
+ mat4 xform;
+ if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_H) {
+ xform *= mtxFlipH;
+ }
+ if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_V) {
+ xform *= mtxFlipV;
+ }
+ if (transform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
+ xform *= mtxRot90;
+ }
+
+ if (!cropRect.isEmpty() && buf.get()) {
+ float tx = 0.0f, ty = 0.0f, sx = 1.0f, sy = 1.0f;
+ float bufferWidth = buf->getWidth();
+ float bufferHeight = buf->getHeight();
+ float shrinkAmount = 0.0f;
+ if (filtering) {
+ // In order to prevent bilinear sampling beyond the edge of the
+ // crop rectangle we may need to shrink it by 2 texels in each
+ // dimension. Normally this would just need to take 1/2 a texel
+ // off each end, but because the chroma channels of YUV420 images
+ // are subsampled we may need to shrink the crop region by a whole
+ // texel on each side.
+ switch (buf->getPixelFormat()) {
+ case PIXEL_FORMAT_RGBA_8888:
+ case PIXEL_FORMAT_RGBX_8888:
+ case PIXEL_FORMAT_RGBA_FP16:
+ case PIXEL_FORMAT_RGBA_1010102:
+ case PIXEL_FORMAT_RGB_888:
+ case PIXEL_FORMAT_RGB_565:
+ case PIXEL_FORMAT_BGRA_8888:
+ // We know there's no subsampling of any channels, so we
+ // only need to shrink by a half a pixel.
+ shrinkAmount = 0.5;
+ break;
+
+ default:
+ // If we don't recognize the format, we must assume the
+ // worst case (that we care about), which is YUV420.
+ shrinkAmount = 1.0;
+ break;
+ }
+ }
+
+ // Only shrink the dimensions that are not the size of the buffer.
+ if (cropRect.width() < bufferWidth) {
+ tx = (float(cropRect.left) + shrinkAmount) / bufferWidth;
+ sx = (float(cropRect.width()) - (2.0f * shrinkAmount)) / bufferWidth;
+ }
+ if (cropRect.height() < bufferHeight) {
+ ty = (float(bufferHeight - cropRect.bottom) + shrinkAmount) / bufferHeight;
+ sy = (float(cropRect.height()) - (2.0f * shrinkAmount)) / bufferHeight;
+ }
+
+ mat4 crop(sx, 0, 0, 0, 0, sy, 0, 0, 0, 0, 1, 0, tx, ty, 0, 1);
+ xform = crop * xform;
+ }
+
+ // SurfaceFlinger expects the top of its window textures to be at a Y
+ // coordinate of 0, so SurfaceTexture must behave the same way. We don't
+ // want to expose this to applications, however, so we must add an
+ // additional vertical flip to the transform after all the other transforms.
+ xform = mtxFlipV * xform;
+
+ memcpy(outTransform, xform.asArray(), sizeof(xform));
+}
+
+Rect SurfaceTexture::scaleDownCrop(const Rect& crop, uint32_t bufferWidth, uint32_t bufferHeight) {
+ Rect outCrop = crop;
+
+ uint32_t newWidth = static_cast<uint32_t>(crop.width());
+ uint32_t newHeight = static_cast<uint32_t>(crop.height());
+
+ if (newWidth * bufferHeight > newHeight * bufferWidth) {
+ newWidth = newHeight * bufferWidth / bufferHeight;
+ ALOGV("too wide: newWidth = %d", newWidth);
+ } else if (newWidth * bufferHeight < newHeight * bufferWidth) {
+ newHeight = newWidth * bufferHeight / bufferWidth;
+ ALOGV("too tall: newHeight = %d", newHeight);
+ }
+
+ uint32_t currentWidth = static_cast<uint32_t>(crop.width());
+ uint32_t currentHeight = static_cast<uint32_t>(crop.height());
+
+ // The crop is too wide
+ if (newWidth < currentWidth) {
+ uint32_t dw = currentWidth - newWidth;
+ auto halfdw = dw / 2;
+ outCrop.left += halfdw;
+ // Not halfdw because it would subtract 1 too few when dw is odd
+ outCrop.right -= (dw - halfdw);
+ // The crop is too tall
+ } else if (newHeight < currentHeight) {
+ uint32_t dh = currentHeight - newHeight;
+ auto halfdh = dh / 2;
+ outCrop.top += halfdh;
+ // Not halfdh because it would subtract 1 too few when dh is odd
+ outCrop.bottom -= (dh - halfdh);
+ }
+
+ ALOGV("getCurrentCrop final crop [%d,%d,%d,%d]", outCrop.left, outCrop.top, outCrop.right,
+ outCrop.bottom);
+
+ return outCrop;
+}
+
+nsecs_t SurfaceTexture::getTimestamp() {
+ SFT_LOGV("getTimestamp");
+ Mutex::Autolock lock(mMutex);
+ return mCurrentTimestamp;
+}
+
+android_dataspace SurfaceTexture::getCurrentDataSpace() {
+ SFT_LOGV("getCurrentDataSpace");
+ Mutex::Autolock lock(mMutex);
+ return mCurrentDataSpace;
+}
+
+uint64_t SurfaceTexture::getFrameNumber() {
+ SFT_LOGV("getFrameNumber");
+ Mutex::Autolock lock(mMutex);
+ return mCurrentFrameNumber;
+}
+
+Rect SurfaceTexture::getCurrentCrop() const {
+ Mutex::Autolock lock(mMutex);
+ return (mCurrentScalingMode == NATIVE_WINDOW_SCALING_MODE_SCALE_CROP)
+ ? scaleDownCrop(mCurrentCrop, mDefaultWidth, mDefaultHeight)
+ : mCurrentCrop;
+}
+
+uint32_t SurfaceTexture::getCurrentTransform() const {
+ Mutex::Autolock lock(mMutex);
+ return mCurrentTransform;
+}
+
+uint32_t SurfaceTexture::getCurrentScalingMode() const {
+ Mutex::Autolock lock(mMutex);
+ return mCurrentScalingMode;
+}
+
+sp<Fence> SurfaceTexture::getCurrentFence() const {
+ Mutex::Autolock lock(mMutex);
+ return mCurrentFence;
+}
+
+std::shared_ptr<FenceTime> SurfaceTexture::getCurrentFenceTime() const {
+ Mutex::Autolock lock(mMutex);
+ return mCurrentFenceTime;
+}
+
+void SurfaceTexture::freeBufferLocked(int slotIndex) {
+ SFT_LOGV("freeBufferLocked: slotIndex=%d", slotIndex);
+ if (slotIndex == mCurrentTexture) {
+ mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT;
+ }
+ // The slotIndex buffer could have EGL cache, but there is no way to tell
+ // for sure. Buffers can be freed after SurfaceTexture has detached from GL
+ // context or View.
+ mEGLConsumer.onFreeBufferLocked(slotIndex);
+ ConsumerBase::freeBufferLocked(slotIndex);
+}
+
+void SurfaceTexture::abandonLocked() {
+ SFT_LOGV("abandonLocked");
+ mEGLConsumer.onAbandonLocked();
+ ConsumerBase::abandonLocked();
+}
+
+status_t SurfaceTexture::setConsumerUsageBits(uint64_t usage) {
+ return ConsumerBase::setConsumerUsageBits(usage | DEFAULT_USAGE_FLAGS);
+}
+
+void SurfaceTexture::dumpLocked(String8& result, const char* prefix) const {
+ result.appendFormat("%smTexName=%d mCurrentTexture=%d\n"
+ "%smCurrentCrop=[%d,%d,%d,%d] mCurrentTransform=%#x\n",
+ prefix, mTexName, mCurrentTexture, prefix, mCurrentCrop.left,
+ mCurrentCrop.top, mCurrentCrop.right, mCurrentCrop.bottom,
+ mCurrentTransform);
+
+ ConsumerBase::dumpLocked(result, prefix);
+}
+
+sp<GraphicBuffer> SurfaceTexture::dequeueBuffer(int* outSlotid, android_dataspace* outDataspace,
+ float* outTransformMatrix, bool* outQueueEmpty,
+ SurfaceTexture_createReleaseFence createFence,
+ SurfaceTexture_fenceWait fenceWait,
+ void* fencePassThroughHandle) {
+ Mutex::Autolock _l(mMutex);
+ sp<GraphicBuffer> buffer;
+
+ if (mAbandoned) {
+ SFT_LOGE("dequeueImage: SurfaceTexture is abandoned!");
+ return buffer;
+ }
+
+ if (mOpMode != OpMode::attachedToConsumer) {
+ SFT_LOGE("dequeueImage: SurfaceTexture is not attached to a View");
+ return buffer;
+ }
+
+ buffer = mImageConsumer.dequeueBuffer(outSlotid, outDataspace, outQueueEmpty, *this,
+ createFence, fenceWait, fencePassThroughHandle);
+ memcpy(outTransformMatrix, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix));
+ return buffer;
+}
+
+} // namespace android
diff --git a/libs/nativedisplay/surfacetexture/surface_texture.cpp b/libs/nativedisplay/surfacetexture/surface_texture.cpp
new file mode 100644
index 0000000..ebe4484
--- /dev/null
+++ b/libs/nativedisplay/surfacetexture/surface_texture.cpp
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2018 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 <android/surface_texture.h>
+#include <android/surface_texture_jni.h>
+
+#define LOG_TAG "ASurfaceTexture"
+
+#include <utils/Log.h>
+
+#include <gui/Surface.h>
+
+#include <surfacetexture/surface_texture_platform.h>
+#include <surfacetexture/SurfaceTexture.h>
+
+#include <mutex>
+
+#include <jni.h>
+#include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedLocalRef.h>
+
+struct ASurfaceTexture {
+ android::sp<android::SurfaceTexture> consumer;
+ android::sp<android::IGraphicBufferProducer> producer;
+};
+
+using namespace android;
+
+const char* const kSurfaceTextureClassPathName = "android/graphics/SurfaceTexture";
+
+struct fields_t {
+ jfieldID surfaceTexture;
+ jfieldID producer;
+};
+static fields_t fields;
+static std::once_flag sInitFieldsOnce;
+
+#define ANDROID_GRAPHICS_SURFACETEXTURE_JNI_ID "mSurfaceTexture"
+#define ANDROID_GRAPHICS_PRODUCER_JNI_ID "mProducer"
+
+static void SurfaceTexture_classInit(JNIEnv* env, jclass clazz)
+{
+ fields.surfaceTexture = env->GetFieldID(clazz,
+ ANDROID_GRAPHICS_SURFACETEXTURE_JNI_ID, "J");
+ if (fields.surfaceTexture == NULL) {
+ ALOGE("can't find android/graphics/SurfaceTexture.%s",
+ ANDROID_GRAPHICS_SURFACETEXTURE_JNI_ID);
+ }
+ fields.producer = env->GetFieldID(clazz,
+ ANDROID_GRAPHICS_PRODUCER_JNI_ID, "J");
+ if (fields.producer == NULL) {
+ ALOGE("can't find android/graphics/SurfaceTexture.%s",
+ ANDROID_GRAPHICS_PRODUCER_JNI_ID);
+ }
+}
+
+static inline jclass FindClassOrDie(JNIEnv* env, const char* class_name) {
+ jclass clazz = env->FindClass(class_name);
+ LOG_ALWAYS_FATAL_IF(clazz == NULL, "Unable to find class %s", class_name);
+ return clazz;
+}
+
+static void register_android_graphics_SurfaceTexture(JNIEnv* env)
+{
+ // Cache some fields.
+ ScopedLocalRef<jclass> klass(env, FindClassOrDie(env, kSurfaceTextureClassPathName));
+ SurfaceTexture_classInit(env, klass.get());
+}
+
+static bool android_SurfaceTexture_isInstanceOf(JNIEnv* env, jobject thiz) {
+ std::call_once(sInitFieldsOnce, [=]() {
+ register_android_graphics_SurfaceTexture(env);
+ });
+
+ jclass surfaceTextureClass = env->FindClass(kSurfaceTextureClassPathName);
+ return env->IsInstanceOf(thiz, surfaceTextureClass);
+}
+
+static sp<SurfaceTexture> SurfaceTexture_getSurfaceTexture(JNIEnv* env, jobject thiz) {
+ std::call_once(sInitFieldsOnce, [=]() {
+ register_android_graphics_SurfaceTexture(env);
+ });
+
+ return (SurfaceTexture*)env->GetLongField(thiz, fields.surfaceTexture);
+}
+
+static sp<IGraphicBufferProducer> SurfaceTexture_getProducer(JNIEnv* env, jobject thiz) {
+ std::call_once(sInitFieldsOnce, [=]() {
+ register_android_graphics_SurfaceTexture(env);
+ });
+
+ return (IGraphicBufferProducer*)env->GetLongField(thiz, fields.producer);
+}
+
+// The following functions implement NDK API.
+ASurfaceTexture* ASurfaceTexture_fromSurfaceTexture(JNIEnv* env, jobject surfacetexture) {
+ if (!surfacetexture || !android_SurfaceTexture_isInstanceOf(env, surfacetexture)) {
+ return nullptr;
+ }
+ ASurfaceTexture* ast = new ASurfaceTexture;
+ ast->consumer = SurfaceTexture_getSurfaceTexture(env, surfacetexture);
+ ast->producer = SurfaceTexture_getProducer(env, surfacetexture);
+ return ast;
+}
+
+ANativeWindow* ASurfaceTexture_acquireANativeWindow(ASurfaceTexture* st) {
+ sp<Surface> surface = new Surface(st->producer);
+ ANativeWindow* win(surface.get());
+ ANativeWindow_acquire(win);
+ return win;
+}
+
+void ASurfaceTexture_release(ASurfaceTexture* st) {
+ delete st;
+}
+
+int ASurfaceTexture_attachToGLContext(ASurfaceTexture* st, uint32_t tex) {
+ return st->consumer->attachToContext(tex);
+}
+
+int ASurfaceTexture_detachFromGLContext(ASurfaceTexture* st) {
+ return st->consumer->detachFromContext();
+}
+
+int ASurfaceTexture_updateTexImage(ASurfaceTexture* st) {
+ return st->consumer->updateTexImage();
+}
+
+void ASurfaceTexture_getTransformMatrix(ASurfaceTexture* st, float mtx[16]) {
+ st->consumer->getTransformMatrix(mtx);
+}
+
+int64_t ASurfaceTexture_getTimestamp(ASurfaceTexture* st) {
+ return st->consumer->getTimestamp();
+}
+
+// The following functions are private/unstable API.
+namespace android {
+ANativeWindow* ASurfaceTexture_routeAcquireANativeWindow(ASurfaceTexture* st) {
+ return ASurfaceTexture_acquireANativeWindow(st);
+}
+
+int ASurfaceTexture_routeAttachToGLContext(ASurfaceTexture* st, uint32_t texName) {
+ return ASurfaceTexture_attachToGLContext(st, texName);
+}
+
+void ASurfaceTexture_routeRelease(ASurfaceTexture* st) {
+ return ASurfaceTexture_release(st);
+}
+
+int ASurfaceTexture_routeDetachFromGLContext(ASurfaceTexture* st) {
+ return ASurfaceTexture_detachFromGLContext(st);
+}
+
+int ASurfaceTexture_routeUpdateTexImage(ASurfaceTexture* st) {
+ return ASurfaceTexture_updateTexImage(st);
+}
+
+void ASurfaceTexture_routeGetTransformMatrix(ASurfaceTexture* st, float mtx[16]) {
+ return ASurfaceTexture_getTransformMatrix(st, mtx);
+}
+
+int64_t ASurfaceTexture_routeGetTimestamp(ASurfaceTexture* st) {
+ return ASurfaceTexture_getTimestamp(st);
+}
+
+ASurfaceTexture* ASurfaceTexture_routeFromSurfaceTexture(JNIEnv* env, jobject surfacetexture) {
+ return ASurfaceTexture_fromSurfaceTexture(env, surfacetexture);
+}
+
+unsigned int ASurfaceTexture_getCurrentTextureTarget(ASurfaceTexture* st) {
+ return st->consumer->getCurrentTextureTarget();
+}
+
+void ASurfaceTexture_takeConsumerOwnership(ASurfaceTexture* texture) {
+ texture->consumer->takeConsumerOwnership();
+}
+
+void ASurfaceTexture_releaseConsumerOwnership(ASurfaceTexture* texture) {
+ texture->consumer->releaseConsumerOwnership();
+}
+
+AHardwareBuffer* ASurfaceTexture_dequeueBuffer(ASurfaceTexture* st, int* outSlotid,
+ android_dataspace* outDataspace,
+ float* outTransformMatrix, bool* outNewContent,
+ ASurfaceTexture_createReleaseFence createFence,
+ ASurfaceTexture_fenceWait fenceWait, void* handle) {
+ sp<GraphicBuffer> buffer;
+ *outNewContent = false;
+ bool queueEmpty;
+ do {
+ buffer = st->consumer->dequeueBuffer(outSlotid, outDataspace, outTransformMatrix,
+ &queueEmpty, createFence, fenceWait, handle);
+ if (!queueEmpty) {
+ *outNewContent = true;
+ }
+ } while (buffer.get() && (!queueEmpty));
+ AHardwareBuffer* result = nullptr;
+ if (buffer.get()) {
+ result = buffer->toAHardwareBuffer();
+ // add a reference to keep the hardware buffer alive, even if
+ // BufferQueueProducer is disconnected. This is needed, because
+ // sp reference is destroyed at the end of this function.
+ AHardwareBuffer_acquire(result);
+ }
+ return result;
+}
+
+} // namespace android
diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp
index 8435dac..fd1793b 100644
--- a/libs/nativewindow/ANativeWindow.cpp
+++ b/libs/nativewindow/ANativeWindow.cpp
@@ -33,6 +33,12 @@
return res < 0 ? res : value;
}
+static int64_t query64(ANativeWindow* window, int what) {
+ int64_t value;
+ int res = window->perform(window, what, &value);
+ return res < 0 ? res : value;
+}
+
static bool isDataSpaceValid(ANativeWindow* window, int32_t dataSpace) {
bool supported = false;
switch (dataSpace) {
@@ -132,6 +138,11 @@
static_assert(static_cast<int>(ADATASPACE_SCRGB) == static_cast<int>(HAL_DATASPACE_V0_SCRGB));
static_assert(static_cast<int>(ADATASPACE_DISPLAY_P3) == static_cast<int>(HAL_DATASPACE_DISPLAY_P3));
static_assert(static_cast<int>(ADATASPACE_BT2020_PQ) == static_cast<int>(HAL_DATASPACE_BT2020_PQ));
+ static_assert(static_cast<int>(ADATASPACE_ADOBE_RGB) == static_cast<int>(HAL_DATASPACE_ADOBE_RGB));
+ static_assert(static_cast<int>(ADATASPACE_BT2020) == static_cast<int>(HAL_DATASPACE_BT2020));
+ static_assert(static_cast<int>(ADATASPACE_BT709) == static_cast<int>(HAL_DATASPACE_V0_BT709));
+ static_assert(static_cast<int>(ADATASPACE_DCI_P3) == static_cast<int>(HAL_DATASPACE_DCI_P3));
+ static_assert(static_cast<int>(ADATASPACE_SRGB_LINEAR) == static_cast<int>(HAL_DATASPACE_V0_SRGB_LINEAR));
if (!window || !query(window, NATIVE_WINDOW_IS_VALID) ||
!isDataSpaceValid(window, dataSpace)) {
@@ -147,6 +158,21 @@
return query(window, NATIVE_WINDOW_DATASPACE);
}
+int32_t ANativeWindow_setFrameRate(ANativeWindow* window, float frameRate, int8_t compatibility) {
+ if (!window || !query(window, NATIVE_WINDOW_IS_VALID)) {
+ return -EINVAL;
+ }
+ return native_window_set_frame_rate(window, frameRate, compatibility);
+}
+
+void ANativeWindow_tryAllocateBuffers(ANativeWindow* window) {
+ if (!window || !query(window, NATIVE_WINDOW_IS_VALID)) {
+ return;
+ }
+ window->perform(window, NATIVE_WINDOW_ALLOCATE_BUFFERS);
+}
+
+
/**************************************************************************************************
* vndk-stable
**************************************************************************************************/
@@ -262,3 +288,50 @@
int ANativeWindow_setAutoRefresh(ANativeWindow* window, bool autoRefresh) {
return native_window_set_auto_refresh(window, autoRefresh);
}
+
+int ANativeWindow_setAutoPrerotation(ANativeWindow* window, bool autoPrerotation) {
+ return native_window_set_auto_prerotation(window, autoPrerotation);
+}
+
+/**************************************************************************************************
+ * apex-stable
+ **************************************************************************************************/
+
+int64_t ANativeWindow_getLastDequeueDuration(ANativeWindow* window) {
+ return query64(window, NATIVE_WINDOW_GET_LAST_DEQUEUE_DURATION);
+}
+
+int64_t ANativeWindow_getLastQueueDuration(ANativeWindow* window) {
+ return query64(window, NATIVE_WINDOW_GET_LAST_QUEUE_DURATION);
+}
+
+int64_t ANativeWindow_getLastDequeueStartTime(ANativeWindow* window) {
+ return query64(window, NATIVE_WINDOW_GET_LAST_DEQUEUE_START);
+}
+
+int ANativeWindow_setDequeueTimeout(ANativeWindow* window, int64_t timeout) {
+ return window->perform(window, NATIVE_WINDOW_SET_DEQUEUE_TIMEOUT, timeout);
+}
+
+int ANativeWindow_setCancelBufferInterceptor(ANativeWindow* window,
+ ANativeWindow_cancelBufferInterceptor interceptor,
+ void* data) {
+ return window->perform(window, NATIVE_WINDOW_SET_CANCEL_INTERCEPTOR, interceptor, data);
+}
+
+int ANativeWindow_setDequeueBufferInterceptor(ANativeWindow* window,
+ ANativeWindow_dequeueBufferInterceptor interceptor,
+ void* data) {
+ return window->perform(window, NATIVE_WINDOW_SET_DEQUEUE_INTERCEPTOR, interceptor, data);
+}
+
+int ANativeWindow_setPerformInterceptor(ANativeWindow* window,
+ ANativeWindow_performInterceptor interceptor, void* data) {
+ return window->perform(window, NATIVE_WINDOW_SET_PERFORM_INTERCEPTOR, interceptor, data);
+}
+
+int ANativeWindow_setQueueBufferInterceptor(ANativeWindow* window,
+ ANativeWindow_queueBufferInterceptor interceptor,
+ void* data) {
+ return window->perform(window, NATIVE_WINDOW_SET_QUEUE_INTERCEPTOR, interceptor, data);
+}
diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp
index a756bc7..52d73e0 100644
--- a/libs/nativewindow/Android.bp
+++ b/libs/nativewindow/Android.bp
@@ -62,7 +62,6 @@
],
shared_libs: [
- "libhardware",
"libcutils",
"liblog",
"libutils",
diff --git a/libs/nativewindow/TEST_MAPPING b/libs/nativewindow/TEST_MAPPING
new file mode 100644
index 0000000..3d7f3c2
--- /dev/null
+++ b/libs/nativewindow/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "libnativewindow_test"
+ }
+ ]
+}
diff --git a/libs/nativewindow/include/android/data_space.h b/libs/nativewindow/include/android/data_space.h
index 2899bcf..e759513 100644
--- a/libs/nativewindow/include/android/data_space.h
+++ b/libs/nativewindow/include/android/data_space.h
@@ -101,6 +101,56 @@
* Use full range, SMPTE 2084 (PQ) transfer and BT2020 standard
*/
ADATASPACE_BT2020_PQ = 163971072, // STANDARD_BT2020 | TRANSFER_ST2084 | RANGE_FULL
+
+ /**
+ * Adobe RGB
+ *
+ * Use full range, gamma 2.2 transfer and Adobe RGB primaries
+ * Note: Application is responsible for gamma encoding the data as
+ * a 2.2 gamma encoding is not supported in HW.
+ */
+ ADATASPACE_ADOBE_RGB = 151715840, // STANDARD_ADOBE_RGB | TRANSFER_GAMMA2_2 | RANGE_FULL
+
+ /**
+ * ITU-R Recommendation 2020 (BT.2020)
+ *
+ * Ultra High-definition television
+ *
+ * Use full range, BT.709 transfer and BT2020 standard
+ */
+ ADATASPACE_BT2020 = 147193856, // STANDARD_BT2020 | TRANSFER_SMPTE_170M | RANGE_FULL
+
+ /**
+ * ITU-R Recommendation 709 (BT.709)
+ *
+ * High-definition television
+ *
+ * Use limited range, BT.709 transfer and BT.709 standard.
+ */
+ ADATASPACE_BT709 = 281083904, // STANDARD_BT709 | TRANSFER_SMPTE_170M | RANGE_LIMITED
+
+ /**
+ * SMPTE EG 432-1 and SMPTE RP 431-2.
+ *
+ * Digital Cinema DCI-P3
+ *
+ * Use full range, gamma 2.6 transfer and D65 DCI-P3 standard
+ * Note: Application is responsible for gamma encoding the data as
+ * a 2.6 gamma encoding is not supported in HW.
+ */
+ ADATASPACE_DCI_P3 = 155844608, // STANDARD_DCI_P3 | TRANSFER_GAMMA2_6 | RANGE_FULL
+
+ /**
+ * sRGB linear encoding:
+ *
+ * The red, green, and blue components are stored in sRGB space, but
+ * are linear, not gamma-encoded.
+ * The RGB primaries and the white point are the same as BT.709.
+ *
+ * The values are encoded using the full range ([0,255] for 8-bit) for all
+ * components.
+ */
+ ADATASPACE_SRGB_LINEAR = 138477568, // STANDARD_BT709 | TRANSFER_LINEAR | RANGE_FULL
};
__END_DECLS
diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h
index 3e436e3..36aad2e 100644
--- a/libs/nativewindow/include/android/native_window.h
+++ b/libs/nativewindow/include/android/native_window.h
@@ -33,6 +33,7 @@
#ifndef ANDROID_NATIVE_WINDOW_H
#define ANDROID_NATIVE_WINDOW_H
+#include <stdint.h>
#include <sys/cdefs.h>
#include <android/data_space.h>
@@ -230,6 +231,78 @@
#endif // __ANDROID_API__ >= 28
+#if __ANDROID_API__ >= 30
+
+/** Compatibility value for ANativeWindow_setFrameRate. */
+enum ANativeWindow_FrameRateCompatibility {
+ /**
+ * There are no inherent restrictions on the frame rate of this window. When
+ * the system selects a frame rate other than what the app requested, the
+ * app will be able to run at the system frame rate without requiring pull
+ * down. This value should be used when displaying game content, UIs, and
+ * anything that isn't video.
+ */
+ ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT = 0,
+ /**
+ * This window is being used to display content with an inherently fixed
+ * frame rate, e.g.\ a video that has a specific frame rate. When the system
+ * selects a frame rate other than what the app requested, the app will need
+ * to do pull down or use some other technique to adapt to the system's
+ * frame rate. The user experience is likely to be worse (e.g. more frame
+ * stuttering) than it would be if the system had chosen the app's requested
+ * frame rate. This value should be used for video content.
+ */
+ ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE = 1
+};
+
+/**
+ * Sets the intended frame rate for this window.
+ *
+ * On devices that are capable of running the display at different refresh
+ * rates, the system may choose a display refresh rate to better match this
+ * window's frame rate. Usage of this API won't introduce frame rate throttling,
+ * or affect other aspects of the application's frame production
+ * pipeline. However, because the system may change the display refresh rate,
+ * calls to this function may result in changes to Choreographer callback
+ * timings, and changes to the time interval at which the system releases
+ * buffers back to the application.
+ *
+ * Note that this only has an effect for windows presented on the display. If
+ * this ANativeWindow is consumed by something other than the system compositor,
+ * e.g. a media codec, this call has no effect.
+ *
+ * Available since API level 30.
+ *
+ * \param frameRate The intended frame rate of this window, in frames per
+ * second. 0 is a special value that indicates the app will accept the system's
+ * choice for the display frame rate, which is the default behavior if this
+ * function isn't called. The frameRate param does <em>not</em> need to be a
+ * valid refresh rate for this device's display - e.g., it's fine to pass 30fps
+ * to a device that can only run the display at 60fps.
+ *
+ * \param compatibility The frame rate compatibility of this window. The
+ * compatibility value may influence the system's choice of display refresh
+ * rate. See the ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* values for more info.
+ *
+ * \return 0 for success, -EINVAL if the window, frame rate, or compatibility
+ * value are invalid.
+ */
+int32_t ANativeWindow_setFrameRate(ANativeWindow* window, float frameRate, int8_t compatibility)
+ __INTRODUCED_IN(30);
+
+/**
+ * Provides a hint to the window that buffers should be preallocated ahead of
+ * time. Note that the window implementation is not guaranteed to preallocate
+ * any buffers, for instance if an implementation disallows allocation of new
+ * buffers, or if there is insufficient memory in the system to preallocate
+ * additional buffers
+ *
+ * Available since API level 30.
+ */
+void ANativeWindow_tryAllocateBuffers(ANativeWindow* window);
+
+#endif // __ANDROID_API__ >= 30
+
#ifdef __cplusplus
};
#endif
diff --git a/libs/nativewindow/include/apex/window.h b/libs/nativewindow/include/apex/window.h
new file mode 100644
index 0000000..2d1354c
--- /dev/null
+++ b/libs/nativewindow/include/apex/window.h
@@ -0,0 +1,210 @@
+/*
+ * Copyright 2019 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 <nativebase/nativebase.h>
+#include <stdarg.h>
+
+// apex is a superset of the NDK
+#include <android/native_window.h>
+
+__BEGIN_DECLS
+
+/*
+ * perform bits that can be used with ANativeWindow_perform()
+ *
+ * This is only to support the intercepting methods below - these should notbe
+ * used directly otherwise.
+ */
+enum ANativeWindowPerform {
+ // clang-format off
+ ANATIVEWINDOW_PERFORM_SET_USAGE = 0,
+ ANATIVEWINDOW_PERFORM_SET_BUFFERS_GEOMETRY = 5,
+ ANATIVEWINDOW_PERFORM_SET_BUFFERS_FORMAT = 9,
+ ANATIVEWINDOW_PERFORM_SET_USAGE64 = 30,
+ // clang-format on
+};
+
+/**
+ * Prototype of the function that an ANativeWindow implementation would call
+ * when ANativeWindow_cancelBuffer is called.
+ */
+typedef int (*ANativeWindow_cancelBufferFn)(ANativeWindow* window, ANativeWindowBuffer* buffer,
+ int fenceFd);
+
+/**
+ * Prototype of the function that intercepts an invocation of
+ * ANativeWindow_cancelBufferFn, along with a data pointer that's passed by the
+ * caller who set the interceptor, as well as arguments that would be
+ * passed to ANativeWindow_cancelBufferFn if it were to be called.
+ */
+typedef int (*ANativeWindow_cancelBufferInterceptor)(ANativeWindow* window,
+ ANativeWindow_cancelBufferFn cancelBuffer,
+ void* data, ANativeWindowBuffer* buffer,
+ int fenceFd);
+
+/**
+ * Prototype of the function that an ANativeWindow implementation would call
+ * when ANativeWindow_dequeueBuffer is called.
+ */
+typedef int (*ANativeWindow_dequeueBufferFn)(ANativeWindow* window, ANativeWindowBuffer** buffer,
+ int* fenceFd);
+
+/**
+ * Prototype of the function that intercepts an invocation of
+ * ANativeWindow_dequeueBufferFn, along with a data pointer that's passed by the
+ * caller who set the interceptor, as well as arguments that would be
+ * passed to ANativeWindow_dequeueBufferFn if it were to be called.
+ */
+typedef int (*ANativeWindow_dequeueBufferInterceptor)(ANativeWindow* window,
+ ANativeWindow_dequeueBufferFn dequeueBuffer,
+ void* data, ANativeWindowBuffer** buffer,
+ int* fenceFd);
+
+/**
+ * Prototype of the function that an ANativeWindow implementation would call
+ * when ANativeWindow_perform is called.
+ */
+typedef int (*ANativeWindow_performFn)(ANativeWindow* window, int operation, va_list args);
+
+/**
+ * Prototype of the function that intercepts an invocation of
+ * ANativeWindow_performFn, along with a data pointer that's passed by the
+ * caller who set the interceptor, as well as arguments that would be
+ * passed to ANativeWindow_performFn if it were to be called.
+ */
+typedef int (*ANativeWindow_performInterceptor)(ANativeWindow* window,
+ ANativeWindow_performFn perform, void* data,
+ int operation, va_list args);
+
+/**
+ * Prototype of the function that an ANativeWindow implementation would call
+ * when ANativeWindow_queueBuffer is called.
+ */
+typedef int (*ANativeWindow_queueBufferFn)(ANativeWindow* window, ANativeWindowBuffer* buffer,
+ int fenceFd);
+
+/**
+ * Prototype of the function that intercepts an invocation of
+ * ANativeWindow_queueBufferFn, along with a data pointer that's passed by the
+ * caller who set the interceptor, as well as arguments that would be
+ * passed to ANativeWindow_queueBufferFn if it were to be called.
+ */
+typedef int (*ANativeWindow_queueBufferInterceptor)(ANativeWindow* window,
+ ANativeWindow_queueBufferFn queueBuffer,
+ void* data, ANativeWindowBuffer* buffer,
+ int fenceFd);
+
+/**
+ * Registers an interceptor for ANativeWindow_cancelBuffer. Instead of calling
+ * the underlying cancelBuffer function, instead the provided interceptor is
+ * called, which may optionally call the underlying cancelBuffer function. An
+ * optional data pointer is also provided to side-channel additional arguments.
+ *
+ * Note that usage of this should only be used for specialized use-cases by
+ * either the system partition or to Mainline modules. This should never be
+ * exposed to NDK or LL-NDK.
+ *
+ * Returns NO_ERROR on success, -errno if registration failed.
+ */
+int ANativeWindow_setCancelBufferInterceptor(ANativeWindow* window,
+ ANativeWindow_cancelBufferInterceptor interceptor,
+ void* data);
+
+/**
+ * Registers an interceptor for ANativeWindow_dequeueBuffer. Instead of calling
+ * the underlying dequeueBuffer function, instead the provided interceptor is
+ * called, which may optionally call the underlying dequeueBuffer function. An
+ * optional data pointer is also provided to side-channel additional arguments.
+ *
+ * Note that usage of this should only be used for specialized use-cases by
+ * either the system partition or to Mainline modules. This should never be
+ * exposed to NDK or LL-NDK.
+ *
+ * Returns NO_ERROR on success, -errno if registration failed.
+ */
+int ANativeWindow_setDequeueBufferInterceptor(ANativeWindow* window,
+ ANativeWindow_dequeueBufferInterceptor interceptor,
+ void* data);
+/**
+ * Registers an interceptor for ANativeWindow_perform. Instead of calling
+ * the underlying perform function, instead the provided interceptor is
+ * called, which may optionally call the underlying perform function. An
+ * optional data pointer is also provided to side-channel additional arguments.
+ *
+ * Note that usage of this should only be used for specialized use-cases by
+ * either the system partition or to Mainline modules. This should never be
+ * exposed to NDK or LL-NDK.
+ *
+ * Returns NO_ERROR on success, -errno if registration failed.
+ */
+int ANativeWindow_setPerformInterceptor(ANativeWindow* window,
+ ANativeWindow_performInterceptor interceptor, void* data);
+/**
+ * Registers an interceptor for ANativeWindow_queueBuffer. Instead of calling
+ * the underlying queueBuffer function, instead the provided interceptor is
+ * called, which may optionally call the underlying queueBuffer function. An
+ * optional data pointer is also provided to side-channel additional arguments.
+ *
+ * Note that usage of this should only be used for specialized use-cases by
+ * either the system partition or to Mainline modules. This should never be
+ * exposed to NDK or LL-NDK.
+ *
+ * Returns NO_ERROR on success, -errno if registration failed.
+ */
+int ANativeWindow_setQueueBufferInterceptor(ANativeWindow* window,
+ ANativeWindow_queueBufferInterceptor interceptor,
+ void* data);
+
+/**
+ * Retrieves how long it took for the last time a buffer was dequeued.
+ *
+ * \return the dequeue duration in nanoseconds
+ */
+int64_t ANativeWindow_getLastDequeueDuration(ANativeWindow* window);
+
+/**
+ * Retrieves how long it took for the last time a buffer was queued.
+ *
+ * \return the queue duration in nanoseconds
+ */
+int64_t ANativeWindow_getLastQueueDuration(ANativeWindow* window);
+
+/**
+ * Retrieves the system time in nanoseconds when the last time a buffer
+ * started to be dequeued.
+ *
+ * \return the start time in nanoseconds
+ */
+int64_t ANativeWindow_getLastDequeueStartTime(ANativeWindow* window);
+
+/**
+ * Sets a timeout in nanoseconds for dequeue calls. All subsequent dequeue calls
+ * made by the window will return -ETIMEDOUT after the timeout if the dequeue
+ * takes too long.
+ *
+ * If the provided timeout is negative, hen this removes the previously configured
+ * timeout. The window then behaves as if ANativeWindow_setDequeueTimeout was
+ * never called.
+ *
+ * \return NO_ERROR on success
+ * \return BAD_VALUE if the dequeue timeout was unabled to be updated, as
+ * updating the dequeue timeout may change internals of the underlying window.
+ */
+int ANativeWindow_setDequeueTimeout(ANativeWindow* window, int64_t timeout);
+
+__END_DECLS
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index 61590e0..b78fc5d 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -34,14 +34,15 @@
#include <cutils/native_handle.h>
#include <errno.h>
#include <limits.h>
+#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <sys/cdefs.h>
#include <system/graphics.h>
#include <unistd.h>
-#include <stdbool.h>
-// system/window.h is a superset of the vndk
+// system/window.h is a superset of the vndk and apex apis
+#include <apex/window.h>
#include <vndk/window.h>
@@ -62,9 +63,9 @@
/* attributes queriable with query() */
enum {
- NATIVE_WINDOW_WIDTH = 0,
- NATIVE_WINDOW_HEIGHT = 1,
- NATIVE_WINDOW_FORMAT = 2,
+ NATIVE_WINDOW_WIDTH = 0,
+ NATIVE_WINDOW_HEIGHT = 1,
+ NATIVE_WINDOW_FORMAT = 2,
/* see ANativeWindowQuery in vndk/window.h */
NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS = ANATIVEWINDOW_QUERY_MIN_UNDEQUEUED_BUFFERS,
@@ -92,7 +93,6 @@
*/
NATIVE_WINDOW_CONCRETE_TYPE = 5,
-
/*
* Default width and height of ANativeWindow buffers, these are the
* dimensions of the window buffers irrespective of the
@@ -147,11 +147,15 @@
/*
* Returns the duration of the last dequeueBuffer call in microseconds
+ * Deprecated: please use NATIVE_WINDOW_GET_LAST_DEQUEUE_DURATION in
+ * perform() instead, which supports nanosecond precision.
*/
NATIVE_WINDOW_LAST_DEQUEUE_DURATION = 14,
/*
* Returns the duration of the last queueBuffer call in microseconds
+ * Deprecated: please use NATIVE_WINDOW_GET_LAST_QUEUE_DURATION in
+ * perform() instead, which supports nanosecond precision.
*/
NATIVE_WINDOW_LAST_QUEUE_DURATION = 15,
@@ -203,41 +207,54 @@
*/
enum {
// clang-format off
- NATIVE_WINDOW_SET_USAGE = 0, /* deprecated */
- NATIVE_WINDOW_CONNECT = 1, /* deprecated */
- NATIVE_WINDOW_DISCONNECT = 2, /* deprecated */
- NATIVE_WINDOW_SET_CROP = 3, /* private */
- NATIVE_WINDOW_SET_BUFFER_COUNT = 4,
- NATIVE_WINDOW_SET_BUFFERS_GEOMETRY = 5, /* deprecated */
- NATIVE_WINDOW_SET_BUFFERS_TRANSFORM = 6,
- NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP = 7,
- NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS = 8,
- NATIVE_WINDOW_SET_BUFFERS_FORMAT = 9,
- NATIVE_WINDOW_SET_SCALING_MODE = 10, /* private */
- NATIVE_WINDOW_LOCK = 11, /* private */
- NATIVE_WINDOW_UNLOCK_AND_POST = 12, /* private */
- NATIVE_WINDOW_API_CONNECT = 13, /* private */
- NATIVE_WINDOW_API_DISCONNECT = 14, /* private */
- NATIVE_WINDOW_SET_BUFFERS_USER_DIMENSIONS = 15, /* private */
- NATIVE_WINDOW_SET_POST_TRANSFORM_CROP = 16, /* deprecated, unimplemented */
- NATIVE_WINDOW_SET_BUFFERS_STICKY_TRANSFORM = 17, /* private */
- NATIVE_WINDOW_SET_SIDEBAND_STREAM = 18,
- NATIVE_WINDOW_SET_BUFFERS_DATASPACE = 19,
- NATIVE_WINDOW_SET_SURFACE_DAMAGE = 20, /* private */
- NATIVE_WINDOW_SET_SHARED_BUFFER_MODE = 21,
- NATIVE_WINDOW_SET_AUTO_REFRESH = 22,
- NATIVE_WINDOW_GET_REFRESH_CYCLE_DURATION = 23,
- NATIVE_WINDOW_GET_NEXT_FRAME_ID = 24,
- NATIVE_WINDOW_ENABLE_FRAME_TIMESTAMPS = 25,
- NATIVE_WINDOW_GET_COMPOSITOR_TIMING = 26,
- NATIVE_WINDOW_GET_FRAME_TIMESTAMPS = 27,
- NATIVE_WINDOW_GET_WIDE_COLOR_SUPPORT = 28,
- NATIVE_WINDOW_GET_HDR_SUPPORT = 29,
- NATIVE_WINDOW_SET_USAGE64 = 30,
- NATIVE_WINDOW_GET_CONSUMER_USAGE64 = 31,
- NATIVE_WINDOW_SET_BUFFERS_SMPTE2086_METADATA = 32,
- NATIVE_WINDOW_SET_BUFFERS_CTA861_3_METADATA = 33,
+ NATIVE_WINDOW_SET_USAGE = ANATIVEWINDOW_PERFORM_SET_USAGE, /* deprecated */
+ NATIVE_WINDOW_CONNECT = 1, /* deprecated */
+ NATIVE_WINDOW_DISCONNECT = 2, /* deprecated */
+ NATIVE_WINDOW_SET_CROP = 3, /* private */
+ NATIVE_WINDOW_SET_BUFFER_COUNT = 4,
+ NATIVE_WINDOW_SET_BUFFERS_GEOMETRY = ANATIVEWINDOW_PERFORM_SET_BUFFERS_GEOMETRY, /* deprecated */
+ NATIVE_WINDOW_SET_BUFFERS_TRANSFORM = 6,
+ NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP = 7,
+ NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS = 8,
+ NATIVE_WINDOW_SET_BUFFERS_FORMAT = ANATIVEWINDOW_PERFORM_SET_BUFFERS_FORMAT,
+ NATIVE_WINDOW_SET_SCALING_MODE = 10, /* private */
+ NATIVE_WINDOW_LOCK = 11, /* private */
+ NATIVE_WINDOW_UNLOCK_AND_POST = 12, /* private */
+ NATIVE_WINDOW_API_CONNECT = 13, /* private */
+ NATIVE_WINDOW_API_DISCONNECT = 14, /* private */
+ NATIVE_WINDOW_SET_BUFFERS_USER_DIMENSIONS = 15, /* private */
+ NATIVE_WINDOW_SET_POST_TRANSFORM_CROP = 16, /* deprecated, unimplemented */
+ NATIVE_WINDOW_SET_BUFFERS_STICKY_TRANSFORM = 17, /* private */
+ NATIVE_WINDOW_SET_SIDEBAND_STREAM = 18,
+ NATIVE_WINDOW_SET_BUFFERS_DATASPACE = 19,
+ NATIVE_WINDOW_SET_SURFACE_DAMAGE = 20, /* private */
+ NATIVE_WINDOW_SET_SHARED_BUFFER_MODE = 21,
+ NATIVE_WINDOW_SET_AUTO_REFRESH = 22,
+ NATIVE_WINDOW_GET_REFRESH_CYCLE_DURATION = 23,
+ NATIVE_WINDOW_GET_NEXT_FRAME_ID = 24,
+ NATIVE_WINDOW_ENABLE_FRAME_TIMESTAMPS = 25,
+ NATIVE_WINDOW_GET_COMPOSITOR_TIMING = 26,
+ NATIVE_WINDOW_GET_FRAME_TIMESTAMPS = 27,
+ NATIVE_WINDOW_GET_WIDE_COLOR_SUPPORT = 28,
+ NATIVE_WINDOW_GET_HDR_SUPPORT = 29,
+ NATIVE_WINDOW_SET_USAGE64 = ANATIVEWINDOW_PERFORM_SET_USAGE64,
+ NATIVE_WINDOW_GET_CONSUMER_USAGE64 = 31,
+ NATIVE_WINDOW_SET_BUFFERS_SMPTE2086_METADATA = 32,
+ NATIVE_WINDOW_SET_BUFFERS_CTA861_3_METADATA = 33,
NATIVE_WINDOW_SET_BUFFERS_HDR10_PLUS_METADATA = 34,
+ NATIVE_WINDOW_SET_AUTO_PREROTATION = 35,
+ NATIVE_WINDOW_GET_LAST_DEQUEUE_START = 36, /* private */
+ NATIVE_WINDOW_SET_DEQUEUE_TIMEOUT = 37, /* private */
+ NATIVE_WINDOW_GET_LAST_DEQUEUE_DURATION = 38, /* private */
+ NATIVE_WINDOW_GET_LAST_QUEUE_DURATION = 39, /* private */
+ NATIVE_WINDOW_SET_FRAME_RATE = 40,
+ NATIVE_WINDOW_SET_CANCEL_INTERCEPTOR = 41, /* private */
+ NATIVE_WINDOW_SET_DEQUEUE_INTERCEPTOR = 42, /* private */
+ NATIVE_WINDOW_SET_PERFORM_INTERCEPTOR = 43, /* private */
+ NATIVE_WINDOW_SET_QUEUE_INTERCEPTOR = 44, /* private */
+ NATIVE_WINDOW_ALLOCATE_BUFFERS = 45, /* private */
+ NATIVE_WINDOW_GET_LAST_QUEUED_BUFFER = 46, /* private */
+ NATIVE_WINDOW_SET_QUERY_INTERCEPTOR = 47, /* private */
// clang-format on
};
@@ -985,4 +1002,99 @@
return window->perform(window, NATIVE_WINDOW_GET_CONSUMER_USAGE64, outUsage);
}
+/*
+ * native_window_set_auto_prerotation(..., autoPrerotation)
+ * Enable/disable the auto prerotation at buffer allocation when the buffer size
+ * is driven by the consumer.
+ *
+ * When buffer size is driven by the consumer and the transform hint specifies
+ * a 90 or 270 degree rotation, if auto prerotation is enabled, the width and
+ * height used for dequeueBuffer will be additionally swapped.
+ */
+static inline int native_window_set_auto_prerotation(struct ANativeWindow* window,
+ bool autoPrerotation) {
+ return window->perform(window, NATIVE_WINDOW_SET_AUTO_PREROTATION, autoPrerotation);
+}
+
+static inline int native_window_set_frame_rate(struct ANativeWindow* window, float frameRate,
+ int8_t compatibility) {
+ return window->perform(window, NATIVE_WINDOW_SET_FRAME_RATE, (double)frameRate,
+ (int)compatibility);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Candidates for APEX visibility
+// These functions are planned to be made stable for APEX modules, but have not
+// yet been stabilized to a specific api version.
+// ------------------------------------------------------------------------------------------------
+
+/**
+ * Retrieves the last queued buffer for this window, along with the fence that
+ * fires when the buffer is ready to be read, and the 4x4 coordinate
+ * transform matrix that should be applied to the buffer's content. The
+ * transform matrix is represented in column-major order.
+ *
+ * If there was no buffer previously queued, then outBuffer will be NULL and
+ * the value of outFence will be -1.
+ *
+ * Note that if outBuffer is not NULL, then the caller will hold a reference
+ * onto the buffer. Accordingly, the caller must call AHardwareBuffer_release
+ * when the buffer is no longer needed so that the system may reclaim the
+ * buffer.
+ *
+ * \return NO_ERROR on success.
+ * \return NO_MEMORY if there was insufficient memory.
+ */
+static inline int ANativeWindow_getLastQueuedBuffer(ANativeWindow* window,
+ AHardwareBuffer** outBuffer, int* outFence,
+ float outTransformMatrix[16]) {
+ return window->perform(window, NATIVE_WINDOW_GET_LAST_QUEUED_BUFFER, outBuffer, outFence,
+ outTransformMatrix);
+}
+
+/**
+ * Retrieves an identifier for the next frame to be queued by this window.
+ *
+ * \return the next frame id.
+ */
+static inline int64_t ANativeWindow_getNextFrameId(ANativeWindow* window) {
+ int64_t value;
+ window->perform(window, NATIVE_WINDOW_GET_NEXT_FRAME_ID, &value);
+ return value;
+}
+
+/**
+ * Prototype of the function that an ANativeWindow implementation would call
+ * when ANativeWindow_query is called.
+ */
+typedef int (*ANativeWindow_queryFn)(const ANativeWindow* window, int what, int* value);
+
+/**
+ * Prototype of the function that intercepts an invocation of
+ * ANativeWindow_queryFn, along with a data pointer that's passed by the
+ * caller who set the interceptor, as well as arguments that would be
+ * passed to ANativeWindow_queryFn if it were to be called.
+ */
+typedef int (*ANativeWindow_queryInterceptor)(const ANativeWindow* window,
+ ANativeWindow_queryFn perform, void* data,
+ int what, int* value);
+
+/**
+ * Registers an interceptor for ANativeWindow_query. Instead of calling
+ * the underlying query function, instead the provided interceptor is
+ * called, which may optionally call the underlying query function. An
+ * optional data pointer is also provided to side-channel additional arguments.
+ *
+ * Note that usage of this should only be used for specialized use-cases by
+ * either the system partition or to Mainline modules. This should never be
+ * exposed to NDK or LL-NDK.
+ *
+ * Returns NO_ERROR on success, -errno if registration failed.
+ */
+static inline int ANativeWindow_setQueryInterceptor(ANativeWindow* window,
+ ANativeWindow_queryInterceptor interceptor,
+ void* data) {
+ return window->perform(window, NATIVE_WINDOW_SET_QUERY_INTERCEPTOR, interceptor, data);
+}
+
__END_DECLS
diff --git a/libs/nativewindow/include/vndk/window.h b/libs/nativewindow/include/vndk/window.h
index 995ba44..500052c 100644
--- a/libs/nativewindow/include/vndk/window.h
+++ b/libs/nativewindow/include/vndk/window.h
@@ -316,6 +316,15 @@
*/
int ANativeWindow_setAutoRefresh(ANativeWindow* window, bool autoRefresh);
+/*
+ * Enable/disable the auto prerotation at buffer allocation when the buffer size
+ * is driven by the consumer.
+ *
+ * When buffer size is driven by the consumer and the transform hint specifies
+ * a 90 or 270 degree rotation, if auto prerotation is enabled, the width and
+ * height used for dequeueBuffer will be additionally swapped.
+ */
+int ANativeWindow_setAutoPrerotation(ANativeWindow* window, bool autoPrerotation);
/*****************************************************************************/
diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt
index db1c9b7..1b5d20d 100644
--- a/libs/nativewindow/libnativewindow.map.txt
+++ b/libs/nativewindow/libnativewindow.map.txt
@@ -22,12 +22,20 @@
ANativeWindow_getBuffersDataSpace; # introduced=28
ANativeWindow_getFormat;
ANativeWindow_getHeight;
+ ANativeWindow_getLastDequeueDuration; # apex # introduced=30
+ ANativeWindow_getLastDequeueStartTime; # apex # introduced=30
+ ANativeWindow_getLastQueueDuration; # apex # introduced=30
ANativeWindow_getWidth;
ANativeWindow_lock;
ANativeWindow_query; # llndk
ANativeWindow_queryf; # llndk
ANativeWindow_queueBuffer; # llndk
+ ANativeWindow_setCancelBufferInterceptor; # apex # introduced=30
+ ANativeWindow_setDequeueBufferInterceptor; # apex # introduced=30
+ ANativeWindow_setPerformInterceptor; # apex # introduced=30
+ ANativeWindow_setQueueBufferInterceptor; # apex # introduced=30
ANativeWindow_release;
+ ANativeWindow_setAutoPrerotation; # llndk
ANativeWindow_setAutoRefresh; # llndk
ANativeWindow_setBufferCount; # llndk
ANativeWindow_setBuffersDataSpace; # introduced=28
@@ -36,9 +44,12 @@
ANativeWindow_setBuffersGeometry;
ANativeWindow_setBuffersTimestamp; # llndk
ANativeWindow_setBuffersTransform;
+ ANativeWindow_setDequeueTimeout; # apex # introduced=30
+ ANativeWindow_setFrameRate; # introduced=30
ANativeWindow_setSharedBufferMode; # llndk
ANativeWindow_setSwapInterval; # llndk
ANativeWindow_setUsage; # llndk
+ ANativeWindow_tryAllocateBuffers; # introduced=30
ANativeWindow_unlockAndPost;
local:
*;
diff --git a/libs/nativewindow/tests/ANativeWindowTest.cpp b/libs/nativewindow/tests/ANativeWindowTest.cpp
new file mode 100644
index 0000000..6cf8291
--- /dev/null
+++ b/libs/nativewindow/tests/ANativeWindowTest.cpp
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2019 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 "ANativeWindow_test"
+//#define LOG_NDEBUG 0
+
+#include <gtest/gtest.h>
+#include <gui/BufferItemConsumer.h>
+#include <gui/BufferQueue.h>
+#include <gui/Surface.h>
+#include <log/log.h>
+#include <sync/sync.h>
+// We need to use the private system apis since not everything is visible to
+// apexes yet.
+#include <system/window.h>
+
+using namespace android;
+
+class TestableSurface final : public Surface {
+public:
+ explicit TestableSurface(const sp<IGraphicBufferProducer>& bufferProducer)
+ : Surface(bufferProducer) {}
+
+ // Exposes the internal last dequeue duration that's stored on the Surface.
+ nsecs_t getLastDequeueDuration() const { return mLastDequeueDuration; }
+
+ // Exposes the internal last queue duration that's stored on the Surface.
+ nsecs_t getLastQueueDuration() const { return mLastQueueDuration; }
+
+ // Exposes the internal last dequeue start time that's stored on the Surface.
+ nsecs_t getLastDequeueStartTime() const { return mLastDequeueStartTime; }
+};
+
+class ANativeWindowTest : public ::testing::Test {
+protected:
+ void SetUp() override {
+ 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());
+ BufferQueue::createBufferQueue(&mProducer, &mConsumer);
+ mItemConsumer = new BufferItemConsumer(mConsumer, GRALLOC_USAGE_SW_READ_OFTEN);
+ mWindow = new TestableSurface(mProducer);
+ const int success = native_window_api_connect(mWindow.get(), NATIVE_WINDOW_API_CPU);
+ EXPECT_EQ(0, success);
+ }
+
+ void TearDown() override {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGV("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+ const int success = native_window_api_disconnect(mWindow.get(), NATIVE_WINDOW_API_CPU);
+ EXPECT_EQ(0, success);
+ }
+ sp<IGraphicBufferProducer> mProducer;
+ sp<IGraphicBufferConsumer> mConsumer;
+ sp<BufferItemConsumer> mItemConsumer;
+
+ sp<TestableSurface> mWindow;
+};
+
+TEST_F(ANativeWindowTest, getLastDequeueDuration_noDequeue_returnsZero) {
+ int result = ANativeWindow_getLastDequeueDuration(mWindow.get());
+ EXPECT_EQ(0, result);
+ EXPECT_EQ(0, mWindow->getLastDequeueDuration());
+}
+
+TEST_F(ANativeWindowTest, getLastDequeueDuration_withDequeue_returnsTime) {
+ ANativeWindowBuffer* buffer;
+ int fd;
+ int result = ANativeWindow_dequeueBuffer(mWindow.get(), &buffer, &fd);
+ close(fd);
+ EXPECT_EQ(0, result);
+
+ result = ANativeWindow_getLastDequeueDuration(mWindow.get());
+ EXPECT_GT(result, 0);
+ EXPECT_EQ(result, mWindow->getLastDequeueDuration());
+}
+
+TEST_F(ANativeWindowTest, getLastQueueDuration_noDequeue_returnsZero) {
+ int result = ANativeWindow_getLastQueueDuration(mWindow.get());
+ EXPECT_EQ(0, result);
+ EXPECT_EQ(0, mWindow->getLastQueueDuration());
+}
+
+TEST_F(ANativeWindowTest, getLastQueueDuration_noQueue_returnsZero) {
+ ANativeWindowBuffer* buffer;
+ int fd;
+ int result = ANativeWindow_dequeueBuffer(mWindow.get(), &buffer, &fd);
+ close(fd);
+ EXPECT_EQ(0, result);
+
+ result = ANativeWindow_getLastQueueDuration(mWindow.get());
+ EXPECT_EQ(result, 0);
+ EXPECT_EQ(result, mWindow->getLastQueueDuration());
+}
+
+TEST_F(ANativeWindowTest, getLastQueueDuration_withQueue_returnsTime) {
+ ANativeWindowBuffer* buffer;
+ int fd;
+ int result = ANativeWindow_dequeueBuffer(mWindow.get(), &buffer, &fd);
+ close(fd);
+ EXPECT_EQ(0, result);
+
+ result = ANativeWindow_queueBuffer(mWindow.get(), buffer, 0);
+
+ result = ANativeWindow_getLastQueueDuration(mWindow.get());
+ EXPECT_GT(result, 0);
+ EXPECT_EQ(result, mWindow->getLastQueueDuration());
+}
+
+TEST_F(ANativeWindowTest, getLastDequeueStartTime_noDequeue_returnsZero) {
+ int64_t result = ANativeWindow_getLastDequeueStartTime(mWindow.get());
+ EXPECT_EQ(0, result);
+ EXPECT_EQ(0, mWindow->getLastQueueDuration());
+}
+
+TEST_F(ANativeWindowTest, getLastDequeueStartTime_withDequeue_returnsTime) {
+ ANativeWindowBuffer* buffer;
+ int fd;
+ int dequeueResult = ANativeWindow_dequeueBuffer(mWindow.get(), &buffer, &fd);
+ close(fd);
+ EXPECT_EQ(0, dequeueResult);
+
+ int64_t result = ANativeWindow_getLastDequeueStartTime(mWindow.get());
+ EXPECT_GT(result, 0);
+ EXPECT_EQ(result, mWindow->getLastDequeueStartTime());
+}
+
+TEST_F(ANativeWindowTest, setDequeueTimeout_causesDequeueTimeout) {
+ nsecs_t timeout = milliseconds_to_nanoseconds(100);
+ int result = ANativeWindow_setDequeueTimeout(mWindow.get(), timeout);
+ EXPECT_EQ(0, result);
+
+ // The two dequeues should not timeout...
+ ANativeWindowBuffer* buffer;
+ int fd;
+ int dequeueResult = ANativeWindow_dequeueBuffer(mWindow.get(), &buffer, &fd);
+ close(fd);
+ EXPECT_EQ(0, dequeueResult);
+ int queueResult = ANativeWindow_queueBuffer(mWindow.get(), buffer, -1);
+ EXPECT_EQ(0, queueResult);
+ dequeueResult = ANativeWindow_dequeueBuffer(mWindow.get(), &buffer, &fd);
+ close(fd);
+ EXPECT_EQ(0, dequeueResult);
+ queueResult = ANativeWindow_queueBuffer(mWindow.get(), buffer, -1);
+ EXPECT_EQ(0, queueResult);
+
+ // ...but the third one should since the queue depth is too deep.
+ nsecs_t start = systemTime(SYSTEM_TIME_MONOTONIC);
+ dequeueResult = ANativeWindow_dequeueBuffer(mWindow.get(), &buffer, &fd);
+ nsecs_t end = systemTime(SYSTEM_TIME_MONOTONIC);
+ close(fd);
+ EXPECT_EQ(TIMED_OUT, dequeueResult);
+ EXPECT_GE(end - start, timeout);
+}
diff --git a/libs/nativewindow/tests/Android.bp b/libs/nativewindow/tests/Android.bp
index 20071be..cdb3d20 100644
--- a/libs/nativewindow/tests/Android.bp
+++ b/libs/nativewindow/tests/Android.bp
@@ -15,13 +15,22 @@
//
cc_test {
- name: "AHardwareBufferTest",
+ name: "libnativewindow_test",
+ test_suites: [
+ "device-tests",
+ ],
shared_libs: [
+ "libgui",
+ "liblog",
"libnativewindow",
+ "libsync",
+ "libutils",
"android.hardware.graphics.common@1.0",
],
srcs: [
"AHardwareBufferTest.cpp",
- "c_compatibility.c"],
+ "ANativeWindowTest.cpp",
+ "c_compatibility.c",
+ ],
cflags: ["-Wall", "-Werror"],
}
diff --git a/libs/nativewindow/tests/c_compatibility.c b/libs/nativewindow/tests/c_compatibility.c
index befd88f..aa9b4f7 100644
--- a/libs/nativewindow/tests/c_compatibility.c
+++ b/libs/nativewindow/tests/c_compatibility.c
@@ -16,6 +16,7 @@
#include <android/hardware_buffer.h>
#include <android/native_window.h>
+#include <apex/window.h>
#include <vndk/hardware_buffer.h>
#include <vndk/window.h>
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index cc252d6..eb6080f 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -52,9 +52,15 @@
"gl/GLExtensions.cpp",
"gl/GLFramebuffer.cpp",
"gl/GLImage.cpp",
+ "gl/GLShadowTexture.cpp",
+ "gl/GLShadowVertexGenerator.cpp",
+ "gl/GLSkiaShadowPort.cpp",
+ "gl/GLVertexBuffer.cpp",
"gl/ImageManager.cpp",
"gl/Program.cpp",
"gl/ProgramCache.cpp",
+ "gl/filters/BlurFilter.cpp",
+ "gl/filters/GenericProgram.cpp",
],
}
@@ -71,9 +77,6 @@
"-fvisibility=hidden",
"-Werror=format",
],
- cppflags: [
- "-fwhole-program-vtables", // requires ThinLTO
- ],
srcs: [
":librenderengine_sources",
":librenderengine_gl_sources",
diff --git a/libs/renderengine/Mesh.cpp b/libs/renderengine/Mesh.cpp
index f5387f2..ed2f45f 100644
--- a/libs/renderengine/Mesh.cpp
+++ b/libs/renderengine/Mesh.cpp
@@ -21,38 +21,46 @@
namespace android {
namespace renderengine {
-Mesh::Mesh(Primitive primitive, size_t vertexCount, size_t vertexSize, size_t texCoordSize)
+Mesh::Mesh(Primitive primitive, size_t vertexCount, size_t vertexSize, size_t texCoordSize,
+ size_t cropCoordsSize, size_t shadowColorSize, size_t shadowParamsSize,
+ size_t indexCount)
: mVertexCount(vertexCount),
mVertexSize(vertexSize),
mTexCoordsSize(texCoordSize),
- mPrimitive(primitive) {
+ mCropCoordsSize(cropCoordsSize),
+ mShadowColorSize(shadowColorSize),
+ mShadowParamsSize(shadowParamsSize),
+ mPrimitive(primitive),
+ mIndexCount(indexCount) {
if (vertexCount == 0) {
mVertices.resize(1);
mVertices[0] = 0.0f;
mStride = 0;
return;
}
-
- const size_t CROP_COORD_SIZE = 2;
- size_t stride = vertexSize + texCoordSize + CROP_COORD_SIZE;
+ size_t stride = vertexSize + texCoordSize + cropCoordsSize + shadowColorSize + shadowParamsSize;
size_t remainder = (stride * vertexCount) / vertexCount;
// Since all of the input parameters are unsigned, if stride is less than
// either vertexSize or texCoordSize, it must have overflowed. remainder
// will be equal to stride as long as stride * vertexCount doesn't overflow.
if ((stride < vertexSize) || (remainder != stride)) {
- ALOGE("Overflow in Mesh(..., %zu, %zu, %zu, %zu)", vertexCount, vertexSize, texCoordSize,
- CROP_COORD_SIZE);
+ ALOGE("Overflow in Mesh(..., %zu, %zu, %zu, %zu, %zu, %zu)", vertexCount, vertexSize,
+ texCoordSize, cropCoordsSize, shadowColorSize, shadowParamsSize);
mVertices.resize(1);
mVertices[0] = 0.0f;
mVertexCount = 0;
mVertexSize = 0;
mTexCoordsSize = 0;
+ mCropCoordsSize = 0;
+ mShadowColorSize = 0;
+ mShadowParamsSize = 0;
mStride = 0;
return;
}
mVertices.resize(stride * vertexCount);
mStride = stride;
+ mIndices.resize(indexCount);
}
Mesh::Primitive Mesh::getPrimitive() const {
@@ -80,6 +88,28 @@
return mVertices.data() + mVertexSize + mTexCoordsSize;
}
+float const* Mesh::getShadowColor() const {
+ return mVertices.data() + mVertexSize + mTexCoordsSize + mCropCoordsSize;
+}
+float* Mesh::getShadowColor() {
+ return mVertices.data() + mVertexSize + mTexCoordsSize + mCropCoordsSize;
+}
+
+float const* Mesh::getShadowParams() const {
+ return mVertices.data() + mVertexSize + mTexCoordsSize + mCropCoordsSize + mShadowColorSize;
+}
+float* Mesh::getShadowParams() {
+ return mVertices.data() + mVertexSize + mTexCoordsSize + mCropCoordsSize + mShadowColorSize;
+}
+
+uint16_t const* Mesh::getIndices() const {
+ return mIndices.data();
+}
+
+uint16_t* Mesh::getIndices() {
+ return mIndices.data();
+}
+
size_t Mesh::getVertexCount() const {
return mVertexCount;
}
@@ -92,6 +122,14 @@
return mTexCoordsSize;
}
+size_t Mesh::getShadowColorSize() const {
+ return mShadowColorSize;
+}
+
+size_t Mesh::getShadowParamsSize() const {
+ return mShadowParamsSize;
+}
+
size_t Mesh::getByteStride() const {
return mStride * sizeof(float);
}
@@ -100,5 +138,9 @@
return mStride;
}
+size_t Mesh::getIndexCount() const {
+ return mIndexCount;
+}
+
} // namespace renderengine
} // namespace android
diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp
index 166c267..0fdf093 100644
--- a/libs/renderengine/RenderEngine.cpp
+++ b/libs/renderengine/RenderEngine.cpp
@@ -24,23 +24,22 @@
namespace android {
namespace renderengine {
-std::unique_ptr<impl::RenderEngine> RenderEngine::create(int hwcFormat, uint32_t featureFlags,
- uint32_t imageCacheSize) {
+std::unique_ptr<impl::RenderEngine> RenderEngine::create(const RenderEngineCreationArgs& args) {
char prop[PROPERTY_VALUE_MAX];
property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, "gles");
if (strcmp(prop, "gles") == 0) {
ALOGD("RenderEngine GLES Backend");
- return renderengine::gl::GLESRenderEngine::create(hwcFormat, featureFlags, imageCacheSize);
+ return renderengine::gl::GLESRenderEngine::create(args);
}
ALOGE("UNKNOWN BackendType: %s, create GLES RenderEngine.", prop);
- return renderengine::gl::GLESRenderEngine::create(hwcFormat, featureFlags, imageCacheSize);
+ return renderengine::gl::GLESRenderEngine::create(args);
}
RenderEngine::~RenderEngine() = default;
namespace impl {
-RenderEngine::RenderEngine(uint32_t featureFlags) : mFeatureFlags(featureFlags) {}
+RenderEngine::RenderEngine(const RenderEngineCreationArgs& args) : mArgs(args) {}
RenderEngine::~RenderEngine() = default;
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 056df5e..2139acb 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -46,8 +46,10 @@
#include "GLExtensions.h"
#include "GLFramebuffer.h"
#include "GLImage.h"
+#include "GLShadowVertexGenerator.h"
#include "Program.h"
#include "ProgramCache.h"
+#include "filters/BlurFilter.h"
extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
@@ -145,52 +147,6 @@
return NAME_NOT_FOUND;
}
-class EGLAttributeVector {
- struct Attribute;
- class Adder;
- friend class Adder;
- KeyedVector<Attribute, EGLint> mList;
- struct Attribute {
- Attribute() : v(0){};
- explicit Attribute(EGLint v) : v(v) {}
- EGLint v;
- bool operator<(const Attribute& other) const {
- // this places EGL_NONE at the end
- EGLint lhs(v);
- EGLint rhs(other.v);
- if (lhs == EGL_NONE) lhs = 0x7FFFFFFF;
- if (rhs == EGL_NONE) rhs = 0x7FFFFFFF;
- return lhs < rhs;
- }
- };
- class Adder {
- friend class EGLAttributeVector;
- EGLAttributeVector& v;
- EGLint attribute;
- Adder(EGLAttributeVector& v, EGLint attribute) : v(v), attribute(attribute) {}
-
- public:
- void operator=(EGLint value) {
- if (attribute != EGL_NONE) {
- v.mList.add(Attribute(attribute), value);
- }
- }
- operator EGLint() const { return v.mList[attribute]; }
- };
-
-public:
- EGLAttributeVector() { mList.add(Attribute(EGL_NONE), EGL_NONE); }
- void remove(EGLint attribute) {
- if (attribute != EGL_NONE) {
- mList.removeItem(Attribute(attribute));
- }
- }
- Adder operator[](EGLint attribute) { return Adder(*this, attribute); }
- EGLint operator[](EGLint attribute) const { return mList[attribute]; }
- // cast-operator to (EGLint const*)
- operator EGLint const*() const { return &mList.keyAt(0).v; }
-};
-
static status_t selectEGLConfig(EGLDisplay display, EGLint format, EGLint renderableType,
EGLConfig* config) {
// select our EGLConfig. It must support EGL_RECORDABLE_ANDROID if
@@ -199,16 +155,33 @@
EGLint wantedAttribute;
EGLint wantedAttributeValue;
- EGLAttributeVector attribs;
+ std::vector<EGLint> attribs;
if (renderableType) {
- attribs[EGL_RENDERABLE_TYPE] = renderableType;
- attribs[EGL_RECORDABLE_ANDROID] = EGL_TRUE;
- attribs[EGL_SURFACE_TYPE] = EGL_WINDOW_BIT | EGL_PBUFFER_BIT;
- attribs[EGL_FRAMEBUFFER_TARGET_ANDROID] = EGL_TRUE;
- attribs[EGL_RED_SIZE] = 8;
- attribs[EGL_GREEN_SIZE] = 8;
- attribs[EGL_BLUE_SIZE] = 8;
- attribs[EGL_ALPHA_SIZE] = 8;
+ const ui::PixelFormat pixelFormat = static_cast<ui::PixelFormat>(format);
+ const bool is1010102 = pixelFormat == ui::PixelFormat::RGBA_1010102;
+
+ // Default to 8 bits per channel.
+ const EGLint tmpAttribs[] = {
+ EGL_RENDERABLE_TYPE,
+ renderableType,
+ EGL_RECORDABLE_ANDROID,
+ EGL_TRUE,
+ EGL_SURFACE_TYPE,
+ EGL_WINDOW_BIT | EGL_PBUFFER_BIT,
+ EGL_FRAMEBUFFER_TARGET_ANDROID,
+ EGL_TRUE,
+ EGL_RED_SIZE,
+ is1010102 ? 10 : 8,
+ EGL_GREEN_SIZE,
+ is1010102 ? 10 : 8,
+ EGL_BLUE_SIZE,
+ is1010102 ? 10 : 8,
+ EGL_ALPHA_SIZE,
+ is1010102 ? 2 : 8,
+ EGL_NONE,
+ };
+ std::copy(tmpAttribs, tmpAttribs + (sizeof(tmpAttribs) / sizeof(EGLint)),
+ std::back_inserter(attribs));
wantedAttribute = EGL_NONE;
wantedAttributeValue = EGL_NONE;
} else {
@@ -217,7 +190,8 @@
wantedAttributeValue = format;
}
- err = selectConfigForAttribute(display, attribs, wantedAttribute, wantedAttributeValue, config);
+ err = selectConfigForAttribute(display, attribs.data(), wantedAttribute, wantedAttributeValue,
+ config);
if (err == NO_ERROR) {
EGLint caveat;
if (eglGetConfigAttrib(display, *config, EGL_CONFIG_CAVEAT, &caveat))
@@ -227,30 +201,39 @@
return err;
}
-std::unique_ptr<GLESRenderEngine> GLESRenderEngine::create(int hwcFormat, uint32_t featureFlags,
- uint32_t imageCacheSize) {
+std::unique_ptr<GLESRenderEngine> GLESRenderEngine::create(const RenderEngineCreationArgs& args) {
// initialize EGL for the default display
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (!eglInitialize(display, nullptr, nullptr)) {
LOG_ALWAYS_FATAL("failed to initialize EGL");
}
+ const auto eglVersion = eglQueryStringImplementationANDROID(display, EGL_VERSION);
+ if (!eglVersion) {
+ checkGlError(__FUNCTION__, __LINE__);
+ LOG_ALWAYS_FATAL("eglQueryStringImplementationANDROID(EGL_VERSION) failed");
+ }
+
+ const auto eglExtensions = eglQueryStringImplementationANDROID(display, EGL_EXTENSIONS);
+ if (!eglExtensions) {
+ checkGlError(__FUNCTION__, __LINE__);
+ LOG_ALWAYS_FATAL("eglQueryStringImplementationANDROID(EGL_EXTENSIONS) failed");
+ }
+
GLExtensions& extensions = GLExtensions::getInstance();
- extensions.initWithEGLStrings(eglQueryStringImplementationANDROID(display, EGL_VERSION),
- eglQueryStringImplementationANDROID(display, EGL_EXTENSIONS));
+ extensions.initWithEGLStrings(eglVersion, eglExtensions);
// The code assumes that ES2 or later is available if this extension is
// supported.
EGLConfig config = EGL_NO_CONFIG;
if (!extensions.hasNoConfigContext()) {
- config = chooseEglConfig(display, hwcFormat, /*logConfig*/ true);
+ config = chooseEglConfig(display, args.pixelFormat, /*logConfig*/ true);
}
- bool useContextPriority = extensions.hasContextPriority() &&
- (featureFlags & RenderEngine::USE_HIGH_PRIORITY_CONTEXT);
+ bool useContextPriority =
+ extensions.hasContextPriority() && args.contextPriority == ContextPriority::HIGH;
EGLContext protectedContext = EGL_NO_CONTEXT;
- if ((featureFlags & RenderEngine::ENABLE_PROTECTED_CONTEXT) &&
- extensions.hasProtectedContent()) {
+ if (args.enableProtectedContext && extensions.hasProtectedContent()) {
protectedContext = createEglContext(display, config, nullptr, useContextPriority,
Protection::PROTECTED);
ALOGE_IF(protectedContext == EGL_NO_CONTEXT, "Can't create protected context");
@@ -262,26 +245,30 @@
// if can't create a GL context, we can only abort.
LOG_ALWAYS_FATAL_IF(ctxt == EGL_NO_CONTEXT, "EGLContext creation failed");
- EGLSurface dummy = EGL_NO_SURFACE;
+ EGLSurface stub = EGL_NO_SURFACE;
if (!extensions.hasSurfacelessContext()) {
- dummy = createDummyEglPbufferSurface(display, config, hwcFormat, Protection::UNPROTECTED);
- LOG_ALWAYS_FATAL_IF(dummy == EGL_NO_SURFACE, "can't create dummy pbuffer");
+ stub = createStubEglPbufferSurface(display, config, args.pixelFormat,
+ Protection::UNPROTECTED);
+ LOG_ALWAYS_FATAL_IF(stub == EGL_NO_SURFACE, "can't create stub pbuffer");
}
- EGLBoolean success = eglMakeCurrent(display, dummy, dummy, ctxt);
- LOG_ALWAYS_FATAL_IF(!success, "can't make dummy pbuffer current");
+ EGLBoolean success = eglMakeCurrent(display, stub, stub, ctxt);
+ LOG_ALWAYS_FATAL_IF(!success, "can't make stub pbuffer current");
extensions.initWithGLStrings(glGetString(GL_VENDOR), glGetString(GL_RENDERER),
glGetString(GL_VERSION), glGetString(GL_EXTENSIONS));
- EGLSurface protectedDummy = EGL_NO_SURFACE;
+ EGLSurface protectedStub = EGL_NO_SURFACE;
if (protectedContext != EGL_NO_CONTEXT && !extensions.hasSurfacelessContext()) {
- protectedDummy =
- createDummyEglPbufferSurface(display, config, hwcFormat, Protection::PROTECTED);
- ALOGE_IF(protectedDummy == EGL_NO_SURFACE, "can't create protected dummy pbuffer");
+ protectedStub = createStubEglPbufferSurface(display, config, args.pixelFormat,
+ Protection::PROTECTED);
+ ALOGE_IF(protectedStub == EGL_NO_SURFACE, "can't create protected stub pbuffer");
}
// now figure out what version of GL did we actually get
GlesVersion version = parseGlesVersion(extensions.getVersion());
+ LOG_ALWAYS_FATAL_IF(args.supportsBackgroundBlur && version < GLES_VERSION_3_0,
+ "Blurs require OpenGL ES 3.0. Please unset ro.surface_flinger.supports_background_blur");
+
// initialize the renderer while GL is current
std::unique_ptr<GLESRenderEngine> engine;
switch (version) {
@@ -291,9 +278,8 @@
break;
case GLES_VERSION_2_0:
case GLES_VERSION_3_0:
- engine = std::make_unique<GLESRenderEngine>(featureFlags, display, config, ctxt, dummy,
- protectedContext, protectedDummy,
- imageCacheSize);
+ engine = std::make_unique<GLESRenderEngine>(args, display, config, ctxt, stub,
+ protectedContext, protectedStub);
break;
}
@@ -347,20 +333,20 @@
return config;
}
-GLESRenderEngine::GLESRenderEngine(uint32_t featureFlags, EGLDisplay display, EGLConfig config,
- EGLContext ctxt, EGLSurface dummy, EGLContext protectedContext,
- EGLSurface protectedDummy, uint32_t imageCacheSize)
- : renderengine::impl::RenderEngine(featureFlags),
+GLESRenderEngine::GLESRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display,
+ EGLConfig config, EGLContext ctxt, EGLSurface stub,
+ EGLContext protectedContext, EGLSurface protectedStub)
+ : renderengine::impl::RenderEngine(args),
mEGLDisplay(display),
mEGLConfig(config),
mEGLContext(ctxt),
- mDummySurface(dummy),
+ mStubSurface(stub),
mProtectedEGLContext(protectedContext),
- mProtectedDummySurface(protectedDummy),
+ mProtectedStubSurface(protectedStub),
mVpWidth(0),
mVpHeight(0),
- mFramebufferImageCacheSize(imageCacheSize),
- mUseColorManagement(featureFlags & USE_COLOR_MANAGEMENT) {
+ mFramebufferImageCacheSize(args.imageCacheSize),
+ mUseColorManagement(args.useColorManagement) {
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
glGetIntegerv(GL_MAX_VIEWPORT_DIMS, mMaxViewportDims);
@@ -369,24 +355,15 @@
// Initialize protected EGL Context.
if (mProtectedEGLContext != EGL_NO_CONTEXT) {
- EGLBoolean success = eglMakeCurrent(display, mProtectedDummySurface, mProtectedDummySurface,
+ EGLBoolean success = eglMakeCurrent(display, mProtectedStubSurface, mProtectedStubSurface,
mProtectedEGLContext);
ALOGE_IF(!success, "can't make protected context current");
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glPixelStorei(GL_PACK_ALIGNMENT, 4);
- success = eglMakeCurrent(display, mDummySurface, mDummySurface, mEGLContext);
+ success = eglMakeCurrent(display, mStubSurface, mStubSurface, mEGLContext);
LOG_ALWAYS_FATAL_IF(!success, "can't make default context current");
}
- const uint16_t protTexData[] = {0};
- glGenTextures(1, &mProtectedTexName);
- glBindTexture(GL_TEXTURE_2D, mProtectedTexName);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, protTexData);
-
// mColorBlindnessCorrection = M;
if (mUseColorManagement) {
@@ -423,6 +400,12 @@
mTraceGpuCompletion = true;
mFlushTracer = std::make_unique<FlushTracer>(this);
}
+
+ if (args.supportsBackgroundBlur) {
+ mBlurFilter = new BlurFilter(*this);
+ checkErrors("BlurFilter creation");
+ }
+
mImageManager = std::make_unique<ImageManager>(this);
mImageManager->initThread();
mDrawingBuffer = createFramebuffer();
@@ -459,11 +442,8 @@
void GLESRenderEngine::primeCache() const {
ProgramCache::getInstance().primeCache(mInProtectedContext ? mProtectedEGLContext : mEGLContext,
- mFeatureFlags & USE_COLOR_MANAGEMENT);
-}
-
-bool GLESRenderEngine::isCurrent() const {
- return mEGLDisplay == eglGetCurrentDisplay() && mEGLContext == eglGetCurrentContext();
+ mArgs.useColorManagement,
+ mArgs.precacheToneMapperShaderOnly);
}
base::unique_fd GLESRenderEngine::flush() {
@@ -572,7 +552,10 @@
float alpha) {
size_t c;
Rect const* r = region.getArray(&c);
- Mesh mesh(Mesh::TRIANGLES, c * 6, 2);
+ Mesh mesh = Mesh::Builder()
+ .setPrimitive(Mesh::TRIANGLES)
+ .setVertices(c * 6 /* count */, 2 /* size */)
+ .build();
Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>());
for (size_t i = 0; i < c; i++, r++) {
position[i * 6 + 0].x = r->left;
@@ -793,34 +776,66 @@
// top rectangle and the bottom rectangle, and turn off blending for the middle rectangle.
FloatRect bounds = layer.geometry.roundedCornersCrop;
- // Firstly, we need to convert the coordination from layer native coordination space to
- // device coordination space.
- const auto transformMatrix = display.globalTransform * layer.geometry.positionTransform;
- const vec4 leftTopCoordinate(bounds.left, bounds.top, 1.0, 1.0);
- const vec4 rightBottomCoordinate(bounds.right, bounds.bottom, 1.0, 1.0);
- const vec4 leftTopCoordinateInBuffer = transformMatrix * leftTopCoordinate;
- const vec4 rightBottomCoordinateInBuffer = transformMatrix * rightBottomCoordinate;
- bounds = FloatRect(leftTopCoordinateInBuffer[0], leftTopCoordinateInBuffer[1],
- rightBottomCoordinateInBuffer[0], rightBottomCoordinateInBuffer[1]);
-
- // Secondly, if the display is rotated, we need to undo the rotation on coordination and
- // align the (left, top) and (right, bottom) coordination with the device coordination
- // space.
+ // Explicitly compute the transform from the clip rectangle to the physical
+ // display. Normally, this is done in glViewport but we explicitly compute
+ // it here so that we can get the scissor bounds correct.
+ const Rect& source = display.clip;
+ const Rect& destination = display.physicalDisplay;
+ // Here we compute the following transform:
+ // 1. Translate the top left corner of the source clip to (0, 0)
+ // 2. Rotate the clip rectangle about the origin in accordance with the
+ // orientation flag
+ // 3. Translate the top left corner back to the origin.
+ // 4. Scale the clip rectangle to the destination rectangle dimensions
+ // 5. Translate the top left corner to the destination rectangle's top left
+ // corner.
+ const mat4 translateSource = mat4::translate(vec4(-source.left, -source.top, 0, 1));
+ mat4 rotation;
+ int displacementX = 0;
+ int displacementY = 0;
+ float destinationWidth = static_cast<float>(destination.getWidth());
+ float destinationHeight = static_cast<float>(destination.getHeight());
+ float sourceWidth = static_cast<float>(source.getWidth());
+ float sourceHeight = static_cast<float>(source.getHeight());
+ const float rot90InRadians = 2.0f * static_cast<float>(M_PI) / 4.0f;
switch (display.orientation) {
case ui::Transform::ROT_90:
- std::swap(bounds.left, bounds.right);
+ rotation = mat4::rotate(rot90InRadians, vec3(0, 0, 1));
+ displacementX = source.getHeight();
+ std::swap(sourceHeight, sourceWidth);
break;
case ui::Transform::ROT_180:
- std::swap(bounds.left, bounds.right);
- std::swap(bounds.top, bounds.bottom);
+ rotation = mat4::rotate(rot90InRadians * 2.0f, vec3(0, 0, 1));
+ displacementY = source.getHeight();
+ displacementX = source.getWidth();
break;
case ui::Transform::ROT_270:
- std::swap(bounds.top, bounds.bottom);
+ rotation = mat4::rotate(rot90InRadians * 3.0f, vec3(0, 0, 1));
+ displacementY = source.getWidth();
+ std::swap(sourceHeight, sourceWidth);
break;
default:
break;
}
+ const mat4 intermediateTranslation = mat4::translate(vec4(displacementX, displacementY, 0, 1));
+ const mat4 scale = mat4::scale(
+ vec4(destinationWidth / sourceWidth, destinationHeight / sourceHeight, 1, 1));
+ const mat4 translateDestination =
+ mat4::translate(vec4(destination.left, destination.top, 0, 1));
+ const mat4 globalTransform =
+ translateDestination * scale * intermediateTranslation * rotation * translateSource;
+
+ const mat4 transformMatrix = globalTransform * layer.geometry.positionTransform;
+ const vec4 leftTopCoordinate(bounds.left, bounds.top, 1.0, 1.0);
+ const vec4 rightBottomCoordinate(bounds.right, bounds.bottom, 1.0, 1.0);
+ const vec4 leftTopCoordinateInBuffer = transformMatrix * leftTopCoordinate;
+ const vec4 rightBottomCoordinateInBuffer = transformMatrix * rightBottomCoordinate;
+ bounds = FloatRect(std::min(leftTopCoordinateInBuffer[0], rightBottomCoordinateInBuffer[0]),
+ std::min(leftTopCoordinateInBuffer[1], rightBottomCoordinateInBuffer[1]),
+ std::max(leftTopCoordinateInBuffer[0], rightBottomCoordinateInBuffer[0]),
+ std::max(leftTopCoordinateInBuffer[1], rightBottomCoordinateInBuffer[1]));
+
// Finally, we cut the layer into 3 parts, with top and bottom parts having rounded corners
// and the middle part without rounded corners.
const int32_t radius = ceil(layer.geometry.roundedCornersRadius);
@@ -832,11 +847,14 @@
drawMesh(mesh);
// The middle part of the layer can turn off blending.
- const Rect middleRect(bounds.left, bounds.top + radius, bounds.right, bounds.bottom - radius);
- setScissor(middleRect);
- mState.cornerRadius = 0.0;
- disableBlending();
- drawMesh(mesh);
+ if (topRect.bottom < bottomRect.top) {
+ const Rect middleRect(bounds.left, bounds.top + radius, bounds.right,
+ bounds.bottom - radius);
+ setScissor(middleRect);
+ mState.cornerRadius = 0.0;
+ disableBlending();
+ drawMesh(mesh);
+ }
disableScissor();
}
@@ -856,26 +874,52 @@
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureName, 0);
uint32_t glStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
-
ALOGE_IF(glStatus != GL_FRAMEBUFFER_COMPLETE_OES, "glCheckFramebufferStatusOES error %d",
glStatus);
return glStatus == GL_FRAMEBUFFER_COMPLETE_OES ? NO_ERROR : BAD_VALUE;
}
-void GLESRenderEngine::unbindFrameBuffer(Framebuffer* /* framebuffer */) {
+void GLESRenderEngine::unbindFrameBuffer(Framebuffer* /*framebuffer*/) {
ATRACE_CALL();
// back to main framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
+bool GLESRenderEngine::cleanupPostRender() {
+ ATRACE_CALL();
+
+ if (mPriorResourcesCleaned ||
+ (mLastDrawFence != nullptr && mLastDrawFence->getStatus() != Fence::Status::Signaled)) {
+ // If we don't have a prior frame needing cleanup, then don't do anything.
+ return false;
+ }
+
+ // Bind the texture to placeholder so that backing image data can be freed.
+ GLFramebuffer* glFramebuffer = static_cast<GLFramebuffer*>(getFramebufferForDrawing());
+ glFramebuffer->allocateBuffers(1, 1, mPlaceholderDrawBuffer);
+ // Release the cached fence here, so that we don't churn reallocations when
+ // we could no-op repeated calls of this method instead.
+ mLastDrawFence = nullptr;
+ mPriorResourcesCleaned = true;
+ return true;
+}
+
void GLESRenderEngine::checkErrors() const {
+ checkErrors(nullptr);
+}
+
+void GLESRenderEngine::checkErrors(const char* tag) const {
do {
// there could be more than one error flag
GLenum error = glGetError();
if (error == GL_NO_ERROR) break;
- ALOGE("GL error 0x%04x", int(error));
+ if (tag == nullptr) {
+ ALOGE("GL error 0x%04x", int(error));
+ } else {
+ ALOGE("GL error: %s -> 0x%04x", tag, int(error));
+ }
} while (true);
}
@@ -890,7 +934,7 @@
if (useProtectedContext && mProtectedEGLContext == EGL_NO_CONTEXT) {
return false;
}
- const EGLSurface surface = useProtectedContext ? mProtectedDummySurface : mDummySurface;
+ const EGLSurface surface = useProtectedContext ? mProtectedStubSurface : mStubSurface;
const EGLContext context = useProtectedContext ? mProtectedEGLContext : mEGLContext;
const bool success = eglMakeCurrent(mEGLDisplay, surface, surface, context) == EGL_TRUE;
if (success) {
@@ -937,7 +981,7 @@
}
status_t GLESRenderEngine::drawLayers(const DisplaySettings& display,
- const std::vector<LayerSettings>& layers,
+ const std::vector<const LayerSettings*>& layers,
ANativeWindowBuffer* const buffer,
const bool useFramebufferCache, base::unique_fd&& bufferFence,
base::unique_fd* drawFence) {
@@ -947,9 +991,13 @@
return NO_ERROR;
}
- if (bufferFence.get() >= 0 && !waitFence(std::move(bufferFence))) {
- ATRACE_NAME("Waiting before draw");
- sync_wait(bufferFence.get(), -1);
+ if (bufferFence.get() >= 0) {
+ // Duplicate the fence for passing to waitFence.
+ base::unique_fd bufferFenceDup(dup(bufferFence.get()));
+ if (bufferFenceDup < 0 || !waitFence(std::move(bufferFenceDup))) {
+ ATRACE_NAME("Waiting before draw");
+ sync_wait(bufferFence.get(), -1);
+ }
}
if (buffer == nullptr) {
@@ -957,13 +1005,38 @@
return BAD_VALUE;
}
- BindNativeBufferAsFramebuffer fbo(*this, buffer, useFramebufferCache);
+ std::unique_ptr<BindNativeBufferAsFramebuffer> fbo;
+ // Gathering layers that requested blur, we'll need them to decide when to render to an
+ // offscreen buffer, and when to render to the native buffer.
+ std::deque<const LayerSettings*> blurLayers;
+ if (CC_LIKELY(mBlurFilter != nullptr)) {
+ for (auto layer : layers) {
+ if (layer->backgroundBlurRadius > 0) {
+ blurLayers.push_back(layer);
+ }
+ }
+ }
+ const auto blurLayersSize = blurLayers.size();
- if (fbo.getStatus() != NO_ERROR) {
- ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).",
- buffer->handle);
- checkErrors();
- return fbo.getStatus();
+ if (blurLayersSize == 0) {
+ fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this, buffer, useFramebufferCache);
+ if (fbo->getStatus() != NO_ERROR) {
+ ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).",
+ buffer->handle);
+ checkErrors();
+ return fbo->getStatus();
+ }
+ setViewportAndProjection(display.physicalDisplay, display.clip);
+ } else {
+ setViewportAndProjection(display.physicalDisplay, display.clip);
+ auto status =
+ mBlurFilter->setAsDrawTarget(display, blurLayers.front()->backgroundBlurRadius);
+ if (status != NO_ERROR) {
+ ALOGE("Failed to prepare blur filter! Aborting GPU composition for buffer (%p).",
+ buffer->handle);
+ checkErrors();
+ return status;
+ }
}
// clear the entire buffer, sometimes when we reuse buffers we'd persist
@@ -973,53 +1046,96 @@
// opaque layers.
clearWithColor(0.0, 0.0, 0.0, 0.0);
- setViewportAndProjection(display.physicalDisplay, display.clip);
-
setOutputDataSpace(display.outputDataspace);
setDisplayMaxLuminance(display.maxLuminance);
- mat4 projectionMatrix = mState.projectionMatrix * display.globalTransform;
- mState.projectionMatrix = projectionMatrix;
+ const mat4 projectionMatrix =
+ ui::Transform(display.orientation).asMatrix4() * mState.projectionMatrix;
if (!display.clearRegion.isEmpty()) {
glDisable(GL_BLEND);
fillRegionWithColor(display.clearRegion, 0.0, 0.0, 0.0, 1.0);
}
- Mesh mesh(Mesh::TRIANGLE_FAN, 4, 2, 2);
- for (auto layer : layers) {
- mState.projectionMatrix = projectionMatrix * layer.geometry.positionTransform;
+ Mesh mesh = Mesh::Builder()
+ .setPrimitive(Mesh::TRIANGLE_FAN)
+ .setVertices(4 /* count */, 2 /* size */)
+ .setTexCoords(2 /* size */)
+ .setCropCoords(2 /* size */)
+ .build();
+ for (auto const layer : layers) {
+ if (blurLayers.size() > 0 && blurLayers.front() == layer) {
+ blurLayers.pop_front();
- const FloatRect bounds = layer.geometry.boundaries;
+ auto status = mBlurFilter->prepare();
+ if (status != NO_ERROR) {
+ ALOGE("Failed to render blur effect! Aborting GPU composition for buffer (%p).",
+ buffer->handle);
+ checkErrors("Can't render first blur pass");
+ return status;
+ }
+
+ if (blurLayers.size() == 0) {
+ // Done blurring, time to bind the native FBO and render our blur onto it.
+ fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this, buffer,
+ useFramebufferCache);
+ status = fbo->getStatus();
+ setViewportAndProjection(display.physicalDisplay, display.clip);
+ } else {
+ // There's still something else to blur, so let's keep rendering to our FBO
+ // instead of to the display.
+ status = mBlurFilter->setAsDrawTarget(display,
+ blurLayers.front()->backgroundBlurRadius);
+ }
+ if (status != NO_ERROR) {
+ ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).",
+ buffer->handle);
+ checkErrors("Can't bind native framebuffer");
+ return status;
+ }
+
+ status = mBlurFilter->render(blurLayersSize > 1);
+ if (status != NO_ERROR) {
+ ALOGE("Failed to render blur effect! Aborting GPU composition for buffer (%p).",
+ buffer->handle);
+ checkErrors("Can't render blur filter");
+ return status;
+ }
+ }
+
+ mState.maxMasteringLuminance = layer->source.buffer.maxMasteringLuminance;
+ mState.maxContentLuminance = layer->source.buffer.maxContentLuminance;
+ mState.projectionMatrix = projectionMatrix * layer->geometry.positionTransform;
+
+ const FloatRect bounds = layer->geometry.boundaries;
Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>());
position[0] = vec2(bounds.left, bounds.top);
position[1] = vec2(bounds.left, bounds.bottom);
position[2] = vec2(bounds.right, bounds.bottom);
position[3] = vec2(bounds.right, bounds.top);
- setupLayerCropping(layer, mesh);
- setColorTransform(display.colorTransform * layer.colorTransform);
+ setupLayerCropping(*layer, mesh);
+ setColorTransform(display.colorTransform * layer->colorTransform);
bool usePremultipliedAlpha = true;
bool disableTexture = true;
bool isOpaque = false;
-
- if (layer.source.buffer.buffer != nullptr) {
+ if (layer->source.buffer.buffer != nullptr) {
disableTexture = false;
- isOpaque = layer.source.buffer.isOpaque;
+ isOpaque = layer->source.buffer.isOpaque;
- sp<GraphicBuffer> gBuf = layer.source.buffer.buffer;
- bindExternalTextureBuffer(layer.source.buffer.textureName, gBuf,
- layer.source.buffer.fence);
+ sp<GraphicBuffer> gBuf = layer->source.buffer.buffer;
+ bindExternalTextureBuffer(layer->source.buffer.textureName, gBuf,
+ layer->source.buffer.fence);
- usePremultipliedAlpha = layer.source.buffer.usePremultipliedAlpha;
- Texture texture(Texture::TEXTURE_EXTERNAL, layer.source.buffer.textureName);
- mat4 texMatrix = layer.source.buffer.textureTransform;
+ usePremultipliedAlpha = layer->source.buffer.usePremultipliedAlpha;
+ Texture texture(Texture::TEXTURE_EXTERNAL, layer->source.buffer.textureName);
+ mat4 texMatrix = layer->source.buffer.textureTransform;
texture.setMatrix(texMatrix.asArray());
- texture.setFiltering(layer.source.buffer.useTextureFiltering);
+ texture.setFiltering(layer->source.buffer.useTextureFiltering);
texture.setDimensions(gBuf->getWidth(), gBuf->getHeight());
- setSourceY410BT2020(layer.source.buffer.isY410BT2020);
+ setSourceY410BT2020(layer->source.buffer.isY410BT2020);
renderengine::Mesh::VertexArray<vec2> texCoords(mesh.getTexCoordArray<vec2>());
texCoords[0] = vec2(0.0, 0.0);
@@ -1029,28 +1145,32 @@
setupLayerTexturing(texture);
}
- const half3 solidColor = layer.source.solidColor;
- const half4 color = half4(solidColor.r, solidColor.g, solidColor.b, layer.alpha);
+ const half3 solidColor = layer->source.solidColor;
+ const half4 color = half4(solidColor.r, solidColor.g, solidColor.b, layer->alpha);
// Buffer sources will have a black solid color ignored in the shader,
// so in that scenario the solid color passed here is arbitrary.
setupLayerBlending(usePremultipliedAlpha, isOpaque, disableTexture, color,
- layer.geometry.roundedCornersRadius);
- if (layer.disableBlending) {
+ layer->geometry.roundedCornersRadius);
+ if (layer->disableBlending) {
glDisable(GL_BLEND);
}
- setSourceDataSpace(layer.sourceDataspace);
+ setSourceDataSpace(layer->sourceDataspace);
+ if (layer->shadow.length > 0.0f) {
+ handleShadow(layer->geometry.boundaries, layer->geometry.roundedCornersRadius,
+ layer->shadow);
+ }
// We only want to do a special handling for rounded corners when having rounded corners
// is the only reason it needs to turn on blending, otherwise, we handle it like the
// usual way since it needs to turn on blending anyway.
- if (layer.geometry.roundedCornersRadius > 0.0 && color.a >= 1.0f && isOpaque) {
- handleRoundedCorners(display, layer, mesh);
+ else if (layer->geometry.roundedCornersRadius > 0.0 && color.a >= 1.0f && isOpaque) {
+ handleRoundedCorners(display, *layer, mesh);
} else {
drawMesh(mesh);
}
// Cleanup if there's a buffer source
- if (layer.source.buffer.buffer != nullptr) {
+ if (layer->source.buffer.buffer != nullptr) {
disableBlending();
setSourceY410BT2020(false);
disableTexturing();
@@ -1071,39 +1191,18 @@
// us bad parameters, or we messed up our shader generation).
return INVALID_OPERATION;
}
+ mLastDrawFence = nullptr;
+ } else {
+ // The caller takes ownership of drawFence, so we need to duplicate the
+ // fd here.
+ mLastDrawFence = new Fence(dup(drawFence->get()));
}
+ mPriorResourcesCleaned = false;
checkErrors();
return NO_ERROR;
}
-void GLESRenderEngine::setViewportAndProjection(size_t vpw, size_t vph, Rect sourceCrop,
- ui::Transform::orientation_flags rotation) {
- setViewportAndProjection(Rect(vpw, vph), sourceCrop);
-
- if (rotation == ui::Transform::ROT_0) {
- return;
- }
-
- // Apply custom rotation to the projection.
- float rot90InRadians = 2.0f * static_cast<float>(M_PI) / 4.0f;
- mat4 m = mState.projectionMatrix;
- switch (rotation) {
- case ui::Transform::ROT_90:
- m = mat4::rotate(rot90InRadians, vec3(0, 0, 1)) * m;
- break;
- case ui::Transform::ROT_180:
- m = mat4::rotate(rot90InRadians * 2.0f, vec3(0, 0, 1)) * m;
- break;
- case ui::Transform::ROT_270:
- m = mat4::rotate(rot90InRadians * 3.0f, vec3(0, 0, 1)) * m;
- break;
- default:
- break;
- }
- mState.projectionMatrix = m;
-}
-
void GLESRenderEngine::setViewportAndProjection(Rect viewport, Rect clip) {
ATRACE_CALL();
mVpWidth = viewport.getWidth();
@@ -1168,14 +1267,6 @@
mState.textureEnabled = true;
}
-void GLESRenderEngine::setupLayerBlackedOut() {
- glBindTexture(GL_TEXTURE_2D, mProtectedTexName);
- Texture texture(Texture::TEXTURE_2D, mProtectedTexName);
- texture.setDimensions(1, 1); // FIXME: we should get that from somewhere
- mState.texture = texture;
- mState.textureEnabled = true;
-}
-
void GLESRenderEngine::setColorTransform(const mat4& colorTransform) {
mState.colorMatrix = colorTransform;
}
@@ -1217,13 +1308,23 @@
mesh.getByteStride(), mesh.getCropCoords());
}
+ if (mState.drawShadows) {
+ glEnableVertexAttribArray(Program::shadowColor);
+ glVertexAttribPointer(Program::shadowColor, mesh.getShadowColorSize(), GL_FLOAT, GL_FALSE,
+ mesh.getByteStride(), mesh.getShadowColor());
+
+ glEnableVertexAttribArray(Program::shadowParams);
+ glVertexAttribPointer(Program::shadowParams, mesh.getShadowParamsSize(), GL_FLOAT, GL_FALSE,
+ mesh.getByteStride(), mesh.getShadowParams());
+ }
+
+ Description managedState = mState;
// By default, DISPLAY_P3 is the only supported wide color output. However,
// when HDR content is present, hardware composer may be able to handle
// BT2020 data space, in that case, the output data space is set to be
// BT2020_HLG or BT2020_PQ respectively. In GPU fall back we need
// to respect this and convert non-HDR content to HDR format.
if (mUseColorManagement) {
- Description managedState = mState;
Dataspace inputStandard = static_cast<Dataspace>(mDataSpace & Dataspace::STANDARD_MASK);
Dataspace inputTransfer = static_cast<Dataspace>(mDataSpace & Dataspace::TRANSFER_MASK);
Dataspace outputStandard =
@@ -1314,27 +1415,25 @@
managedState.outputTransferFunction =
Description::dataSpaceToTransferFunction(outputTransfer);
}
+ }
- ProgramCache::getInstance().useProgram(mInProtectedContext ? mProtectedEGLContext
- : mEGLContext,
- managedState);
+ ProgramCache::getInstance().useProgram(mInProtectedContext ? mProtectedEGLContext : mEGLContext,
+ managedState);
- glDrawArrays(mesh.getPrimitive(), 0, mesh.getVertexCount());
-
- if (outputDebugPPMs) {
- static uint64_t managedColorFrameCount = 0;
- std::ostringstream out;
- out << "/data/texture_out" << managedColorFrameCount++;
- writePPM(out.str().c_str(), mVpWidth, mVpHeight);
- }
+ if (mState.drawShadows) {
+ glDrawElements(mesh.getPrimitive(), mesh.getIndexCount(), GL_UNSIGNED_SHORT,
+ mesh.getIndices());
} else {
- ProgramCache::getInstance().useProgram(mInProtectedContext ? mProtectedEGLContext
- : mEGLContext,
- mState);
-
glDrawArrays(mesh.getPrimitive(), 0, mesh.getVertexCount());
}
+ if (mUseColorManagement && outputDebugPPMs) {
+ static uint64_t managedColorFrameCount = 0;
+ std::ostringstream out;
+ out << "/data/texture_out" << managedColorFrameCount++;
+ writePPM(out.str().c_str(), mVpWidth, mVpHeight);
+ }
+
if (mesh.getTexCoordsSize()) {
glDisableVertexAttribArray(Program::texCoords);
}
@@ -1342,6 +1441,11 @@
if (mState.cornerRadius > 0.0f) {
glDisableVertexAttribArray(Program::cropCoords);
}
+
+ if (mState.drawShadows) {
+ glDisableVertexAttribArray(Program::shadowColor);
+ glDisableVertexAttribArray(Program::shadowParams);
+ }
}
size_t GLESRenderEngine::getMaxTextureSize() const {
@@ -1459,11 +1563,11 @@
return context;
}
-EGLSurface GLESRenderEngine::createDummyEglPbufferSurface(EGLDisplay display, EGLConfig config,
- int hwcFormat, Protection protection) {
- EGLConfig dummyConfig = config;
- if (dummyConfig == EGL_NO_CONFIG) {
- dummyConfig = chooseEglConfig(display, hwcFormat, /*logConfig*/ true);
+EGLSurface GLESRenderEngine::createStubEglPbufferSurface(EGLDisplay display, EGLConfig config,
+ int hwcFormat, Protection protection) {
+ EGLConfig stubConfig = config;
+ if (stubConfig == EGL_NO_CONFIG) {
+ stubConfig = chooseEglConfig(display, hwcFormat, /*logConfig*/ true);
}
std::vector<EGLint> attributes;
attributes.reserve(7);
@@ -1477,7 +1581,7 @@
}
attributes.push_back(EGL_NONE);
- return eglCreatePbufferSurface(display, dummyConfig, attributes.data());
+ return eglCreatePbufferSurface(display, stubConfig, attributes.data());
}
bool GLESRenderEngine::isHdrDataSpace(const Dataspace dataSpace) const {
@@ -1576,6 +1680,36 @@
}
}
+void GLESRenderEngine::handleShadow(const FloatRect& casterRect, float casterCornerRadius,
+ const ShadowSettings& settings) {
+ ATRACE_CALL();
+ const float casterZ = settings.length / 2.0f;
+ const GLShadowVertexGenerator shadows(casterRect, casterCornerRadius, casterZ,
+ settings.casterIsTranslucent, settings.ambientColor,
+ settings.spotColor, settings.lightPos,
+ settings.lightRadius);
+
+ // setup mesh for both shadows
+ Mesh mesh = Mesh::Builder()
+ .setPrimitive(Mesh::TRIANGLES)
+ .setVertices(shadows.getVertexCount(), 2 /* size */)
+ .setShadowAttrs()
+ .setIndices(shadows.getIndexCount())
+ .build();
+
+ Mesh::VertexArray<vec2> position = mesh.getPositionArray<vec2>();
+ Mesh::VertexArray<vec4> shadowColor = mesh.getShadowColorArray<vec4>();
+ Mesh::VertexArray<vec3> shadowParams = mesh.getShadowParamsArray<vec3>();
+ shadows.fillVertices(position, shadowColor, shadowParams);
+ shadows.fillIndices(mesh.getIndicesArray());
+
+ mState.cornerRadius = 0.0f;
+ mState.drawShadows = true;
+ setupLayerTexturing(mShadowTexture.getTexture());
+ drawMesh(mesh);
+ mState.drawShadows = false;
+}
+
} // namespace gl
} // namespace renderengine
} // namespace android
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index dd60e50..61986ff 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -17,7 +17,6 @@
#ifndef SF_GLESRENDERENGINE_H_
#define SF_GLESRENDERENGINE_H_
-#include <stdint.h>
#include <condition_variable>
#include <deque>
#include <mutex>
@@ -32,6 +31,7 @@
#include <renderengine/RenderEngine.h>
#include <renderengine/private/Description.h>
#include <sys/types.h>
+#include "GLShadowTexture.h"
#include "ImageManager.h"
#define EGL_NO_CONFIG ((EGLConfig)0)
@@ -46,30 +46,18 @@
namespace gl {
class GLImage;
+class BlurFilter;
class GLESRenderEngine : public impl::RenderEngine {
public:
- static std::unique_ptr<GLESRenderEngine> create(int hwcFormat, uint32_t featureFlags,
- uint32_t imageCacheSize);
- static EGLConfig chooseEglConfig(EGLDisplay display, int format, bool logConfig);
+ static std::unique_ptr<GLESRenderEngine> create(const RenderEngineCreationArgs& args);
- GLESRenderEngine(uint32_t featureFlags, // See RenderEngine::FeatureFlag
- EGLDisplay display, EGLConfig config, EGLContext ctxt, EGLSurface dummy,
- EGLContext protectedContext, EGLSurface protectedDummy,
- uint32_t imageCacheSize);
+ GLESRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display, EGLConfig config,
+ EGLContext ctxt, EGLSurface stub, EGLContext protectedContext,
+ EGLSurface protectedStub);
~GLESRenderEngine() override EXCLUDES(mRenderingMutex);
- std::unique_ptr<Framebuffer> createFramebuffer() override;
- std::unique_ptr<Image> createImage() override;
-
void primeCache() const override;
- bool isCurrent() const override;
- base::unique_fd flush() override;
- bool finish() override;
- bool waitFence(base::unique_fd fenceFd) override;
- void clearWithColor(float red, float green, float blue, float alpha) override;
- void fillRegionWithColor(const Region& region, float red, float green, float blue,
- float alpha) override;
void genTextures(size_t count, uint32_t* names) override;
void deleteTextures(size_t count, uint32_t const* names) override;
void bindExternalTextureImage(uint32_t texName, const Image& image) override;
@@ -79,18 +67,17 @@
void unbindExternalTextureBuffer(uint64_t bufferId) EXCLUDES(mRenderingMutex);
status_t bindFrameBuffer(Framebuffer* framebuffer) override;
void unbindFrameBuffer(Framebuffer* framebuffer) override;
- void checkErrors() const override;
bool isProtected() const override { return mInProtectedContext; }
bool supportsProtectedContent() const override;
bool useProtectedContext(bool useProtectedContext) override;
- status_t drawLayers(const DisplaySettings& display, const std::vector<LayerSettings>& layers,
+ status_t drawLayers(const DisplaySettings& display,
+ const std::vector<const LayerSettings*>& layers,
ANativeWindowBuffer* buffer, const bool useFramebufferCache,
base::unique_fd&& bufferFence, base::unique_fd* drawFence) override;
+ bool cleanupPostRender() override;
- // internal to RenderEngine
EGLDisplay getEGLDisplay() const { return mEGLDisplay; }
- EGLConfig getEGLConfig() const { return mEGLConfig; }
// Creates an output image for rendering to
EGLImageKHR createFramebufferImageIfNeeded(ANativeWindowBuffer* nativeBuffer, bool isProtected,
bool useFramebufferCache)
@@ -112,27 +99,6 @@
Framebuffer* getFramebufferForDrawing() override;
void dump(std::string& result) override EXCLUDES(mRenderingMutex)
EXCLUDES(mFramebufferImageCacheMutex);
- void setViewportAndProjection(size_t vpw, size_t vph, Rect sourceCrop,
- ui::Transform::orientation_flags rotation) override;
- void setupLayerBlending(bool premultipliedAlpha, bool opaque, bool disableTexture,
- const half4& color, float cornerRadius) override;
- void setupLayerTexturing(const Texture& texture) override;
- void setupLayerBlackedOut() override;
- void setupFillWithColor(float r, float g, float b, float a) override;
- void setColorTransform(const mat4& colorTransform) override;
- void disableTexturing() override;
- void disableBlending() override;
- void setupCornerRadiusCropSize(float width, float height) override;
-
- // HDR and color management related functions and state
- void setSourceY410BT2020(bool enable) override;
- void setSourceDataSpace(ui::Dataspace source) override;
- void setOutputDataSpace(ui::Dataspace dataspace) override;
- void setDisplayMaxLuminance(const float maxLuminance) override;
-
- // drawing
- void drawMesh(const Mesh& mesh) override;
-
size_t getMaxTextureSize() const override;
size_t getMaxViewportDims() const override;
@@ -144,12 +110,17 @@
GLES_VERSION_3_0 = 0x30000,
};
+ static EGLConfig chooseEglConfig(EGLDisplay display, int format, bool logConfig);
static GlesVersion parseGlesVersion(const char* str);
static EGLContext createEglContext(EGLDisplay display, EGLConfig config,
EGLContext shareContext, bool useContextPriority,
Protection protection);
- static EGLSurface createDummyEglPbufferSurface(EGLDisplay display, EGLConfig config,
- int hwcFormat, Protection protection);
+ static EGLSurface createStubEglPbufferSurface(EGLDisplay display, EGLConfig config,
+ int hwcFormat, Protection protection);
+ std::unique_ptr<Framebuffer> createFramebuffer();
+ std::unique_ptr<Image> createImage();
+ void checkErrors() const;
+ void checkErrors(const char* tag) const;
void setScissor(const Rect& region);
void disableScissor();
bool waitSync(EGLSyncKHR sync, EGLint flags);
@@ -176,19 +147,43 @@
// blending is an expensive operation, we want to turn off blending when it's not necessary.
void handleRoundedCorners(const DisplaySettings& display, const LayerSettings& layer,
const Mesh& mesh);
+ base::unique_fd flush();
+ bool finish();
+ bool waitFence(base::unique_fd fenceFd);
+ void clearWithColor(float red, float green, float blue, float alpha);
+ void fillRegionWithColor(const Region& region, float red, float green, float blue, float alpha);
+ void handleShadow(const FloatRect& casterRect, float casterCornerRadius,
+ const ShadowSettings& shadowSettings);
+ void setupLayerBlending(bool premultipliedAlpha, bool opaque, bool disableTexture,
+ const half4& color, float cornerRadius);
+ void setupLayerTexturing(const Texture& texture);
+ void setupFillWithColor(float r, float g, float b, float a);
+ void setColorTransform(const mat4& colorTransform);
+ void disableTexturing();
+ void disableBlending();
+ void setupCornerRadiusCropSize(float width, float height);
+
+ // HDR and color management related functions and state
+ void setSourceY410BT2020(bool enable);
+ void setSourceDataSpace(ui::Dataspace source);
+ void setOutputDataSpace(ui::Dataspace dataspace);
+ void setDisplayMaxLuminance(const float maxLuminance);
+
+ // drawing
+ void drawMesh(const Mesh& mesh);
EGLDisplay mEGLDisplay;
EGLConfig mEGLConfig;
EGLContext mEGLContext;
- EGLSurface mDummySurface;
+ EGLSurface mStubSurface;
EGLContext mProtectedEGLContext;
- EGLSurface mProtectedDummySurface;
- GLuint mProtectedTexName;
+ EGLSurface mProtectedStubSurface;
GLint mMaxViewportDims[2];
GLint mMaxTextureSize;
GLuint mVpWidth;
GLuint mVpHeight;
Description mState;
+ GLShadowTexture mShadowTexture;
mat4 mSrgbToXyz;
mat4 mDisplayP3ToXyz;
@@ -236,6 +231,20 @@
std::mutex mRenderingMutex;
std::unique_ptr<Framebuffer> mDrawingBuffer;
+ // this is a 1x1 RGB buffer, but over-allocate in case a driver wants more
+ // memory or if it needs to satisfy alignment requirements. In this case:
+ // assume that each channel requires 4 bytes, and add 3 additional bytes to
+ // ensure that we align on a word. Allocating 16 bytes will provide a
+ // guarantee that we don't clobber memory.
+ uint32_t mPlaceholderDrawBuffer[4];
+ sp<Fence> mLastDrawFence;
+ // Store a separate boolean checking if prior resources were cleaned up, as
+ // devices that don't support native sync fences can't rely on a last draw
+ // fence that doesn't exist.
+ bool mPriorResourcesCleaned = true;
+
+ // Blur effect processor, only instantiated when a layer requests it.
+ BlurFilter* mBlurFilter = nullptr;
class FlushTracer {
public:
@@ -260,6 +269,9 @@
};
friend class FlushTracer;
friend class ImageManager;
+ friend class GLFramebuffer;
+ friend class BlurFilter;
+ friend class GenericProgram;
std::unique_ptr<FlushTracer> mFlushTracer;
std::unique_ptr<ImageManager> mImageManager = std::make_unique<ImageManager>(this);
};
diff --git a/libs/renderengine/gl/GLFramebuffer.cpp b/libs/renderengine/gl/GLFramebuffer.cpp
index 5fbb5ba..383486b 100644
--- a/libs/renderengine/gl/GLFramebuffer.cpp
+++ b/libs/renderengine/gl/GLFramebuffer.cpp
@@ -20,8 +20,8 @@
#include <GLES/gl.h>
#include <GLES/glext.h>
-#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
+#include <GLES3/gl3.h>
#include <gui/DebugEGLImageTracker.h>
#include <nativebase/nativebase.h>
#include <utils/Trace.h>
@@ -68,6 +68,47 @@
return true;
}
+void GLFramebuffer::allocateBuffers(uint32_t width, uint32_t height, void* data) {
+ ATRACE_CALL();
+
+ glBindTexture(GL_TEXTURE_2D, mTextureName);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
+
+ mBufferHeight = height;
+ mBufferWidth = width;
+ mEngine.checkErrors("Allocating Fbo texture");
+
+ bind();
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextureName, 0);
+ mStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ unbind();
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ if (mStatus != GL_FRAMEBUFFER_COMPLETE) {
+ ALOGE("Frame buffer is not complete. Error %d", mStatus);
+ }
+}
+
+void GLFramebuffer::bind() const {
+ glBindFramebuffer(GL_FRAMEBUFFER, mFramebufferName);
+}
+
+void GLFramebuffer::bindAsReadBuffer() const {
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, mFramebufferName);
+}
+
+void GLFramebuffer::bindAsDrawBuffer() const {
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mFramebufferName);
+}
+
+void GLFramebuffer::unbind() const {
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+}
+
} // namespace gl
} // namespace renderengine
} // namespace android
diff --git a/libs/renderengine/gl/GLFramebuffer.h b/libs/renderengine/gl/GLFramebuffer.h
index b7650bb..6757695 100644
--- a/libs/renderengine/gl/GLFramebuffer.h
+++ b/libs/renderengine/gl/GLFramebuffer.h
@@ -20,6 +20,7 @@
#include <EGL/egl.h>
#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
#include <renderengine/Framebuffer.h>
struct ANativeWindowBuffer;
@@ -33,21 +34,29 @@
class GLFramebuffer : public renderengine::Framebuffer {
public:
explicit GLFramebuffer(GLESRenderEngine& engine);
+ explicit GLFramebuffer(GLESRenderEngine& engine, bool multiTarget);
~GLFramebuffer() override;
bool setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected,
const bool useFramebufferCache) override;
+ void allocateBuffers(uint32_t width, uint32_t height, void* data = nullptr);
EGLImageKHR getEGLImage() const { return mEGLImage; }
uint32_t getTextureName() const { return mTextureName; }
uint32_t getFramebufferName() const { return mFramebufferName; }
int32_t getBufferHeight() const { return mBufferHeight; }
int32_t getBufferWidth() const { return mBufferWidth; }
+ GLenum getStatus() const { return mStatus; }
+ void bind() const;
+ void bindAsReadBuffer() const;
+ void bindAsDrawBuffer() const;
+ void unbind() const;
private:
GLESRenderEngine& mEngine;
EGLDisplay mEGLDisplay;
EGLImageKHR mEGLImage;
bool usingFramebufferCache = false;
+ GLenum mStatus = GL_FRAMEBUFFER_UNSUPPORTED;
uint32_t mTextureName, mFramebufferName;
int32_t mBufferHeight = 0;
diff --git a/libs/renderengine/gl/GLShadowTexture.cpp b/libs/renderengine/gl/GLShadowTexture.cpp
new file mode 100644
index 0000000..2423a34
--- /dev/null
+++ b/libs/renderengine/gl/GLShadowTexture.cpp
@@ -0,0 +1,52 @@
+/*
+ * 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 <GLES/gl.h>
+#include <GLES/glext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <GLES3/gl3.h>
+
+#include "GLShadowTexture.h"
+#include "GLSkiaShadowPort.h"
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+GLShadowTexture::GLShadowTexture() {
+ fillShadowTextureData(mTextureData, SHADOW_TEXTURE_WIDTH);
+
+ glGenTextures(1, &mName);
+ glBindTexture(GL_TEXTURE_2D, mName);
+ glTexImage2D(GL_TEXTURE_2D, 0 /* base image level */, GL_ALPHA, SHADOW_TEXTURE_WIDTH,
+ SHADOW_TEXTURE_HEIGHT, 0 /* border */, GL_ALPHA, GL_UNSIGNED_BYTE, mTextureData);
+ mTexture.init(Texture::TEXTURE_2D, mName);
+ mTexture.setFiltering(true);
+ mTexture.setDimensions(SHADOW_TEXTURE_WIDTH, 1);
+}
+
+GLShadowTexture::~GLShadowTexture() {
+ glDeleteTextures(1, &mName);
+}
+
+const Texture& GLShadowTexture::getTexture() {
+ return mTexture;
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/GLShadowTexture.h b/libs/renderengine/gl/GLShadowTexture.h
new file mode 100644
index 0000000..250a9d7
--- /dev/null
+++ b/libs/renderengine/gl/GLShadowTexture.h
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <renderengine/Texture.h>
+#include <cstdint>
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+class GLShadowTexture {
+public:
+ GLShadowTexture();
+ ~GLShadowTexture();
+
+ const Texture& getTexture();
+
+private:
+ static constexpr int SHADOW_TEXTURE_WIDTH = 128;
+ static constexpr int SHADOW_TEXTURE_HEIGHT = 1;
+
+ GLuint mName;
+ Texture mTexture;
+ uint8_t mTextureData[SHADOW_TEXTURE_WIDTH];
+};
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/GLShadowVertexGenerator.cpp b/libs/renderengine/gl/GLShadowVertexGenerator.cpp
new file mode 100644
index 0000000..3181f9b
--- /dev/null
+++ b/libs/renderengine/gl/GLShadowVertexGenerator.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2019 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 <renderengine/Mesh.h>
+
+#include <math/vec4.h>
+
+#include <ui/Rect.h>
+#include <ui/Transform.h>
+
+#include "GLShadowVertexGenerator.h"
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+GLShadowVertexGenerator::GLShadowVertexGenerator(const FloatRect& casterRect,
+ float casterCornerRadius, float casterZ,
+ bool casterIsTranslucent, const vec4& ambientColor,
+ const vec4& spotColor, const vec3& lightPosition,
+ float lightRadius) {
+ mDrawAmbientShadow = ambientColor.a > 0.f;
+ mDrawSpotShadow = spotColor.a > 0.f;
+
+ // Generate geometries and find number of vertices to generate
+ if (mDrawAmbientShadow) {
+ mAmbientShadowGeometry = getAmbientShadowGeometry(casterRect, casterCornerRadius, casterZ,
+ casterIsTranslucent, ambientColor);
+ mAmbientShadowVertexCount = getVertexCountForGeometry(*mAmbientShadowGeometry.get());
+ mAmbientShadowIndexCount = getIndexCountForGeometry(*mAmbientShadowGeometry.get());
+ } else {
+ mAmbientShadowVertexCount = 0;
+ mAmbientShadowIndexCount = 0;
+ }
+
+ if (mDrawSpotShadow) {
+ mSpotShadowGeometry =
+ getSpotShadowGeometry(casterRect, casterCornerRadius, casterZ, casterIsTranslucent,
+ spotColor, lightPosition, lightRadius);
+ mSpotShadowVertexCount = getVertexCountForGeometry(*mSpotShadowGeometry.get());
+ mSpotShadowIndexCount = getIndexCountForGeometry(*mSpotShadowGeometry.get());
+ } else {
+ mSpotShadowVertexCount = 0;
+ mSpotShadowIndexCount = 0;
+ }
+}
+
+size_t GLShadowVertexGenerator::getVertexCount() const {
+ return mAmbientShadowVertexCount + mSpotShadowVertexCount;
+}
+
+size_t GLShadowVertexGenerator::getIndexCount() const {
+ return mAmbientShadowIndexCount + mSpotShadowIndexCount;
+}
+
+void GLShadowVertexGenerator::fillVertices(Mesh::VertexArray<vec2>& position,
+ Mesh::VertexArray<vec4>& color,
+ Mesh::VertexArray<vec3>& params) const {
+ if (mDrawAmbientShadow) {
+ fillVerticesForGeometry(*mAmbientShadowGeometry.get(), mAmbientShadowVertexCount, position,
+ color, params);
+ }
+ if (mDrawSpotShadow) {
+ fillVerticesForGeometry(*mSpotShadowGeometry.get(), mSpotShadowVertexCount,
+ Mesh::VertexArray<vec2>(position, mAmbientShadowVertexCount),
+ Mesh::VertexArray<vec4>(color, mAmbientShadowVertexCount),
+ Mesh::VertexArray<vec3>(params, mAmbientShadowVertexCount));
+ }
+}
+
+void GLShadowVertexGenerator::fillIndices(uint16_t* indices) const {
+ if (mDrawAmbientShadow) {
+ fillIndicesForGeometry(*mAmbientShadowGeometry.get(), mAmbientShadowIndexCount,
+ 0 /* starting vertex offset */, indices);
+ }
+ if (mDrawSpotShadow) {
+ fillIndicesForGeometry(*mSpotShadowGeometry.get(), mSpotShadowIndexCount,
+ mAmbientShadowVertexCount /* starting vertex offset */,
+ &(indices[mAmbientShadowIndexCount]));
+ }
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/GLShadowVertexGenerator.h b/libs/renderengine/gl/GLShadowVertexGenerator.h
new file mode 100644
index 0000000..112f976
--- /dev/null
+++ b/libs/renderengine/gl/GLShadowVertexGenerator.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2019 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 <math/vec4.h>
+#include <ui/Rect.h>
+
+#include "GLSkiaShadowPort.h"
+
+namespace android {
+namespace renderengine {
+
+class Mesh;
+
+namespace gl {
+
+/**
+ * Generates gl attributes required to draw shadow spot and/or ambient shadows.
+ *
+ * Each shadow can support different colors. This class generates three vertex attributes for
+ * each shadow, its position, color and shadow params(offset and distance). These can be sent
+ * using a single glDrawElements call.
+ */
+class GLShadowVertexGenerator {
+public:
+ GLShadowVertexGenerator(const FloatRect& casterRect, float casterCornerRadius, float casterZ,
+ bool casterIsTranslucent, const vec4& ambientColor,
+ const vec4& spotColor, const vec3& lightPosition, float lightRadius);
+ ~GLShadowVertexGenerator() = default;
+
+ size_t getVertexCount() const;
+ size_t getIndexCount() const;
+ void fillVertices(Mesh::VertexArray<vec2>& position, Mesh::VertexArray<vec4>& color,
+ Mesh::VertexArray<vec3>& params) const;
+ void fillIndices(uint16_t* indices) const;
+
+private:
+ bool mDrawAmbientShadow;
+ std::unique_ptr<Geometry> mAmbientShadowGeometry;
+ int mAmbientShadowVertexCount = 0;
+ int mAmbientShadowIndexCount = 0;
+
+ bool mDrawSpotShadow;
+ std::unique_ptr<Geometry> mSpotShadowGeometry;
+ int mSpotShadowVertexCount = 0;
+ int mSpotShadowIndexCount = 0;
+};
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/GLSkiaShadowPort.cpp b/libs/renderengine/gl/GLSkiaShadowPort.cpp
new file mode 100644
index 0000000..da8b435
--- /dev/null
+++ b/libs/renderengine/gl/GLSkiaShadowPort.cpp
@@ -0,0 +1,656 @@
+/*
+ * Copyright 2019 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 <math/vec4.h>
+
+#include <renderengine/Mesh.h>
+
+#include <ui/Rect.h>
+#include <ui/Transform.h>
+
+#include <utils/Log.h>
+
+#include "GLSkiaShadowPort.h"
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+/**
+ * The shadow geometry logic and vertex generation code has been ported from skia shadow
+ * fast path OpenGL implementation to draw shadows around rects and rounded rects including
+ * circles.
+ *
+ * path: skia/src/gpu/GrRenderTargetContext.cpp GrRenderTargetContext::drawFastShadow
+ *
+ * Modifications made:
+ * - Switched to using std lib math functions
+ * - Fall off function is implemented in vertex shader rather than a shadow texture
+ * - Removed transformations applied on the caster rect since the caster will be in local
+ * coordinate space and will be transformed by the vertex shader.
+ */
+
+static inline float divide_and_pin(float numer, float denom, float min, float max) {
+ if (denom == 0.0f) return min;
+ return std::clamp(numer / denom, min, max);
+}
+
+static constexpr auto SK_ScalarSqrt2 = 1.41421356f;
+static constexpr auto kAmbientHeightFactor = 1.0f / 128.0f;
+static constexpr auto kAmbientGeomFactor = 64.0f;
+// Assuming that we have a light height of 600 for the spot shadow,
+// the spot values will reach their maximum at a height of approximately 292.3077.
+// We'll round up to 300 to keep it simple.
+static constexpr auto kMaxAmbientRadius = 300 * kAmbientHeightFactor * kAmbientGeomFactor;
+
+inline float AmbientBlurRadius(float height) {
+ return std::min(height * kAmbientHeightFactor * kAmbientGeomFactor, kMaxAmbientRadius);
+}
+inline float AmbientRecipAlpha(float height) {
+ return 1.0f + std::max(height * kAmbientHeightFactor, 0.0f);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Circle Data
+//
+// We have two possible cases for geometry for a circle:
+
+// In the case of a normal fill, we draw geometry for the circle as an octagon.
+static const uint16_t gFillCircleIndices[] = {
+ // enter the octagon
+ // clang-format off
+ 0, 1, 8, 1, 2, 8,
+ 2, 3, 8, 3, 4, 8,
+ 4, 5, 8, 5, 6, 8,
+ 6, 7, 8, 7, 0, 8,
+ // clang-format on
+};
+
+// For stroked circles, we use two nested octagons.
+static const uint16_t gStrokeCircleIndices[] = {
+ // enter the octagon
+ // clang-format off
+ 0, 1, 9, 0, 9, 8,
+ 1, 2, 10, 1, 10, 9,
+ 2, 3, 11, 2, 11, 10,
+ 3, 4, 12, 3, 12, 11,
+ 4, 5, 13, 4, 13, 12,
+ 5, 6, 14, 5, 14, 13,
+ 6, 7, 15, 6, 15, 14,
+ 7, 0, 8, 7, 8, 15,
+ // clang-format on
+};
+
+#define SK_ARRAY_COUNT(a) (sizeof(a) / sizeof((a)[0]))
+static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
+static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
+static const int kVertsPerStrokeCircle = 16;
+static const int kVertsPerFillCircle = 9;
+
+static int circle_type_to_vert_count(bool stroked) {
+ return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
+}
+
+static int circle_type_to_index_count(bool stroked) {
+ return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
+}
+
+static const uint16_t* circle_type_to_indices(bool stroked) {
+ return stroked ? gStrokeCircleIndices : gFillCircleIndices;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// RoundRect Data
+//
+// The geometry for a shadow roundrect is similar to a 9-patch:
+// ____________
+// |_|________|_|
+// | | | |
+// | | | |
+// | | | |
+// |_|________|_|
+// |_|________|_|
+//
+// However, each corner is rendered as a fan rather than a simple quad, as below. (The diagram
+// shows the upper part of the upper left corner. The bottom triangle would similarly be split
+// into two triangles.)
+// ________
+// |\ \ |
+// | \ \ |
+// | \\ |
+// | \|
+// --------
+//
+// The center of the fan handles the curve of the corner. For roundrects where the stroke width
+// is greater than the corner radius, the outer triangles blend from the curve to the straight
+// sides. Otherwise these triangles will be degenerate.
+//
+// In the case where the stroke width is greater than the corner radius and the
+// blur radius (overstroke), we add additional geometry to mark out the rectangle in the center.
+// This rectangle extends the coverage values of the center edges of the 9-patch.
+// ____________
+// |_|________|_|
+// | |\ ____ /| |
+// | | | | | |
+// | | |____| | |
+// |_|/______\|_|
+// |_|________|_|
+//
+// For filled rrects we reuse the stroke geometry but add an additional quad to the center.
+
+static const uint16_t gRRectIndices[] = {
+ // clang-format off
+ // overstroke quads
+ // we place this at the beginning so that we can skip these indices when rendering as filled
+ 0, 6, 25, 0, 25, 24,
+ 6, 18, 27, 6, 27, 25,
+ 18, 12, 26, 18, 26, 27,
+ 12, 0, 24, 12, 24, 26,
+
+ // corners
+ 0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 5,
+ 6, 11, 10, 6, 10, 9, 6, 9, 8, 6, 8, 7,
+ 12, 17, 16, 12, 16, 15, 12, 15, 14, 12, 14, 13,
+ 18, 19, 20, 18, 20, 21, 18, 21, 22, 18, 22, 23,
+
+ // edges
+ 0, 5, 11, 0, 11, 6,
+ 6, 7, 19, 6, 19, 18,
+ 18, 23, 17, 18, 17, 12,
+ 12, 13, 1, 12, 1, 0,
+
+ // fill quad
+ // we place this at the end so that we can skip these indices when rendering as stroked
+ 0, 6, 18, 0, 18, 12,
+ // clang-format on
+};
+
+// overstroke count
+static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gRRectIndices) - 6;
+// simple stroke count skips overstroke indices
+static const int kIndicesPerStrokeRRect = kIndicesPerOverstrokeRRect - 6 * 4;
+// fill count adds final quad to stroke count
+static const int kIndicesPerFillRRect = kIndicesPerStrokeRRect + 6;
+static const int kVertsPerStrokeRRect = 24;
+static const int kVertsPerOverstrokeRRect = 28;
+static const int kVertsPerFillRRect = 24;
+
+static int rrect_type_to_vert_count(RRectType type) {
+ switch (type) {
+ case kFill_RRectType:
+ return kVertsPerFillRRect;
+ case kStroke_RRectType:
+ return kVertsPerStrokeRRect;
+ case kOverstroke_RRectType:
+ return kVertsPerOverstrokeRRect;
+ }
+ ALOGE("Invalid rect type: %d", type);
+ return -1;
+}
+
+static int rrect_type_to_index_count(RRectType type) {
+ switch (type) {
+ case kFill_RRectType:
+ return kIndicesPerFillRRect;
+ case kStroke_RRectType:
+ return kIndicesPerStrokeRRect;
+ case kOverstroke_RRectType:
+ return kIndicesPerOverstrokeRRect;
+ }
+ ALOGE("Invalid rect type: %d", type);
+ return -1;
+}
+
+static const uint16_t* rrect_type_to_indices(RRectType type) {
+ switch (type) {
+ case kFill_RRectType:
+ case kStroke_RRectType:
+ return gRRectIndices + 6 * 4;
+ case kOverstroke_RRectType:
+ return gRRectIndices;
+ }
+ ALOGE("Invalid rect type: %d", type);
+ return nullptr;
+}
+
+static void fillInCircleVerts(const Geometry& args, bool isStroked,
+ Mesh::VertexArray<vec2>& position,
+ Mesh::VertexArray<vec4>& shadowColor,
+ Mesh::VertexArray<vec3>& shadowParams) {
+ vec4 color = args.fColor;
+ float outerRadius = args.fOuterRadius;
+ float innerRadius = args.fInnerRadius;
+ float blurRadius = args.fBlurRadius;
+ float distanceCorrection = outerRadius / blurRadius;
+
+ const FloatRect& bounds = args.fDevBounds;
+
+ // The inner radius in the vertex data must be specified in normalized space.
+ innerRadius = innerRadius / outerRadius;
+
+ vec2 center = vec2(bounds.getWidth() / 2.0f, bounds.getHeight() / 2.0f);
+ float halfWidth = 0.5f * bounds.getWidth();
+ float octOffset = 0.41421356237f; // sqrt(2) - 1
+ int vertexCount = 0;
+
+ position[vertexCount] = center + vec2(-octOffset * halfWidth, -halfWidth);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(-octOffset, -1, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(octOffset * halfWidth, -halfWidth);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(octOffset, -1, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(halfWidth, -octOffset * halfWidth);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(1, -octOffset, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(halfWidth, octOffset * halfWidth);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(1, octOffset, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(octOffset * halfWidth, halfWidth);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(octOffset, 1, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(-octOffset * halfWidth, halfWidth);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(-octOffset, 1, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(-halfWidth, octOffset * halfWidth);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(-1, octOffset, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(-halfWidth, -octOffset * halfWidth);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(-1, -octOffset, distanceCorrection);
+ vertexCount++;
+
+ if (isStroked) {
+ // compute the inner ring
+
+ // cosine and sine of pi/8
+ float c = 0.923579533f;
+ float s = 0.382683432f;
+ float r = args.fInnerRadius;
+
+ position[vertexCount] = center + vec2(-s * r, -c * r);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(-s * innerRadius, -c * innerRadius, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(s * r, -c * r);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(s * innerRadius, -c * innerRadius, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(c * r, -s * r);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(c * innerRadius, -s * innerRadius, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(c * r, s * r);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(c * innerRadius, s * innerRadius, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(s * r, c * r);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(s * innerRadius, c * innerRadius, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(-s * r, c * r);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(-s * innerRadius, c * innerRadius, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(-c * r, s * r);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(-c * innerRadius, s * innerRadius, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(-c * r, -s * r);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(-c * innerRadius, -s * innerRadius, distanceCorrection);
+ vertexCount++;
+ } else {
+ // filled
+ position[vertexCount] = center;
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(0, 0, distanceCorrection);
+ vertexCount++;
+ }
+}
+
+static void fillInRRectVerts(const Geometry& args, Mesh::VertexArray<vec2>& position,
+ Mesh::VertexArray<vec4>& shadowColor,
+ Mesh::VertexArray<vec3>& shadowParams) {
+ vec4 color = args.fColor;
+ float outerRadius = args.fOuterRadius;
+
+ const FloatRect& bounds = args.fDevBounds;
+
+ float umbraInset = args.fUmbraInset;
+ float minDim = 0.5f * std::min(bounds.getWidth(), bounds.getHeight());
+ if (umbraInset > minDim) {
+ umbraInset = minDim;
+ }
+
+ float xInner[4] = {bounds.left + umbraInset, bounds.right - umbraInset,
+ bounds.left + umbraInset, bounds.right - umbraInset};
+ float xMid[4] = {bounds.left + outerRadius, bounds.right - outerRadius,
+ bounds.left + outerRadius, bounds.right - outerRadius};
+ float xOuter[4] = {bounds.left, bounds.right, bounds.left, bounds.right};
+ float yInner[4] = {bounds.top + umbraInset, bounds.top + umbraInset, bounds.bottom - umbraInset,
+ bounds.bottom - umbraInset};
+ float yMid[4] = {bounds.top + outerRadius, bounds.top + outerRadius,
+ bounds.bottom - outerRadius, bounds.bottom - outerRadius};
+ float yOuter[4] = {bounds.top, bounds.top, bounds.bottom, bounds.bottom};
+
+ float blurRadius = args.fBlurRadius;
+
+ // In the case where we have to inset more for the umbra, our two triangles in the
+ // corner get skewed to a diamond rather than a square. To correct for that,
+ // we also skew the vectors we send to the shader that help define the circle.
+ // By doing so, we end up with a quarter circle in the corner rather than the
+ // elliptical curve.
+
+ // This is a bit magical, but it gives us the correct results at extrema:
+ // a) umbraInset == outerRadius produces an orthogonal vector
+ // b) outerRadius == 0 produces a diagonal vector
+ // And visually the corner looks correct.
+ vec2 outerVec = vec2(outerRadius - umbraInset, -outerRadius - umbraInset);
+ outerVec = normalize(outerVec);
+ // We want the circle edge to fall fractionally along the diagonal at
+ // (sqrt(2)*(umbraInset - outerRadius) + outerRadius)/sqrt(2)*umbraInset
+ //
+ // Setting the components of the diagonal offset to the following value will give us that.
+ float diagVal = umbraInset / (SK_ScalarSqrt2 * (outerRadius - umbraInset) - outerRadius);
+ vec2 diagVec = vec2(diagVal, diagVal);
+ float distanceCorrection = umbraInset / blurRadius;
+
+ int vertexCount = 0;
+ // build corner by corner
+ for (int i = 0; i < 4; ++i) {
+ // inner point
+ position[vertexCount] = vec2(xInner[i], yInner[i]);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(0, 0, distanceCorrection);
+ vertexCount++;
+
+ // outer points
+ position[vertexCount] = vec2(xOuter[i], yInner[i]);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(0, -1, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = vec2(xOuter[i], yMid[i]);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(outerVec.x, outerVec.y, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = vec2(xOuter[i], yOuter[i]);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(diagVec.x, diagVec.y, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = vec2(xMid[i], yOuter[i]);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(outerVec.x, outerVec.y, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = vec2(xInner[i], yOuter[i]);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(0, -1, distanceCorrection);
+ vertexCount++;
+ }
+
+ // Add the additional vertices for overstroked rrects.
+ // Effectively this is an additional stroked rrect, with its
+ // parameters equal to those in the center of the 9-patch. This will
+ // give constant values across this inner ring.
+ if (kOverstroke_RRectType == args.fType) {
+ float inset = umbraInset + args.fInnerRadius;
+
+ // TL
+ position[vertexCount] = vec2(bounds.left + inset, bounds.top + inset);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(0, 0, distanceCorrection);
+ vertexCount++;
+
+ // TR
+ position[vertexCount] = vec2(bounds.right - inset, bounds.top + inset);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(0, 0, distanceCorrection);
+ vertexCount++;
+
+ // BL
+ position[vertexCount] = vec2(bounds.left + inset, bounds.bottom - inset);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(0, 0, distanceCorrection);
+ vertexCount++;
+
+ // BR
+ position[vertexCount] = vec2(bounds.right - inset, bounds.bottom - inset);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(0, 0, distanceCorrection);
+ vertexCount++;
+ }
+}
+
+int getVertexCountForGeometry(const Geometry& shadowGeometry) {
+ if (shadowGeometry.fIsCircle) {
+ return circle_type_to_vert_count(shadowGeometry.fType);
+ }
+
+ return rrect_type_to_vert_count(shadowGeometry.fType);
+}
+
+int getIndexCountForGeometry(const Geometry& shadowGeometry) {
+ if (shadowGeometry.fIsCircle) {
+ return circle_type_to_index_count(kStroke_RRectType == shadowGeometry.fType);
+ }
+
+ return rrect_type_to_index_count(shadowGeometry.fType);
+}
+
+void fillVerticesForGeometry(const Geometry& shadowGeometry, int /* vertexCount */,
+ Mesh::VertexArray<vec2> position, Mesh::VertexArray<vec4> shadowColor,
+ Mesh::VertexArray<vec3> shadowParams) {
+ if (shadowGeometry.fIsCircle) {
+ fillInCircleVerts(shadowGeometry, shadowGeometry.fIsStroked, position, shadowColor,
+ shadowParams);
+ } else {
+ fillInRRectVerts(shadowGeometry, position, shadowColor, shadowParams);
+ }
+}
+
+void fillIndicesForGeometry(const Geometry& shadowGeometry, int indexCount,
+ int startingVertexOffset, uint16_t* indices) {
+ if (shadowGeometry.fIsCircle) {
+ const uint16_t* primIndices = circle_type_to_indices(shadowGeometry.fIsStroked);
+ for (int i = 0; i < indexCount; ++i) {
+ indices[i] = primIndices[i] + startingVertexOffset;
+ }
+ } else {
+ const uint16_t* primIndices = rrect_type_to_indices(shadowGeometry.fType);
+ for (int i = 0; i < indexCount; ++i) {
+ indices[i] = primIndices[i] + startingVertexOffset;
+ }
+ }
+}
+
+inline void GetSpotParams(float occluderZ, float lightX, float lightY, float lightZ,
+ float lightRadius, float& blurRadius, float& scale, vec2& translate) {
+ float zRatio = divide_and_pin(occluderZ, lightZ - occluderZ, 0.0f, 0.95f);
+ blurRadius = lightRadius * zRatio;
+ scale = divide_and_pin(lightZ, lightZ - occluderZ, 1.0f, 1.95f);
+ translate.x = -zRatio * lightX;
+ translate.y = -zRatio * lightY;
+}
+
+static std::unique_ptr<Geometry> getShadowGeometry(const vec4& color, const FloatRect& devRect,
+ float devRadius, float blurRadius,
+ float insetWidth) {
+ // An insetWidth > 1/2 rect width or height indicates a simple fill.
+ const bool isCircle = ((devRadius >= devRect.getWidth()) && (devRadius >= devRect.getHeight()));
+
+ FloatRect bounds = devRect;
+ float innerRadius = 0.0f;
+ float outerRadius = devRadius;
+ float umbraInset;
+
+ RRectType type = kFill_RRectType;
+ if (isCircle) {
+ umbraInset = 0;
+ } else {
+ umbraInset = std::max(outerRadius, blurRadius);
+ }
+
+ // If stroke is greater than width or height, this is still a fill,
+ // otherwise we compute stroke params.
+ if (isCircle) {
+ innerRadius = devRadius - insetWidth;
+ type = innerRadius > 0 ? kStroke_RRectType : kFill_RRectType;
+ } else {
+ if (insetWidth <= 0.5f * std::min(devRect.getWidth(), devRect.getHeight())) {
+ // We don't worry about a real inner radius, we just need to know if we
+ // need to create overstroke vertices.
+ innerRadius = std::max(insetWidth - umbraInset, 0.0f);
+ type = innerRadius > 0 ? kOverstroke_RRectType : kStroke_RRectType;
+ }
+ }
+ const bool isStroked = (kStroke_RRectType == type);
+ return std::make_unique<Geometry>(Geometry{color, outerRadius, umbraInset, innerRadius,
+ blurRadius, bounds, type, isCircle, isStroked});
+}
+
+std::unique_ptr<Geometry> getAmbientShadowGeometry(const FloatRect& casterRect,
+ float casterCornerRadius, float casterZ,
+ bool casterIsTranslucent,
+ const vec4& ambientColor) {
+ float devSpaceInsetWidth = AmbientBlurRadius(casterZ);
+ const float umbraRecipAlpha = AmbientRecipAlpha(casterZ);
+ const float devSpaceAmbientBlur = devSpaceInsetWidth * umbraRecipAlpha;
+
+ // Outset the shadow rrect to the border of the penumbra
+ float ambientPathOutset = devSpaceInsetWidth;
+ FloatRect outsetRect(casterRect);
+ outsetRect.left -= ambientPathOutset;
+ outsetRect.top -= ambientPathOutset;
+ outsetRect.right += ambientPathOutset;
+ outsetRect.bottom += ambientPathOutset;
+
+ float outsetRad = casterCornerRadius + ambientPathOutset;
+ if (casterIsTranslucent) {
+ // set a large inset to force a fill
+ devSpaceInsetWidth = outsetRect.getWidth();
+ }
+
+ return getShadowGeometry(ambientColor, outsetRect, std::abs(outsetRad), devSpaceAmbientBlur,
+ std::abs(devSpaceInsetWidth));
+}
+
+std::unique_ptr<Geometry> getSpotShadowGeometry(const FloatRect& casterRect,
+ float casterCornerRadius, float casterZ,
+ bool casterIsTranslucent, const vec4& spotColor,
+ const vec3& lightPosition, float lightRadius) {
+ float devSpaceSpotBlur;
+ float spotScale;
+ vec2 spotOffset;
+ GetSpotParams(casterZ, lightPosition.x, lightPosition.y, lightPosition.z, lightRadius,
+ devSpaceSpotBlur, spotScale, spotOffset);
+ // handle scale of radius due to CTM
+ const float srcSpaceSpotBlur = devSpaceSpotBlur;
+
+ // Adjust translate for the effect of the scale.
+ spotOffset.x += spotScale;
+ spotOffset.y += spotScale;
+
+ // Compute the transformed shadow rect
+ ui::Transform shadowTransform;
+ shadowTransform.set(spotOffset.x, spotOffset.y);
+ shadowTransform.set(spotScale, 0, 0, spotScale);
+ FloatRect spotShadowRect = shadowTransform.transform(casterRect);
+ float spotShadowRadius = casterCornerRadius * spotScale;
+
+ // Compute the insetWidth
+ float blurOutset = srcSpaceSpotBlur;
+ float insetWidth = blurOutset;
+ if (casterIsTranslucent) {
+ // If transparent, just do a fill
+ insetWidth += spotShadowRect.getWidth();
+ } else {
+ // For shadows, instead of using a stroke we specify an inset from the penumbra
+ // border. We want to extend this inset area so that it meets up with the caster
+ // geometry. The inset geometry will by default already be inset by the blur width.
+ //
+ // We compare the min and max corners inset by the radius between the original
+ // rrect and the shadow rrect. The distance between the two plus the difference
+ // between the scaled radius and the original radius gives the distance from the
+ // transformed shadow shape to the original shape in that corner. The max
+ // of these gives the maximum distance we need to cover.
+ //
+ // Since we are outsetting by 1/2 the blur distance, we just add the maxOffset to
+ // that to get the full insetWidth.
+ float maxOffset;
+ if (casterCornerRadius <= 0.f) {
+ // Manhattan distance works better for rects
+ maxOffset = std::max(std::max(std::abs(spotShadowRect.left - casterRect.left),
+ std::abs(spotShadowRect.top - casterRect.top)),
+ std::max(std::abs(spotShadowRect.right - casterRect.right),
+ std::abs(spotShadowRect.bottom - casterRect.bottom)));
+ } else {
+ float dr = spotShadowRadius - casterCornerRadius;
+ vec2 upperLeftOffset = vec2(spotShadowRect.left - casterRect.left + dr,
+ spotShadowRect.top - casterRect.top + dr);
+ vec2 lowerRightOffset = vec2(spotShadowRect.right - casterRect.right - dr,
+ spotShadowRect.bottom - casterRect.bottom - dr);
+ maxOffset = sqrt(std::max(dot(upperLeftOffset, lowerRightOffset),
+ dot(lowerRightOffset, lowerRightOffset))) +
+ dr;
+ }
+ insetWidth += std::max(blurOutset, maxOffset);
+ }
+
+ // Outset the shadow rrect to the border of the penumbra
+ spotShadowRadius += blurOutset;
+ spotShadowRect.left -= blurOutset;
+ spotShadowRect.top -= blurOutset;
+ spotShadowRect.right += blurOutset;
+ spotShadowRect.bottom += blurOutset;
+
+ return getShadowGeometry(spotColor, spotShadowRect, std::abs(spotShadowRadius),
+ 2.0f * devSpaceSpotBlur, std::abs(insetWidth));
+}
+
+void fillShadowTextureData(uint8_t* data, size_t shadowTextureWidth) {
+ for (int i = 0; i < shadowTextureWidth; i++) {
+ const float d = 1 - i / ((shadowTextureWidth * 1.0f) - 1.0f);
+ data[i] = static_cast<uint8_t>((exp(-4.0f * d * d) - 0.018f) * 255);
+ }
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/GLSkiaShadowPort.h b/libs/renderengine/gl/GLSkiaShadowPort.h
new file mode 100644
index 0000000..912c8bb
--- /dev/null
+++ b/libs/renderengine/gl/GLSkiaShadowPort.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2019 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 <math/vec4.h>
+#include <renderengine/Mesh.h>
+#include <ui/Rect.h>
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+/**
+ * The shadow geometry logic and vertex generation code has been ported from skia shadow
+ * fast path OpenGL implementation to draw shadows around rects and rounded rects including
+ * circles.
+ *
+ * path: skia/src/gpu/GrRenderTargetContext.cpp GrRenderTargetContext::drawFastShadow
+ *
+ * Modifications made:
+ * - Switched to using std lib math functions
+ * - Fall off function is implemented in vertex shader rather than a shadow texture
+ * - Removed transformations applied on the caster rect since the caster will be in local
+ * coordinate space and will be transformed by the vertex shader.
+ */
+
+enum RRectType {
+ kFill_RRectType,
+ kStroke_RRectType,
+ kOverstroke_RRectType,
+};
+
+struct Geometry {
+ vec4 fColor;
+ float fOuterRadius;
+ float fUmbraInset;
+ float fInnerRadius;
+ float fBlurRadius;
+ FloatRect fDevBounds;
+ RRectType fType;
+ bool fIsCircle;
+ bool fIsStroked;
+};
+
+std::unique_ptr<Geometry> getSpotShadowGeometry(const FloatRect& casterRect,
+ float casterCornerRadius, float casterZ,
+ bool casterIsTranslucent, const vec4& spotColor,
+ const vec3& lightPosition, float lightRadius);
+
+std::unique_ptr<Geometry> getAmbientShadowGeometry(const FloatRect& casterRect,
+ float casterCornerRadius, float casterZ,
+ bool casterIsTranslucent,
+ const vec4& ambientColor);
+
+int getVertexCountForGeometry(const Geometry& shadowGeometry);
+
+int getIndexCountForGeometry(const Geometry& shadowGeometry);
+
+void fillVerticesForGeometry(const Geometry& shadowGeometry, int vertexCount,
+ Mesh::VertexArray<vec2> position, Mesh::VertexArray<vec4> shadowColor,
+ Mesh::VertexArray<vec3> shadowParams);
+
+void fillIndicesForGeometry(const Geometry& shadowGeometry, int indexCount,
+ int startingVertexOffset, uint16_t* indices);
+
+/**
+ * Maps shadow geometry 'alpha' varying (1 for darkest, 0 for transparent) to
+ * darkness at that spot. Values are determined by an exponential falloff
+ * function provided by UX.
+ *
+ * The texture is used for quick lookup in theshadow shader.
+ *
+ * textureData - filled with shadow texture data that needs to be at least of
+ * size textureWidth
+ *
+ * textureWidth - width of the texture, height is always 1
+ */
+void fillShadowTextureData(uint8_t* textureData, size_t textureWidth);
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/GLVertexBuffer.cpp b/libs/renderengine/gl/GLVertexBuffer.cpp
new file mode 100644
index 0000000..e50c471
--- /dev/null
+++ b/libs/renderengine/gl/GLVertexBuffer.cpp
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "GLVertexBuffer.h"
+
+#include <GLES/gl.h>
+#include <GLES2/gl2.h>
+#include <nativebase/nativebase.h>
+#include <utils/Trace.h>
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+GLVertexBuffer::GLVertexBuffer() {
+ glGenBuffers(1, &mBufferName);
+}
+
+GLVertexBuffer::~GLVertexBuffer() {
+ glDeleteBuffers(1, &mBufferName);
+}
+
+void GLVertexBuffer::allocateBuffers(const GLfloat data[], const GLuint size) {
+ ATRACE_CALL();
+ bind();
+ glBufferData(GL_ARRAY_BUFFER, size * sizeof(GLfloat), data, GL_STATIC_DRAW);
+ unbind();
+}
+
+void GLVertexBuffer::bind() const {
+ glBindBuffer(GL_ARRAY_BUFFER, mBufferName);
+}
+
+void GLVertexBuffer::unbind() const {
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/GLVertexBuffer.h b/libs/renderengine/gl/GLVertexBuffer.h
new file mode 100644
index 0000000..c0fd0c1
--- /dev/null
+++ b/libs/renderengine/gl/GLVertexBuffer.h
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <cstdint>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+
+struct ANativeWindowBuffer;
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+class GLESRenderEngine;
+
+class GLVertexBuffer {
+public:
+ explicit GLVertexBuffer();
+ ~GLVertexBuffer();
+
+ void allocateBuffers(const GLfloat data[], const GLuint size);
+ uint32_t getBufferName() const { return mBufferName; }
+ void bind() const;
+ void unbind() const;
+
+private:
+ uint32_t mBufferName;
+};
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/Program.cpp b/libs/renderengine/gl/Program.cpp
index fe9d909..f4fbf35 100644
--- a/libs/renderengine/gl/Program.cpp
+++ b/libs/renderengine/gl/Program.cpp
@@ -37,6 +37,8 @@
glBindAttribLocation(programId, position, "position");
glBindAttribLocation(programId, texCoords, "texCoords");
glBindAttribLocation(programId, cropCoords, "cropCoords");
+ glBindAttribLocation(programId, shadowColor, "shadowColor");
+ glBindAttribLocation(programId, shadowParams, "shadowParams");
glLinkProgram(programId);
GLint status;
@@ -65,6 +67,8 @@
mSamplerLoc = glGetUniformLocation(programId, "sampler");
mColorLoc = glGetUniformLocation(programId, "color");
mDisplayMaxLuminanceLoc = glGetUniformLocation(programId, "displayMaxLuminance");
+ mMaxMasteringLuminanceLoc = glGetUniformLocation(programId, "maxMasteringLuminance");
+ mMaxContentLuminanceLoc = glGetUniformLocation(programId, "maxContentLuminance");
mInputTransformMatrixLoc = glGetUniformLocation(programId, "inputTransformMatrix");
mOutputTransformMatrixLoc = glGetUniformLocation(programId, "outputTransformMatrix");
mCornerRadiusLoc = glGetUniformLocation(programId, "cornerRadius");
@@ -138,6 +142,12 @@
if (mDisplayMaxLuminanceLoc >= 0) {
glUniform1f(mDisplayMaxLuminanceLoc, desc.displayMaxLuminance);
}
+ if (mMaxMasteringLuminanceLoc >= 0) {
+ glUniform1f(mMaxMasteringLuminanceLoc, desc.maxMasteringLuminance);
+ }
+ if (mMaxContentLuminanceLoc >= 0) {
+ glUniform1f(mMaxContentLuminanceLoc, desc.maxContentLuminance);
+ }
if (mCornerRadiusLoc >= 0) {
glUniform1f(mCornerRadiusLoc, desc.cornerRadius);
}
diff --git a/libs/renderengine/gl/Program.h b/libs/renderengine/gl/Program.h
index bc9cf08..fc3755e 100644
--- a/libs/renderengine/gl/Program.h
+++ b/libs/renderengine/gl/Program.h
@@ -44,7 +44,13 @@
texCoords = 1,
/* Crop coordinates, in pixels */
- cropCoords = 2
+ cropCoords = 2,
+
+ /* Shadow color */
+ shadowColor = 3,
+
+ /* Shadow params */
+ shadowParams = 4,
};
Program(const ProgramCache::Key& needs, const char* vertex, const char* fragment);
@@ -90,6 +96,10 @@
/* location of display luminance uniform */
GLint mDisplayMaxLuminanceLoc;
+ /* location of max mastering luminance uniform */
+ GLint mMaxMasteringLuminanceLoc;
+ /* location of max content luminance uniform */
+ GLint mMaxContentLuminanceLoc;
/* location of transform matrix */
GLint mInputTransformMatrixLoc;
diff --git a/libs/renderengine/gl/ProgramCache.cpp b/libs/renderengine/gl/ProgramCache.cpp
index d242677..3ae35ec 100644
--- a/libs/renderengine/gl/ProgramCache.cpp
+++ b/libs/renderengine/gl/ProgramCache.cpp
@@ -77,9 +77,38 @@
return f;
}
-void ProgramCache::primeCache(EGLContext context, bool useColorManagement) {
+void ProgramCache::primeCache(
+ EGLContext context, bool useColorManagement, bool toneMapperShaderOnly) {
auto& cache = mCaches[context];
uint32_t shaderCount = 0;
+
+ if (toneMapperShaderOnly) {
+ Key shaderKey;
+ // base settings used by HDR->SDR tonemap only
+ shaderKey.set(Key::BLEND_MASK | Key::INPUT_TRANSFORM_MATRIX_MASK |
+ Key::OUTPUT_TRANSFORM_MATRIX_MASK | Key::OUTPUT_TF_MASK |
+ Key::OPACITY_MASK | Key::ALPHA_MASK |
+ Key::ROUNDED_CORNERS_MASK | Key::TEXTURE_MASK,
+ Key::BLEND_NORMAL | Key::INPUT_TRANSFORM_MATRIX_ON |
+ Key::OUTPUT_TRANSFORM_MATRIX_ON | Key::OUTPUT_TF_SRGB |
+ Key::OPACITY_OPAQUE | Key::ALPHA_EQ_ONE |
+ Key::ROUNDED_CORNERS_OFF | Key::TEXTURE_EXT);
+ for (int i = 0; i < 4; i++) {
+ // Cache input transfer for HLG & ST2084
+ shaderKey.set(Key::INPUT_TF_MASK, (i & 1) ?
+ Key::INPUT_TF_HLG : Key::INPUT_TF_ST2084);
+
+ // Cache Y410 input on or off
+ shaderKey.set(Key::Y410_BT2020_MASK, (i & 2) ?
+ Key::Y410_BT2020_ON : Key::Y410_BT2020_OFF);
+ if (cache.count(shaderKey) == 0) {
+ cache.emplace(shaderKey, generateProgram(shaderKey));
+ shaderCount++;
+ }
+ }
+ return;
+ }
+
uint32_t keyMask = Key::BLEND_MASK | Key::OPACITY_MASK | Key::ALPHA_MASK | Key::TEXTURE_MASK
| Key::ROUNDED_CORNERS_MASK;
// Prime the cache for all combinations of the above masks,
@@ -145,16 +174,15 @@
.set(Key::OPACITY_MASK,
description.isOpaque ? Key::OPACITY_OPAQUE : Key::OPACITY_TRANSLUCENT)
.set(Key::Key::INPUT_TRANSFORM_MATRIX_MASK,
- description.hasInputTransformMatrix()
- ? Key::INPUT_TRANSFORM_MATRIX_ON : Key::INPUT_TRANSFORM_MATRIX_OFF)
+ description.hasInputTransformMatrix() ? Key::INPUT_TRANSFORM_MATRIX_ON
+ : Key::INPUT_TRANSFORM_MATRIX_OFF)
.set(Key::Key::OUTPUT_TRANSFORM_MATRIX_MASK,
description.hasOutputTransformMatrix() || description.hasColorMatrix()
? Key::OUTPUT_TRANSFORM_MATRIX_ON
: Key::OUTPUT_TRANSFORM_MATRIX_OFF)
.set(Key::ROUNDED_CORNERS_MASK,
- description.cornerRadius > 0
- ? Key::ROUNDED_CORNERS_ON : Key::ROUNDED_CORNERS_OFF);
-
+ description.cornerRadius > 0 ? Key::ROUNDED_CORNERS_ON : Key::ROUNDED_CORNERS_OFF)
+ .set(Key::SHADOW_MASK, description.drawShadows ? Key::SHADOW_ON : Key::SHADOW_OFF);
needs.set(Key::Y410_BT2020_MASK,
description.isY410BT2020 ? Key::Y410_BT2020_ON : Key::Y410_BT2020_OFF);
@@ -310,9 +338,9 @@
default:
fs << R"__SHADER__(
highp vec3 ToneMap(highp vec3 color) {
- const float maxMasteringLumi = 1000.0;
- const float maxContentLumi = 1000.0;
- const float maxInLumi = min(maxMasteringLumi, maxContentLumi);
+ float maxMasteringLumi = maxMasteringLuminance;
+ float maxContentLumi = maxContentLuminance;
+ float maxInLumi = min(maxMasteringLumi, maxContentLumi);
float maxOutLumi = displayMaxLuminance;
float nits = color.y;
@@ -522,7 +550,7 @@
String8 ProgramCache::generateVertexShader(const Key& needs) {
Formatter vs;
- if (needs.isTexturing()) {
+ if (needs.hasTextureCoords()) {
vs << "attribute vec4 texCoords;"
<< "varying vec2 outTexCoords;";
}
@@ -530,16 +558,26 @@
vs << "attribute lowp vec4 cropCoords;";
vs << "varying lowp vec2 outCropCoords;";
}
+ if (needs.drawShadows()) {
+ vs << "attribute lowp vec4 shadowColor;";
+ vs << "varying lowp vec4 outShadowColor;";
+ vs << "attribute lowp vec4 shadowParams;";
+ vs << "varying lowp vec3 outShadowParams;";
+ }
vs << "attribute vec4 position;"
<< "uniform mat4 projection;"
<< "uniform mat4 texture;"
<< "void main(void) {" << indent << "gl_Position = projection * position;";
- if (needs.isTexturing()) {
+ if (needs.hasTextureCoords()) {
vs << "outTexCoords = (texture * texCoords).st;";
}
if (needs.hasRoundedCorners()) {
vs << "outCropCoords = cropCoords.st;";
}
+ if (needs.drawShadows()) {
+ vs << "outShadowColor = shadowColor;";
+ vs << "outShadowParams = shadowParams.xyz;";
+ }
vs << dedent << "}";
return vs.getString();
}
@@ -554,11 +592,13 @@
fs << "precision mediump float;";
if (needs.getTextureTarget() == Key::TEXTURE_EXT) {
- fs << "uniform samplerExternalOES sampler;"
- << "varying vec2 outTexCoords;";
+ fs << "uniform samplerExternalOES sampler;";
} else if (needs.getTextureTarget() == Key::TEXTURE_2D) {
- fs << "uniform sampler2D sampler;"
- << "varying vec2 outTexCoords;";
+ fs << "uniform sampler2D sampler;";
+ }
+
+ if (needs.hasTextureCoords()) {
+ fs << "varying vec2 outTexCoords;";
}
if (needs.hasRoundedCorners()) {
@@ -585,6 +625,24 @@
)__SHADER__";
}
+ if (needs.drawShadows()) {
+ fs << R"__SHADER__(
+ varying lowp vec4 outShadowColor;
+ varying lowp vec3 outShadowParams;
+
+ /**
+ * Returns the shadow color.
+ */
+ vec4 getShadowColor()
+ {
+ lowp float d = length(outShadowParams.xy);
+ vec2 uv = vec2(outShadowParams.z * (1.0 - d), 0.5);
+ lowp float factor = texture2D(sampler, uv).a;
+ return outShadowColor * factor;
+ }
+ )__SHADER__";
+ }
+
if (needs.getTextureTarget() == Key::TEXTURE_OFF || needs.hasAlpha()) {
fs << "uniform vec4 color;";
}
@@ -604,9 +662,10 @@
}
if (needs.hasTransformMatrix() || (needs.getInputTF() != needs.getOutputTF())) {
- // Currently, display maximum luminance is needed when doing tone mapping.
if (needs.needsToneMapping()) {
fs << "uniform float displayMaxLuminance;";
+ fs << "uniform float maxMasteringLuminance;";
+ fs << "uniform float maxContentLuminance;";
}
if (needs.hasInputTransformMatrix()) {
@@ -647,25 +706,29 @@
}
fs << "void main(void) {" << indent;
- if (needs.isTexturing()) {
- fs << "gl_FragColor = texture2D(sampler, outTexCoords);";
- if (needs.isY410BT2020()) {
- fs << "gl_FragColor.rgb = convertY410BT2020(gl_FragColor.rgb);";
- }
+ if (needs.drawShadows()) {
+ fs << "gl_FragColor = getShadowColor();";
} else {
- fs << "gl_FragColor.rgb = color.rgb;";
- fs << "gl_FragColor.a = 1.0;";
- }
- if (needs.isOpaque()) {
- fs << "gl_FragColor.a = 1.0;";
- }
- if (needs.hasAlpha()) {
- // modulate the current alpha value with alpha set
- if (needs.isPremultiplied()) {
- // ... and the color too if we're premultiplied
- fs << "gl_FragColor *= color.a;";
+ if (needs.isTexturing()) {
+ fs << "gl_FragColor = texture2D(sampler, outTexCoords);";
+ if (needs.isY410BT2020()) {
+ fs << "gl_FragColor.rgb = convertY410BT2020(gl_FragColor.rgb);";
+ }
} else {
- fs << "gl_FragColor.a *= color.a;";
+ fs << "gl_FragColor.rgb = color.rgb;";
+ fs << "gl_FragColor.a = 1.0;";
+ }
+ if (needs.isOpaque()) {
+ fs << "gl_FragColor.a = 1.0;";
+ }
+ if (needs.hasAlpha()) {
+ // modulate the current alpha value with alpha set
+ if (needs.isPremultiplied()) {
+ // ... and the color too if we're premultiplied
+ fs << "gl_FragColor *= color.a;";
+ } else {
+ fs << "gl_FragColor.a *= color.a;";
+ }
}
}
diff --git a/libs/renderengine/gl/ProgramCache.h b/libs/renderengine/gl/ProgramCache.h
index 400ad74..901e631 100644
--- a/libs/renderengine/gl/ProgramCache.h
+++ b/libs/renderengine/gl/ProgramCache.h
@@ -112,6 +112,11 @@
Y410_BT2020_MASK = 1 << Y410_BT2020_SHIFT,
Y410_BT2020_OFF = 0 << Y410_BT2020_SHIFT,
Y410_BT2020_ON = 1 << Y410_BT2020_SHIFT,
+
+ SHADOW_SHIFT = 13,
+ SHADOW_MASK = 1 << SHADOW_SHIFT,
+ SHADOW_OFF = 0 << SHADOW_SHIFT,
+ SHADOW_ON = 1 << SHADOW_SHIFT,
};
inline Key() : mKey(0) {}
@@ -123,6 +128,7 @@
}
inline bool isTexturing() const { return (mKey & TEXTURE_MASK) != TEXTURE_OFF; }
+ inline bool hasTextureCoords() const { return isTexturing() && !drawShadows(); }
inline int getTextureTarget() const { return (mKey & TEXTURE_MASK); }
inline bool isPremultiplied() const { return (mKey & BLEND_MASK) == BLEND_PREMULT; }
inline bool isOpaque() const { return (mKey & OPACITY_MASK) == OPACITY_OPAQUE; }
@@ -130,6 +136,7 @@
inline bool hasRoundedCorners() const {
return (mKey & ROUNDED_CORNERS_MASK) == ROUNDED_CORNERS_ON;
}
+ inline bool drawShadows() const { return (mKey & SHADOW_MASK) == SHADOW_ON; }
inline bool hasInputTransformMatrix() const {
return (mKey & INPUT_TRANSFORM_MATRIX_MASK) == INPUT_TRANSFORM_MATRIX_ON;
}
@@ -179,7 +186,7 @@
~ProgramCache() = default;
// Generate shaders to populate the cache
- void primeCache(const EGLContext context, bool useColorManagement);
+ void primeCache(const EGLContext context, bool useColorManagement, bool toneMapperShaderOnly);
size_t getSize(const EGLContext context) { return mCaches[context].size(); }
diff --git a/libs/renderengine/gl/filters/BlurFilter.cpp b/libs/renderengine/gl/filters/BlurFilter.cpp
new file mode 100644
index 0000000..19f18c0
--- /dev/null
+++ b/libs/renderengine/gl/filters/BlurFilter.cpp
@@ -0,0 +1,268 @@
+/*
+ * Copyright 2019 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 ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "BlurFilter.h"
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES3/gl3.h>
+#include <GLES3/gl3ext.h>
+#include <ui/GraphicTypes.h>
+#include <cstdint>
+
+#include <utils/Trace.h>
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+BlurFilter::BlurFilter(GLESRenderEngine& engine)
+ : mEngine(engine),
+ mCompositionFbo(engine),
+ mPingFbo(engine),
+ mPongFbo(engine),
+ mMixProgram(engine),
+ mBlurProgram(engine) {
+ mMixProgram.compile(getVertexShader(), getMixFragShader());
+ mMPosLoc = mMixProgram.getAttributeLocation("aPosition");
+ mMUvLoc = mMixProgram.getAttributeLocation("aUV");
+ mMTextureLoc = mMixProgram.getUniformLocation("uTexture");
+ mMCompositionTextureLoc = mMixProgram.getUniformLocation("uCompositionTexture");
+ mMMixLoc = mMixProgram.getUniformLocation("uMix");
+
+ mBlurProgram.compile(getVertexShader(), getFragmentShader());
+ mBPosLoc = mBlurProgram.getAttributeLocation("aPosition");
+ mBUvLoc = mBlurProgram.getAttributeLocation("aUV");
+ mBTextureLoc = mBlurProgram.getUniformLocation("uTexture");
+ mBOffsetLoc = mBlurProgram.getUniformLocation("uOffset");
+
+ static constexpr auto size = 2.0f;
+ static constexpr auto translation = 1.0f;
+ const GLfloat vboData[] = {
+ // Vertex data
+ translation - size, -translation - size,
+ translation - size, -translation + size,
+ translation + size, -translation + size,
+ // UV data
+ 0.0f, 0.0f - translation,
+ 0.0f, size - translation,
+ size, size - translation
+ };
+ mMeshBuffer.allocateBuffers(vboData, 12 /* size */);
+}
+
+status_t BlurFilter::setAsDrawTarget(const DisplaySettings& display, uint32_t radius) {
+ ATRACE_NAME("BlurFilter::setAsDrawTarget");
+ mRadius = radius;
+ mDisplayX = display.physicalDisplay.left;
+ mDisplayY = display.physicalDisplay.top;
+
+ if (mDisplayWidth < display.physicalDisplay.width() ||
+ mDisplayHeight < display.physicalDisplay.height()) {
+ ATRACE_NAME("BlurFilter::allocatingTextures");
+
+ mDisplayWidth = display.physicalDisplay.width();
+ mDisplayHeight = display.physicalDisplay.height();
+ mCompositionFbo.allocateBuffers(mDisplayWidth, mDisplayHeight);
+
+ const uint32_t fboWidth = floorf(mDisplayWidth * kFboScale);
+ const uint32_t fboHeight = floorf(mDisplayHeight * kFboScale);
+ mPingFbo.allocateBuffers(fboWidth, fboHeight);
+ mPongFbo.allocateBuffers(fboWidth, fboHeight);
+
+ if (mPingFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
+ ALOGE("Invalid ping buffer");
+ return mPingFbo.getStatus();
+ }
+ if (mPongFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
+ ALOGE("Invalid pong buffer");
+ return mPongFbo.getStatus();
+ }
+ if (mCompositionFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
+ ALOGE("Invalid composition buffer");
+ return mCompositionFbo.getStatus();
+ }
+ if (!mBlurProgram.isValid()) {
+ ALOGE("Invalid shader");
+ return GL_INVALID_OPERATION;
+ }
+ }
+
+ mCompositionFbo.bind();
+ glViewport(0, 0, mCompositionFbo.getBufferWidth(), mCompositionFbo.getBufferHeight());
+ return NO_ERROR;
+}
+
+void BlurFilter::drawMesh(GLuint uv, GLuint position) {
+
+ glEnableVertexAttribArray(uv);
+ glEnableVertexAttribArray(position);
+ mMeshBuffer.bind();
+ glVertexAttribPointer(position, 2 /* size */, GL_FLOAT, GL_FALSE,
+ 2 * sizeof(GLfloat) /* stride */, 0 /* offset */);
+ glVertexAttribPointer(uv, 2 /* size */, GL_FLOAT, GL_FALSE, 0 /* stride */,
+ (GLvoid*)(6 * sizeof(GLfloat)) /* offset */);
+ mMeshBuffer.unbind();
+
+ // draw mesh
+ glDrawArrays(GL_TRIANGLES, 0 /* first */, 3 /* count */);
+}
+
+status_t BlurFilter::prepare() {
+ ATRACE_NAME("BlurFilter::prepare");
+
+ // Kawase is an approximation of Gaussian, but it behaves differently from it.
+ // A radius transformation is required for approximating them, and also to introduce
+ // non-integer steps, necessary to smoothly interpolate large radii.
+ const auto radius = mRadius / 6.0f;
+
+ // Calculate how many passes we'll do, based on the radius.
+ // Too many passes will make the operation expensive.
+ const auto passes = min(kMaxPasses, (uint32_t)ceil(radius));
+
+ const float radiusByPasses = radius / (float)passes;
+ const float stepX = radiusByPasses / (float)mCompositionFbo.getBufferWidth();
+ const float stepY = radiusByPasses / (float)mCompositionFbo.getBufferHeight();
+
+ // Let's start by downsampling and blurring the composited frame simultaneously.
+ mBlurProgram.useProgram();
+ glActiveTexture(GL_TEXTURE0);
+ glUniform1i(mBTextureLoc, 0);
+ glBindTexture(GL_TEXTURE_2D, mCompositionFbo.getTextureName());
+ glUniform2f(mBOffsetLoc, stepX, stepY);
+ glViewport(0, 0, mPingFbo.getBufferWidth(), mPingFbo.getBufferHeight());
+ mPingFbo.bind();
+ drawMesh(mBUvLoc, mBPosLoc);
+
+ // And now we'll ping pong between our textures, to accumulate the result of various offsets.
+ GLFramebuffer* read = &mPingFbo;
+ GLFramebuffer* draw = &mPongFbo;
+ glViewport(0, 0, draw->getBufferWidth(), draw->getBufferHeight());
+ for (auto i = 1; i < passes; i++) {
+ ATRACE_NAME("BlurFilter::renderPass");
+ draw->bind();
+
+ glBindTexture(GL_TEXTURE_2D, read->getTextureName());
+ glUniform2f(mBOffsetLoc, stepX * i, stepY * i);
+
+ drawMesh(mBUvLoc, mBPosLoc);
+
+ // Swap buffers for next iteration
+ auto tmp = draw;
+ draw = read;
+ read = tmp;
+ }
+ mLastDrawTarget = read;
+
+ return NO_ERROR;
+}
+
+status_t BlurFilter::render(bool multiPass) {
+ ATRACE_NAME("BlurFilter::render");
+
+ // Now let's scale our blur up. It will be interpolated with the larger composited
+ // texture for the first frames, to hide downscaling artifacts.
+ GLfloat mix = fmin(1.0, mRadius / kMaxCrossFadeRadius);
+
+ // When doing multiple passes, we cannot try to read mCompositionFbo, given that we'll
+ // be writing onto it. Let's disable the crossfade, otherwise we'd need 1 extra frame buffer,
+ // as large as the screen size.
+ if (mix >= 1 || multiPass) {
+ mLastDrawTarget->bindAsReadBuffer();
+ glBlitFramebuffer(0, 0, mLastDrawTarget->getBufferWidth(),
+ mLastDrawTarget->getBufferHeight(), mDisplayX, mDisplayY, mDisplayWidth,
+ mDisplayHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR);
+ return NO_ERROR;
+ }
+
+ mMixProgram.useProgram();
+ glUniform1f(mMMixLoc, mix);
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, mLastDrawTarget->getTextureName());
+ glUniform1i(mMTextureLoc, 0);
+ glActiveTexture(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D, mCompositionFbo.getTextureName());
+ glUniform1i(mMCompositionTextureLoc, 1);
+
+ drawMesh(mMUvLoc, mMPosLoc);
+
+ glUseProgram(0);
+ glActiveTexture(GL_TEXTURE0);
+ mEngine.checkErrors("Drawing blur mesh");
+ return NO_ERROR;
+}
+
+string BlurFilter::getVertexShader() const {
+ return R"SHADER(#version 310 es
+ precision mediump float;
+
+ in vec2 aPosition;
+ in highp vec2 aUV;
+ out highp vec2 vUV;
+
+ void main() {
+ vUV = aUV;
+ gl_Position = vec4(aPosition, 0.0, 1.0);
+ }
+ )SHADER";
+}
+
+string BlurFilter::getFragmentShader() const {
+ return R"SHADER(#version 310 es
+ precision mediump float;
+
+ uniform sampler2D uTexture;
+ uniform vec2 uOffset;
+
+ in highp vec2 vUV;
+ out vec4 fragColor;
+
+ void main() {
+ fragColor = texture(uTexture, vUV, 0.0);
+ fragColor += texture(uTexture, vUV + vec2( uOffset.x, uOffset.y), 0.0);
+ fragColor += texture(uTexture, vUV + vec2( uOffset.x, -uOffset.y), 0.0);
+ fragColor += texture(uTexture, vUV + vec2(-uOffset.x, uOffset.y), 0.0);
+ fragColor += texture(uTexture, vUV + vec2(-uOffset.x, -uOffset.y), 0.0);
+
+ fragColor = vec4(fragColor.rgb * 0.2, 1.0);
+ }
+ )SHADER";
+}
+
+string BlurFilter::getMixFragShader() const {
+ string shader = R"SHADER(#version 310 es
+ precision mediump float;
+
+ in highp vec2 vUV;
+ out vec4 fragColor;
+
+ uniform sampler2D uCompositionTexture;
+ uniform sampler2D uTexture;
+ uniform float uMix;
+
+ void main() {
+ vec4 blurred = texture(uTexture, vUV);
+ vec4 composition = texture(uCompositionTexture, vUV);
+ fragColor = mix(composition, blurred, uMix);
+ }
+ )SHADER";
+ return shader;
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/filters/BlurFilter.h b/libs/renderengine/gl/filters/BlurFilter.h
new file mode 100644
index 0000000..593a8fd
--- /dev/null
+++ b/libs/renderengine/gl/filters/BlurFilter.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2019 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/GraphicTypes.h>
+#include "../GLESRenderEngine.h"
+#include "../GLFramebuffer.h"
+#include "../GLVertexBuffer.h"
+#include "GenericProgram.h"
+
+using namespace std;
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+/**
+ * This is an implementation of a Kawase blur, as described in here:
+ * https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/
+ * 00-00-00-20-66/siggraph2015_2D00_mmg_2D00_marius_2D00_notes.pdf
+ */
+class BlurFilter {
+public:
+ // Downsample FBO to improve performance
+ static constexpr float kFboScale = 0.25f;
+ // Maximum number of render passes
+ static constexpr uint32_t kMaxPasses = 4;
+ // To avoid downscaling artifacts, we interpolate the blurred fbo with the full composited
+ // image, up to this radius.
+ static constexpr float kMaxCrossFadeRadius = 30.0f;
+
+ explicit BlurFilter(GLESRenderEngine& engine);
+ virtual ~BlurFilter(){};
+
+ // Set up render targets, redirecting output to offscreen texture.
+ status_t setAsDrawTarget(const DisplaySettings&, uint32_t radius);
+ // Execute blur passes, rendering to offscreen texture.
+ status_t prepare();
+ // Render blur to the bound framebuffer (screen).
+ status_t render(bool multiPass);
+
+private:
+ uint32_t mRadius;
+ void drawMesh(GLuint uv, GLuint position);
+ string getVertexShader() const;
+ string getFragmentShader() const;
+ string getMixFragShader() const;
+
+ GLESRenderEngine& mEngine;
+ // Frame buffer holding the composited background.
+ GLFramebuffer mCompositionFbo;
+ // Frame buffers holding the blur passes.
+ GLFramebuffer mPingFbo;
+ GLFramebuffer mPongFbo;
+ uint32_t mDisplayWidth = 0;
+ uint32_t mDisplayHeight = 0;
+ uint32_t mDisplayX = 0;
+ uint32_t mDisplayY = 0;
+ // Buffer holding the final blur pass.
+ GLFramebuffer* mLastDrawTarget;
+
+ // VBO containing vertex and uv data of a fullscreen triangle.
+ GLVertexBuffer mMeshBuffer;
+
+ GenericProgram mMixProgram;
+ GLuint mMPosLoc;
+ GLuint mMUvLoc;
+ GLuint mMMixLoc;
+ GLuint mMTextureLoc;
+ GLuint mMCompositionTextureLoc;
+
+ GenericProgram mBlurProgram;
+ GLuint mBPosLoc;
+ GLuint mBUvLoc;
+ GLuint mBTextureLoc;
+ GLuint mBOffsetLoc;
+};
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/filters/GenericProgram.cpp b/libs/renderengine/gl/filters/GenericProgram.cpp
new file mode 100644
index 0000000..bb35889
--- /dev/null
+++ b/libs/renderengine/gl/filters/GenericProgram.cpp
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2019 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 "GenericProgram.h"
+
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+GenericProgram::GenericProgram(GLESRenderEngine& engine) : mEngine(engine) {}
+
+GenericProgram::~GenericProgram() {
+ if (mVertexShaderHandle != 0) {
+ if (mProgramHandle != 0) {
+ glDetachShader(mProgramHandle, mVertexShaderHandle);
+ }
+ glDeleteShader(mVertexShaderHandle);
+ }
+
+ if (mFragmentShaderHandle != 0) {
+ if (mProgramHandle != 0) {
+ glDetachShader(mProgramHandle, mFragmentShaderHandle);
+ }
+ glDeleteShader(mFragmentShaderHandle);
+ }
+
+ if (mProgramHandle != 0) {
+ glDeleteProgram(mProgramHandle);
+ }
+}
+
+void GenericProgram::compile(string vertexShader, string fragmentShader) {
+ mVertexShaderHandle = compileShader(GL_VERTEX_SHADER, vertexShader);
+ mFragmentShaderHandle = compileShader(GL_FRAGMENT_SHADER, fragmentShader);
+ if (mVertexShaderHandle == 0 || mFragmentShaderHandle == 0) {
+ ALOGE("Aborting program creation.");
+ return;
+ }
+ mProgramHandle = createAndLink(mVertexShaderHandle, mFragmentShaderHandle);
+ mEngine.checkErrors("Linking program");
+}
+
+void GenericProgram::useProgram() const {
+ glUseProgram(mProgramHandle);
+}
+
+GLuint GenericProgram::compileShader(GLuint type, string src) const {
+ const GLuint shader = glCreateShader(type);
+ if (shader == 0) {
+ mEngine.checkErrors("Creating shader");
+ return 0;
+ }
+ const GLchar* charSrc = (const GLchar*)src.c_str();
+ glShaderSource(shader, 1, &charSrc, nullptr);
+ glCompileShader(shader);
+
+ GLint isCompiled = 0;
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &isCompiled);
+ if (isCompiled == GL_FALSE) {
+ GLint maxLength = 0;
+ glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength);
+ string errorLog;
+ errorLog.reserve(maxLength);
+ glGetShaderInfoLog(shader, maxLength, &maxLength, errorLog.data());
+ glDeleteShader(shader);
+ ALOGE("Error compiling shader: %s", errorLog.c_str());
+ return 0;
+ }
+ return shader;
+}
+GLuint GenericProgram::createAndLink(GLuint vertexShader, GLuint fragmentShader) const {
+ const GLuint program = glCreateProgram();
+ mEngine.checkErrors("Creating program");
+
+ glAttachShader(program, vertexShader);
+ glAttachShader(program, fragmentShader);
+ glLinkProgram(program);
+ mEngine.checkErrors("Linking program");
+ return program;
+}
+
+GLuint GenericProgram::getUniformLocation(const string name) const {
+ if (mProgramHandle == 0) {
+ ALOGE("Can't get location of %s on an invalid program.", name.c_str());
+ return -1;
+ }
+ return glGetUniformLocation(mProgramHandle, (const GLchar*)name.c_str());
+}
+
+GLuint GenericProgram::getAttributeLocation(const string name) const {
+ if (mProgramHandle == 0) {
+ ALOGE("Can't get location of %s on an invalid program.", name.c_str());
+ return -1;
+ }
+ return glGetAttribLocation(mProgramHandle, (const GLchar*)name.c_str());
+}
+
+bool GenericProgram::isValid() const {
+ return mProgramHandle != 0;
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/filters/GenericProgram.h b/libs/renderengine/gl/filters/GenericProgram.h
new file mode 100644
index 0000000..6da2a5a
--- /dev/null
+++ b/libs/renderengine/gl/filters/GenericProgram.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2019 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/GraphicTypes.h>
+#include "../GLESRenderEngine.h"
+#include "../GLFramebuffer.h"
+
+using namespace std;
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+class GenericProgram {
+public:
+ explicit GenericProgram(GLESRenderEngine& renderEngine);
+ ~GenericProgram();
+ void compile(string vertexShader, string fragmentShader);
+ bool isValid() const;
+ void useProgram() const;
+ GLuint getAttributeLocation(const string name) const;
+ GLuint getUniformLocation(const string name) const;
+
+private:
+ GLuint compileShader(GLuint type, const string src) const;
+ GLuint createAndLink(GLuint vertexShader, GLuint fragmentShader) const;
+
+ GLESRenderEngine& mEngine;
+ GLuint mVertexShaderHandle = 0;
+ GLuint mFragmentShaderHandle = 0;
+ GLuint mProgramHandle = 0;
+};
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/include/renderengine/DisplaySettings.h b/libs/renderengine/include/renderengine/DisplaySettings.h
index 9c9884a..ca16d2c 100644
--- a/libs/renderengine/include/renderengine/DisplaySettings.h
+++ b/libs/renderengine/include/renderengine/DisplaySettings.h
@@ -16,6 +16,8 @@
#pragma once
+#include <iosfwd>
+
#include <math/mat4.h>
#include <ui/GraphicTypes.h>
#include <ui/Rect.h>
@@ -38,9 +40,6 @@
// z=1.
Rect clip = Rect::INVALID_RECT;
- // Global transform to apply to all layers.
- mat4 globalTransform = mat4();
-
// Maximum luminance pulled from the display's HDR capabilities.
float maxLuminance = 1.0f;
@@ -53,14 +52,39 @@
mat4 colorTransform = mat4();
// Region that will be cleared to (0, 0, 0, 1) prior to rendering.
- // RenderEngine will transform the clearRegion passed in here, by
- // globalTransform, so that it will be in the same coordinate space as the
- // rendered layers.
+ // This is specified in layer-stack space.
Region clearRegion = Region::INVALID_REGION;
- // The orientation of the physical display.
+ // An additional orientation flag to be applied after clipping the output.
+ // By way of example, this may be used for supporting fullscreen screenshot
+ // capture of a device in landscape while the buffer is in portrait
+ // orientation.
uint32_t orientation = ui::Transform::ROT_0;
};
+static inline bool operator==(const DisplaySettings& lhs, const DisplaySettings& rhs) {
+ return lhs.physicalDisplay == rhs.physicalDisplay && lhs.clip == rhs.clip &&
+ lhs.maxLuminance == rhs.maxLuminance && lhs.outputDataspace == rhs.outputDataspace &&
+ lhs.colorTransform == rhs.colorTransform &&
+ lhs.clearRegion.hasSameRects(rhs.clearRegion) && lhs.orientation == rhs.orientation;
+}
+
+// Defining PrintTo helps with Google Tests.
+static inline void PrintTo(const DisplaySettings& settings, ::std::ostream* os) {
+ *os << "DisplaySettings {";
+ *os << "\n .physicalDisplay = ";
+ PrintTo(settings.physicalDisplay, os);
+ *os << "\n .clip = ";
+ PrintTo(settings.clip, os);
+ *os << "\n .maxLuminance = " << settings.maxLuminance;
+ *os << "\n .outputDataspace = ";
+ PrintTo(settings.outputDataspace, os);
+ *os << "\n .colorTransform = " << settings.colorTransform;
+ *os << "\n .clearRegion = ";
+ PrintTo(settings.clearRegion, os);
+ *os << "\n .orientation = " << settings.orientation;
+ *os << "\n}";
+}
+
} // namespace renderengine
} // namespace android
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index b8bf801..95e9367 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -16,6 +16,8 @@
#pragma once
+#include <iosfwd>
+
#include <math/mat4.h>
#include <math/vec3.h>
#include <renderengine/Texture.h>
@@ -50,7 +52,7 @@
// Transform matrix to apply to texture coordinates.
mat4 textureTransform = mat4();
- // Wheteher to use pre-multiplied alpha
+ // Whether to use pre-multiplied alpha.
bool usePremultipliedAlpha = true;
// Override flag that alpha for each pixel in the buffer *must* be 1.0.
@@ -60,6 +62,8 @@
// HDR color-space setting for Y410.
bool isY410BT2020 = false;
+ float maxMasteringLuminance = 0.0;
+ float maxContentLuminance = 0.0;
};
// Metadata describing the layer geometry.
@@ -96,6 +100,33 @@
half3 solidColor = half3(0.0f, 0.0f, 0.0f);
};
+/*
+ * Contains the configuration for the shadows drawn by single layer. Shadow follows
+ * material design guidelines.
+ */
+struct ShadowSettings {
+ // Color to the ambient shadow. The alpha is premultiplied.
+ vec4 ambientColor = vec4();
+
+ // Color to the spot shadow. The alpha is premultiplied. The position of the spot shadow
+ // depends on the light position.
+ vec4 spotColor = vec4();
+
+ // Position of the light source used to cast the spot shadow.
+ vec3 lightPos = vec3();
+
+ // Radius of the spot light source. Smaller radius will have sharper edges,
+ // larger radius will have softer shadows
+ float lightRadius = 0.f;
+
+ // Length of the cast shadow. If length is <= 0.f no shadows will be drawn.
+ float length = 0.f;
+
+ // If true fill in the casting layer is translucent and the shadow needs to fill the bounds.
+ // Otherwise the shadow will only be drawn around the edges of the casting layer.
+ bool casterIsTranslucent = false;
+};
+
// The settings that RenderEngine requires for correctly rendering a Layer.
struct LayerSettings {
// Geometry information
@@ -116,7 +147,112 @@
// True if blending will be forced to be disabled.
bool disableBlending = false;
+
+ ShadowSettings shadow;
+
+ int backgroundBlurRadius = 0;
};
+// Keep in sync with custom comparison function in
+// compositionengine/impl/ClientCompositionRequestCache.cpp
+static inline bool operator==(const Buffer& lhs, const Buffer& rhs) {
+ return lhs.buffer == rhs.buffer && lhs.fence == rhs.fence &&
+ lhs.textureName == rhs.textureName &&
+ lhs.useTextureFiltering == rhs.useTextureFiltering &&
+ lhs.textureTransform == rhs.textureTransform &&
+ lhs.usePremultipliedAlpha == rhs.usePremultipliedAlpha &&
+ lhs.isOpaque == rhs.isOpaque && lhs.isY410BT2020 == rhs.isY410BT2020 &&
+ lhs.maxMasteringLuminance == rhs.maxMasteringLuminance &&
+ lhs.maxContentLuminance == rhs.maxContentLuminance;
+}
+
+static inline bool operator==(const Geometry& lhs, const Geometry& rhs) {
+ return lhs.boundaries == rhs.boundaries && lhs.positionTransform == rhs.positionTransform &&
+ lhs.roundedCornersRadius == rhs.roundedCornersRadius &&
+ lhs.roundedCornersCrop == rhs.roundedCornersCrop;
+}
+
+static inline bool operator==(const PixelSource& lhs, const PixelSource& rhs) {
+ return lhs.buffer == rhs.buffer && lhs.solidColor == rhs.solidColor;
+}
+
+static inline bool operator==(const ShadowSettings& lhs, const ShadowSettings& rhs) {
+ return lhs.ambientColor == rhs.ambientColor && lhs.spotColor == rhs.spotColor &&
+ lhs.lightPos == rhs.lightPos && lhs.lightRadius == rhs.lightRadius &&
+ lhs.length == rhs.length && lhs.casterIsTranslucent == rhs.casterIsTranslucent;
+}
+
+static inline bool operator==(const LayerSettings& lhs, const LayerSettings& rhs) {
+ return lhs.geometry == rhs.geometry && lhs.source == rhs.source && lhs.alpha == rhs.alpha &&
+ lhs.sourceDataspace == rhs.sourceDataspace &&
+ lhs.colorTransform == rhs.colorTransform &&
+ lhs.disableBlending == rhs.disableBlending && lhs.shadow == rhs.shadow &&
+ lhs.backgroundBlurRadius == rhs.backgroundBlurRadius;
+}
+
+// Defining PrintTo helps with Google Tests.
+
+static inline void PrintTo(const Buffer& settings, ::std::ostream* os) {
+ *os << "Buffer {";
+ *os << "\n .buffer = " << settings.buffer.get();
+ *os << "\n .fence = " << settings.fence.get();
+ *os << "\n .textureName = " << settings.textureName;
+ *os << "\n .useTextureFiltering = " << settings.useTextureFiltering;
+ *os << "\n .textureTransform = " << settings.textureTransform;
+ *os << "\n .usePremultipliedAlpha = " << settings.usePremultipliedAlpha;
+ *os << "\n .isOpaque = " << settings.isOpaque;
+ *os << "\n .isY410BT2020 = " << settings.isY410BT2020;
+ *os << "\n .maxMasteringLuminance = " << settings.maxMasteringLuminance;
+ *os << "\n .maxContentLuminance = " << settings.maxContentLuminance;
+ *os << "\n}";
+}
+
+static inline void PrintTo(const Geometry& settings, ::std::ostream* os) {
+ *os << "Geometry {";
+ *os << "\n .boundaries = ";
+ PrintTo(settings.boundaries, os);
+ *os << "\n .positionTransform = " << settings.positionTransform;
+ *os << "\n .roundedCornersRadius = " << settings.roundedCornersRadius;
+ *os << "\n .roundedCornersCrop = ";
+ PrintTo(settings.roundedCornersCrop, os);
+ *os << "\n}";
+}
+
+static inline void PrintTo(const PixelSource& settings, ::std::ostream* os) {
+ *os << "PixelSource {";
+ *os << "\n .buffer = ";
+ PrintTo(settings.buffer, os);
+ *os << "\n .solidColor = " << settings.solidColor;
+ *os << "\n}";
+}
+
+static inline void PrintTo(const ShadowSettings& settings, ::std::ostream* os) {
+ *os << "ShadowSettings {";
+ *os << "\n .ambientColor = " << settings.ambientColor;
+ *os << "\n .spotColor = " << settings.spotColor;
+ *os << "\n .lightPos = " << settings.lightPos;
+ *os << "\n .lightRadius = " << settings.lightRadius;
+ *os << "\n .length = " << settings.length;
+ *os << "\n .casterIsTranslucent = " << settings.casterIsTranslucent;
+ *os << "\n}";
+}
+
+static inline void PrintTo(const LayerSettings& settings, ::std::ostream* os) {
+ *os << "LayerSettings {";
+ *os << "\n .geometry = ";
+ PrintTo(settings.geometry, os);
+ *os << "\n .source = ";
+ PrintTo(settings.source, os);
+ *os << "\n .alpha = " << settings.alpha;
+ *os << "\n .sourceDataspace = ";
+ PrintTo(settings.sourceDataspace, os);
+ *os << "\n .colorTransform = " << settings.colorTransform;
+ *os << "\n .disableBlending = " << settings.disableBlending;
+ *os << "\n .backgroundBlurRadius = " << settings.backgroundBlurRadius;
+ *os << "\n .shadow = ";
+ PrintTo(settings.shadow, os);
+ *os << "\n}";
+}
+
} // namespace renderengine
} // namespace android
diff --git a/libs/renderengine/include/renderengine/Mesh.h b/libs/renderengine/include/renderengine/Mesh.h
index 7618424..167f13f 100644
--- a/libs/renderengine/include/renderengine/Mesh.h
+++ b/libs/renderengine/include/renderengine/Mesh.h
@@ -26,13 +26,14 @@
class Mesh {
public:
+ class Builder;
+
enum Primitive {
TRIANGLES = 0x0004, // GL_TRIANGLES
TRIANGLE_STRIP = 0x0005, // GL_TRIANGLE_STRIP
TRIANGLE_FAN = 0x0006 // GL_TRIANGLE_FAN
};
- Mesh(Primitive primitive, size_t vertexCount, size_t vertexSize, size_t texCoordsSize = 0);
~Mesh() = default;
/*
@@ -43,12 +44,20 @@
friend class Mesh;
float* mData;
size_t mStride;
+ size_t mOffset = 0;
VertexArray(float* data, size_t stride) : mData(data), mStride(stride) {}
public:
- TYPE& operator[](size_t index) { return *reinterpret_cast<TYPE*>(&mData[index * mStride]); }
+ // Returns a vertex array at an offset so its easier to append attributes from
+ // multiple sources.
+ VertexArray(VertexArray<TYPE>& other, size_t offset)
+ : mData(other.mData), mStride(other.mStride), mOffset(offset) {}
+
+ TYPE& operator[](size_t index) {
+ return *reinterpret_cast<TYPE*>(&mData[(index + mOffset) * mStride]);
+ }
TYPE const& operator[](size_t index) const {
- return *reinterpret_cast<TYPE const*>(&mData[index * mStride]);
+ return *reinterpret_cast<TYPE const*>(&mData[(index + mOffset) * mStride]);
}
};
@@ -67,6 +76,18 @@
return VertexArray<TYPE>(getCropCoords(), mStride);
}
+ template <typename TYPE>
+ VertexArray<TYPE> getShadowColorArray() {
+ return VertexArray<TYPE>(getShadowColor(), mStride);
+ }
+
+ template <typename TYPE>
+ VertexArray<TYPE> getShadowParamsArray() {
+ return VertexArray<TYPE>(getShadowParams(), mStride);
+ }
+
+ uint16_t* getIndicesArray() { return getIndices(); }
+
Primitive getPrimitive() const;
// returns a pointer to the vertices positions
@@ -78,6 +99,15 @@
// returns a pointer to the vertices crop coordinates
float const* getCropCoords() const;
+ // returns a pointer to colors
+ float const* getShadowColor() const;
+
+ // returns a pointer to the shadow params
+ float const* getShadowParams() const;
+
+ // returns a pointer to indices
+ uint16_t const* getIndices() const;
+
// number of vertices in this mesh
size_t getVertexCount() const;
@@ -87,6 +117,12 @@
// dimension of texture coordinates
size_t getTexCoordsSize() const;
+ size_t getShadowParamsSize() const;
+
+ size_t getShadowColorSize() const;
+
+ size_t getIndexCount() const;
+
// return stride in bytes
size_t getByteStride() const;
@@ -94,6 +130,8 @@
size_t getStride() const;
private:
+ Mesh(Primitive primitive, size_t vertexCount, size_t vertexSize, size_t texCoordSize,
+ size_t cropCoordsSize, size_t shadowColorSize, size_t shadowParamsSize, size_t indexCount);
Mesh(const Mesh&);
Mesh& operator=(const Mesh&);
Mesh const& operator=(const Mesh&) const;
@@ -101,13 +139,65 @@
float* getPositions();
float* getTexCoords();
float* getCropCoords();
+ float* getShadowColor();
+ float* getShadowParams();
+ uint16_t* getIndices();
std::vector<float> mVertices;
size_t mVertexCount;
size_t mVertexSize;
size_t mTexCoordsSize;
+ size_t mCropCoordsSize;
+ size_t mShadowColorSize;
+ size_t mShadowParamsSize;
size_t mStride;
Primitive mPrimitive;
+ std::vector<uint16_t> mIndices;
+ size_t mIndexCount;
+};
+
+class Mesh::Builder {
+public:
+ Builder& setPrimitive(Primitive primitive) {
+ mPrimitive = primitive;
+ return *this;
+ };
+ Builder& setVertices(size_t vertexCount, size_t vertexSize) {
+ mVertexCount = vertexCount;
+ mVertexSize = vertexSize;
+ return *this;
+ };
+ Builder& setTexCoords(size_t texCoordsSize) {
+ mTexCoordsSize = texCoordsSize;
+ return *this;
+ };
+ Builder& setCropCoords(size_t cropCoordsSize) {
+ mCropCoordsSize = cropCoordsSize;
+ return *this;
+ };
+ Builder& setShadowAttrs() {
+ mShadowParamsSize = 3;
+ mShadowColorSize = 4;
+ return *this;
+ };
+ Builder& setIndices(size_t indexCount) {
+ mIndexCount = indexCount;
+ return *this;
+ };
+ Mesh build() const {
+ return Mesh{mPrimitive, mVertexCount, mVertexSize, mTexCoordsSize,
+ mCropCoordsSize, mShadowColorSize, mShadowParamsSize, mIndexCount};
+ }
+
+private:
+ size_t mVertexCount = 0;
+ size_t mVertexSize = 0;
+ size_t mTexCoordsSize = 0;
+ size_t mCropCoordsSize = 0;
+ size_t mShadowColorSize = 0;
+ size_t mShadowParamsSize = 0;
+ size_t mIndexCount = 0;
+ Primitive mPrimitive;
};
} // namespace renderengine
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index c6a7bd8..e06e128 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -48,6 +48,7 @@
class Image;
class Mesh;
class Texture;
+struct RenderEngineCreationArgs;
namespace impl {
class RenderEngine;
@@ -60,16 +61,13 @@
class RenderEngine {
public:
- enum FeatureFlag {
- USE_COLOR_MANAGEMENT = 1 << 0, // Device manages color
- USE_HIGH_PRIORITY_CONTEXT = 1 << 1, // Use high priority context
-
- // Create a protected context when if possible
- ENABLE_PROTECTED_CONTEXT = 1 << 2,
+ enum class ContextPriority {
+ LOW = 1,
+ MEDIUM = 2,
+ HIGH = 3,
};
- static std::unique_ptr<impl::RenderEngine> create(int hwcFormat, uint32_t featureFlags,
- uint32_t imageCacheSize);
+ static std::unique_ptr<impl::RenderEngine> create(const RenderEngineCreationArgs& args);
virtual ~RenderEngine() = 0;
@@ -77,10 +75,6 @@
// This interface, while still in use until a suitable replacement is built,
// should be considered deprecated, minus some methods which still may be
// used to support legacy behavior.
-
- virtual std::unique_ptr<Framebuffer> createFramebuffer() = 0;
- virtual std::unique_ptr<Image> createImage() = 0;
-
virtual void primeCache() const = 0;
// dump the extension strings. always call the base class.
@@ -88,24 +82,6 @@
virtual bool useNativeFenceSync() const = 0;
virtual bool useWaitSync() const = 0;
-
- virtual bool isCurrent() const = 0;
-
- // helpers
- // flush submits RenderEngine command stream for execution and returns a
- // native fence fd that is signaled when the execution has completed. It
- // returns -1 on errors.
- virtual base::unique_fd flush() = 0;
- // finish waits until RenderEngine command stream has been executed. It
- // returns false on errors.
- virtual bool finish() = 0;
- // waitFence inserts a wait on an external fence fd to RenderEngine
- // command stream. It returns false on errors.
- virtual bool waitFence(base::unique_fd fenceFd) = 0;
-
- virtual void clearWithColor(float red, float green, float blue, float alpha) = 0;
- virtual void fillRegionWithColor(const Region& region, float red, float green, float blue,
- float alpha) = 0;
virtual void genTextures(size_t count, uint32_t* names) = 0;
virtual void deleteTextures(size_t count, uint32_t const* names) = 0;
virtual void bindExternalTextureImage(uint32_t texName, const Image& image) = 0;
@@ -135,40 +111,14 @@
// Returns NO_ERROR when binds successfully, NO_MEMORY when there's no memory for allocation.
virtual status_t bindFrameBuffer(Framebuffer* framebuffer) = 0;
virtual void unbindFrameBuffer(Framebuffer* framebuffer) = 0;
-
- // set-up
- virtual void checkErrors() const = 0;
- virtual void setViewportAndProjection(size_t vpw, size_t vph, Rect sourceCrop,
- ui::Transform::orientation_flags rotation) = 0;
- virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque, bool disableTexture,
- const half4& color, float cornerRadius) = 0;
- virtual void setupLayerTexturing(const Texture& texture) = 0;
- virtual void setupLayerBlackedOut() = 0;
- virtual void setupFillWithColor(float r, float g, float b, float a) = 0;
- // Sets up the crop size for corner radius clipping.
+ // Clean-up method that should be called on the main thread after the
+ // drawFence returned by drawLayers fires. This method will free up
+ // resources used by the most recently drawn frame. If the frame is still
+ // being drawn, then this call is silently ignored.
//
- // Having corner radius will force GPU composition on the layer and its children, drawing it
- // with a special shader. The shader will receive the radius and the crop rectangle as input,
- // modifying the opacity of the destination texture, multiplying it by a number between 0 and 1.
- // We query Layer#getRoundedCornerState() to retrieve the radius as well as the rounded crop
- // rectangle to figure out how to apply the radius for this layer. The crop rectangle will be
- // in local layer coordinate space, so we have to take the layer transform into account when
- // walking up the tree.
- virtual void setupCornerRadiusCropSize(float width, float height) = 0;
-
- // Set a color transform matrix that is applied in linear space right before OETF.
- virtual void setColorTransform(const mat4& /* colorTransform */) = 0;
- virtual void disableTexturing() = 0;
- virtual void disableBlending() = 0;
-
- // HDR and color management support
- virtual void setSourceY410BT2020(bool enable) = 0;
- virtual void setSourceDataSpace(ui::Dataspace source) = 0;
- virtual void setOutputDataSpace(ui::Dataspace dataspace) = 0;
- virtual void setDisplayMaxLuminance(const float maxLuminance) = 0;
-
- // drawing
- virtual void drawMesh(const Mesh& mesh) = 0;
+ // Returns true if resources were cleaned up, and false if we didn't need to
+ // do any work.
+ virtual bool cleanupPostRender() = 0;
// queries
virtual size_t getMaxTextureSize() const = 0;
@@ -212,7 +162,7 @@
// @return An error code indicating whether drawing was successful. For
// now, this always returns NO_ERROR.
virtual status_t drawLayers(const DisplaySettings& display,
- const std::vector<LayerSettings>& layers,
+ const std::vector<const LayerSettings*>& layers,
ANativeWindowBuffer* buffer, const bool useFramebufferCache,
base::unique_fd&& bufferFence, base::unique_fd* drawFence) = 0;
@@ -226,6 +176,85 @@
friend class BindNativeBufferAsFramebuffer;
};
+struct RenderEngineCreationArgs {
+ int pixelFormat;
+ uint32_t imageCacheSize;
+ bool useColorManagement;
+ bool enableProtectedContext;
+ bool precacheToneMapperShaderOnly;
+ bool supportsBackgroundBlur;
+ RenderEngine::ContextPriority contextPriority;
+
+ struct Builder;
+
+private:
+ // must be created by Builder via constructor with full argument list
+ RenderEngineCreationArgs(
+ int _pixelFormat,
+ uint32_t _imageCacheSize,
+ bool _useColorManagement,
+ bool _enableProtectedContext,
+ bool _precacheToneMapperShaderOnly,
+ bool _supportsBackgroundBlur,
+ RenderEngine::ContextPriority _contextPriority)
+ : pixelFormat(_pixelFormat)
+ , imageCacheSize(_imageCacheSize)
+ , useColorManagement(_useColorManagement)
+ , enableProtectedContext(_enableProtectedContext)
+ , precacheToneMapperShaderOnly(_precacheToneMapperShaderOnly)
+ , supportsBackgroundBlur(_supportsBackgroundBlur)
+ , contextPriority(_contextPriority) {}
+ RenderEngineCreationArgs() = delete;
+};
+
+struct RenderEngineCreationArgs::Builder {
+ Builder() {}
+
+ Builder& setPixelFormat(int pixelFormat) {
+ this->pixelFormat = pixelFormat;
+ return *this;
+ }
+ Builder& setImageCacheSize(uint32_t imageCacheSize) {
+ this->imageCacheSize = imageCacheSize;
+ return *this;
+ }
+ Builder& setUseColorManagerment(bool useColorManagement) {
+ this->useColorManagement = useColorManagement;
+ return *this;
+ }
+ Builder& setEnableProtectedContext(bool enableProtectedContext) {
+ this->enableProtectedContext = enableProtectedContext;
+ return *this;
+ }
+ Builder& setPrecacheToneMapperShaderOnly(bool precacheToneMapperShaderOnly) {
+ this->precacheToneMapperShaderOnly = precacheToneMapperShaderOnly;
+ return *this;
+ }
+ Builder& setSupportsBackgroundBlur(bool supportsBackgroundBlur) {
+ this->supportsBackgroundBlur = supportsBackgroundBlur;
+ return *this;
+ }
+ Builder& setContextPriority(RenderEngine::ContextPriority contextPriority) {
+ this->contextPriority = contextPriority;
+ return *this;
+ }
+ RenderEngineCreationArgs build() const {
+ return RenderEngineCreationArgs(pixelFormat, imageCacheSize, useColorManagement,
+ enableProtectedContext, precacheToneMapperShaderOnly,
+ supportsBackgroundBlur, contextPriority);
+ }
+
+private:
+ // 1 means RGBA_8888
+ int pixelFormat = 1;
+ uint32_t imageCacheSize = 0;
+ bool useColorManagement = true;
+ bool enableProtectedContext = false;
+ bool precacheToneMapperShaderOnly = false;
+ bool supportsBackgroundBlur = false;
+ RenderEngine::ContextPriority contextPriority = RenderEngine::ContextPriority::MEDIUM;
+};
+
class BindNativeBufferAsFramebuffer {
public:
BindNativeBufferAsFramebuffer(RenderEngine& engine, ANativeWindowBuffer* buffer,
@@ -259,8 +288,8 @@
bool useWaitSync() const override;
protected:
- RenderEngine(uint32_t featureFlags);
- const uint32_t mFeatureFlags;
+ RenderEngine(const RenderEngineCreationArgs& args);
+ const RenderEngineCreationArgs mArgs;
};
} // namespace impl
diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h
index b4d3ef2..df0f17a 100644
--- a/libs/renderengine/include/renderengine/mock/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -22,6 +22,7 @@
#include <renderengine/Mesh.h>
#include <renderengine/RenderEngine.h>
#include <renderengine/Texture.h>
+#include <ui/Fence.h>
#include <ui/GraphicBuffer.h>
#include <ui/Region.h>
@@ -34,20 +35,12 @@
RenderEngine();
~RenderEngine() override;
- MOCK_METHOD0(createFramebuffer, std::unique_ptr<renderengine::Framebuffer>());
- MOCK_METHOD0(createImage, std::unique_ptr<renderengine::Image>());
MOCK_METHOD0(getFramebufferForDrawing, Framebuffer*());
MOCK_CONST_METHOD0(primeCache, void());
MOCK_METHOD1(dump, void(std::string&));
MOCK_CONST_METHOD0(useNativeFenceSync, bool());
MOCK_CONST_METHOD0(useWaitSync, bool());
MOCK_CONST_METHOD0(isCurrent, bool());
- MOCK_METHOD0(flush, base::unique_fd());
- MOCK_METHOD0(finish, bool());
- MOCK_METHOD1(waitFence, bool(base::unique_fd*));
- bool waitFence(base::unique_fd fd) override { return waitFence(&fd); };
- MOCK_METHOD4(clearWithColor, void(float, float, float, float));
- MOCK_METHOD5(fillRegionWithColor, void(const Region&, float, float, float, float));
MOCK_METHOD2(genTextures, void(size_t, uint32_t*));
MOCK_METHOD2(deleteTextures, void(size_t, uint32_t const*));
MOCK_METHOD2(bindExternalTextureImage, void(uint32_t, const renderengine::Image&));
@@ -55,22 +48,6 @@
MOCK_METHOD3(bindExternalTextureBuffer,
status_t(uint32_t, const sp<GraphicBuffer>&, const sp<Fence>&));
MOCK_METHOD1(unbindExternalTextureBuffer, void(uint64_t));
- MOCK_CONST_METHOD0(checkErrors, void());
- MOCK_METHOD4(setViewportAndProjection,
- void(size_t, size_t, Rect, ui::Transform::orientation_flags));
- MOCK_METHOD5(setupLayerBlending, void(bool, bool, bool, const half4&, float));
- MOCK_METHOD1(setupLayerTexturing, void(const Texture&));
- MOCK_METHOD0(setupLayerBlackedOut, void());
- MOCK_METHOD4(setupFillWithColor, void(float, float, float, float));
- MOCK_METHOD2(setupCornerRadiusCropSize, void(float, float));
- MOCK_METHOD1(setColorTransform, void(const mat4&));
- MOCK_METHOD1(setSaturationMatrix, void(const mat4&));
- MOCK_METHOD0(disableTexturing, void());
- MOCK_METHOD0(disableBlending, void());
- MOCK_METHOD1(setSourceY410BT2020, void(bool));
- MOCK_METHOD1(setSourceDataSpace, void(ui::Dataspace));
- MOCK_METHOD1(setOutputDataSpace, void(ui::Dataspace));
- MOCK_METHOD1(setDisplayMaxLuminance, void(const float));
MOCK_METHOD1(bindFrameBuffer, status_t(renderengine::Framebuffer*));
MOCK_METHOD1(unbindFrameBuffer, void(renderengine::Framebuffer*));
MOCK_METHOD1(drawMesh, void(const renderengine::Mesh&));
@@ -79,8 +56,9 @@
MOCK_CONST_METHOD0(isProtected, bool());
MOCK_CONST_METHOD0(supportsProtectedContent, bool());
MOCK_METHOD1(useProtectedContext, bool(bool));
+ MOCK_METHOD0(cleanupPostRender, bool());
MOCK_METHOD6(drawLayers,
- status_t(const DisplaySettings&, const std::vector<LayerSettings>&,
+ status_t(const DisplaySettings&, const std::vector<const LayerSettings*>&,
ANativeWindowBuffer*, const bool, base::unique_fd&&, base::unique_fd*));
};
diff --git a/libs/renderengine/include/renderengine/private/Description.h b/libs/renderengine/include/renderengine/private/Description.h
index bd2055f..a62161a 100644
--- a/libs/renderengine/include/renderengine/private/Description.h
+++ b/libs/renderengine/include/renderengine/private/Description.h
@@ -71,6 +71,8 @@
TransferFunction outputTransferFunction = TransferFunction::LINEAR;
float displayMaxLuminance;
+ float maxMasteringLuminance;
+ float maxContentLuminance;
// projection matrix
mat4 projectionMatrix;
@@ -79,6 +81,9 @@
mat4 colorMatrix;
mat4 inputTransformMatrix;
mat4 outputTransformMatrix;
+
+ // True if this layer will draw a shadow.
+ bool drawShadows = false;
};
} // namespace renderengine
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index fce5e69..0b5b1e4 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -14,10 +14,16 @@
* limitations under the License.
*/
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
#include <chrono>
#include <condition_variable>
+#include <fstream>
#include <gtest/gtest.h>
+#include <cutils/properties.h>
#include <renderengine/RenderEngine.h>
#include <sync/sync.h>
#include <ui/PixelFormat.h>
@@ -26,14 +32,22 @@
constexpr int DEFAULT_DISPLAY_WIDTH = 128;
constexpr int DEFAULT_DISPLAY_HEIGHT = 256;
constexpr int DEFAULT_DISPLAY_OFFSET = 64;
+constexpr bool WRITE_BUFFER_TO_FILE_ON_FAILURE = false;
namespace android {
struct RenderEngineTest : public ::testing::Test {
static void SetUpTestSuite() {
- sRE = renderengine::gl::GLESRenderEngine::create(static_cast<int32_t>(
- ui::PixelFormat::RGBA_8888),
- 0, 1);
+ sRE = renderengine::gl::GLESRenderEngine::create(
+ renderengine::RenderEngineCreationArgs::Builder()
+ .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888))
+ .setImageCacheSize(1)
+ .setUseColorManagerment(false)
+ .setEnableProtectedContext(false)
+ .setPrecacheToneMapperShaderOnly(false)
+ .setSupportsBackgroundBlur(true)
+ .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM)
+ .build());
}
static void TearDownTestSuite() {
@@ -62,21 +76,80 @@
RenderEngineTest() { mBuffer = allocateDefaultBuffer(); }
~RenderEngineTest() {
+ if (WRITE_BUFFER_TO_FILE_ON_FAILURE && ::testing::Test::HasFailure()) {
+ writeBufferToFile("/data/texture_out_");
+ }
for (uint32_t texName : mTexNames) {
sRE->deleteTextures(1, &texName);
}
}
- void expectBufferColor(const Rect& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a,
- uint8_t tolerance = 0) {
+ void writeBufferToFile(const char* basename) {
+ std::string filename(basename);
+ filename.append(::testing::UnitTest::GetInstance()->current_test_info()->name());
+ filename.append(".ppm");
+ std::ofstream file(filename.c_str(), std::ios::binary);
+ if (!file.is_open()) {
+ ALOGE("Unable to open file: %s", filename.c_str());
+ ALOGE("You may need to do: \"adb shell setenforce 0\" to enable "
+ "surfaceflinger to write debug images");
+ return;
+ }
+
uint8_t* pixels;
mBuffer->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
reinterpret_cast<void**>(&pixels));
- auto colorCompare = [tolerance](uint8_t a, uint8_t b) {
- uint8_t tmp = a >= b ? a - b : b - a;
- return tmp <= tolerance;
+ file << "P6\n";
+ file << mBuffer->getWidth() << "\n";
+ file << mBuffer->getHeight() << "\n";
+ file << 255 << "\n";
+
+ std::vector<uint8_t> outBuffer(mBuffer->getWidth() * mBuffer->getHeight() * 3);
+ auto outPtr = reinterpret_cast<uint8_t*>(outBuffer.data());
+
+ for (int32_t j = 0; j < mBuffer->getHeight(); j++) {
+ const uint8_t* src = pixels + (mBuffer->getStride() * j) * 4;
+ for (int32_t i = 0; i < mBuffer->getWidth(); i++) {
+ // Only copy R, G and B components
+ outPtr[0] = src[0];
+ outPtr[1] = src[1];
+ outPtr[2] = src[2];
+ outPtr += 3;
+
+ src += 4;
+ }
+ }
+ file.write(reinterpret_cast<char*>(outBuffer.data()), outBuffer.size());
+ mBuffer->unlock();
+ }
+
+ void expectBufferColor(const Region& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
+ size_t c;
+ Rect const* rect = region.getArray(&c);
+ for (size_t i = 0; i < c; i++, rect++) {
+ expectBufferColor(*rect, r, g, b, a);
+ }
+ }
+
+ void expectBufferColor(const Rect& rect, uint8_t r, uint8_t g, uint8_t b, uint8_t a,
+ uint8_t tolerance = 0) {
+ auto colorCompare = [tolerance](const uint8_t* colorA, const uint8_t* colorB) {
+ auto colorBitCompare = [tolerance](uint8_t a, uint8_t b) {
+ uint8_t tmp = a >= b ? a - b : b - a;
+ return tmp <= tolerance;
+ };
+ return std::equal(colorA, colorA + 4, colorB, colorBitCompare);
};
+
+ expectBufferColor(rect, r, g, b, a, colorCompare);
+ }
+
+ void expectBufferColor(const Rect& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a,
+ std::function<bool(const uint8_t* a, const uint8_t* b)> colorCompare) {
+ uint8_t* pixels;
+ mBuffer->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+ reinterpret_cast<void**>(&pixels));
int32_t maxFails = 10;
int32_t fails = 0;
for (int32_t j = 0; j < region.getHeight(); j++) {
@@ -84,7 +157,7 @@
pixels + (mBuffer->getStride() * (region.top + j) + region.left) * 4;
for (int32_t i = 0; i < region.getWidth(); i++) {
const uint8_t expected[4] = {r, g, b, a};
- bool equal = std::equal(src, src + 4, expected, colorCompare);
+ bool equal = colorCompare(src, expected);
EXPECT_TRUE(equal)
<< "pixel @ (" << region.left + i << ", " << region.top + j << "): "
<< "expected (" << static_cast<uint32_t>(r) << ", "
@@ -105,6 +178,64 @@
mBuffer->unlock();
}
+ void expectAlpha(const Rect& rect, uint8_t a) {
+ auto colorCompare = [](const uint8_t* colorA, const uint8_t* colorB) {
+ return colorA[3] == colorB[3];
+ };
+ expectBufferColor(rect, 0.0f /* r */, 0.0f /*g */, 0.0f /* b */, a, colorCompare);
+ }
+
+ void expectShadowColor(const renderengine::LayerSettings& castingLayer,
+ const renderengine::ShadowSettings& shadow, const ubyte4& casterColor,
+ const ubyte4& backgroundColor) {
+ const Rect casterRect(castingLayer.geometry.boundaries);
+ Region casterRegion = Region(casterRect);
+ const float casterCornerRadius = castingLayer.geometry.roundedCornersRadius;
+ if (casterCornerRadius > 0.0f) {
+ // ignore the corners if a corner radius is set
+ Rect cornerRect(casterCornerRadius, casterCornerRadius);
+ casterRegion.subtractSelf(cornerRect.offsetTo(casterRect.left, casterRect.top));
+ casterRegion.subtractSelf(
+ cornerRect.offsetTo(casterRect.right - casterCornerRadius, casterRect.top));
+ casterRegion.subtractSelf(
+ cornerRect.offsetTo(casterRect.left, casterRect.bottom - casterCornerRadius));
+ casterRegion.subtractSelf(cornerRect.offsetTo(casterRect.right - casterCornerRadius,
+ casterRect.bottom - casterCornerRadius));
+ }
+
+ const float shadowInset = shadow.length * -1.0f;
+ const Rect casterWithShadow =
+ Rect(casterRect).inset(shadowInset, shadowInset, shadowInset, shadowInset);
+ const Region shadowRegion = Region(casterWithShadow).subtractSelf(casterRect);
+ const Region backgroundRegion = Region(fullscreenRect()).subtractSelf(casterWithShadow);
+
+ // verify casting layer
+ expectBufferColor(casterRegion, casterColor.r, casterColor.g, casterColor.b, casterColor.a);
+
+ // verify shadows by testing just the alpha since its difficult to validate the shadow color
+ size_t c;
+ Rect const* r = shadowRegion.getArray(&c);
+ for (size_t i = 0; i < c; i++, r++) {
+ expectAlpha(*r, 255);
+ }
+
+ // verify background
+ expectBufferColor(backgroundRegion, backgroundColor.r, backgroundColor.g, backgroundColor.b,
+ backgroundColor.a);
+ }
+
+ static renderengine::ShadowSettings getShadowSettings(const vec2& casterPos, float shadowLength,
+ bool casterIsTranslucent) {
+ renderengine::ShadowSettings shadow;
+ shadow.ambientColor = {0.0f, 0.0f, 0.0f, 0.039f};
+ shadow.spotColor = {0.0f, 0.0f, 0.0f, 0.19f};
+ shadow.lightPos = vec3(casterPos.x, casterPos.y, 0);
+ shadow.lightRadius = 0.0f;
+ shadow.length = shadowLength;
+ shadow.casterIsTranslucent = casterIsTranslucent;
+ return shadow;
+ }
+
static Rect fullscreenRect() { return Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT); }
static Rect offsetRect() {
@@ -118,7 +249,8 @@
}
void invokeDraw(renderengine::DisplaySettings settings,
- std::vector<renderengine::LayerSettings> layers, sp<GraphicBuffer> buffer) {
+ std::vector<const renderengine::LayerSettings*> layers,
+ sp<GraphicBuffer> buffer) {
base::unique_fd fence;
status_t status = sRE->drawLayers(settings, layers, buffer->getNativeBuffer(), true,
base::unique_fd(), &fence);
@@ -138,7 +270,7 @@
void drawEmptyLayers() {
renderengine::DisplaySettings settings;
- std::vector<renderengine::LayerSettings> layers;
+ std::vector<const renderengine::LayerSettings*> layers;
// Meaningless buffer since we don't do any drawing
sp<GraphicBuffer> buffer = new GraphicBuffer();
invokeDraw(settings, layers, buffer);
@@ -166,7 +298,7 @@
void fillBufferPhysicalOffset();
template <typename SourceVariant>
- void fillBufferCheckers(mat4 transform);
+ void fillBufferCheckers(uint32_t rotation);
template <typename SourceVariant>
void fillBufferCheckersRotate0();
@@ -199,6 +331,9 @@
void fillBufferWithRoundedCorners();
template <typename SourceVariant>
+ void fillBufferAndBlurBackground();
+
+ template <typename SourceVariant>
void overlayCorners();
void fillRedBufferTextureTransform();
@@ -219,6 +354,11 @@
void clearRegion();
+ template <typename SourceVariant>
+ void drawShadow(const renderengine::LayerSettings& castingLayer,
+ const renderengine::ShadowSettings& shadow, const ubyte4& casterColor,
+ const ubyte4& backgroundColor);
+
// Keep around the same renderengine object to save on initialization time.
// For now, exercise the GL backend directly so that some caching specifics
// can be tested without changing the interface.
@@ -301,14 +441,14 @@
settings.physicalDisplay = fullscreenRect();
settings.clip = fullscreenRect();
- std::vector<renderengine::LayerSettings> layers;
+ std::vector<const renderengine::LayerSettings*> layers;
renderengine::LayerSettings layer;
layer.geometry.boundaries = fullscreenRect().toFloatRect();
SourceVariant::fillColor(layer, r, g, b, this);
layer.alpha = a;
- layers.push_back(layer);
+ layers.push_back(&layer);
invokeDraw(settings, layers, mBuffer);
}
@@ -343,14 +483,14 @@
settings.physicalDisplay = offsetRect();
settings.clip = offsetRectAtZero();
- std::vector<renderengine::LayerSettings> layers;
+ std::vector<const renderengine::LayerSettings*> layers;
renderengine::LayerSettings layer;
layer.geometry.boundaries = offsetRectAtZero().toFloatRect();
SourceVariant::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
layer.alpha = 1.0f;
- layers.push_back(layer);
+ layers.push_back(&layer);
invokeDraw(settings, layers, mBuffer);
}
@@ -369,14 +509,14 @@
}
template <typename SourceVariant>
-void RenderEngineTest::fillBufferCheckers(mat4 transform) {
+void RenderEngineTest::fillBufferCheckers(uint32_t orientationFlag) {
renderengine::DisplaySettings settings;
settings.physicalDisplay = fullscreenRect();
// Here logical space is 2x2
settings.clip = Rect(2, 2);
- settings.globalTransform = transform;
+ settings.orientation = orientationFlag;
- std::vector<renderengine::LayerSettings> layers;
+ std::vector<const renderengine::LayerSettings*> layers;
renderengine::LayerSettings layerOne;
Rect rectOne(0, 0, 1, 1);
@@ -396,16 +536,16 @@
SourceVariant::fillColor(layerThree, 0.0f, 0.0f, 1.0f, this);
layerThree.alpha = 1.0f;
- layers.push_back(layerOne);
- layers.push_back(layerTwo);
- layers.push_back(layerThree);
+ layers.push_back(&layerOne);
+ layers.push_back(&layerTwo);
+ layers.push_back(&layerThree);
invokeDraw(settings, layers, mBuffer);
}
template <typename SourceVariant>
void RenderEngineTest::fillBufferCheckersRotate0() {
- fillBufferCheckers<SourceVariant>(mat4());
+ fillBufferCheckers<SourceVariant>(ui::Transform::ROT_0);
expectBufferColor(Rect(0, 0, DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2), 255, 0, 0,
255);
expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, 0, DEFAULT_DISPLAY_WIDTH,
@@ -421,8 +561,7 @@
template <typename SourceVariant>
void RenderEngineTest::fillBufferCheckersRotate90() {
- mat4 matrix = mat4(0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 1);
- fillBufferCheckers<SourceVariant>(matrix);
+ fillBufferCheckers<SourceVariant>(ui::Transform::ROT_90);
expectBufferColor(Rect(0, 0, DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2), 0, 255, 0,
255);
expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, 0, DEFAULT_DISPLAY_WIDTH,
@@ -438,8 +577,7 @@
template <typename SourceVariant>
void RenderEngineTest::fillBufferCheckersRotate180() {
- mat4 matrix = mat4(-1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 2, 2, 0, 1);
- fillBufferCheckers<SourceVariant>(matrix);
+ fillBufferCheckers<SourceVariant>(ui::Transform::ROT_180);
expectBufferColor(Rect(0, 0, DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2), 0, 0, 0,
0);
expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, 0, DEFAULT_DISPLAY_WIDTH,
@@ -455,8 +593,7 @@
template <typename SourceVariant>
void RenderEngineTest::fillBufferCheckersRotate270() {
- mat4 matrix = mat4(0, -1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 1);
- fillBufferCheckers<SourceVariant>(matrix);
+ fillBufferCheckers<SourceVariant>(ui::Transform::ROT_270);
expectBufferColor(Rect(0, 0, DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2), 0, 0, 255,
255);
expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, 0, DEFAULT_DISPLAY_WIDTH,
@@ -477,7 +614,7 @@
// Here logical space is 2x2
settings.clip = Rect(2, 2);
- std::vector<renderengine::LayerSettings> layers;
+ std::vector<const renderengine::LayerSettings*> layers;
renderengine::LayerSettings layer;
layer.geometry.boundaries = Rect(1, 1).toFloatRect();
@@ -487,7 +624,7 @@
layer.source.solidColor = half3(1.0f, 0.0f, 0.0f);
layer.alpha = 1.0f;
- layers.push_back(layer);
+ layers.push_back(&layer);
invokeDraw(settings, layers, mBuffer);
}
@@ -508,7 +645,7 @@
settings.physicalDisplay = fullscreenRect();
settings.clip = Rect(1, 1);
- std::vector<renderengine::LayerSettings> layers;
+ std::vector<const renderengine::LayerSettings*> layers;
renderengine::LayerSettings layer;
layer.geometry.boundaries = Rect(1, 1).toFloatRect();
@@ -524,7 +661,7 @@
layer.alpha = 1.0f;
layer.geometry.boundaries = Rect(1, 1).toFloatRect();
- layers.push_back(layer);
+ layers.push_back(&layer);
invokeDraw(settings, layers, mBuffer);
}
@@ -541,7 +678,7 @@
settings.physicalDisplay = fullscreenRect();
settings.clip = fullscreenRect();
- std::vector<renderengine::LayerSettings> layers;
+ std::vector<const renderengine::LayerSettings*> layers;
renderengine::LayerSettings layer;
layer.geometry.boundaries = fullscreenRect().toFloatRect();
@@ -550,7 +687,7 @@
SourceVariant::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
layer.alpha = 1.0f;
- layers.push_back(layer);
+ layers.push_back(&layer);
invokeDraw(settings, layers, mBuffer);
}
@@ -572,12 +709,57 @@
}
template <typename SourceVariant>
+void RenderEngineTest::fillBufferAndBlurBackground() {
+ char value[PROPERTY_VALUE_MAX];
+ property_get("ro.surface_flinger.supports_background_blur", value, "0");
+ if (!atoi(value)) {
+ // This device doesn't support blurs, no-op.
+ return;
+ }
+
+ auto blurRadius = 50;
+ auto center = DEFAULT_DISPLAY_WIDTH / 2;
+
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = fullscreenRect();
+
+ std::vector<const renderengine::LayerSettings*> layers;
+
+ renderengine::LayerSettings backgroundLayer;
+ backgroundLayer.geometry.boundaries = fullscreenRect().toFloatRect();
+ SourceVariant::fillColor(backgroundLayer, 0.0f, 1.0f, 0.0f, this);
+ backgroundLayer.alpha = 1.0f;
+ layers.push_back(&backgroundLayer);
+
+ renderengine::LayerSettings leftLayer;
+ leftLayer.geometry.boundaries =
+ Rect(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT).toFloatRect();
+ SourceVariant::fillColor(leftLayer, 1.0f, 0.0f, 0.0f, this);
+ leftLayer.alpha = 1.0f;
+ layers.push_back(&leftLayer);
+
+ renderengine::LayerSettings blurLayer;
+ blurLayer.geometry.boundaries = fullscreenRect().toFloatRect();
+ blurLayer.backgroundBlurRadius = blurRadius;
+ blurLayer.alpha = 0;
+ layers.push_back(&blurLayer);
+
+ invokeDraw(settings, layers, mBuffer);
+
+ expectBufferColor(Rect(center - 1, center - 5, center, center + 5), 150, 150, 0, 255,
+ 50 /* tolerance */);
+ expectBufferColor(Rect(center, center - 5, center + 1, center + 5), 150, 150, 0, 255,
+ 50 /* tolerance */);
+}
+
+template <typename SourceVariant>
void RenderEngineTest::overlayCorners() {
renderengine::DisplaySettings settings;
settings.physicalDisplay = fullscreenRect();
settings.clip = fullscreenRect();
- std::vector<renderengine::LayerSettings> layersFirst;
+ std::vector<const renderengine::LayerSettings*> layersFirst;
renderengine::LayerSettings layerOne;
layerOne.geometry.boundaries =
@@ -585,14 +767,14 @@
SourceVariant::fillColor(layerOne, 1.0f, 0.0f, 0.0f, this);
layerOne.alpha = 0.2;
- layersFirst.push_back(layerOne);
+ layersFirst.push_back(&layerOne);
invokeDraw(settings, layersFirst, mBuffer);
expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 3, DEFAULT_DISPLAY_HEIGHT / 3), 51, 0, 0, 51);
expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 3 + 1, DEFAULT_DISPLAY_HEIGHT / 3 + 1,
DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
0, 0, 0, 0);
- std::vector<renderengine::LayerSettings> layersSecond;
+ std::vector<const renderengine::LayerSettings*> layersSecond;
renderengine::LayerSettings layerTwo;
layerTwo.geometry.boundaries =
FloatRect(DEFAULT_DISPLAY_WIDTH / 3.0, DEFAULT_DISPLAY_HEIGHT / 3.0,
@@ -600,7 +782,7 @@
SourceVariant::fillColor(layerTwo, 0.0f, 1.0f, 0.0f, this);
layerTwo.alpha = 1.0f;
- layersSecond.push_back(layerTwo);
+ layersSecond.push_back(&layerTwo);
invokeDraw(settings, layersSecond, mBuffer);
expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 3, DEFAULT_DISPLAY_HEIGHT / 3), 0, 0, 0, 0);
@@ -614,7 +796,7 @@
settings.physicalDisplay = fullscreenRect();
settings.clip = Rect(1, 1);
- std::vector<renderengine::LayerSettings> layers;
+ std::vector<const renderengine::LayerSettings*> layers;
renderengine::LayerSettings layer;
// Here will allocate a checker board texture, but transform texture
@@ -649,7 +831,7 @@
layer.alpha = 1.0f;
layer.geometry.boundaries = Rect(1, 1).toFloatRect();
- layers.push_back(layer);
+ layers.push_back(&layer);
invokeDraw(settings, layers, mBuffer);
}
@@ -665,7 +847,7 @@
// Here logical space is 1x1
settings.clip = Rect(1, 1);
- std::vector<renderengine::LayerSettings> layers;
+ std::vector<const renderengine::LayerSettings*> layers;
renderengine::LayerSettings layer;
sp<GraphicBuffer> buf = allocateSourceBuffer(1, 1);
@@ -688,7 +870,7 @@
layer.alpha = 0.5f;
layer.geometry.boundaries = Rect(1, 1).toFloatRect();
- layers.push_back(layer);
+ layers.push_back(&layer);
invokeDraw(settings, layers, mBuffer);
}
@@ -704,7 +886,7 @@
// Here logical space is 1x1
settings.clip = Rect(1, 1);
- std::vector<renderengine::LayerSettings> layers;
+ std::vector<const renderengine::LayerSettings*> layers;
renderengine::LayerSettings layer;
sp<GraphicBuffer> buf = allocateSourceBuffer(1, 1);
@@ -727,7 +909,7 @@
layer.alpha = 0.5f;
layer.geometry.boundaries = Rect(1, 1).toFloatRect();
- layers.push_back(layer);
+ layers.push_back(&layer);
invokeDraw(settings, layers, mBuffer);
}
@@ -742,12 +924,11 @@
settings.physicalDisplay = fullscreenRect();
// Here logical space is 4x4
settings.clip = Rect(4, 4);
- settings.globalTransform = mat4::scale(vec4(2, 4, 0, 1));
- settings.clearRegion = Region(Rect(1, 1));
- std::vector<renderengine::LayerSettings> layers;
- // dummy layer, without bounds should not render anything
+ settings.clearRegion = Region(Rect(2, 4));
+ std::vector<const renderengine::LayerSettings*> layers;
+ // fake layer, without bounds should not render anything
renderengine::LayerSettings layer;
- layers.push_back(layer);
+ layers.push_back(&layer);
invokeDraw(settings, layers, mBuffer);
}
@@ -760,17 +941,51 @@
0, 0, 0, 0);
}
+template <typename SourceVariant>
+void RenderEngineTest::drawShadow(const renderengine::LayerSettings& castingLayer,
+ const renderengine::ShadowSettings& shadow,
+ const ubyte4& casterColor, const ubyte4& backgroundColor) {
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = fullscreenRect();
+
+ std::vector<const renderengine::LayerSettings*> layers;
+
+ // add background layer
+ renderengine::LayerSettings bgLayer;
+ bgLayer.geometry.boundaries = fullscreenRect().toFloatRect();
+ ColorSourceVariant::fillColor(bgLayer, backgroundColor.r / 255.0f, backgroundColor.g / 255.0f,
+ backgroundColor.b / 255.0f, this);
+ bgLayer.alpha = backgroundColor.a / 255.0f;
+ layers.push_back(&bgLayer);
+
+ // add shadow layer
+ renderengine::LayerSettings shadowLayer;
+ shadowLayer.geometry.boundaries = castingLayer.geometry.boundaries;
+ shadowLayer.alpha = castingLayer.alpha;
+ shadowLayer.shadow = shadow;
+ layers.push_back(&shadowLayer);
+
+ // add layer casting the shadow
+ renderengine::LayerSettings layer = castingLayer;
+ SourceVariant::fillColor(layer, casterColor.r / 255.0f, casterColor.g / 255.0f,
+ casterColor.b / 255.0f, this);
+ layers.push_back(&layer);
+
+ invokeDraw(settings, layers, mBuffer);
+}
+
TEST_F(RenderEngineTest, drawLayers_noLayersToDraw) {
drawEmptyLayers();
}
TEST_F(RenderEngineTest, drawLayers_nullOutputBuffer) {
renderengine::DisplaySettings settings;
- std::vector<renderengine::LayerSettings> layers;
+ std::vector<const renderengine::LayerSettings*> layers;
renderengine::LayerSettings layer;
layer.geometry.boundaries = fullscreenRect().toFloatRect();
BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
- layers.push_back(layer);
+ layers.push_back(&layer);
base::unique_fd fence;
status_t status = sRE->drawLayers(settings, layers, nullptr, true, base::unique_fd(), &fence);
@@ -782,12 +997,12 @@
settings.physicalDisplay = fullscreenRect();
settings.clip = fullscreenRect();
- std::vector<renderengine::LayerSettings> layers;
+ std::vector<const renderengine::LayerSettings*> layers;
renderengine::LayerSettings layer;
layer.geometry.boundaries = fullscreenRect().toFloatRect();
BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
layer.alpha = 1.0;
- layers.push_back(layer);
+ layers.push_back(&layer);
status_t status = sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(), true,
base::unique_fd(), nullptr);
@@ -801,12 +1016,12 @@
settings.physicalDisplay = fullscreenRect();
settings.clip = fullscreenRect();
- std::vector<renderengine::LayerSettings> layers;
+ std::vector<const renderengine::LayerSettings*> layers;
renderengine::LayerSettings layer;
layer.geometry.boundaries = fullscreenRect().toFloatRect();
BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
layer.alpha = 1.0;
- layers.push_back(layer);
+ layers.push_back(&layer);
status_t status = sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(), false,
base::unique_fd(), nullptr);
@@ -864,6 +1079,10 @@
fillBufferWithRoundedCorners<ColorSourceVariant>();
}
+TEST_F(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_colorSource) {
+ fillBufferAndBlurBackground<ColorSourceVariant>();
+}
+
TEST_F(RenderEngineTest, drawLayers_overlayCorners_colorSource) {
overlayCorners<ColorSourceVariant>();
}
@@ -916,6 +1135,10 @@
fillBufferWithRoundedCorners<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
+TEST_F(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_opaqueBufferSource) {
+ fillBufferAndBlurBackground<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
TEST_F(RenderEngineTest, drawLayers_overlayCorners_opaqueBufferSource) {
overlayCorners<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
@@ -968,6 +1191,10 @@
fillBufferWithRoundedCorners<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
+TEST_F(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_bufferSource) {
+ fillBufferAndBlurBackground<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
TEST_F(RenderEngineTest, drawLayers_overlayCorners_bufferSource) {
overlayCorners<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
@@ -993,13 +1220,13 @@
settings.physicalDisplay = fullscreenRect();
settings.clip = fullscreenRect();
- std::vector<renderengine::LayerSettings> layers;
+ std::vector<const renderengine::LayerSettings*> layers;
renderengine::LayerSettings layer;
layer.geometry.boundaries = fullscreenRect().toFloatRect();
BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
- layers.push_back(layer);
+ layers.push_back(&layer);
invokeDraw(settings, layers, mBuffer);
uint64_t bufferId = layer.source.buffer.buffer->getId();
EXPECT_TRUE(sRE->isImageCachedForTesting(bufferId));
@@ -1014,12 +1241,12 @@
EXPECT_EQ(NO_ERROR, barrier->result);
}
-TEST_F(RenderEngineTest, drawLayers_bindExternalBufferWithNullBuffer) {
+TEST_F(RenderEngineTest, bindExternalBuffer_withNullBuffer) {
status_t result = sRE->bindExternalTextureBuffer(0, nullptr, nullptr);
ASSERT_EQ(BAD_VALUE, result);
}
-TEST_F(RenderEngineTest, drawLayers_bindExternalBufferCachesImages) {
+TEST_F(RenderEngineTest, bindExternalBuffer_cachesImages) {
sp<GraphicBuffer> buf = allocateSourceBuffer(1, 1);
uint32_t texName;
sRE->genTextures(1, &texName);
@@ -1039,7 +1266,7 @@
EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId));
}
-TEST_F(RenderEngineTest, drawLayers_cacheExternalBufferWithNullBuffer) {
+TEST_F(RenderEngineTest, cacheExternalBuffer_withNullBuffer) {
std::shared_ptr<renderengine::gl::ImageManager::Barrier> barrier =
sRE->cacheExternalTextureBufferForTesting(nullptr);
std::lock_guard<std::mutex> lock(barrier->mutex);
@@ -1051,7 +1278,7 @@
EXPECT_EQ(BAD_VALUE, barrier->result);
}
-TEST_F(RenderEngineTest, drawLayers_cacheExternalBufferCachesImages) {
+TEST_F(RenderEngineTest, cacheExternalBuffer_cachesImages) {
sp<GraphicBuffer> buf = allocateSourceBuffer(1, 1);
uint64_t bufferId = buf->getId();
std::shared_ptr<renderengine::gl::ImageManager::Barrier> barrier =
@@ -1077,4 +1304,133 @@
EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId));
}
+TEST_F(RenderEngineTest, drawLayers_fillShadow_casterLayerMinSize) {
+ const ubyte4 casterColor(255, 0, 0, 255);
+ const ubyte4 backgroundColor(255, 255, 255, 255);
+ const float shadowLength = 5.0f;
+ Rect casterBounds(1, 1);
+ casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
+ renderengine::LayerSettings castingLayer;
+ castingLayer.geometry.boundaries = casterBounds.toFloatRect();
+ castingLayer.alpha = 1.0f;
+ renderengine::ShadowSettings settings =
+ getShadowSettings(vec2(casterBounds.left, casterBounds.top), shadowLength,
+ false /* casterIsTranslucent */);
+
+ drawShadow<ColorSourceVariant>(castingLayer, settings, casterColor, backgroundColor);
+ expectShadowColor(castingLayer, settings, casterColor, backgroundColor);
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillShadow_casterColorLayer) {
+ const ubyte4 casterColor(255, 0, 0, 255);
+ const ubyte4 backgroundColor(255, 255, 255, 255);
+ const float shadowLength = 5.0f;
+ Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f);
+ casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
+ renderengine::LayerSettings castingLayer;
+ castingLayer.geometry.boundaries = casterBounds.toFloatRect();
+ castingLayer.alpha = 1.0f;
+ renderengine::ShadowSettings settings =
+ getShadowSettings(vec2(casterBounds.left, casterBounds.top), shadowLength,
+ false /* casterIsTranslucent */);
+
+ drawShadow<ColorSourceVariant>(castingLayer, settings, casterColor, backgroundColor);
+ expectShadowColor(castingLayer, settings, casterColor, backgroundColor);
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillShadow_casterOpaqueBufferLayer) {
+ const ubyte4 casterColor(255, 0, 0, 255);
+ const ubyte4 backgroundColor(255, 255, 255, 255);
+ const float shadowLength = 5.0f;
+ Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f);
+ casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
+ renderengine::LayerSettings castingLayer;
+ castingLayer.geometry.boundaries = casterBounds.toFloatRect();
+ castingLayer.alpha = 1.0f;
+ renderengine::ShadowSettings settings =
+ getShadowSettings(vec2(casterBounds.left, casterBounds.top), shadowLength,
+ false /* casterIsTranslucent */);
+
+ drawShadow<BufferSourceVariant<ForceOpaqueBufferVariant>>(castingLayer, settings, casterColor,
+ backgroundColor);
+ expectShadowColor(castingLayer, settings, casterColor, backgroundColor);
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillShadow_casterWithRoundedCorner) {
+ const ubyte4 casterColor(255, 0, 0, 255);
+ const ubyte4 backgroundColor(255, 255, 255, 255);
+ const float shadowLength = 5.0f;
+ Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f);
+ casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
+ renderengine::LayerSettings castingLayer;
+ castingLayer.geometry.boundaries = casterBounds.toFloatRect();
+ castingLayer.geometry.roundedCornersRadius = 3.0f;
+ castingLayer.geometry.roundedCornersCrop = casterBounds.toFloatRect();
+ castingLayer.alpha = 1.0f;
+ renderengine::ShadowSettings settings =
+ getShadowSettings(vec2(casterBounds.left, casterBounds.top), shadowLength,
+ false /* casterIsTranslucent */);
+
+ drawShadow<BufferSourceVariant<ForceOpaqueBufferVariant>>(castingLayer, settings, casterColor,
+ backgroundColor);
+ expectShadowColor(castingLayer, settings, casterColor, backgroundColor);
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillShadow_translucentCasterWithAlpha) {
+ const ubyte4 casterColor(255, 0, 0, 255);
+ const ubyte4 backgroundColor(255, 255, 255, 255);
+ const float shadowLength = 5.0f;
+ Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f);
+ casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
+ renderengine::LayerSettings castingLayer;
+ castingLayer.geometry.boundaries = casterBounds.toFloatRect();
+ castingLayer.alpha = 0.5f;
+ renderengine::ShadowSettings settings =
+ getShadowSettings(vec2(casterBounds.left, casterBounds.top), shadowLength,
+ true /* casterIsTranslucent */);
+
+ drawShadow<BufferSourceVariant<RelaxOpaqueBufferVariant>>(castingLayer, settings, casterColor,
+ backgroundColor);
+
+ // verify only the background since the shadow will draw behind the caster
+ const float shadowInset = settings.length * -1.0f;
+ const Rect casterWithShadow =
+ Rect(casterBounds).inset(shadowInset, shadowInset, shadowInset, shadowInset);
+ const Region backgroundRegion = Region(fullscreenRect()).subtractSelf(casterWithShadow);
+ expectBufferColor(backgroundRegion, backgroundColor.r, backgroundColor.g, backgroundColor.b,
+ backgroundColor.a);
+}
+
+TEST_F(RenderEngineTest, cleanupPostRender_cleansUpOnce) {
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = fullscreenRect();
+
+ std::vector<const renderengine::LayerSettings*> layers;
+ renderengine::LayerSettings layer;
+ layer.geometry.boundaries = fullscreenRect().toFloatRect();
+ BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
+ layer.alpha = 1.0;
+ layers.push_back(&layer);
+
+ base::unique_fd fenceOne;
+ sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(), true, base::unique_fd(),
+ &fenceOne);
+ base::unique_fd fenceTwo;
+ sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(), true, std::move(fenceOne),
+ &fenceTwo);
+
+ const int fd = fenceTwo.get();
+ if (fd >= 0) {
+ sync_wait(fd, -1);
+ }
+
+ // Only cleanup the first time.
+ EXPECT_TRUE(sRE->cleanupPostRender());
+ EXPECT_FALSE(sRE->cleanupPostRender());
+}
+
} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/libs/sensor/ISensorServer.cpp b/libs/sensor/ISensorServer.cpp
index 5200545..8ed09f8 100644
--- a/libs/sensor/ISensorServer.cpp
+++ b/libs/sensor/ISensorServer.cpp
@@ -199,6 +199,10 @@
int32_t type = data.readInt32();
int32_t format = data.readInt32();
native_handle_t *resource = data.readNativeHandle();
+ // Avoid a crash in native_handle_close if resource is nullptr
+ if (resource == nullptr) {
+ return BAD_VALUE;
+ }
sp<ISensorEventConnection> ch =
createSensorDirectConnection(opPackageName, size, type, format, resource);
native_handle_close(resource);
diff --git a/libs/sensor/Sensor.cpp b/libs/sensor/Sensor.cpp
index abc9103..9d817ae 100644
--- a/libs/sensor/Sensor.cpp
+++ b/libs/sensor/Sensor.cpp
@@ -268,6 +268,10 @@
mStringType = SENSOR_STRING_TYPE_ACCELEROMETER_UNCALIBRATED;
mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
break;
+ case SENSOR_TYPE_HINGE_ANGLE:
+ mStringType = SENSOR_STRING_TYPE_HINGE_ANGLE;
+ mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
+ break;
default:
// Only pipe the stringType, requiredPermission and flags for custom sensors.
if (halVersion > SENSORS_DEVICE_API_VERSION_1_0 && hwSensor.stringType) {
diff --git a/libs/sensor/SensorManager.cpp b/libs/sensor/SensorManager.cpp
index bf8b9f7..a4a5d13 100644
--- a/libs/sensor/SensorManager.cpp
+++ b/libs/sensor/SensorManager.cpp
@@ -209,7 +209,7 @@
type == SENSOR_TYPE_TILT_DETECTOR || type == SENSOR_TYPE_WAKE_GESTURE ||
type == SENSOR_TYPE_GLANCE_GESTURE || type == SENSOR_TYPE_PICK_UP_GESTURE ||
type == SENSOR_TYPE_WRIST_TILT_GESTURE ||
- type == SENSOR_TYPE_LOW_LATENCY_OFFBODY_DETECT) {
+ type == SENSOR_TYPE_LOW_LATENCY_OFFBODY_DETECT || type == SENSOR_TYPE_HINGE_ANGLE) {
wakeUpSensor = true;
}
// For now we just return the first sensor of that type we find.
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index 8388743..1ee8c71 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -36,9 +36,6 @@
srcs: [
"ColorSpace.cpp",
- "BufferHubBuffer.cpp",
- "BufferHubEventFd.cpp",
- "BufferHubMetadata.cpp",
"DebugUtils.cpp",
"Fence.cpp",
"FenceTime.cpp",
@@ -46,6 +43,7 @@
"Gralloc.cpp",
"Gralloc2.cpp",
"Gralloc3.cpp",
+ "Gralloc4.cpp",
"GraphicBuffer.cpp",
"GraphicBufferAllocator.cpp",
"GraphicBufferMapper.cpp",
@@ -62,20 +60,27 @@
include_dirs: [
"frameworks/native/include",
],
+ export_include_dirs: [
+ "include",
+ "include_private",
+ ],
// Uncomment the following line to enable VALIDATE_REGIONS traces
//defaults: ["libui-validate-regions-defaults"],
shared_libs: [
- "android.frameworks.bufferhub@1.0",
"android.hardware.graphics.allocator@2.0",
"android.hardware.graphics.allocator@3.0",
+ "android.hardware.graphics.allocator@4.0",
+ "android.hardware.graphics.common-ndk_platform",
"android.hardware.graphics.common@1.2",
"android.hardware.graphics.mapper@2.0",
"android.hardware.graphics.mapper@2.1",
"android.hardware.graphics.mapper@3.0",
+ "android.hardware.graphics.mapper@4.0",
"libbase",
"libcutils",
+ "libgralloctypes",
"libhidlbase",
"libsync",
"libutils",
@@ -84,6 +89,9 @@
export_shared_lib_headers: [
"android.hardware.graphics.common@1.2",
+ "android.hardware.graphics.common-ndk_platform",
+ "android.hardware.graphics.mapper@4.0",
+ "libgralloctypes",
],
static_libs: [
@@ -97,30 +105,20 @@
vendor: {
cflags: ["-DLIBUI_IN_VNDK"],
exclude_srcs: [
- "BufferHubBuffer.cpp",
- "BufferHubEventFd.cpp",
- "BufferHubMetadata.cpp",
],
exclude_header_libs: [
- "libbufferhub_headers",
- "libdvr_headers",
],
exclude_shared_libs: [
- "android.frameworks.bufferhub@1.0",
- "libpdx_default_transport",
],
},
},
header_libs: [
"libbase_headers",
- "libbufferhub_headers",
- "libdvr_headers",
"libnativebase_headers",
"libnativewindow_headers",
"libhardware_headers",
"libui_headers",
- "libpdx_headers",
],
export_static_lib_headers: [
@@ -168,3 +166,11 @@
"tests",
"tools",
]
+
+filegroup {
+ name: "libui_host_common",
+ srcs: [
+ "Rect.cpp",
+ "PixelFormat.cpp"
+ ],
+}
diff --git a/libs/ui/BufferHubBuffer.cpp b/libs/ui/BufferHubBuffer.cpp
deleted file mode 100644
index da91a97..0000000
--- a/libs/ui/BufferHubBuffer.cpp
+++ /dev/null
@@ -1,357 +0,0 @@
-/*
- * Copyright (C) 2018 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 <poll.h>
-
-#include <android-base/unique_fd.h>
-#include <android/frameworks/bufferhub/1.0/IBufferHub.h>
-#include <log/log.h>
-#include <ui/BufferHubBuffer.h>
-#include <ui/BufferHubDefs.h>
-#include <utils/Trace.h>
-
-using ::android::base::unique_fd;
-using ::android::BufferHubDefs::isAnyClientAcquired;
-using ::android::BufferHubDefs::isAnyClientGained;
-using ::android::BufferHubDefs::isClientAcquired;
-using ::android::BufferHubDefs::isClientGained;
-using ::android::BufferHubDefs::isClientPosted;
-using ::android::BufferHubDefs::isClientReleased;
-using ::android::frameworks::bufferhub::V1_0::BufferHubStatus;
-using ::android::frameworks::bufferhub::V1_0::BufferTraits;
-using ::android::frameworks::bufferhub::V1_0::IBufferClient;
-using ::android::frameworks::bufferhub::V1_0::IBufferHub;
-using ::android::hardware::hidl_handle;
-using ::android::hardware::graphics::common::V1_2::HardwareBufferDescription;
-
-namespace android {
-
-std::unique_ptr<BufferHubBuffer> BufferHubBuffer::create(uint32_t width, uint32_t height,
- uint32_t layerCount, uint32_t format,
- uint64_t usage, size_t userMetadataSize) {
- auto buffer = std::unique_ptr<BufferHubBuffer>(
- new BufferHubBuffer(width, height, layerCount, format, usage, userMetadataSize));
- return buffer->isValid() ? std::move(buffer) : nullptr;
-}
-
-std::unique_ptr<BufferHubBuffer> BufferHubBuffer::import(const sp<NativeHandle>& token) {
- if (token == nullptr || token.get() == nullptr) {
- ALOGE("%s: token cannot be nullptr!", __FUNCTION__);
- return nullptr;
- }
-
- auto buffer = std::unique_ptr<BufferHubBuffer>(new BufferHubBuffer(token));
- return buffer->isValid() ? std::move(buffer) : nullptr;
-}
-
-BufferHubBuffer::BufferHubBuffer(uint32_t width, uint32_t height, uint32_t layerCount,
- uint32_t format, uint64_t usage, size_t userMetadataSize) {
- ATRACE_CALL();
- ALOGD("%s: width=%u height=%u layerCount=%u, format=%u "
- "usage=%" PRIx64 " mUserMetadataSize=%zu",
- __FUNCTION__, width, height, layerCount, format, usage, userMetadataSize);
-
- sp<IBufferHub> bufferhub = IBufferHub::getService();
- if (bufferhub.get() == nullptr) {
- ALOGE("%s: BufferHub service not found!", __FUNCTION__);
- return;
- }
-
- AHardwareBuffer_Desc aDesc = {width, height, layerCount, format,
- usage, /*stride=*/0UL, /*rfu0=*/0UL, /*rfu1=*/0ULL};
- HardwareBufferDescription desc;
- memcpy(&desc, &aDesc, sizeof(HardwareBufferDescription));
-
- BufferHubStatus ret;
- sp<IBufferClient> client;
- BufferTraits bufferTraits;
- IBufferHub::allocateBuffer_cb allocCb = [&](const auto& status, const auto& outClient,
- const auto& outTraits) {
- ret = status;
- client = std::move(outClient);
- bufferTraits = std::move(outTraits);
- };
-
- if (!bufferhub->allocateBuffer(desc, static_cast<uint32_t>(userMetadataSize), allocCb).isOk()) {
- ALOGE("%s: allocateBuffer transaction failed!", __FUNCTION__);
- return;
- } else if (ret != BufferHubStatus::NO_ERROR) {
- ALOGE("%s: allocateBuffer failed with error %u.", __FUNCTION__, ret);
- return;
- } else if (client == nullptr) {
- ALOGE("%s: allocateBuffer got null BufferClient.", __FUNCTION__);
- return;
- }
-
- const int importRet = initWithBufferTraits(bufferTraits);
- if (importRet < 0) {
- ALOGE("%s: Failed to import buffer: %s", __FUNCTION__, strerror(-importRet));
- client->close();
- }
- mBufferClient = std::move(client);
-}
-
-BufferHubBuffer::BufferHubBuffer(const sp<NativeHandle>& token) {
- sp<IBufferHub> bufferhub = IBufferHub::getService();
- if (bufferhub.get() == nullptr) {
- ALOGE("%s: BufferHub service not found!", __FUNCTION__);
- return;
- }
-
- BufferHubStatus ret;
- sp<IBufferClient> client;
- BufferTraits bufferTraits;
- IBufferHub::importBuffer_cb importCb = [&](const auto& status, const auto& outClient,
- const auto& outTraits) {
- ret = status;
- client = std::move(outClient);
- bufferTraits = std::move(outTraits);
- };
-
- // hidl_handle(native_handle_t*) simply creates a raw pointer reference withouth ownership
- // transfer.
- if (!bufferhub->importBuffer(hidl_handle(token.get()->handle()), importCb).isOk()) {
- ALOGE("%s: importBuffer transaction failed!", __FUNCTION__);
- return;
- } else if (ret != BufferHubStatus::NO_ERROR) {
- ALOGE("%s: importBuffer failed with error %u.", __FUNCTION__, ret);
- return;
- } else if (client == nullptr) {
- ALOGE("%s: importBuffer got null BufferClient.", __FUNCTION__);
- return;
- }
-
- const int importRet = initWithBufferTraits(bufferTraits);
- if (importRet < 0) {
- ALOGE("%s: Failed to import buffer: %s", __FUNCTION__, strerror(-importRet));
- client->close();
- }
- mBufferClient = std::move(client);
-}
-
-BufferHubBuffer::~BufferHubBuffer() {
- // Close buffer client to avoid possible race condition: user could first duplicate and hold
- // token with the original buffer gone, and then try to import the token. The close function
- // will explicitly invalidate the token to avoid this.
- if (mBufferClient != nullptr) {
- if (!mBufferClient->close().isOk()) {
- ALOGE("%s: close BufferClient transaction failed!", __FUNCTION__);
- }
- }
-}
-
-int BufferHubBuffer::initWithBufferTraits(const BufferTraits& bufferTraits) {
- ATRACE_CALL();
-
- if (bufferTraits.bufferInfo.getNativeHandle() == nullptr) {
- ALOGE("%s: missing buffer info handle.", __FUNCTION__);
- return -EINVAL;
- }
-
- if (bufferTraits.bufferHandle.getNativeHandle() == nullptr) {
- ALOGE("%s: missing gralloc handle.", __FUNCTION__);
- return -EINVAL;
- }
-
- // Import fds. Dup fds because hidl_handle owns the fds.
- unique_fd ashmemFd(fcntl(bufferTraits.bufferInfo->data[0], F_DUPFD_CLOEXEC, 0));
- mMetadata = BufferHubMetadata::import(std::move(ashmemFd));
- if (!mMetadata.isValid()) {
- ALOGE("%s: Received an invalid metadata.", __FUNCTION__);
- return -EINVAL;
- }
-
- mEventFd = BufferHubEventFd(fcntl(bufferTraits.bufferInfo->data[1], F_DUPFD_CLOEXEC, 0));
- if (!mEventFd.isValid()) {
- ALOGE("%s: Received ad invalid event fd.", __FUNCTION__);
- return -EINVAL;
- }
-
- int bufferId = bufferTraits.bufferInfo->data[2];
- if (bufferId < 0) {
- ALOGE("%s: Received an invalid (negative) id.", __FUNCTION__);
- return -EINVAL;
- }
-
- uint32_t clientBitMask;
- memcpy(&clientBitMask, &bufferTraits.bufferInfo->data[3], sizeof(clientBitMask));
- if (clientBitMask == 0U) {
- ALOGE("%s: Received an invalid client state mask.", __FUNCTION__);
- return -EINVAL;
- }
-
- uint32_t userMetadataSize;
- memcpy(&userMetadataSize, &bufferTraits.bufferInfo->data[4], sizeof(userMetadataSize));
- if (mMetadata.userMetadataSize() != userMetadataSize) {
- ALOGE("%s: user metadata size not match: expected %u, actual %zu.", __FUNCTION__,
- userMetadataSize, mMetadata.userMetadataSize());
- return -EINVAL;
- }
-
- size_t metadataSize = static_cast<size_t>(mMetadata.metadataSize());
- if (metadataSize < BufferHubDefs::kMetadataHeaderSize) {
- ALOGE("%s: metadata too small: %zu", __FUNCTION__, metadataSize);
- return -EINVAL;
- }
-
- // Populate shortcuts to the atomics in metadata.
- auto metadataHeader = mMetadata.metadataHeader();
- mBufferState = &metadataHeader->bufferState;
- mFenceState = &metadataHeader->fenceState;
- mActiveClientsBitMask = &metadataHeader->activeClientsBitMask;
- // The C++ standard recommends (but does not require) that lock-free atomic operations are
- // also address-free, that is, suitable for communication between processes using shared
- // memory.
- LOG_ALWAYS_FATAL_IF(!std::atomic_is_lock_free(mBufferState) ||
- !std::atomic_is_lock_free(mFenceState) ||
- !std::atomic_is_lock_free(mActiveClientsBitMask),
- "Atomic variables in ashmen are not lock free.");
-
- // Import the buffer: We only need to hold on the native_handle_t here so that
- // GraphicBuffer instance can be created in future.
- mBufferHandle = std::move(bufferTraits.bufferHandle);
- memcpy(&mBufferDesc, &bufferTraits.bufferDesc, sizeof(AHardwareBuffer_Desc));
-
- mId = bufferId;
- mClientStateMask = clientBitMask;
-
- // TODO(b/112012161) Set up shared fences.
- ALOGD("%s: id=%d, mBufferState=%" PRIx32 ".", __FUNCTION__, mId,
- mBufferState->load(std::memory_order_acquire));
- return 0;
-}
-
-int BufferHubBuffer::gain() {
- uint32_t currentBufferState = mBufferState->load(std::memory_order_acquire);
- if (isClientGained(currentBufferState, mClientStateMask)) {
- ALOGV("%s: Buffer is already gained by this client %" PRIx32 ".", __FUNCTION__,
- mClientStateMask);
- return 0;
- }
- do {
- if (isAnyClientGained(currentBufferState & (~mClientStateMask)) ||
- isAnyClientAcquired(currentBufferState)) {
- ALOGE("%s: Buffer is in use, id=%d mClientStateMask=%" PRIx32 " state=%" PRIx32 ".",
- __FUNCTION__, mId, mClientStateMask, currentBufferState);
- return -EBUSY;
- }
- // Change the buffer state to gained state, whose value happens to be the same as
- // mClientStateMask.
- } while (!mBufferState->compare_exchange_weak(currentBufferState, mClientStateMask,
- std::memory_order_acq_rel,
- std::memory_order_acquire));
- // TODO(b/119837586): Update fence state and return GPU fence.
- return 0;
-}
-
-int BufferHubBuffer::post() {
- uint32_t currentBufferState = mBufferState->load(std::memory_order_acquire);
- uint32_t updatedBufferState = (~mClientStateMask) & BufferHubDefs::kHighBitsMask;
- do {
- if (!isClientGained(currentBufferState, mClientStateMask)) {
- ALOGE("%s: Cannot post a buffer that is not gained by this client. buffer_id=%d "
- "mClientStateMask=%" PRIx32 " state=%" PRIx32 ".",
- __FUNCTION__, mId, mClientStateMask, currentBufferState);
- return -EBUSY;
- }
- // Set the producer client buffer state to released, other clients' buffer state to posted.
- // Post to all existing and non-existing clients.
- } while (!mBufferState->compare_exchange_weak(currentBufferState, updatedBufferState,
- std::memory_order_acq_rel,
- std::memory_order_acquire));
- // TODO(b/119837586): Update fence state and return GPU fence if needed.
- return 0;
-}
-
-int BufferHubBuffer::acquire() {
- uint32_t currentBufferState = mBufferState->load(std::memory_order_acquire);
- if (isClientAcquired(currentBufferState, mClientStateMask)) {
- ALOGV("%s: Buffer is already acquired by this client %" PRIx32 ".", __FUNCTION__,
- mClientStateMask);
- return 0;
- }
- uint32_t updatedBufferState = 0U;
- do {
- if (!isClientPosted(currentBufferState, mClientStateMask)) {
- ALOGE("%s: Cannot acquire a buffer that is not in posted state. buffer_id=%d "
- "mClientStateMask=%" PRIx32 " state=%" PRIx32 ".",
- __FUNCTION__, mId, mClientStateMask, currentBufferState);
- return -EBUSY;
- }
- // Change the buffer state for this consumer from posted to acquired.
- updatedBufferState = currentBufferState ^ mClientStateMask;
- } while (!mBufferState->compare_exchange_weak(currentBufferState, updatedBufferState,
- std::memory_order_acq_rel,
- std::memory_order_acquire));
- // TODO(b/119837586): Update fence state and return GPU fence.
- return 0;
-}
-
-int BufferHubBuffer::release() {
- uint32_t currentBufferState = mBufferState->load(std::memory_order_acquire);
- if (isClientReleased(currentBufferState, mClientStateMask)) {
- ALOGV("%s: Buffer is already released by this client %" PRIx32 ".", __FUNCTION__,
- mClientStateMask);
- return 0;
- }
- uint32_t updatedBufferState = 0U;
- do {
- updatedBufferState = currentBufferState & (~mClientStateMask);
- } while (!mBufferState->compare_exchange_weak(currentBufferState, updatedBufferState,
- std::memory_order_acq_rel,
- std::memory_order_acquire));
- // TODO(b/119837586): Update fence state and return GPU fence if needed.
- return 0;
-}
-
-bool BufferHubBuffer::isReleased() const {
- return (mBufferState->load(std::memory_order_acquire) &
- mActiveClientsBitMask->load(std::memory_order_acquire)) == 0;
-}
-
-bool BufferHubBuffer::isValid() const {
- return mBufferHandle.getNativeHandle() != nullptr && mId >= 0 && mClientStateMask != 0U &&
- mEventFd.get() >= 0 && mMetadata.isValid() && mBufferClient != nullptr;
-}
-
-sp<NativeHandle> BufferHubBuffer::duplicate() {
- if (mBufferClient == nullptr) {
- ALOGE("%s: missing BufferClient!", __FUNCTION__);
- return nullptr;
- }
-
- hidl_handle token;
- BufferHubStatus ret;
- IBufferClient::duplicate_cb dupCb = [&](const auto& outToken, const auto& status) {
- token = std::move(outToken);
- ret = status;
- };
-
- if (!mBufferClient->duplicate(dupCb).isOk()) {
- ALOGE("%s: duplicate transaction failed!", __FUNCTION__);
- return nullptr;
- } else if (ret != BufferHubStatus::NO_ERROR) {
- ALOGE("%s: duplicate failed with error %u.", __FUNCTION__, ret);
- return nullptr;
- } else if (token.getNativeHandle() == nullptr) {
- ALOGE("%s: duplicate got null token.", __FUNCTION__);
- return nullptr;
- }
-
- return NativeHandle::create(native_handle_clone(token.getNativeHandle()), /*ownsHandle=*/true);
-}
-
-} // namespace android
diff --git a/libs/ui/BufferHubEventFd.cpp b/libs/ui/BufferHubEventFd.cpp
deleted file mode 100644
index bffc2ca..0000000
--- a/libs/ui/BufferHubEventFd.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2018 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 <sys/eventfd.h>
-
-#include <log/log.h>
-#include <ui/BufferHubEventFd.h>
-
-namespace android {
-
-BufferHubEventFd::BufferHubEventFd() : mFd(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK)) {}
-
-BufferHubEventFd::BufferHubEventFd(int fd) : mFd(fd) {}
-
-status_t BufferHubEventFd::signal() const {
- if (!isValid()) {
- ALOGE("%s: cannot signal an invalid eventfd.", __FUNCTION__);
- return DEAD_OBJECT;
- }
-
- eventfd_write(mFd.get(), 1);
- return OK;
-}
-
-status_t BufferHubEventFd::clear() const {
- if (!isValid()) {
- ALOGE("%s: cannot clear an invalid eventfd.", __FUNCTION__);
- return DEAD_OBJECT;
- }
-
- eventfd_t value;
- eventfd_read(mFd.get(), &value);
- return OK;
-}
-
-} // namespace android
diff --git a/libs/ui/BufferHubMetadata.cpp b/libs/ui/BufferHubMetadata.cpp
deleted file mode 100644
index 05bc7dd..0000000
--- a/libs/ui/BufferHubMetadata.cpp
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright (C) 2018 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 <errno.h>
-#include <sys/mman.h>
-#include <limits>
-
-#include <cutils/ashmem.h>
-#include <log/log.h>
-#include <ui/BufferHubMetadata.h>
-
-namespace android {
-
-namespace {
-
-static const int kAshmemProt = PROT_READ | PROT_WRITE;
-
-} // namespace
-
-using BufferHubDefs::kMetadataHeaderSize;
-using BufferHubDefs::MetadataHeader;
-
-/* static */
-BufferHubMetadata BufferHubMetadata::create(size_t userMetadataSize) {
- // The size the of metadata buffer is used as the "width" parameter during allocation. Thus it
- // cannot overflow uint32_t.
- if (userMetadataSize >= (std::numeric_limits<uint32_t>::max() - kMetadataHeaderSize)) {
- ALOGE("BufferHubMetadata::Create: metadata size too big: %zu.", userMetadataSize);
- return {};
- }
-
- const size_t metadataSize = userMetadataSize + kMetadataHeaderSize;
- int fd = ashmem_create_region(/*name=*/"BufferHubMetadata", metadataSize);
- if (fd < 0) {
- ALOGE("BufferHubMetadata::Create: failed to create ashmem region.");
- return {};
- }
-
- // Hand over the ownership of the fd to a unique_fd immediately after the successful
- // return of ashmem_create_region. The ashmemFd is going to own the fd and to prevent fd
- // leaks during error handling.
- unique_fd ashmemFd{fd};
-
- if (ashmem_set_prot_region(ashmemFd.get(), kAshmemProt) != 0) {
- ALOGE("BufferHubMetadata::Create: failed to set protect region.");
- return {};
- }
-
- return BufferHubMetadata::import(std::move(ashmemFd));
-}
-
-/* static */
-BufferHubMetadata BufferHubMetadata::import(unique_fd ashmemFd) {
- if (!ashmem_valid(ashmemFd.get())) {
- ALOGE("BufferHubMetadata::Import: invalid ashmem fd.");
- return {};
- }
-
- size_t metadataSize = static_cast<size_t>(ashmem_get_size_region(ashmemFd.get()));
- size_t userMetadataSize = metadataSize - kMetadataHeaderSize;
-
- // Note that here the buffer state is mapped from shared memory as an atomic object. The
- // std::atomic's constructor will not be called so that the original value stored in the memory
- // region can be preserved.
- auto metadataHeader = static_cast<MetadataHeader*>(mmap(nullptr, metadataSize, kAshmemProt,
- MAP_SHARED, ashmemFd.get(),
- /*offset=*/0));
- if (metadataHeader == nullptr) {
- ALOGE("BufferHubMetadata::Import: failed to map region.");
- return {};
- }
-
- return BufferHubMetadata(userMetadataSize, std::move(ashmemFd), metadataHeader);
-}
-
-BufferHubMetadata::BufferHubMetadata(size_t userMetadataSize, unique_fd ashmemFd,
- MetadataHeader* metadataHeader)
- : mUserMetadataSize(userMetadataSize),
- mAshmemFd(std::move(ashmemFd)),
- mMetadataHeader(metadataHeader) {}
-
-BufferHubMetadata::~BufferHubMetadata() {
- if (mMetadataHeader != nullptr) {
- int ret = munmap(mMetadataHeader, metadataSize());
- ALOGE_IF(ret != 0,
- "BufferHubMetadata::~BufferHubMetadata: failed to unmap ashmem, error=%d.", errno);
- mMetadataHeader = nullptr;
- }
-}
-
-} // namespace android
diff --git a/libs/ui/DebugUtils.cpp b/libs/ui/DebugUtils.cpp
index ee06d93..f394635 100644
--- a/libs/ui/DebugUtils.cpp
+++ b/libs/ui/DebugUtils.cpp
@@ -15,12 +15,14 @@
*/
#include <ui/DebugUtils.h>
+#include <ui/DeviceProductInfo.h>
#include <ui/PixelFormat.h>
#include <ui/Rect.h>
#include <android-base/stringprintf.h>
#include <string>
+using android::base::StringAppendF;
using android::base::StringPrintf;
using android::ui::ColorMode;
using android::ui::RenderIntent;
@@ -85,12 +87,11 @@
case HAL_DATASPACE_UNKNOWN:
// Fallthrough
default:
- return android::base::StringPrintf("Unknown deprecated dataspace code %d",
- dataspace);
+ return StringPrintf("Unknown deprecated dataspace code %d", dataspace);
}
}
- return android::base::StringPrintf("Unknown dataspace code %d", dataspaceSelect);
+ return StringPrintf("Unknown dataspace code %d", dataspaceSelect);
}
std::string decodeTransfer(android_dataspace dataspace) {
@@ -147,7 +148,7 @@
return std::string("STD-B67");
}
- return android::base::StringPrintf("Unknown dataspace transfer %d", dataspaceTransfer);
+ return StringPrintf("Unknown dataspace transfer %d", dataspaceTransfer);
}
std::string decodeRange(android_dataspace dataspace) {
@@ -187,16 +188,15 @@
return std::string("Extended range");
}
- return android::base::StringPrintf("Unknown dataspace range %d", dataspaceRange);
+ return StringPrintf("Unknown dataspace range %d", dataspaceRange);
}
std::string dataspaceDetails(android_dataspace dataspace) {
if (dataspace == 0) {
return "Default";
}
- return android::base::StringPrintf("%s %s %s", decodeStandard(dataspace).c_str(),
- decodeTransfer(dataspace).c_str(),
- decodeRange(dataspace).c_str());
+ return StringPrintf("%s %s %s", decodeStandard(dataspace).c_str(),
+ decodeTransfer(dataspace).c_str(), decodeRange(dataspace).c_str());
}
std::string decodeColorMode(ColorMode colorMode) {
@@ -244,7 +244,7 @@
return std::string("ColorMode::BT2100_HLG");
}
- return android::base::StringPrintf("Unknown color mode %d", colorMode);
+ return StringPrintf("Unknown color mode %d", colorMode);
}
std::string decodeColorTransform(android_color_transform colorTransform) {
@@ -271,7 +271,7 @@
return std::string("Correct tritanopia");
}
- return android::base::StringPrintf("Unknown color transform %d", colorTransform);
+ return StringPrintf("Unknown color transform %d", colorTransform);
}
// Converts a PixelFormat to a human-readable string. Max 11 chars.
@@ -303,7 +303,7 @@
case android::PIXEL_FORMAT_BGRA_8888:
return std::string("BGRA_8888");
default:
- return android::base::StringPrintf("Unknown %#08x", format);
+ return StringPrintf("Unknown %#08x", format);
}
}
@@ -324,3 +324,28 @@
std::string to_string(const android::Rect& rect) {
return StringPrintf("(%4d,%4d,%4d,%4d)", rect.left, rect.top, rect.right, rect.bottom);
}
+
+std::string toString(const android::DeviceProductInfo::ManufactureOrModelDate& date) {
+ using ModelYear = android::DeviceProductInfo::ModelYear;
+ using ManufactureYear = android::DeviceProductInfo::ManufactureYear;
+ using ManufactureWeekAndYear = android::DeviceProductInfo::ManufactureWeekAndYear;
+
+ if (const auto* model = std::get_if<ModelYear>(&date)) {
+ return StringPrintf("ModelYear{%d}", model->year);
+ } else if (const auto* manufacture = std::get_if<ManufactureYear>(&date)) {
+ return StringPrintf("ManufactureDate{year=%d}", manufacture->year);
+ } else if (const auto* manufacture = std::get_if<ManufactureWeekAndYear>(&date)) {
+ return StringPrintf("ManufactureDate{week=%d, year=%d}", manufacture->week,
+ manufacture->year);
+ } else {
+ LOG_FATAL("Unknown alternative for variant DeviceProductInfo::ManufactureOrModelDate");
+ return {};
+ }
+}
+
+std::string toString(const android::DeviceProductInfo& info) {
+ return StringPrintf("DeviceProductInfo{name=%s, productId=%s, manufacturerPnpId=%s, "
+ "manufactureOrModelDate=%s}",
+ info.name.data(), info.productId.data(), info.manufacturerPnpId.data(),
+ toString(info.manufactureOrModelDate).c_str());
+}
diff --git a/libs/ui/Fence.cpp b/libs/ui/Fence.cpp
index 4ce891e..33ab7c4 100644
--- a/libs/ui/Fence.cpp
+++ b/libs/ui/Fence.cpp
@@ -62,8 +62,26 @@
int warningTimeout = 3000;
int err = sync_wait(mFenceFd, warningTimeout);
if (err < 0 && errno == ETIME) {
- ALOGE("%s: fence %d didn't signal in %u ms", logname, mFenceFd.get(),
- warningTimeout);
+ ALOGE("waitForever: %s: fence %d didn't signal in %u ms", logname, mFenceFd.get(),
+ warningTimeout);
+
+ struct sync_file_info* finfo = sync_file_info(mFenceFd);
+ if (finfo) {
+ // status: active(0) signaled(1) error(<0)
+ ALOGI("waitForever: fence(%s) status(%d)", finfo->name, finfo->status);
+
+ struct sync_fence_info* pinfo = sync_get_fence_info(finfo);
+ for (uint32_t i = 0; i < finfo->num_fences; i++) {
+ uint64_t ts_sec = pinfo[i].timestamp_ns / 1000000000LL;
+ uint64_t ts_usec = (pinfo[i].timestamp_ns % 1000000000LL) / 1000LL;
+
+ ALOGI("waitForever: sync point: timeline(%s) drv(%s) status(%d) timestamp(%" PRIu64
+ ".%06" PRIu64 ")",
+ pinfo[i].obj_name, pinfo[i].driver_name, pinfo[i].status, ts_sec, ts_usec);
+ }
+ sync_file_info_free(finfo);
+ }
+
err = sync_wait(mFenceFd, TIMEOUT_NEVER);
}
return err < 0 ? -errno : status_t(NO_ERROR);
diff --git a/libs/ui/Gralloc2.cpp b/libs/ui/Gralloc2.cpp
index 5dc4530..040a62b 100644
--- a/libs/ui/Gralloc2.cpp
+++ b/libs/ui/Gralloc2.cpp
@@ -351,12 +351,6 @@
return releaseFence;
}
-status_t Gralloc2Mapper::isSupported(uint32_t /*width*/, uint32_t /*height*/,
- android::PixelFormat /*format*/, uint32_t /*layerCount*/,
- uint64_t /*usage*/, bool* /*outSupported*/) const {
- return INVALID_OPERATION;
-}
-
Gralloc2Allocator::Gralloc2Allocator(const Gralloc2Mapper& mapper) : mMapper(mapper) {
mAllocator = IAllocator::getService();
if (mAllocator == nullptr) {
@@ -369,7 +363,7 @@
return mAllocator != nullptr;
}
-std::string Gralloc2Allocator::dumpDebugInfo() const {
+std::string Gralloc2Allocator::dumpDebugInfo(bool /*less*/) const {
std::string debugInfo;
mAllocator->dumpDebugInfo([&](const auto& tmpDebugInfo) {
@@ -379,9 +373,10 @@
return debugInfo;
}
-status_t Gralloc2Allocator::allocate(uint32_t width, uint32_t height, PixelFormat format,
- uint32_t layerCount, uint64_t usage, uint32_t bufferCount,
- uint32_t* outStride, buffer_handle_t* outBufferHandles) const {
+status_t Gralloc2Allocator::allocate(std::string /*requestorName*/, uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount, uint64_t usage,
+ uint32_t bufferCount, uint32_t* outStride,
+ buffer_handle_t* outBufferHandles, bool importBuffers) const {
IMapper::BufferDescriptorInfo descriptorInfo = {};
descriptorInfo.width = width;
descriptorInfo.height = height;
@@ -404,19 +399,33 @@
return;
}
- // import buffers
- for (uint32_t i = 0; i < bufferCount; i++) {
- error = mMapper.importBuffer(tmpBuffers[i],
- &outBufferHandles[i]);
- if (error != NO_ERROR) {
- for (uint32_t j = 0; j < i; j++) {
- mMapper.freeBuffer(outBufferHandles[j]);
- outBufferHandles[j] = nullptr;
+ if (importBuffers) {
+ for (uint32_t i = 0; i < bufferCount; i++) {
+ error = mMapper.importBuffer(tmpBuffers[i],
+ &outBufferHandles[i]);
+ if (error != NO_ERROR) {
+ for (uint32_t j = 0; j < i; j++) {
+ mMapper.freeBuffer(outBufferHandles[j]);
+ outBufferHandles[j] = nullptr;
+ }
+ return;
}
- return;
+ }
+ } else {
+ for (uint32_t i = 0; i < bufferCount; i++) {
+ outBufferHandles[i] = native_handle_clone(
+ tmpBuffers[i].getNativeHandle());
+ if (!outBufferHandles[i]) {
+ for (uint32_t j = 0; j < i; j++) {
+ auto buffer = const_cast<native_handle_t*>(
+ outBufferHandles[j]);
+ native_handle_close(buffer);
+ native_handle_delete(buffer);
+ outBufferHandles[j] = nullptr;
+ }
+ }
}
}
-
*outStride = tmpStride;
});
diff --git a/libs/ui/Gralloc3.cpp b/libs/ui/Gralloc3.cpp
index eb43765..882674f 100644
--- a/libs/ui/Gralloc3.cpp
+++ b/libs/ui/Gralloc3.cpp
@@ -352,7 +352,7 @@
return mAllocator != nullptr;
}
-std::string Gralloc3Allocator::dumpDebugInfo() const {
+std::string Gralloc3Allocator::dumpDebugInfo(bool /*less*/) const {
std::string debugInfo;
mAllocator->dumpDebugInfo([&](const auto& tmpDebugInfo) { debugInfo = tmpDebugInfo.c_str(); });
@@ -360,9 +360,10 @@
return debugInfo;
}
-status_t Gralloc3Allocator::allocate(uint32_t width, uint32_t height, android::PixelFormat format,
- uint32_t layerCount, uint64_t usage, uint32_t bufferCount,
- uint32_t* outStride, buffer_handle_t* outBufferHandles) const {
+status_t Gralloc3Allocator::allocate(std::string /*requestorName*/, uint32_t width, uint32_t height,
+ android::PixelFormat format, uint32_t layerCount,
+ uint64_t usage, uint32_t bufferCount, uint32_t* outStride,
+ buffer_handle_t* outBufferHandles, bool importBuffers) const {
IMapper::BufferDescriptorInfo descriptorInfo;
sBufferDescriptorInfo(width, height, format, layerCount, usage, &descriptorInfo);
@@ -381,16 +382,31 @@
return;
}
- // import buffers
- for (uint32_t i = 0; i < bufferCount; i++) {
- error = mMapper.importBuffer(tmpBuffers[i],
- &outBufferHandles[i]);
- if (error != NO_ERROR) {
- for (uint32_t j = 0; j < i; j++) {
- mMapper.freeBuffer(outBufferHandles[j]);
- outBufferHandles[j] = nullptr;
+ if (importBuffers) {
+ for (uint32_t i = 0; i < bufferCount; i++) {
+ error = mMapper.importBuffer(tmpBuffers[i],
+ &outBufferHandles[i]);
+ if (error != NO_ERROR) {
+ for (uint32_t j = 0; j < i; j++) {
+ mMapper.freeBuffer(outBufferHandles[j]);
+ outBufferHandles[j] = nullptr;
+ }
+ return;
}
- return;
+ }
+ } else {
+ for (uint32_t i = 0; i < bufferCount; i++) {
+ outBufferHandles[i] = native_handle_clone(
+ tmpBuffers[i].getNativeHandle());
+ if (!outBufferHandles[i]) {
+ for (uint32_t j = 0; j < i; j++) {
+ auto buffer = const_cast<native_handle_t*>(
+ outBufferHandles[j]);
+ native_handle_close(buffer);
+ native_handle_delete(buffer);
+ outBufferHandles[j] = nullptr;
+ }
+ }
}
}
*outStride = tmpStride;
diff --git a/libs/ui/Gralloc4.cpp b/libs/ui/Gralloc4.cpp
new file mode 100644
index 0000000..f799ce4
--- /dev/null
+++ b/libs/ui/Gralloc4.cpp
@@ -0,0 +1,1126 @@
+/*
+ * Copyright 2019 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 "Gralloc4"
+
+#include <hidl/ServiceManagement.h>
+#include <hwbinder/IPCThreadState.h>
+#include <ui/Gralloc4.h>
+
+#include <inttypes.h>
+#include <log/log.h>
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wzero-length-array"
+#include <sync/sync.h>
+#pragma clang diagnostic pop
+
+using aidl::android::hardware::graphics::common::ExtendableType;
+using aidl::android::hardware::graphics::common::PlaneLayoutComponentType;
+using aidl::android::hardware::graphics::common::StandardMetadataType;
+using android::hardware::hidl_vec;
+using android::hardware::graphics::allocator::V4_0::IAllocator;
+using android::hardware::graphics::common::V1_2::BufferUsage;
+using android::hardware::graphics::mapper::V4_0::BufferDescriptor;
+using android::hardware::graphics::mapper::V4_0::Error;
+using android::hardware::graphics::mapper::V4_0::IMapper;
+using BufferDump = android::hardware::graphics::mapper::V4_0::IMapper::BufferDump;
+using MetadataDump = android::hardware::graphics::mapper::V4_0::IMapper::MetadataDump;
+using MetadataType = android::hardware::graphics::mapper::V4_0::IMapper::MetadataType;
+using MetadataTypeDescription =
+ android::hardware::graphics::mapper::V4_0::IMapper::MetadataTypeDescription;
+
+namespace android {
+
+namespace {
+
+static constexpr Error kTransactionError = Error::NO_RESOURCES;
+
+uint64_t getValidUsageBits() {
+ static const uint64_t validUsageBits = []() -> uint64_t {
+ uint64_t bits = 0;
+ for (const auto bit :
+ hardware::hidl_enum_range<hardware::graphics::common::V1_2::BufferUsage>()) {
+ bits = bits | bit;
+ }
+ return bits;
+ }();
+ return validUsageBits;
+}
+
+static inline IMapper::Rect sGralloc4Rect(const Rect& rect) {
+ IMapper::Rect outRect{};
+ outRect.left = rect.left;
+ outRect.top = rect.top;
+ outRect.width = rect.width();
+ outRect.height = rect.height();
+ return outRect;
+}
+static inline void sBufferDescriptorInfo(std::string name, uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount, uint64_t usage,
+ IMapper::BufferDescriptorInfo* outDescriptorInfo) {
+ outDescriptorInfo->name = name;
+ outDescriptorInfo->width = width;
+ outDescriptorInfo->height = height;
+ outDescriptorInfo->layerCount = layerCount;
+ outDescriptorInfo->format = static_cast<hardware::graphics::common::V1_2::PixelFormat>(format);
+ outDescriptorInfo->usage = usage;
+ outDescriptorInfo->reservedSize = 0;
+}
+
+} // anonymous namespace
+
+void Gralloc4Mapper::preload() {
+ android::hardware::preloadPassthroughService<IMapper>();
+}
+
+Gralloc4Mapper::Gralloc4Mapper() {
+ mMapper = IMapper::getService();
+ if (mMapper == nullptr) {
+ ALOGI("mapper 4.x is not supported");
+ return;
+ }
+ if (mMapper->isRemote()) {
+ LOG_ALWAYS_FATAL("gralloc-mapper must be in passthrough mode");
+ }
+}
+
+bool Gralloc4Mapper::isLoaded() const {
+ return mMapper != nullptr;
+}
+
+status_t Gralloc4Mapper::validateBufferDescriptorInfo(
+ IMapper::BufferDescriptorInfo* descriptorInfo) const {
+ uint64_t validUsageBits = getValidUsageBits();
+
+ if (descriptorInfo->usage & ~validUsageBits) {
+ ALOGE("buffer descriptor contains invalid usage bits 0x%" PRIx64,
+ descriptorInfo->usage & ~validUsageBits);
+ return BAD_VALUE;
+ }
+ return NO_ERROR;
+}
+
+status_t Gralloc4Mapper::createDescriptor(void* bufferDescriptorInfo,
+ void* outBufferDescriptor) const {
+ IMapper::BufferDescriptorInfo* descriptorInfo =
+ static_cast<IMapper::BufferDescriptorInfo*>(bufferDescriptorInfo);
+ BufferDescriptor* outDescriptor = static_cast<BufferDescriptor*>(outBufferDescriptor);
+
+ status_t status = validateBufferDescriptorInfo(descriptorInfo);
+ if (status != NO_ERROR) {
+ return status;
+ }
+
+ Error error;
+ auto hidl_cb = [&](const auto& tmpError, const auto& tmpDescriptor) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+ *outDescriptor = tmpDescriptor;
+ };
+
+ hardware::Return<void> ret = mMapper->createDescriptor(*descriptorInfo, hidl_cb);
+
+ return static_cast<status_t>((ret.isOk()) ? error : kTransactionError);
+}
+
+status_t Gralloc4Mapper::importBuffer(const hardware::hidl_handle& rawHandle,
+ buffer_handle_t* outBufferHandle) const {
+ Error error;
+ auto ret = mMapper->importBuffer(rawHandle, [&](const auto& tmpError, const auto& tmpBuffer) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+ *outBufferHandle = static_cast<buffer_handle_t>(tmpBuffer);
+ });
+
+ return static_cast<status_t>((ret.isOk()) ? error : kTransactionError);
+}
+
+void Gralloc4Mapper::freeBuffer(buffer_handle_t bufferHandle) const {
+ auto buffer = const_cast<native_handle_t*>(bufferHandle);
+ auto ret = mMapper->freeBuffer(buffer);
+
+ auto error = (ret.isOk()) ? static_cast<Error>(ret) : kTransactionError;
+ ALOGE_IF(error != Error::NONE, "freeBuffer(%p) failed with %d", buffer, error);
+}
+
+status_t Gralloc4Mapper::validateBufferSize(buffer_handle_t bufferHandle, uint32_t width,
+ uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ uint32_t stride) const {
+ IMapper::BufferDescriptorInfo descriptorInfo;
+ sBufferDescriptorInfo("validateBufferSize", width, height, format, layerCount, usage,
+ &descriptorInfo);
+
+ auto buffer = const_cast<native_handle_t*>(bufferHandle);
+ auto ret = mMapper->validateBufferSize(buffer, descriptorInfo, stride);
+
+ return static_cast<status_t>((ret.isOk()) ? static_cast<Error>(ret) : kTransactionError);
+}
+
+void Gralloc4Mapper::getTransportSize(buffer_handle_t bufferHandle, uint32_t* outNumFds,
+ uint32_t* outNumInts) const {
+ *outNumFds = uint32_t(bufferHandle->numFds);
+ *outNumInts = uint32_t(bufferHandle->numInts);
+
+ Error error;
+ auto buffer = const_cast<native_handle_t*>(bufferHandle);
+ auto ret = mMapper->getTransportSize(buffer,
+ [&](const auto& tmpError, const auto& tmpNumFds,
+ const auto& tmpNumInts) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+ *outNumFds = tmpNumFds;
+ *outNumInts = tmpNumInts;
+ });
+
+ error = (ret.isOk()) ? error : kTransactionError;
+
+ ALOGE_IF(error != Error::NONE, "getTransportSize(%p) failed with %d", buffer, error);
+}
+
+status_t Gralloc4Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
+ int acquireFence, void** outData, int32_t* outBytesPerPixel,
+ int32_t* outBytesPerStride) const {
+ std::vector<ui::PlaneLayout> planeLayouts;
+ status_t err = getPlaneLayouts(bufferHandle, &planeLayouts);
+
+ if (err == NO_ERROR && !planeLayouts.empty()) {
+ if (outBytesPerPixel) {
+ int32_t bitsPerPixel = planeLayouts.front().sampleIncrementInBits;
+ for (const auto& planeLayout : planeLayouts) {
+ if (bitsPerPixel != planeLayout.sampleIncrementInBits) {
+ bitsPerPixel = -1;
+ }
+ }
+ if (bitsPerPixel >= 0 && bitsPerPixel % 8 == 0) {
+ *outBytesPerPixel = bitsPerPixel / 8;
+ } else {
+ *outBytesPerPixel = -1;
+ }
+ }
+ if (outBytesPerStride) {
+ int32_t bytesPerStride = planeLayouts.front().strideInBytes;
+ for (const auto& planeLayout : planeLayouts) {
+ if (bytesPerStride != planeLayout.strideInBytes) {
+ bytesPerStride = -1;
+ }
+ }
+ if (bytesPerStride >= 0) {
+ *outBytesPerStride = bytesPerStride;
+ } else {
+ *outBytesPerStride = -1;
+ }
+ }
+ }
+
+ auto buffer = const_cast<native_handle_t*>(bufferHandle);
+
+ IMapper::Rect accessRegion = sGralloc4Rect(bounds);
+
+ // put acquireFence in a hidl_handle
+ hardware::hidl_handle acquireFenceHandle;
+ NATIVE_HANDLE_DECLARE_STORAGE(acquireFenceStorage, 1, 0);
+ if (acquireFence >= 0) {
+ auto h = native_handle_init(acquireFenceStorage, 1, 0);
+ h->data[0] = acquireFence;
+ acquireFenceHandle = h;
+ }
+
+ Error error;
+ auto ret = mMapper->lock(buffer, usage, accessRegion, acquireFenceHandle,
+ [&](const auto& tmpError, const auto& tmpData) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+ *outData = tmpData;
+ });
+
+ // we own acquireFence even on errors
+ if (acquireFence >= 0) {
+ close(acquireFence);
+ }
+
+ error = (ret.isOk()) ? error : kTransactionError;
+
+ ALOGW_IF(error != Error::NONE, "lock(%p, ...) failed: %d", bufferHandle, error);
+
+ return static_cast<status_t>(error);
+}
+
+status_t Gralloc4Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
+ int acquireFence, android_ycbcr* outYcbcr) const {
+ if (!outYcbcr) {
+ return BAD_VALUE;
+ }
+
+ std::vector<ui::PlaneLayout> planeLayouts;
+ status_t error = getPlaneLayouts(bufferHandle, &planeLayouts);
+ if (error != NO_ERROR) {
+ return error;
+ }
+
+ void* data = nullptr;
+ error = lock(bufferHandle, usage, bounds, acquireFence, &data, nullptr, nullptr);
+ if (error != NO_ERROR) {
+ return error;
+ }
+
+ android_ycbcr ycbcr;
+
+ ycbcr.y = nullptr;
+ ycbcr.cb = nullptr;
+ ycbcr.cr = nullptr;
+ ycbcr.ystride = 0;
+ ycbcr.cstride = 0;
+ ycbcr.chroma_step = 0;
+
+ for (const auto& planeLayout : planeLayouts) {
+ for (const auto& planeLayoutComponent : planeLayout.components) {
+ if (!gralloc4::isStandardPlaneLayoutComponentType(planeLayoutComponent.type)) {
+ continue;
+ }
+ if (0 != planeLayoutComponent.offsetInBits % 8) {
+ unlock(bufferHandle);
+ return BAD_VALUE;
+ }
+
+ uint8_t* tmpData = static_cast<uint8_t*>(data) + planeLayout.offsetInBytes +
+ (planeLayoutComponent.offsetInBits / 8);
+ uint64_t sampleIncrementInBytes;
+
+ auto type = static_cast<PlaneLayoutComponentType>(planeLayoutComponent.type.value);
+ switch (type) {
+ case PlaneLayoutComponentType::Y:
+ if ((ycbcr.y != nullptr) || (planeLayoutComponent.sizeInBits != 8) ||
+ (planeLayout.sampleIncrementInBits != 8)) {
+ unlock(bufferHandle);
+ return BAD_VALUE;
+ }
+ ycbcr.y = tmpData;
+ ycbcr.ystride = planeLayout.strideInBytes;
+ break;
+
+ case PlaneLayoutComponentType::CB:
+ case PlaneLayoutComponentType::CR:
+ if (planeLayout.sampleIncrementInBits % 8 != 0) {
+ unlock(bufferHandle);
+ return BAD_VALUE;
+ }
+
+ sampleIncrementInBytes = planeLayout.sampleIncrementInBits / 8;
+ if ((sampleIncrementInBytes != 1) && (sampleIncrementInBytes != 2)) {
+ unlock(bufferHandle);
+ return BAD_VALUE;
+ }
+
+ if (ycbcr.cstride == 0 && ycbcr.chroma_step == 0) {
+ ycbcr.cstride = planeLayout.strideInBytes;
+ ycbcr.chroma_step = sampleIncrementInBytes;
+ } else {
+ if ((static_cast<int64_t>(ycbcr.cstride) != planeLayout.strideInBytes) ||
+ (ycbcr.chroma_step != sampleIncrementInBytes)) {
+ unlock(bufferHandle);
+ return BAD_VALUE;
+ }
+ }
+
+ if (type == PlaneLayoutComponentType::CB) {
+ if (ycbcr.cb != nullptr) {
+ unlock(bufferHandle);
+ return BAD_VALUE;
+ }
+ ycbcr.cb = tmpData;
+ } else {
+ if (ycbcr.cr != nullptr) {
+ unlock(bufferHandle);
+ return BAD_VALUE;
+ }
+ ycbcr.cr = tmpData;
+ }
+ break;
+ default:
+ break;
+ };
+ }
+ }
+
+ *outYcbcr = ycbcr;
+ return static_cast<status_t>(Error::NONE);
+}
+
+int Gralloc4Mapper::unlock(buffer_handle_t bufferHandle) const {
+ auto buffer = const_cast<native_handle_t*>(bufferHandle);
+
+ int releaseFence = -1;
+ Error error;
+ auto ret = mMapper->unlock(buffer, [&](const auto& tmpError, const auto& tmpReleaseFence) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ auto fenceHandle = tmpReleaseFence.getNativeHandle();
+ if (fenceHandle && fenceHandle->numFds == 1) {
+ int fd = dup(fenceHandle->data[0]);
+ if (fd >= 0) {
+ releaseFence = fd;
+ } else {
+ ALOGD("failed to dup unlock release fence");
+ sync_wait(fenceHandle->data[0], -1);
+ }
+ }
+ });
+
+ if (!ret.isOk()) {
+ error = kTransactionError;
+ }
+
+ if (error != Error::NONE) {
+ ALOGE("unlock(%p) failed with %d", buffer, error);
+ }
+
+ return releaseFence;
+}
+
+status_t Gralloc4Mapper::isSupported(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ bool* outSupported) const {
+ IMapper::BufferDescriptorInfo descriptorInfo;
+ sBufferDescriptorInfo("isSupported", width, height, format, layerCount, usage, &descriptorInfo);
+
+ Error error;
+ auto ret = mMapper->isSupported(descriptorInfo,
+ [&](const auto& tmpError, const auto& tmpSupported) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+ if (outSupported) {
+ *outSupported = tmpSupported;
+ }
+ });
+
+ if (!ret.isOk()) {
+ error = kTransactionError;
+ }
+
+ if (error != Error::NONE) {
+ ALOGE("isSupported(%u, %u, %d, %u, ...) failed with %d", width, height, format, layerCount,
+ error);
+ }
+
+ return static_cast<status_t>(error);
+}
+
+template <class T>
+status_t Gralloc4Mapper::get(buffer_handle_t bufferHandle, const MetadataType& metadataType,
+ DecodeFunction<T> decodeFunction, T* outMetadata) const {
+ if (!outMetadata) {
+ return BAD_VALUE;
+ }
+
+ hidl_vec<uint8_t> vec;
+ Error error;
+ auto ret = mMapper->get(const_cast<native_handle_t*>(bufferHandle), metadataType,
+ [&](const auto& tmpError, const hidl_vec<uint8_t>& tmpVec) {
+ error = tmpError;
+ vec = tmpVec;
+ });
+
+ if (!ret.isOk()) {
+ error = kTransactionError;
+ }
+
+ if (error != Error::NONE) {
+ ALOGE("get(%s, %" PRIu64 ", ...) failed with %d", metadataType.name.c_str(),
+ metadataType.value, error);
+ return static_cast<status_t>(error);
+ }
+
+ return decodeFunction(vec, outMetadata);
+}
+
+status_t Gralloc4Mapper::getBufferId(buffer_handle_t bufferHandle, uint64_t* outBufferId) const {
+ return get(bufferHandle, gralloc4::MetadataType_BufferId, gralloc4::decodeBufferId,
+ outBufferId);
+}
+
+status_t Gralloc4Mapper::getName(buffer_handle_t bufferHandle, std::string* outName) const {
+ return get(bufferHandle, gralloc4::MetadataType_Name, gralloc4::decodeName, outName);
+}
+
+status_t Gralloc4Mapper::getWidth(buffer_handle_t bufferHandle, uint64_t* outWidth) const {
+ return get(bufferHandle, gralloc4::MetadataType_Width, gralloc4::decodeWidth, outWidth);
+}
+
+status_t Gralloc4Mapper::getHeight(buffer_handle_t bufferHandle, uint64_t* outHeight) const {
+ return get(bufferHandle, gralloc4::MetadataType_Height, gralloc4::decodeHeight, outHeight);
+}
+
+status_t Gralloc4Mapper::getLayerCount(buffer_handle_t bufferHandle,
+ uint64_t* outLayerCount) const {
+ return get(bufferHandle, gralloc4::MetadataType_LayerCount, gralloc4::decodeLayerCount,
+ outLayerCount);
+}
+
+status_t Gralloc4Mapper::getPixelFormatRequested(buffer_handle_t bufferHandle,
+ ui::PixelFormat* outPixelFormatRequested) const {
+ return get(bufferHandle, gralloc4::MetadataType_PixelFormatRequested,
+ gralloc4::decodePixelFormatRequested, outPixelFormatRequested);
+}
+
+status_t Gralloc4Mapper::getPixelFormatFourCC(buffer_handle_t bufferHandle,
+ uint32_t* outPixelFormatFourCC) const {
+ return get(bufferHandle, gralloc4::MetadataType_PixelFormatFourCC,
+ gralloc4::decodePixelFormatFourCC, outPixelFormatFourCC);
+}
+
+status_t Gralloc4Mapper::getPixelFormatModifier(buffer_handle_t bufferHandle,
+ uint64_t* outPixelFormatModifier) const {
+ return get(bufferHandle, gralloc4::MetadataType_PixelFormatModifier,
+ gralloc4::decodePixelFormatModifier, outPixelFormatModifier);
+}
+
+status_t Gralloc4Mapper::getUsage(buffer_handle_t bufferHandle, uint64_t* outUsage) const {
+ return get(bufferHandle, gralloc4::MetadataType_Usage, gralloc4::decodeUsage, outUsage);
+}
+
+status_t Gralloc4Mapper::getAllocationSize(buffer_handle_t bufferHandle,
+ uint64_t* outAllocationSize) const {
+ return get(bufferHandle, gralloc4::MetadataType_AllocationSize, gralloc4::decodeAllocationSize,
+ outAllocationSize);
+}
+
+status_t Gralloc4Mapper::getProtectedContent(buffer_handle_t bufferHandle,
+ uint64_t* outProtectedContent) const {
+ return get(bufferHandle, gralloc4::MetadataType_ProtectedContent,
+ gralloc4::decodeProtectedContent, outProtectedContent);
+}
+
+status_t Gralloc4Mapper::getCompression(buffer_handle_t bufferHandle,
+ ExtendableType* outCompression) const {
+ return get(bufferHandle, gralloc4::MetadataType_Compression, gralloc4::decodeCompression,
+ outCompression);
+}
+
+status_t Gralloc4Mapper::getCompression(buffer_handle_t bufferHandle,
+ ui::Compression* outCompression) const {
+ if (!outCompression) {
+ return BAD_VALUE;
+ }
+ ExtendableType compression;
+ status_t error = getCompression(bufferHandle, &compression);
+ if (error) {
+ return error;
+ }
+ if (!gralloc4::isStandardCompression(compression)) {
+ return BAD_TYPE;
+ }
+ *outCompression = gralloc4::getStandardCompressionValue(compression);
+ return NO_ERROR;
+}
+
+status_t Gralloc4Mapper::getInterlaced(buffer_handle_t bufferHandle,
+ ExtendableType* outInterlaced) const {
+ return get(bufferHandle, gralloc4::MetadataType_Interlaced, gralloc4::decodeInterlaced,
+ outInterlaced);
+}
+
+status_t Gralloc4Mapper::getInterlaced(buffer_handle_t bufferHandle,
+ ui::Interlaced* outInterlaced) const {
+ if (!outInterlaced) {
+ return BAD_VALUE;
+ }
+ ExtendableType interlaced;
+ status_t error = getInterlaced(bufferHandle, &interlaced);
+ if (error) {
+ return error;
+ }
+ if (!gralloc4::isStandardInterlaced(interlaced)) {
+ return BAD_TYPE;
+ }
+ *outInterlaced = gralloc4::getStandardInterlacedValue(interlaced);
+ return NO_ERROR;
+}
+
+status_t Gralloc4Mapper::getChromaSiting(buffer_handle_t bufferHandle,
+ ExtendableType* outChromaSiting) const {
+ return get(bufferHandle, gralloc4::MetadataType_ChromaSiting, gralloc4::decodeChromaSiting,
+ outChromaSiting);
+}
+
+status_t Gralloc4Mapper::getChromaSiting(buffer_handle_t bufferHandle,
+ ui::ChromaSiting* outChromaSiting) const {
+ if (!outChromaSiting) {
+ return BAD_VALUE;
+ }
+ ExtendableType chromaSiting;
+ status_t error = getChromaSiting(bufferHandle, &chromaSiting);
+ if (error) {
+ return error;
+ }
+ if (!gralloc4::isStandardChromaSiting(chromaSiting)) {
+ return BAD_TYPE;
+ }
+ *outChromaSiting = gralloc4::getStandardChromaSitingValue(chromaSiting);
+ return NO_ERROR;
+}
+
+status_t Gralloc4Mapper::getPlaneLayouts(buffer_handle_t bufferHandle,
+ std::vector<ui::PlaneLayout>* outPlaneLayouts) const {
+ return get(bufferHandle, gralloc4::MetadataType_PlaneLayouts, gralloc4::decodePlaneLayouts,
+ outPlaneLayouts);
+}
+
+status_t Gralloc4Mapper::getDataspace(buffer_handle_t bufferHandle,
+ ui::Dataspace* outDataspace) const {
+ if (!outDataspace) {
+ return BAD_VALUE;
+ }
+ aidl::android::hardware::graphics::common::Dataspace dataspace;
+ status_t error = get(bufferHandle, gralloc4::MetadataType_Dataspace, gralloc4::decodeDataspace,
+ &dataspace);
+ if (error) {
+ return error;
+ }
+
+ // Gralloc4 uses stable AIDL dataspace but the rest of the system still uses HIDL dataspace
+ *outDataspace = static_cast<ui::Dataspace>(dataspace);
+ return NO_ERROR;
+}
+
+status_t Gralloc4Mapper::getBlendMode(buffer_handle_t bufferHandle,
+ ui::BlendMode* outBlendMode) const {
+ return get(bufferHandle, gralloc4::MetadataType_BlendMode, gralloc4::decodeBlendMode,
+ outBlendMode);
+}
+
+status_t Gralloc4Mapper::getSmpte2086(buffer_handle_t bufferHandle,
+ std::optional<ui::Smpte2086>* outSmpte2086) const {
+ return get(bufferHandle, gralloc4::MetadataType_Smpte2086, gralloc4::decodeSmpte2086,
+ outSmpte2086);
+}
+
+status_t Gralloc4Mapper::getCta861_3(buffer_handle_t bufferHandle,
+ std::optional<ui::Cta861_3>* outCta861_3) const {
+ return get(bufferHandle, gralloc4::MetadataType_Cta861_3, gralloc4::decodeCta861_3,
+ outCta861_3);
+}
+
+status_t Gralloc4Mapper::getSmpte2094_40(
+ buffer_handle_t bufferHandle, std::optional<std::vector<uint8_t>>* outSmpte2094_40) const {
+ return get(bufferHandle, gralloc4::MetadataType_Smpte2094_40, gralloc4::decodeSmpte2094_40,
+ outSmpte2094_40);
+}
+
+template <class T>
+status_t Gralloc4Mapper::getDefault(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ const MetadataType& metadataType,
+ DecodeFunction<T> decodeFunction, T* outMetadata) const {
+ if (!outMetadata) {
+ return BAD_VALUE;
+ }
+
+ IMapper::BufferDescriptorInfo descriptorInfo;
+ sBufferDescriptorInfo("getDefault", width, height, format, layerCount, usage, &descriptorInfo);
+
+ hidl_vec<uint8_t> vec;
+ Error error;
+ auto ret = mMapper->getFromBufferDescriptorInfo(descriptorInfo, metadataType,
+ [&](const auto& tmpError,
+ const hidl_vec<uint8_t>& tmpVec) {
+ error = tmpError;
+ vec = tmpVec;
+ });
+
+ if (!ret.isOk()) {
+ error = kTransactionError;
+ }
+
+ if (error != Error::NONE) {
+ ALOGE("getDefault(%s, %" PRIu64 ", ...) failed with %d", metadataType.name.c_str(),
+ metadataType.value, error);
+ return static_cast<status_t>(error);
+ }
+
+ return decodeFunction(vec, outMetadata);
+}
+
+status_t Gralloc4Mapper::getDefaultPixelFormatFourCC(uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount,
+ uint64_t usage,
+ uint32_t* outPixelFormatFourCC) const {
+ return getDefault(width, height, format, layerCount, usage,
+ gralloc4::MetadataType_PixelFormatFourCC, gralloc4::decodePixelFormatFourCC,
+ outPixelFormatFourCC);
+}
+
+status_t Gralloc4Mapper::getDefaultPixelFormatModifier(uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount,
+ uint64_t usage,
+ uint64_t* outPixelFormatModifier) const {
+ return getDefault(width, height, format, layerCount, usage,
+ gralloc4::MetadataType_PixelFormatModifier,
+ gralloc4::decodePixelFormatModifier, outPixelFormatModifier);
+}
+
+status_t Gralloc4Mapper::getDefaultAllocationSize(uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount,
+ uint64_t usage,
+ uint64_t* outAllocationSize) const {
+ return getDefault(width, height, format, layerCount, usage,
+ gralloc4::MetadataType_AllocationSize, gralloc4::decodeAllocationSize,
+ outAllocationSize);
+}
+
+status_t Gralloc4Mapper::getDefaultProtectedContent(uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount,
+ uint64_t usage,
+ uint64_t* outProtectedContent) const {
+ return getDefault(width, height, format, layerCount, usage,
+ gralloc4::MetadataType_ProtectedContent, gralloc4::decodeProtectedContent,
+ outProtectedContent);
+}
+
+status_t Gralloc4Mapper::getDefaultCompression(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ ExtendableType* outCompression) const {
+ return getDefault(width, height, format, layerCount, usage, gralloc4::MetadataType_Compression,
+ gralloc4::decodeCompression, outCompression);
+}
+
+status_t Gralloc4Mapper::getDefaultCompression(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ ui::Compression* outCompression) const {
+ if (!outCompression) {
+ return BAD_VALUE;
+ }
+ ExtendableType compression;
+ status_t error = getDefaultCompression(width, height, format, layerCount, usage, &compression);
+ if (error) {
+ return error;
+ }
+ if (!gralloc4::isStandardCompression(compression)) {
+ return BAD_TYPE;
+ }
+ *outCompression = gralloc4::getStandardCompressionValue(compression);
+ return NO_ERROR;
+}
+
+status_t Gralloc4Mapper::getDefaultInterlaced(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ ExtendableType* outInterlaced) const {
+ return getDefault(width, height, format, layerCount, usage, gralloc4::MetadataType_Interlaced,
+ gralloc4::decodeInterlaced, outInterlaced);
+}
+
+status_t Gralloc4Mapper::getDefaultInterlaced(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ ui::Interlaced* outInterlaced) const {
+ if (!outInterlaced) {
+ return BAD_VALUE;
+ }
+ ExtendableType interlaced;
+ status_t error = getDefaultInterlaced(width, height, format, layerCount, usage, &interlaced);
+ if (error) {
+ return error;
+ }
+ if (!gralloc4::isStandardInterlaced(interlaced)) {
+ return BAD_TYPE;
+ }
+ *outInterlaced = gralloc4::getStandardInterlacedValue(interlaced);
+ return NO_ERROR;
+}
+
+status_t Gralloc4Mapper::getDefaultChromaSiting(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ ExtendableType* outChromaSiting) const {
+ return getDefault(width, height, format, layerCount, usage, gralloc4::MetadataType_ChromaSiting,
+ gralloc4::decodeChromaSiting, outChromaSiting);
+}
+
+status_t Gralloc4Mapper::getDefaultChromaSiting(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ ui::ChromaSiting* outChromaSiting) const {
+ if (!outChromaSiting) {
+ return BAD_VALUE;
+ }
+ ExtendableType chromaSiting;
+ status_t error =
+ getDefaultChromaSiting(width, height, format, layerCount, usage, &chromaSiting);
+ if (error) {
+ return error;
+ }
+ if (!gralloc4::isStandardChromaSiting(chromaSiting)) {
+ return BAD_TYPE;
+ }
+ *outChromaSiting = gralloc4::getStandardChromaSitingValue(chromaSiting);
+ return NO_ERROR;
+}
+
+status_t Gralloc4Mapper::getDefaultPlaneLayouts(
+ uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage,
+ std::vector<ui::PlaneLayout>* outPlaneLayouts) const {
+ return getDefault(width, height, format, layerCount, usage, gralloc4::MetadataType_PlaneLayouts,
+ gralloc4::decodePlaneLayouts, outPlaneLayouts);
+}
+
+std::vector<MetadataTypeDescription> Gralloc4Mapper::listSupportedMetadataTypes() const {
+ hidl_vec<MetadataTypeDescription> descriptions;
+ Error error;
+ auto ret = mMapper->listSupportedMetadataTypes(
+ [&](const auto& tmpError, const auto& tmpDescriptions) {
+ error = tmpError;
+ descriptions = tmpDescriptions;
+ });
+
+ if (!ret.isOk()) {
+ error = kTransactionError;
+ }
+
+ if (error != Error::NONE) {
+ ALOGE("listSupportedMetadataType() failed with %d", error);
+ return {};
+ }
+
+ return static_cast<std::vector<MetadataTypeDescription>>(descriptions);
+}
+
+template <class T>
+status_t Gralloc4Mapper::metadataDumpHelper(const BufferDump& bufferDump,
+ StandardMetadataType metadataType,
+ DecodeFunction<T> decodeFunction, T* outT) const {
+ const auto& metadataDump = bufferDump.metadataDump;
+
+ auto itr =
+ std::find_if(metadataDump.begin(), metadataDump.end(),
+ [&](const MetadataDump& tmpMetadataDump) {
+ if (!gralloc4::isStandardMetadataType(tmpMetadataDump.metadataType)) {
+ return false;
+ }
+ return metadataType ==
+ gralloc4::getStandardMetadataTypeValue(
+ tmpMetadataDump.metadataType);
+ });
+ if (itr == metadataDump.end()) {
+ return BAD_VALUE;
+ }
+
+ return decodeFunction(itr->metadata, outT);
+}
+
+status_t Gralloc4Mapper::bufferDumpHelper(const BufferDump& bufferDump, std::ostringstream* outDump,
+ uint64_t* outAllocationSize, bool less) const {
+ uint64_t bufferId;
+ std::string name;
+ uint64_t width;
+ uint64_t height;
+ uint64_t layerCount;
+ ui::PixelFormat pixelFormatRequested;
+ uint32_t pixelFormatFourCC;
+ uint64_t pixelFormatModifier;
+ uint64_t usage;
+ uint64_t allocationSize;
+ uint64_t protectedContent;
+ ExtendableType compression;
+ ExtendableType interlaced;
+ ExtendableType chromaSiting;
+ std::vector<ui::PlaneLayout> planeLayouts;
+
+ status_t error = metadataDumpHelper(bufferDump, StandardMetadataType::BUFFER_ID,
+ gralloc4::decodeBufferId, &bufferId);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = metadataDumpHelper(bufferDump, StandardMetadataType::NAME, gralloc4::decodeName, &name);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = metadataDumpHelper(bufferDump, StandardMetadataType::WIDTH, gralloc4::decodeWidth,
+ &width);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = metadataDumpHelper(bufferDump, StandardMetadataType::HEIGHT, gralloc4::decodeHeight,
+ &height);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = metadataDumpHelper(bufferDump, StandardMetadataType::LAYER_COUNT,
+ gralloc4::decodeLayerCount, &layerCount);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = metadataDumpHelper(bufferDump, StandardMetadataType::PIXEL_FORMAT_REQUESTED,
+ gralloc4::decodePixelFormatRequested, &pixelFormatRequested);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = metadataDumpHelper(bufferDump, StandardMetadataType::PIXEL_FORMAT_FOURCC,
+ gralloc4::decodePixelFormatFourCC, &pixelFormatFourCC);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = metadataDumpHelper(bufferDump, StandardMetadataType::PIXEL_FORMAT_MODIFIER,
+ gralloc4::decodePixelFormatModifier, &pixelFormatModifier);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = metadataDumpHelper(bufferDump, StandardMetadataType::USAGE, gralloc4::decodeUsage,
+ &usage);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = metadataDumpHelper(bufferDump, StandardMetadataType::ALLOCATION_SIZE,
+ gralloc4::decodeAllocationSize, &allocationSize);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = metadataDumpHelper(bufferDump, StandardMetadataType::PROTECTED_CONTENT,
+ gralloc4::decodeProtectedContent, &protectedContent);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = metadataDumpHelper(bufferDump, StandardMetadataType::COMPRESSION,
+ gralloc4::decodeCompression, &compression);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = metadataDumpHelper(bufferDump, StandardMetadataType::INTERLACED,
+ gralloc4::decodeInterlaced, &interlaced);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = metadataDumpHelper(bufferDump, StandardMetadataType::CHROMA_SITING,
+ gralloc4::decodeChromaSiting, &chromaSiting);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = metadataDumpHelper(bufferDump, StandardMetadataType::PLANE_LAYOUTS,
+ gralloc4::decodePlaneLayouts, &planeLayouts);
+ if (error != NO_ERROR) {
+ return error;
+ }
+
+ if (outAllocationSize) {
+ *outAllocationSize = allocationSize;
+ }
+ double allocationSizeKiB = static_cast<double>(allocationSize) / 1024;
+
+ *outDump << "+ name:" << name << ", id:" << bufferId << ", size:" << allocationSizeKiB
+ << "KiB, w/h:" << width << "x" << height << ", usage: 0x" << std::hex << usage
+ << std::dec << ", req fmt:" << static_cast<int32_t>(pixelFormatRequested)
+ << ", fourcc/mod:" << pixelFormatFourCC << "/" << pixelFormatModifier
+ << ", compressed: ";
+
+ if (less) {
+ bool isCompressed = !gralloc4::isStandardCompression(compression) ||
+ (gralloc4::getStandardCompressionValue(compression) != ui::Compression::NONE);
+ *outDump << std::boolalpha << isCompressed << "\n";
+ } else {
+ *outDump << gralloc4::getCompressionName(compression) << "\n";
+ }
+
+ bool firstPlane = true;
+ for (const auto& planeLayout : planeLayouts) {
+ if (firstPlane) {
+ firstPlane = false;
+ *outDump << "\tplanes: ";
+ } else {
+ *outDump << "\t ";
+ }
+
+ for (size_t i = 0; i < planeLayout.components.size(); i++) {
+ const auto& planeLayoutComponent = planeLayout.components[i];
+ *outDump << gralloc4::getPlaneLayoutComponentTypeName(planeLayoutComponent.type);
+ if (i < planeLayout.components.size() - 1) {
+ *outDump << "/";
+ } else {
+ *outDump << ":\t";
+ }
+ }
+ *outDump << " w/h:" << planeLayout.widthInSamples << "x" << planeLayout.heightInSamples
+ << ", stride:" << planeLayout.strideInBytes
+ << " bytes, size:" << planeLayout.totalSizeInBytes;
+ if (!less) {
+ *outDump << ", inc:" << planeLayout.sampleIncrementInBits
+ << " bits, subsampling w/h:" << planeLayout.horizontalSubsampling << "x"
+ << planeLayout.verticalSubsampling;
+ }
+ *outDump << "\n";
+ }
+
+ if (!less) {
+ *outDump << "\tlayer cnt: " << layerCount << ", protected content: " << protectedContent
+ << ", interlaced: " << gralloc4::getInterlacedName(interlaced)
+ << ", chroma siting:" << gralloc4::getChromaSitingName(chromaSiting) << "\n";
+ }
+
+ return NO_ERROR;
+}
+
+std::string Gralloc4Mapper::dumpBuffer(buffer_handle_t bufferHandle, bool less) const {
+ auto buffer = const_cast<native_handle_t*>(bufferHandle);
+
+ BufferDump bufferDump;
+ Error error;
+ auto ret = mMapper->dumpBuffer(buffer, [&](const auto& tmpError, const auto& tmpBufferDump) {
+ error = tmpError;
+ bufferDump = tmpBufferDump;
+ });
+
+ if (!ret.isOk()) {
+ error = kTransactionError;
+ }
+
+ if (error != Error::NONE) {
+ ALOGE("dumpBuffer() failed with %d", error);
+ return "";
+ }
+
+ std::ostringstream stream;
+ stream.precision(2);
+
+ status_t err = bufferDumpHelper(bufferDump, &stream, nullptr, less);
+ if (err != NO_ERROR) {
+ ALOGE("bufferDumpHelper() failed with %d", err);
+ return "";
+ }
+
+ return stream.str();
+}
+
+std::string Gralloc4Mapper::dumpBuffers(bool less) const {
+ hidl_vec<BufferDump> bufferDumps;
+ Error error;
+ auto ret = mMapper->dumpBuffers([&](const auto& tmpError, const auto& tmpBufferDump) {
+ error = tmpError;
+ bufferDumps = tmpBufferDump;
+ });
+
+ if (!ret.isOk()) {
+ error = kTransactionError;
+ }
+
+ if (error != Error::NONE) {
+ ALOGE("dumpBuffer() failed with %d", error);
+ return "";
+ }
+
+ uint64_t totalAllocationSize = 0;
+ std::ostringstream stream;
+ stream.precision(2);
+
+ stream << "Imported gralloc buffers:\n";
+
+ for (const auto& bufferDump : bufferDumps) {
+ uint64_t allocationSize = 0;
+ status_t err = bufferDumpHelper(bufferDump, &stream, &allocationSize, less);
+ if (err != NO_ERROR) {
+ ALOGE("bufferDumpHelper() failed with %d", err);
+ return "";
+ }
+ totalAllocationSize += allocationSize;
+ }
+
+ double totalAllocationSizeKiB = static_cast<double>(totalAllocationSize) / 1024;
+ stream << "Total imported by gralloc: " << totalAllocationSizeKiB << "KiB\n";
+ return stream.str();
+}
+
+Gralloc4Allocator::Gralloc4Allocator(const Gralloc4Mapper& mapper) : mMapper(mapper) {
+ mAllocator = IAllocator::getService();
+ if (mAllocator == nullptr) {
+ ALOGW("allocator 3.x is not supported");
+ return;
+ }
+}
+
+bool Gralloc4Allocator::isLoaded() const {
+ return mAllocator != nullptr;
+}
+
+std::string Gralloc4Allocator::dumpDebugInfo(bool less) const {
+ return mMapper.dumpBuffers(less);
+}
+
+status_t Gralloc4Allocator::allocate(std::string requestorName, uint32_t width, uint32_t height,
+ android::PixelFormat format, uint32_t layerCount,
+ uint64_t usage, uint32_t bufferCount, uint32_t* outStride,
+ buffer_handle_t* outBufferHandles, bool importBuffers) const {
+ IMapper::BufferDescriptorInfo descriptorInfo;
+ sBufferDescriptorInfo(requestorName, width, height, format, layerCount, usage, &descriptorInfo);
+
+ BufferDescriptor descriptor;
+ status_t error = mMapper.createDescriptor(static_cast<void*>(&descriptorInfo),
+ static_cast<void*>(&descriptor));
+ if (error != NO_ERROR) {
+ return error;
+ }
+
+ auto ret = mAllocator->allocate(descriptor, bufferCount,
+ [&](const auto& tmpError, const auto& tmpStride,
+ const auto& tmpBuffers) {
+ error = static_cast<status_t>(tmpError);
+ if (tmpError != Error::NONE) {
+ return;
+ }
+
+ if (importBuffers) {
+ for (uint32_t i = 0; i < bufferCount; i++) {
+ error = mMapper.importBuffer(tmpBuffers[i],
+ &outBufferHandles[i]);
+ if (error != NO_ERROR) {
+ for (uint32_t j = 0; j < i; j++) {
+ mMapper.freeBuffer(outBufferHandles[j]);
+ outBufferHandles[j] = nullptr;
+ }
+ return;
+ }
+ }
+ } else {
+ for (uint32_t i = 0; i < bufferCount; i++) {
+ outBufferHandles[i] = native_handle_clone(
+ tmpBuffers[i].getNativeHandle());
+ if (!outBufferHandles[i]) {
+ for (uint32_t j = 0; j < i; j++) {
+ auto buffer = const_cast<native_handle_t*>(
+ outBufferHandles[j]);
+ native_handle_close(buffer);
+ native_handle_delete(buffer);
+ outBufferHandles[j] = nullptr;
+ }
+ }
+ }
+ }
+ *outStride = tmpStride;
+ });
+
+ // make sure the kernel driver sees BC_FREE_BUFFER and closes the fds now
+ hardware::IPCThreadState::self()->flushCommands();
+
+ return (ret.isOk()) ? error : static_cast<status_t>(kTransactionError);
+}
+
+} // namespace android
diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp
index 3fc6a2d..3732fee 100644
--- a/libs/ui/GraphicBuffer.cpp
+++ b/libs/ui/GraphicBuffer.cpp
@@ -23,11 +23,6 @@
#include <grallocusage/GrallocUsageConversion.h>
-#ifndef LIBUI_IN_VNDK
-#include <ui/BufferHubBuffer.h>
-#endif // LIBUI_IN_VNDK
-
-#include <ui/Gralloc2.h>
#include <ui/GraphicBufferAllocator.h>
#include <ui/GraphicBufferMapper.h>
#include <utils/Trace.h>
@@ -111,22 +106,6 @@
inUsage, inStride);
}
-#ifndef LIBUI_IN_VNDK
-GraphicBuffer::GraphicBuffer(std::unique_ptr<BufferHubBuffer> buffer) : GraphicBuffer() {
- if (buffer == nullptr) {
- mInitCheck = BAD_VALUE;
- return;
- }
-
- mInitCheck = initWithHandle(buffer->duplicateHandle(), /*method=*/TAKE_UNREGISTERED_HANDLE,
- buffer->desc().width, buffer->desc().height,
- static_cast<PixelFormat>(buffer->desc().format),
- buffer->desc().layers, buffer->desc().usage, buffer->desc().stride);
- mBufferId = buffer->id();
- mBufferHubBuffer = std::move(buffer);
-}
-#endif // LIBUI_IN_VNDK
-
GraphicBuffer::~GraphicBuffer()
{
ATRACE_CALL();
@@ -375,29 +354,14 @@
}
size_t GraphicBuffer::getFlattenedSize() const {
-#ifndef LIBUI_IN_VNDK
- if (mBufferHubBuffer != nullptr) {
- return 48;
- }
-#endif
return static_cast<size_t>(13 + (handle ? mTransportNumInts : 0)) * sizeof(int);
}
size_t GraphicBuffer::getFdCount() const {
-#ifndef LIBUI_IN_VNDK
- if (mBufferHubBuffer != nullptr) {
- return 0;
- }
-#endif
return static_cast<size_t>(handle ? mTransportNumFds : 0);
}
status_t GraphicBuffer::flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const {
-#ifndef LIBUI_IN_VNDK
- if (mBufferHubBuffer != nullptr) {
- return flattenBufferHubBuffer(buffer, size);
- }
-#endif
size_t sizeNeeded = GraphicBuffer::getFlattenedSize();
if (size < sizeNeeded) return NO_MEMORY;
@@ -454,12 +418,6 @@
} else if (buf[0] == 'GBFR') {
// old version, when usage bits were 32-bits
flattenWordCount = 12;
- } else if (buf[0] == 'BHBB') { // BufferHub backed buffer.
-#ifndef LIBUI_IN_VNDK
- return unflattenBufferHubBuffer(buffer, size);
-#else
- return BAD_TYPE;
-#endif
} else {
return BAD_TYPE;
}
@@ -566,76 +524,6 @@
mDeathCallbacks.emplace_back(deathCallback, context);
}
-#ifndef LIBUI_IN_VNDK
-status_t GraphicBuffer::flattenBufferHubBuffer(void*& buffer, size_t& size) const {
- sp<NativeHandle> tokenHandle = mBufferHubBuffer->duplicate();
- if (tokenHandle == nullptr || tokenHandle->handle() == nullptr ||
- tokenHandle->handle()->numFds != 0) {
- return BAD_VALUE;
- }
-
- // Size needed for one label, one number of ints inside the token, one generation number and
- // the token itself.
- int numIntsInToken = tokenHandle->handle()->numInts;
- const size_t sizeNeeded = static_cast<size_t>(3 + numIntsInToken) * sizeof(int);
- if (size < sizeNeeded) {
- ALOGE("%s: needed size %d, given size %d. Not enough memory.", __FUNCTION__,
- static_cast<int>(sizeNeeded), static_cast<int>(size));
- return NO_MEMORY;
- }
- size -= sizeNeeded;
-
- int* buf = static_cast<int*>(buffer);
- buf[0] = 'BHBB';
- buf[1] = numIntsInToken;
- memcpy(buf + 2, tokenHandle->handle()->data, static_cast<size_t>(numIntsInToken) * sizeof(int));
- buf[2 + numIntsInToken] = static_cast<int32_t>(mGenerationNumber);
-
- return NO_ERROR;
-}
-
-status_t GraphicBuffer::unflattenBufferHubBuffer(void const*& buffer, size_t& size) {
- const int* buf = static_cast<const int*>(buffer);
- int numIntsInToken = buf[1];
- // Size needed for one label, one number of ints inside the token, one generation number and
- // the token itself.
- const size_t sizeNeeded = static_cast<size_t>(3 + numIntsInToken) * sizeof(int);
- if (size < sizeNeeded) {
- ALOGE("%s: needed size %d, given size %d. Not enough memory.", __FUNCTION__,
- static_cast<int>(sizeNeeded), static_cast<int>(size));
- return NO_MEMORY;
- }
- size -= sizeNeeded;
- native_handle_t* importToken = native_handle_create(/*numFds=*/0, /*numInts=*/numIntsInToken);
- memcpy(importToken->data, buf + 2, static_cast<size_t>(buf[1]) * sizeof(int));
- sp<NativeHandle> importTokenHandle = NativeHandle::create(importToken, /*ownHandle=*/true);
- std::unique_ptr<BufferHubBuffer> bufferHubBuffer = BufferHubBuffer::import(importTokenHandle);
- if (bufferHubBuffer == nullptr || bufferHubBuffer.get() == nullptr) {
- return BAD_VALUE;
- }
- // Reconstruct this GraphicBuffer object using the new BufferHubBuffer object.
- if (handle) {
- free_handle();
- }
- mId = 0;
- mGenerationNumber = static_cast<uint32_t>(buf[2 + numIntsInToken]);
- mInitCheck =
- initWithHandle(bufferHubBuffer->duplicateHandle(), /*method=*/TAKE_UNREGISTERED_HANDLE,
- bufferHubBuffer->desc().width, bufferHubBuffer->desc().height,
- static_cast<PixelFormat>(bufferHubBuffer->desc().format),
- bufferHubBuffer->desc().layers, bufferHubBuffer->desc().usage,
- bufferHubBuffer->desc().stride);
- mBufferId = bufferHubBuffer->id();
- mBufferHubBuffer.reset(std::move(bufferHubBuffer.get()));
-
- return NO_ERROR;
-}
-
-bool GraphicBuffer::isBufferHubBuffer() const {
- return mBufferHubBuffer != nullptr;
-}
-#endif // LIBUI_IN_VNDK
-
// ---------------------------------------------------------------------------
}; // namespace android
diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp
index b54ce0f..943d13e 100644
--- a/libs/ui/GraphicBufferAllocator.cpp
+++ b/libs/ui/GraphicBufferAllocator.cpp
@@ -33,6 +33,7 @@
#include <ui/Gralloc.h>
#include <ui/Gralloc2.h>
#include <ui/Gralloc3.h>
+#include <ui/Gralloc4.h>
#include <ui/GraphicBufferMapper.h>
namespace android {
@@ -47,16 +48,23 @@
GraphicBufferAllocator::alloc_rec_t> GraphicBufferAllocator::sAllocList;
GraphicBufferAllocator::GraphicBufferAllocator() : mMapper(GraphicBufferMapper::getInstance()) {
+ mAllocator = std::make_unique<const Gralloc4Allocator>(
+ reinterpret_cast<const Gralloc4Mapper&>(mMapper.getGrallocMapper()));
+ if (mAllocator->isLoaded()) {
+ return;
+ }
mAllocator = std::make_unique<const Gralloc3Allocator>(
reinterpret_cast<const Gralloc3Mapper&>(mMapper.getGrallocMapper()));
- if (!mAllocator->isLoaded()) {
- mAllocator = std::make_unique<const Gralloc2Allocator>(
- reinterpret_cast<const Gralloc2Mapper&>(mMapper.getGrallocMapper()));
+ if (mAllocator->isLoaded()) {
+ return;
+ }
+ mAllocator = std::make_unique<const Gralloc2Allocator>(
+ reinterpret_cast<const Gralloc2Mapper&>(mMapper.getGrallocMapper()));
+ if (mAllocator->isLoaded()) {
+ return;
}
- if (!mAllocator->isLoaded()) {
- LOG_ALWAYS_FATAL("gralloc-allocator is missing");
- }
+ LOG_ALWAYS_FATAL("gralloc-allocator is missing");
}
GraphicBufferAllocator::~GraphicBufferAllocator() {}
@@ -70,11 +78,11 @@
return total;
}
-void GraphicBufferAllocator::dump(std::string& result) const {
+void GraphicBufferAllocator::dump(std::string& result, bool less) const {
Mutex::Autolock _l(sLock);
KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList);
uint64_t total = 0;
- result.append("Allocated buffers:\n");
+ result.append("GraphicBufferAllocator buffers:\n");
const size_t c = list.size();
for (size_t i=0 ; i<c ; i++) {
const alloc_rec_t& rec(list.valueAt(i));
@@ -91,23 +99,22 @@
}
total += rec.size;
}
- StringAppendF(&result, "Total allocated (estimate): %.2f KB\n", static_cast<double>(total) / 1024.0);
+ StringAppendF(&result, "Total allocated by GraphicBufferAllocator (estimate): %.2f KB\n",
+ static_cast<double>(total) / 1024.0);
- result.append(mAllocator->dumpDebugInfo());
+ result.append(mAllocator->dumpDebugInfo(less));
}
-void GraphicBufferAllocator::dumpToSystemLog()
-{
+void GraphicBufferAllocator::dumpToSystemLog(bool less) {
std::string s;
- GraphicBufferAllocator::getInstance().dump(s);
+ GraphicBufferAllocator::getInstance().dump(s, less);
ALOGD("%s", s.c_str());
}
-status_t GraphicBufferAllocator::allocate(uint32_t width, uint32_t height,
- PixelFormat format, uint32_t layerCount, uint64_t usage,
- buffer_handle_t* handle, uint32_t* stride,
- uint64_t /*graphicBufferId*/, std::string requestorName)
-{
+status_t GraphicBufferAllocator::allocateHelper(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ buffer_handle_t* handle, uint32_t* stride,
+ std::string requestorName, bool importBuffer) {
ATRACE_CALL();
// make sure to not allocate a N x 0 or 0 x N buffer, since this is
@@ -130,8 +137,18 @@
// TODO(b/72323293, b/72703005): Remove these invalid bits from callers
usage &= ~static_cast<uint64_t>((1 << 10) | (1 << 13));
- status_t error =
- mAllocator->allocate(width, height, format, layerCount, usage, 1, stride, handle);
+ status_t error = mAllocator->allocate(requestorName, width, height, format, layerCount, usage,
+ 1, stride, handle, importBuffer);
+ if (error != NO_ERROR) {
+ ALOGE("Failed to allocate (%u x %u) layerCount %u format %d "
+ "usage %" PRIx64 ": %d",
+ width, height, layerCount, format, usage, error);
+ return NO_MEMORY;
+ }
+
+ if (!importBuffer) {
+ return NO_ERROR;
+ }
size_t bufSize;
// if stride has no meaning or is too large,
@@ -143,28 +160,44 @@
bufSize = static_cast<size_t>((*stride)) * height * bpp;
}
- if (error == NO_ERROR) {
- Mutex::Autolock _l(sLock);
- KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList);
- alloc_rec_t rec;
- rec.width = width;
- rec.height = height;
- rec.stride = *stride;
- rec.format = format;
- rec.layerCount = layerCount;
- rec.usage = usage;
- rec.size = bufSize;
- rec.requestorName = std::move(requestorName);
- list.add(*handle, rec);
+ Mutex::Autolock _l(sLock);
+ KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList);
+ alloc_rec_t rec;
+ rec.width = width;
+ rec.height = height;
+ rec.stride = *stride;
+ rec.format = format;
+ rec.layerCount = layerCount;
+ rec.usage = usage;
+ rec.size = bufSize;
+ rec.requestorName = std::move(requestorName);
+ list.add(*handle, rec);
- return NO_ERROR;
- } else {
- ALOGE("Failed to allocate (%u x %u) layerCount %u format %d "
- "usage %" PRIx64 ": %d",
- width, height, layerCount, format, usage,
- error);
- return NO_MEMORY;
- }
+ return NO_ERROR;
+}
+status_t GraphicBufferAllocator::allocate(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ buffer_handle_t* handle, uint32_t* stride,
+ std::string requestorName) {
+ return allocateHelper(width, height, format, layerCount, usage, handle, stride, requestorName,
+ true);
+}
+
+status_t GraphicBufferAllocator::allocateRawHandle(uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount,
+ uint64_t usage, buffer_handle_t* handle,
+ uint32_t* stride, std::string requestorName) {
+ return allocateHelper(width, height, format, layerCount, usage, handle, stride, requestorName,
+ false);
+}
+
+// DEPRECATED
+status_t GraphicBufferAllocator::allocate(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ buffer_handle_t* handle, uint32_t* stride,
+ uint64_t /*graphicBufferId*/, std::string requestorName) {
+ return allocateHelper(width, height, format, layerCount, usage, handle, stride, requestorName,
+ true);
}
status_t GraphicBufferAllocator::free(buffer_handle_t handle)
diff --git a/libs/ui/GraphicBufferMapper.cpp b/libs/ui/GraphicBufferMapper.cpp
index 25b7247..d20bd7a 100644
--- a/libs/ui/GraphicBufferMapper.cpp
+++ b/libs/ui/GraphicBufferMapper.cpp
@@ -35,6 +35,7 @@
#include <ui/Gralloc.h>
#include <ui/Gralloc2.h>
#include <ui/Gralloc3.h>
+#include <ui/Gralloc4.h>
#include <ui/GraphicBuffer.h>
#include <system/graphics.h>
@@ -47,20 +48,38 @@
void GraphicBufferMapper::preloadHal() {
Gralloc2Mapper::preload();
Gralloc3Mapper::preload();
+ Gralloc4Mapper::preload();
}
GraphicBufferMapper::GraphicBufferMapper() {
+ mMapper = std::make_unique<const Gralloc4Mapper>();
+ if (mMapper->isLoaded()) {
+ mMapperVersion = Version::GRALLOC_4;
+ return;
+ }
mMapper = std::make_unique<const Gralloc3Mapper>();
- if (!mMapper->isLoaded()) {
- mMapper = std::make_unique<const Gralloc2Mapper>();
- mMapperVersion = Version::GRALLOC_2;
- } else {
+ if (mMapper->isLoaded()) {
mMapperVersion = Version::GRALLOC_3;
+ return;
+ }
+ mMapper = std::make_unique<const Gralloc2Mapper>();
+ if (mMapper->isLoaded()) {
+ mMapperVersion = Version::GRALLOC_2;
+ return;
}
- if (!mMapper->isLoaded()) {
- LOG_ALWAYS_FATAL("gralloc-mapper is missing");
- }
+ LOG_ALWAYS_FATAL("gralloc-mapper is missing");
+}
+
+void GraphicBufferMapper::dumpBuffer(buffer_handle_t bufferHandle, std::string& result,
+ bool less) const {
+ result.append(mMapper->dumpBuffer(bufferHandle, less));
+}
+
+void GraphicBufferMapper::dumpBufferToSystemLog(buffer_handle_t bufferHandle, bool less) {
+ std::string s;
+ GraphicBufferMapper::getInstance().dumpBuffer(bufferHandle, s, less);
+ ALOGD("%s", s.c_str());
}
status_t GraphicBufferMapper::importBuffer(buffer_handle_t rawHandle,
@@ -169,5 +188,197 @@
uint64_t usage, bool* outSupported) {
return mMapper->isSupported(width, height, format, layerCount, usage, outSupported);
}
+
+status_t GraphicBufferMapper::getBufferId(buffer_handle_t bufferHandle, uint64_t* outBufferId) {
+ return mMapper->getBufferId(bufferHandle, outBufferId);
+}
+
+status_t GraphicBufferMapper::getName(buffer_handle_t bufferHandle, std::string* outName) {
+ return mMapper->getName(bufferHandle, outName);
+}
+
+status_t GraphicBufferMapper::getWidth(buffer_handle_t bufferHandle, uint64_t* outWidth) {
+ return mMapper->getWidth(bufferHandle, outWidth);
+}
+
+status_t GraphicBufferMapper::getHeight(buffer_handle_t bufferHandle, uint64_t* outHeight) {
+ return mMapper->getHeight(bufferHandle, outHeight);
+}
+
+status_t GraphicBufferMapper::getLayerCount(buffer_handle_t bufferHandle, uint64_t* outLayerCount) {
+ return mMapper->getLayerCount(bufferHandle, outLayerCount);
+}
+
+status_t GraphicBufferMapper::getPixelFormatRequested(buffer_handle_t bufferHandle,
+ ui::PixelFormat* outPixelFormatRequested) {
+ return mMapper->getPixelFormatRequested(bufferHandle, outPixelFormatRequested);
+}
+
+status_t GraphicBufferMapper::getPixelFormatFourCC(buffer_handle_t bufferHandle,
+ uint32_t* outPixelFormatFourCC) {
+ return mMapper->getPixelFormatFourCC(bufferHandle, outPixelFormatFourCC);
+}
+
+status_t GraphicBufferMapper::getPixelFormatModifier(buffer_handle_t bufferHandle,
+ uint64_t* outPixelFormatModifier) {
+ return mMapper->getPixelFormatModifier(bufferHandle, outPixelFormatModifier);
+}
+
+status_t GraphicBufferMapper::getUsage(buffer_handle_t bufferHandle, uint64_t* outUsage) {
+ return mMapper->getUsage(bufferHandle, outUsage);
+}
+
+status_t GraphicBufferMapper::getAllocationSize(buffer_handle_t bufferHandle,
+ uint64_t* outAllocationSize) {
+ return mMapper->getAllocationSize(bufferHandle, outAllocationSize);
+}
+
+status_t GraphicBufferMapper::getProtectedContent(buffer_handle_t bufferHandle,
+ uint64_t* outProtectedContent) {
+ return mMapper->getProtectedContent(bufferHandle, outProtectedContent);
+}
+
+status_t GraphicBufferMapper::getCompression(
+ buffer_handle_t bufferHandle,
+ aidl::android::hardware::graphics::common::ExtendableType* outCompression) {
+ return mMapper->getCompression(bufferHandle, outCompression);
+}
+
+status_t GraphicBufferMapper::getCompression(buffer_handle_t bufferHandle,
+ ui::Compression* outCompression) {
+ return mMapper->getCompression(bufferHandle, outCompression);
+}
+
+status_t GraphicBufferMapper::getInterlaced(
+ buffer_handle_t bufferHandle,
+ aidl::android::hardware::graphics::common::ExtendableType* outInterlaced) {
+ return mMapper->getInterlaced(bufferHandle, outInterlaced);
+}
+
+status_t GraphicBufferMapper::getInterlaced(buffer_handle_t bufferHandle,
+ ui::Interlaced* outInterlaced) {
+ return mMapper->getInterlaced(bufferHandle, outInterlaced);
+}
+
+status_t GraphicBufferMapper::getChromaSiting(
+ buffer_handle_t bufferHandle,
+ aidl::android::hardware::graphics::common::ExtendableType* outChromaSiting) {
+ return mMapper->getChromaSiting(bufferHandle, outChromaSiting);
+}
+
+status_t GraphicBufferMapper::getChromaSiting(buffer_handle_t bufferHandle,
+ ui::ChromaSiting* outChromaSiting) {
+ return mMapper->getChromaSiting(bufferHandle, outChromaSiting);
+}
+
+status_t GraphicBufferMapper::getPlaneLayouts(buffer_handle_t bufferHandle,
+ std::vector<ui::PlaneLayout>* outPlaneLayouts) {
+ return mMapper->getPlaneLayouts(bufferHandle, outPlaneLayouts);
+}
+
+status_t GraphicBufferMapper::getDataspace(buffer_handle_t bufferHandle,
+ ui::Dataspace* outDataspace) {
+ return mMapper->getDataspace(bufferHandle, outDataspace);
+}
+
+status_t GraphicBufferMapper::getBlendMode(buffer_handle_t bufferHandle,
+ ui::BlendMode* outBlendMode) {
+ return mMapper->getBlendMode(bufferHandle, outBlendMode);
+}
+
+status_t GraphicBufferMapper::getSmpte2086(buffer_handle_t bufferHandle,
+ std::optional<ui::Smpte2086>* outSmpte2086) {
+ return mMapper->getSmpte2086(bufferHandle, outSmpte2086);
+}
+
+status_t GraphicBufferMapper::getCta861_3(buffer_handle_t bufferHandle,
+ std::optional<ui::Cta861_3>* outCta861_3) {
+ return mMapper->getCta861_3(bufferHandle, outCta861_3);
+}
+
+status_t GraphicBufferMapper::getSmpte2094_40(
+ buffer_handle_t bufferHandle, std::optional<std::vector<uint8_t>>* outSmpte2094_40) {
+ return mMapper->getSmpte2094_40(bufferHandle, outSmpte2094_40);
+}
+
+status_t GraphicBufferMapper::getDefaultPixelFormatFourCC(uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount,
+ uint64_t usage,
+ uint32_t* outPixelFormatFourCC) {
+ return mMapper->getDefaultPixelFormatFourCC(width, height, format, layerCount, usage,
+ outPixelFormatFourCC);
+}
+
+status_t GraphicBufferMapper::getDefaultPixelFormatModifier(uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount,
+ uint64_t usage,
+ uint64_t* outPixelFormatModifier) {
+ return mMapper->getDefaultPixelFormatModifier(width, height, format, layerCount, usage,
+ outPixelFormatModifier);
+}
+
+status_t GraphicBufferMapper::getDefaultAllocationSize(uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount,
+ uint64_t usage,
+ uint64_t* outAllocationSize) {
+ return mMapper->getDefaultAllocationSize(width, height, format, layerCount, usage,
+ outAllocationSize);
+}
+
+status_t GraphicBufferMapper::getDefaultProtectedContent(uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount,
+ uint64_t usage,
+ uint64_t* outProtectedContent) {
+ return mMapper->getDefaultProtectedContent(width, height, format, layerCount, usage,
+ outProtectedContent);
+}
+
+status_t GraphicBufferMapper::getDefaultCompression(
+ uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage,
+ aidl::android::hardware::graphics::common::ExtendableType* outCompression) {
+ return mMapper->getDefaultCompression(width, height, format, layerCount, usage, outCompression);
+}
+
+status_t GraphicBufferMapper::getDefaultCompression(uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount,
+ uint64_t usage,
+ ui::Compression* outCompression) {
+ return mMapper->getDefaultCompression(width, height, format, layerCount, usage, outCompression);
+}
+
+status_t GraphicBufferMapper::getDefaultInterlaced(
+ uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage,
+ aidl::android::hardware::graphics::common::ExtendableType* outInterlaced) {
+ return mMapper->getDefaultInterlaced(width, height, format, layerCount, usage, outInterlaced);
+}
+
+status_t GraphicBufferMapper::getDefaultInterlaced(uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount,
+ uint64_t usage, ui::Interlaced* outInterlaced) {
+ return mMapper->getDefaultInterlaced(width, height, format, layerCount, usage, outInterlaced);
+}
+
+status_t GraphicBufferMapper::getDefaultChromaSiting(
+ uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage,
+ aidl::android::hardware::graphics::common::ExtendableType* outChromaSiting) {
+ return mMapper->getDefaultChromaSiting(width, height, format, layerCount, usage,
+ outChromaSiting);
+}
+
+status_t GraphicBufferMapper::getDefaultChromaSiting(uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount,
+ uint64_t usage,
+ ui::ChromaSiting* outChromaSiting) {
+ return mMapper->getDefaultChromaSiting(width, height, format, layerCount, usage,
+ outChromaSiting);
+}
+
+status_t GraphicBufferMapper::getDefaultPlaneLayouts(
+ uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage,
+ std::vector<ui::PlaneLayout>* outPlaneLayouts) {
+ return mMapper->getDefaultPlaneLayouts(width, height, format, layerCount, usage,
+ outPlaneLayouts);
+}
+
// ---------------------------------------------------------------------------
}; // namespace android
diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp
index 1222cd6..e01309b 100644
--- a/libs/ui/Region.cpp
+++ b/libs/ui/Region.cpp
@@ -23,11 +23,10 @@
#include <utils/Log.h>
+#include <ui/Point.h>
#include <ui/Rect.h>
#include <ui/Region.h>
-#include <ui/Point.h>
-
-#include <private/ui/RegionHelper.h>
+#include <ui/RegionHelper.h>
// ----------------------------------------------------------------------------
@@ -68,19 +67,20 @@
// ----------------------------------------------------------------------------
Region::Region() {
- mStorage.add(Rect(0,0));
+ mStorage.push_back(Rect(0, 0));
}
Region::Region(const Region& rhs)
- : mStorage(rhs.mStorage)
{
+ mStorage.clear();
+ mStorage.insert(mStorage.begin(), rhs.mStorage.begin(), rhs.mStorage.end());
#if defined(VALIDATE_REGIONS)
validate(rhs, "rhs copy-ctor");
#endif
}
Region::Region(const Rect& rhs) {
- mStorage.add(rhs);
+ mStorage.push_back(rhs);
}
Region::~Region()
@@ -101,8 +101,8 @@
* final, correctly ordered region buffer. Each rectangle will be compared with the span directly
* above it, and subdivided to resolve any remaining T-junctions.
*/
-static void reverseRectsResolvingJunctions(const Rect* begin, const Rect* end,
- Vector<Rect>& dst, int spanDirection) {
+static void reverseRectsResolvingJunctions(const Rect* begin, const Rect* end, FatVector<Rect>& dst,
+ int spanDirection) {
dst.clear();
const Rect* current = end - 1;
@@ -110,7 +110,7 @@
// add first span immediately
do {
- dst.add(*current);
+ dst.push_back(*current);
current--;
} while (current->top == lastTop && current >= begin);
@@ -148,12 +148,12 @@
if (prev.right <= left) break;
if (prev.right > left && prev.right < right) {
- dst.add(Rect(prev.right, top, right, bottom));
+ dst.push_back(Rect(prev.right, top, right, bottom));
right = prev.right;
}
if (prev.left > left && prev.left < right) {
- dst.add(Rect(prev.left, top, right, bottom));
+ dst.push_back(Rect(prev.left, top, right, bottom));
right = prev.left;
}
@@ -167,12 +167,12 @@
if (prev.left >= right) break;
if (prev.left > left && prev.left < right) {
- dst.add(Rect(left, top, prev.left, bottom));
+ dst.push_back(Rect(left, top, prev.left, bottom));
left = prev.left;
}
if (prev.right > left && prev.right < right) {
- dst.add(Rect(left, top, prev.right, bottom));
+ dst.push_back(Rect(left, top, prev.right, bottom));
left = prev.right;
}
// if an entry in the previous span is too far left, nothing further right in the
@@ -184,7 +184,7 @@
}
if (left < right) {
- dst.add(Rect(left, top, right, bottom));
+ dst.push_back(Rect(left, top, right, bottom));
}
current--;
@@ -202,13 +202,14 @@
if (r.isEmpty()) return r;
if (r.isRect()) return r;
- Vector<Rect> reversed;
+ FatVector<Rect> reversed;
reverseRectsResolvingJunctions(r.begin(), r.end(), reversed, direction_RTL);
Region outputRegion;
- reverseRectsResolvingJunctions(reversed.begin(), reversed.end(),
- outputRegion.mStorage, direction_LTR);
- outputRegion.mStorage.add(r.getBounds()); // to make region valid, mStorage must end with bounds
+ reverseRectsResolvingJunctions(reversed.data(), reversed.data() + reversed.size(),
+ outputRegion.mStorage, direction_LTR);
+ outputRegion.mStorage.push_back(
+ r.getBounds()); // to make region valid, mStorage must end with bounds
#if defined(VALIDATE_REGIONS)
validate(outputRegion, "T-Junction free region");
@@ -223,7 +224,13 @@
validate(*this, "this->operator=");
validate(rhs, "rhs.operator=");
#endif
- mStorage = rhs.mStorage;
+ if (this == &rhs) {
+ // Already equal to itself
+ return *this;
+ }
+
+ mStorage.clear();
+ mStorage.insert(mStorage.begin(), rhs.mStorage.begin(), rhs.mStorage.end());
return *this;
}
@@ -232,7 +239,7 @@
if (mStorage.size() >= 2) {
const Rect bounds(getBounds());
mStorage.clear();
- mStorage.add(bounds);
+ mStorage.push_back(bounds);
}
return *this;
}
@@ -256,43 +263,60 @@
void Region::clear()
{
mStorage.clear();
- mStorage.add(Rect(0,0));
+ mStorage.push_back(Rect(0, 0));
}
void Region::set(const Rect& r)
{
mStorage.clear();
- mStorage.add(r);
+ mStorage.push_back(r);
}
void Region::set(int32_t w, int32_t h)
{
mStorage.clear();
- mStorage.add(Rect(w, h));
+ mStorage.push_back(Rect(w, h));
}
void Region::set(uint32_t w, uint32_t h)
{
mStorage.clear();
- mStorage.add(Rect(w, h));
+ mStorage.push_back(Rect(w, h));
}
bool Region::isTriviallyEqual(const Region& region) const {
return begin() == region.begin();
}
+bool Region::hasSameRects(const Region& other) const {
+ size_t thisRectCount = 0;
+ android::Rect const* thisRects = getArray(&thisRectCount);
+ size_t otherRectCount = 0;
+ android::Rect const* otherRects = other.getArray(&otherRectCount);
+
+ if (thisRectCount != otherRectCount) return false;
+
+ for (size_t i = 0; i < thisRectCount; i++) {
+ if (thisRects[i] != otherRects[i]) return false;
+ }
+ return true;
+}
+
// ----------------------------------------------------------------------------
void Region::addRectUnchecked(int l, int t, int r, int b)
{
Rect rect(l,t,r,b);
- size_t where = mStorage.size() - 1;
- mStorage.insertAt(rect, where, 1);
+ mStorage.insert(mStorage.end() - 1, rect);
}
// ----------------------------------------------------------------------------
Region& Region::orSelf(const Rect& r) {
+ if (isEmpty()) {
+ set(r);
+ return *this;
+ }
return operationSelf(r, op_or);
}
Region& Region::xorSelf(const Rect& r) {
@@ -313,6 +337,10 @@
// ----------------------------------------------------------------------------
Region& Region::orSelf(const Region& rhs) {
+ if (isEmpty()) {
+ *this = rhs;
+ return *this;
+ }
return operationSelf(rhs, op_or);
}
Region& Region::xorSelf(const Region& rhs) {
@@ -337,7 +365,7 @@
Region& Region::scaleSelf(float sx, float sy) {
size_t count = mStorage.size();
- Rect* rects = mStorage.editArray();
+ Rect* rects = mStorage.data();
while (count) {
rects->left = static_cast<int32_t>(static_cast<float>(rects->left) * sx + 0.5f);
rects->right = static_cast<int32_t>(static_cast<float>(rects->right) * sx + 0.5f);
@@ -442,10 +470,10 @@
class Region::rasterizer : public region_operator<Rect>::region_rasterizer
{
Rect bounds;
- Vector<Rect>& storage;
+ FatVector<Rect>& storage;
Rect* head;
Rect* tail;
- Vector<Rect> span;
+ FatVector<Rect> span;
Rect* cur;
public:
explicit rasterizer(Region& reg)
@@ -472,8 +500,8 @@
flushSpan();
}
if (storage.size()) {
- bounds.top = storage.itemAt(0).top;
- bounds.bottom = storage.top().bottom;
+ bounds.top = storage.front().top;
+ bounds.bottom = storage.back().bottom;
if (storage.size() == 1) {
storage.clear();
}
@@ -481,7 +509,7 @@
bounds.left = 0;
bounds.right = 0;
}
- storage.add(bounds);
+ storage.push_back(bounds);
}
void Region::rasterizer::operator()(const Rect& rect)
@@ -496,15 +524,15 @@
return;
}
}
- span.add(rect);
- cur = span.editArray() + (span.size() - 1);
+ span.push_back(rect);
+ cur = span.data() + (span.size() - 1);
}
void Region::rasterizer::flushSpan()
{
bool merge = false;
if (tail-head == ssize_t(span.size())) {
- Rect const* p = span.editArray();
+ Rect const* p = span.data();
Rect const* q = head;
if (p->top == q->bottom) {
merge = true;
@@ -519,17 +547,17 @@
}
}
if (merge) {
- const int bottom = span[0].bottom;
+ const int bottom = span.front().bottom;
Rect* r = head;
while (r != tail) {
r->bottom = bottom;
r++;
}
} else {
- bounds.left = min(span.itemAt(0).left, bounds.left);
- bounds.right = max(span.top().right, bounds.right);
- storage.appendVector(span);
- tail = storage.editArray() + storage.size();
+ bounds.left = min(span.front().left, bounds.left);
+ bounds.right = max(span.back().right, bounds.right);
+ storage.insert(storage.end(), span.begin(), span.end());
+ tail = storage.data() + storage.size();
head = tail - span.size();
}
span.clear();
@@ -537,7 +565,7 @@
bool Region::validate(const Region& reg, const char* name, bool silent)
{
- if (reg.mStorage.isEmpty()) {
+ if (reg.mStorage.empty()) {
ALOGE_IF(!silent, "%s: mStorage is empty, which is never valid", name);
// return immediately as the code below assumes mStorage is non-empty
return false;
@@ -676,9 +704,8 @@
}
sk_dst.op(sk_lhs, sk_rhs, sk_op);
- if (sk_dst.isEmpty() && dst.isEmpty())
- return;
-
+ if (sk_dst.empty() && dst.empty()) return;
+
bool same = true;
Region::const_iterator head = dst.begin();
Region::const_iterator const tail = dst.end();
@@ -773,7 +800,7 @@
validate(reg, "translate (before)");
#endif
size_t count = reg.mStorage.size();
- Rect* rects = reg.mStorage.editArray();
+ Rect* rects = reg.mStorage.data();
while (count) {
rects->offsetBy(dx, dy);
rects++;
@@ -853,24 +880,25 @@
ALOGE("Region::unflatten() failed, invalid region");
return BAD_VALUE;
}
- mStorage = result.mStorage;
+ mStorage.clear();
+ mStorage.insert(mStorage.begin(), result.mStorage.begin(), result.mStorage.end());
return NO_ERROR;
}
// ----------------------------------------------------------------------------
Region::const_iterator Region::begin() const {
- return mStorage.array();
+ return mStorage.data();
}
Region::const_iterator Region::end() const {
// Workaround for b/77643177
// mStorage should never be empty, but somehow it is and it's causing
// an abort in ubsan
- if (mStorage.isEmpty()) return mStorage.array();
+ if (mStorage.empty()) return mStorage.data();
size_t numRects = isRect() ? 1 : mStorage.size() - 1;
- return mStorage.array() + numRects;
+ return mStorage.data() + numRects;
}
Rect const* Region::getArray(size_t* count) const {
diff --git a/libs/ui/Transform.cpp b/libs/ui/Transform.cpp
index 28c3f7b..06b6bfe 100644
--- a/libs/ui/Transform.cpp
+++ b/libs/ui/Transform.cpp
@@ -33,8 +33,8 @@
: mMatrix(other.mMatrix), mType(other.mType) {
}
-Transform::Transform(uint32_t orientation) {
- set(orientation, 0, 0);
+Transform::Transform(uint32_t orientation, int w, int h) {
+ set(orientation, w, h);
}
Transform::~Transform() = default;
@@ -49,6 +49,15 @@
return isZero(fabs(f) - 1.0f);
}
+bool Transform::operator==(const Transform& other) const {
+ return mMatrix[0][0] == other.mMatrix[0][0] && mMatrix[0][1] == other.mMatrix[0][1] &&
+ mMatrix[0][2] == other.mMatrix[0][2] && mMatrix[1][0] == other.mMatrix[1][0] &&
+ mMatrix[1][1] == other.mMatrix[1][1] && mMatrix[1][2] == other.mMatrix[1][2] &&
+ mMatrix[2][0] == other.mMatrix[2][0] && mMatrix[2][1] == other.mMatrix[2][1] &&
+ mMatrix[2][2] == other.mMatrix[2][2];
+ ;
+}
+
Transform Transform::operator * (const Transform& rhs) const
{
if (CC_LIKELY(mType == IDENTITY))
diff --git a/libs/ui/include/ui/BufferHubBuffer.h b/libs/ui/include/ui/BufferHubBuffer.h
deleted file mode 100644
index 5ba189c..0000000
--- a/libs/ui/include/ui/BufferHubBuffer.h
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * Copyright (C) 2018 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.
- */
-
-#ifndef ANDROID_BUFFER_HUB_BUFFER_H_
-#define ANDROID_BUFFER_HUB_BUFFER_H_
-
-#include <android/frameworks/bufferhub/1.0/IBufferClient.h>
-#include <android/hardware_buffer.h>
-#include <cutils/native_handle.h>
-#include <ui/BufferHubDefs.h>
-#include <ui/BufferHubEventFd.h>
-#include <ui/BufferHubMetadata.h>
-#include <utils/NativeHandle.h>
-
-namespace android {
-
-class BufferHubBuffer {
-public:
- // Allocates a standalone BufferHubBuffer.
- static std::unique_ptr<BufferHubBuffer> create(uint32_t width, uint32_t height,
- uint32_t layerCount, uint32_t format,
- uint64_t usage, size_t userMetadataSize);
-
- // Imports the given token to a BufferHubBuffer. Not taking ownership of the token.
- static std::unique_ptr<BufferHubBuffer> import(const sp<NativeHandle>& token);
-
- BufferHubBuffer(const BufferHubBuffer&) = delete;
- void operator=(const BufferHubBuffer&) = delete;
-
- virtual ~BufferHubBuffer();
-
- // Gets ID of the buffer client. All BufferHubBuffer clients derived from the same buffer in
- // BufferHub share the same buffer id.
- int id() const { return mId; }
-
- // Returns the buffer description, which is guaranteed to be faithful values from BufferHub.
- const AHardwareBuffer_Desc& desc() const { return mBufferDesc; }
-
- // Duplicate the underlying Gralloc buffer handle. Caller is responsible to free the handle
- // after use.
- native_handle_t* duplicateHandle() {
- return native_handle_clone(mBufferHandle.getNativeHandle());
- }
-
- const BufferHubEventFd& eventFd() const { return mEventFd; }
-
- // Returns the current value of MetadataHeader::bufferState.
- uint32_t bufferState() const { return mBufferState->load(std::memory_order_acquire); }
-
- // A state mask which is unique to a buffer hub client among all its siblings sharing the same
- // concrete graphic buffer.
- uint32_t clientStateMask() const { return mClientStateMask; }
-
- size_t userMetadataSize() const { return mMetadata.userMetadataSize(); }
-
- // Returns true if the BufferClient is still alive.
- bool isConnected() const { return mBufferClient->ping().isOk(); }
-
- // Returns true if the buffer is valid: non-null buffer handle, valid id, valid client bit mask,
- // valid metadata and valid buffer client
- bool isValid() const;
-
- // Gains the buffer for exclusive write permission. Read permission is implied once a buffer is
- // gained.
- // The buffer can be gained as long as there is no other client in acquired or gained state.
- int gain();
-
- // Posts the gained buffer for other buffer clients to use the buffer.
- // The buffer can be posted iff the buffer state for this client is gained.
- // After posting the buffer, this client is put to released state and does not have access to
- // the buffer for this cycle of the usage of the buffer.
- int post();
-
- // Acquires the buffer for shared read permission.
- // The buffer can be acquired iff the buffer state for this client is posted.
- int acquire();
-
- // Releases the buffer.
- // The buffer can be released from any buffer state.
- // After releasing the buffer, this client no longer have any permissions to the buffer for the
- // current cycle of the usage of the buffer.
- int release();
-
- // Returns whether the buffer is released by all active clients or not.
- bool isReleased() const;
-
- // Creates a token that stands for this BufferHubBuffer client and could be used for Import to
- // create another BufferHubBuffer. The new BufferHubBuffer will share the same underlying
- // gralloc buffer and ashmem region for metadata. Not taking ownership of the token.
- // Returns a valid token on success, nullptr on failure.
- sp<NativeHandle> duplicate();
-
-private:
- BufferHubBuffer(uint32_t width, uint32_t height, uint32_t layerCount, uint32_t format,
- uint64_t usage, size_t userMetadataSize);
-
- BufferHubBuffer(const sp<NativeHandle>& token);
-
- int initWithBufferTraits(const frameworks::bufferhub::V1_0::BufferTraits& bufferTraits);
-
- // Global id for the buffer that is consistent across processes.
- int mId = 0;
-
- // Client state mask of this BufferHubBuffer object. It is unique amoung all
- // clients/users of the buffer.
- uint32_t mClientStateMask = 0U;
-
- // Stores ground truth of the buffer.
- AHardwareBuffer_Desc mBufferDesc;
-
- // Wraps the gralloc buffer handle of this buffer.
- hardware::hidl_handle mBufferHandle;
-
- // Event fd used for signalling buffer state changes. Shared by all clients of the same buffer.
- BufferHubEventFd mEventFd;
-
- // An ashmem-based metadata object. The same shared memory are mapped to the
- // bufferhubd daemon and all buffer clients.
- BufferHubMetadata mMetadata;
- // Shortcuts to the atomics inside the header of mMetadata.
- std::atomic<uint32_t>* mBufferState = nullptr;
- std::atomic<uint32_t>* mFenceState = nullptr;
- std::atomic<uint32_t>* mActiveClientsBitMask = nullptr;
-
- // HwBinder backend
- sp<frameworks::bufferhub::V1_0::IBufferClient> mBufferClient;
-};
-
-} // namespace android
-
-#endif // ANDROID_BUFFER_HUB_BUFFER_H_
diff --git a/libs/ui/include/ui/BufferHubEventFd.h b/libs/ui/include/ui/BufferHubEventFd.h
deleted file mode 100644
index 8772304..0000000
--- a/libs/ui/include/ui/BufferHubEventFd.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2018 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.
- */
-
-#ifndef ANDROID_BUFFER_HUB_EVENT_FD_H_
-#define ANDROID_BUFFER_HUB_EVENT_FD_H_
-
-#include <android-base/unique_fd.h>
-#include <utils/Errors.h>
-
-namespace android {
-
-class BufferHubEventFd {
-public:
- /**
- * Constructs a valid event fd.
- */
- BufferHubEventFd();
-
- /**
- * Constructs from a valid event fd. Caller is responsible for the validity of the fd. Takes
- * ownership.
- */
- BufferHubEventFd(int fd);
-
- /**
- * Returns whether this BufferHubEventFd holds a valid event_fd.
- */
- bool isValid() const { return get() >= 0; }
-
- /**
- * Returns the fd number of the BufferHubEventFd object. Note that there is no ownership
- * transfer.
- */
- int get() const { return mFd.get(); }
-
- /**
- * Signals the eventfd.
- */
- status_t signal() const;
-
- /**
- * Clears the signal from this eventfd if it is signaled.
- */
- status_t clear() const;
-
-private:
- base::unique_fd mFd;
-};
-
-} // namespace android
-
-#endif // ANDROID_BUFFER_HUB_EVENT_FD_H_
diff --git a/libs/ui/include/ui/BufferHubMetadata.h b/libs/ui/include/ui/BufferHubMetadata.h
deleted file mode 100644
index 3482507..0000000
--- a/libs/ui/include/ui/BufferHubMetadata.h
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2018 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.
- */
-
-#ifndef ANDROID_BUFFER_HUB_METADATA_H_
-#define ANDROID_BUFFER_HUB_METADATA_H_
-
-#include <android-base/unique_fd.h>
-#include <ui/BufferHubDefs.h>
-
-namespace android {
-
-namespace {
-using base::unique_fd;
-} // namespace
-
-class BufferHubMetadata {
-public:
- // Creates a new BufferHubMetadata backed by an ashmem region.
- //
- // @param userMetadataSize Size in bytes of the user defined metadata. The entire metadata
- // shared memory region to be allocated is the size of canonical
- // BufferHubDefs::MetadataHeader plus userMetadataSize.
- static BufferHubMetadata create(size_t userMetadataSize);
-
- // Imports an existing BufferHubMetadata from an ashmem FD.
- //
- // @param ashmemFd Ashmem file descriptor representing an ashmem region.
- static BufferHubMetadata import(unique_fd ashmemFd);
-
- BufferHubMetadata() = default;
-
- BufferHubMetadata(BufferHubMetadata&& other) { *this = std::move(other); }
-
- ~BufferHubMetadata();
-
- BufferHubMetadata& operator=(BufferHubMetadata&& other) {
- if (this != &other) {
- mUserMetadataSize = other.mUserMetadataSize;
- other.mUserMetadataSize = 0;
-
- mAshmemFd = std::move(other.mAshmemFd);
-
- // The old raw mMetadataHeader pointer must be cleared, otherwise the destructor will
- // automatically mummap() the shared memory.
- mMetadataHeader = other.mMetadataHeader;
- other.mMetadataHeader = nullptr;
- }
- return *this;
- }
-
- // Returns true if the metadata is valid, i.e. the metadata has a valid ashmem fd and the ashmem
- // has been mapped into virtual address space.
- bool isValid() const { return mAshmemFd.get() != -1 && mMetadataHeader != nullptr; }
-
- size_t userMetadataSize() const { return mUserMetadataSize; }
- size_t metadataSize() const { return mUserMetadataSize + BufferHubDefs::kMetadataHeaderSize; }
-
- const unique_fd& ashmemFd() const { return mAshmemFd; }
- BufferHubDefs::MetadataHeader* metadataHeader() { return mMetadataHeader; }
-
-private:
- BufferHubMetadata(size_t userMetadataSize, unique_fd ashmemFd,
- BufferHubDefs::MetadataHeader* metadataHeader);
-
- BufferHubMetadata(const BufferHubMetadata&) = delete;
- void operator=(const BufferHubMetadata&) = delete;
-
- size_t mUserMetadataSize = 0;
- unique_fd mAshmemFd;
- BufferHubDefs::MetadataHeader* mMetadataHeader = nullptr;
-};
-
-} // namespace android
-
-#endif // ANDROID_BUFFER_HUB_METADATA_H_
diff --git a/libs/ui/include/ui/DebugUtils.h b/libs/ui/include/ui/DebugUtils.h
index 92b2bfb..4685575 100644
--- a/libs/ui/include/ui/DebugUtils.h
+++ b/libs/ui/include/ui/DebugUtils.h
@@ -23,6 +23,7 @@
namespace android {
class Rect;
+struct DeviceProductInfo;
}
std::string decodeStandard(android_dataspace dataspace);
@@ -34,3 +35,4 @@
std::string decodePixelFormat(android::PixelFormat format);
std::string decodeRenderIntent(android::ui::RenderIntent renderIntent);
std::string to_string(const android::Rect& rect);
+std::string toString(const android::DeviceProductInfo&);
diff --git a/libs/ui/include/ui/DeviceProductInfo.h b/libs/ui/include/ui/DeviceProductInfo.h
new file mode 100644
index 0000000..af00342
--- /dev/null
+++ b/libs/ui/include/ui/DeviceProductInfo.h
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <array>
+#include <cstdint>
+#include <optional>
+#include <variant>
+
+namespace android {
+
+// NUL-terminated plug and play ID.
+using PnpId = std::array<char, 4>;
+
+// Product-specific information about the display or the directly connected device on the
+// display chain. For example, if the display is transitively connected, this field may contain
+// product information about the intermediate device.
+struct DeviceProductInfo {
+ static constexpr size_t TEXT_BUFFER_SIZE = 20;
+ static constexpr size_t RELATIVE_ADDRESS_SIZE = 4;
+
+ using RelativeAddress = std::array<uint8_t, RELATIVE_ADDRESS_SIZE>;
+ static constexpr RelativeAddress NO_RELATIVE_ADDRESS = {0xff, 0xff, 0xff, 0xff};
+
+ struct ModelYear {
+ uint32_t year;
+ };
+
+ struct ManufactureYear : ModelYear {};
+
+ struct ManufactureWeekAndYear : ManufactureYear {
+ // 1-base week number. Week numbering may not be consistent between manufacturers.
+ uint8_t week;
+ };
+
+ // Display name.
+ std::array<char, TEXT_BUFFER_SIZE> name;
+
+ // Manufacturer Plug and Play ID.
+ PnpId manufacturerPnpId;
+
+ // Manufacturer product ID.
+ std::array<char, TEXT_BUFFER_SIZE> productId;
+
+ using ManufactureOrModelDate = std::variant<ModelYear, ManufactureYear, ManufactureWeekAndYear>;
+ ManufactureOrModelDate manufactureOrModelDate;
+
+ // Relative address in the display network. Unavailable address is indicated
+ // by all elements equal to 255.
+ // For example, for HDMI connected device this will be the physical address.
+ RelativeAddress relativeAddress;
+};
+
+} // namespace android
diff --git a/libs/ui/include/ui/DisplayConfig.h b/libs/ui/include/ui/DisplayConfig.h
new file mode 100644
index 0000000..d6fbaab
--- /dev/null
+++ b/libs/ui/include/ui/DisplayConfig.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2019 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 <type_traits>
+
+#include <ui/Size.h>
+#include <utils/Timers.h>
+
+namespace android {
+
+// Configuration supported by physical display.
+struct DisplayConfig {
+ ui::Size resolution;
+ float xDpi = 0;
+ float yDpi = 0;
+
+ float refreshRate = 0;
+ nsecs_t appVsyncOffset = 0;
+ nsecs_t sfVsyncOffset = 0;
+ nsecs_t presentationDeadline = 0;
+ int configGroup = -1;
+};
+
+static_assert(std::is_trivially_copyable_v<DisplayConfig>);
+
+} // namespace android
diff --git a/libs/ui/include/ui/DisplayInfo.h b/libs/ui/include/ui/DisplayInfo.h
index 8976d2d..897060c 100644
--- a/libs/ui/include/ui/DisplayInfo.h
+++ b/libs/ui/include/ui/DisplayInfo.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007 The Android Open Source Project
+ * Copyright 2019 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.
@@ -14,39 +14,25 @@
* limitations under the License.
*/
-#ifndef ANDROID_UI_DISPLAY_INFO_H
-#define ANDROID_UI_DISPLAY_INFO_H
+#pragma once
-#include <stdint.h>
-#include <sys/types.h>
+#include <optional>
+#include <type_traits>
-#include <utils/Timers.h>
+#include <ui/DeviceProductInfo.h>
namespace android {
+enum class DisplayConnectionType { Internal, External };
+
+// Immutable information about physical display.
struct DisplayInfo {
- uint32_t w{0};
- uint32_t h{0};
- float xdpi{0};
- float ydpi{0};
- float fps{0};
- float density{0};
- uint8_t orientation{0};
- bool secure{false};
- nsecs_t appVsyncOffset{0};
- nsecs_t presentationDeadline{0};
- uint32_t viewportW{0};
- uint32_t viewportH{0};
+ DisplayConnectionType connectionType = DisplayConnectionType::Internal;
+ float density = 0.f;
+ bool secure = false;
+ std::optional<DeviceProductInfo> deviceProductInfo;
};
-/* Display orientations as defined in Surface.java and ISurfaceComposer.h. */
-enum {
- DISPLAY_ORIENTATION_0 = 0,
- DISPLAY_ORIENTATION_90 = 1,
- DISPLAY_ORIENTATION_180 = 2,
- DISPLAY_ORIENTATION_270 = 3
-};
+static_assert(std::is_trivially_copyable_v<DisplayInfo>);
-}; // namespace android
-
-#endif // ANDROID_COMPOSER_DISPLAY_INFO_H
+} // namespace android
diff --git a/libs/ui/include/ui/DisplayState.h b/libs/ui/include/ui/DisplayState.h
new file mode 100644
index 0000000..64efc84
--- /dev/null
+++ b/libs/ui/include/ui/DisplayState.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2019 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/Rotation.h>
+#include <ui/Size.h>
+
+#include <cstdint>
+#include <type_traits>
+
+namespace android::ui {
+
+using LayerStack = uint32_t;
+constexpr LayerStack NO_LAYER_STACK = static_cast<LayerStack>(-1);
+
+// Transactional state of physical or virtual display. Note that libgui defines
+// android::DisplayState as a superset of android::ui::DisplayState.
+struct DisplayState {
+ LayerStack layerStack = NO_LAYER_STACK;
+ Rotation orientation = ROTATION_0;
+ Size viewport;
+};
+
+static_assert(std::is_trivially_copyable_v<DisplayState>);
+
+} // namespace android::ui
diff --git a/libs/ui/include/ui/FatVector.h b/libs/ui/include/ui/FatVector.h
new file mode 100644
index 0000000..25fe3a0
--- /dev/null
+++ b/libs/ui/include/ui/FatVector.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2019, 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.
+ */
+
+#ifndef ANDROID_REGION_FAT_VECTOR_H
+#define ANDROID_REGION_FAT_VECTOR_H
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <utils/Log.h>
+#include <type_traits>
+
+#include <vector>
+
+namespace android {
+
+template <typename T, size_t SIZE = 4>
+class InlineStdAllocator {
+public:
+ struct Allocation {
+ private:
+ Allocation(const Allocation&) = delete;
+ void operator=(const Allocation&) = delete;
+
+ public:
+ Allocation() {}
+ // char array instead of T array, so memory is uninitialized, with no destructors run
+ char array[sizeof(T) * SIZE];
+ bool inUse = false;
+ };
+
+ typedef T value_type; // needed to implement std::allocator
+ typedef T* pointer; // needed to implement std::allocator
+
+ explicit InlineStdAllocator(Allocation& allocation) : mAllocation(allocation) {}
+ InlineStdAllocator(const InlineStdAllocator& other) : mAllocation(other.mAllocation) {}
+ ~InlineStdAllocator() {}
+
+ T* allocate(size_t num, const void* = 0) {
+ if (!mAllocation.inUse && num <= SIZE) {
+ mAllocation.inUse = true;
+ return static_cast<T*>(static_cast<void*>(mAllocation.array));
+ } else {
+ return static_cast<T*>(static_cast<void*>(malloc(num * sizeof(T))));
+ }
+ }
+
+ void deallocate(pointer p, size_t) {
+ if (p == static_cast<T*>(static_cast<void*>(mAllocation.array))) {
+ mAllocation.inUse = false;
+ } else {
+ // 'free' instead of delete here - destruction handled separately
+ free(p);
+ }
+ }
+ Allocation& mAllocation;
+};
+
+/**
+ * std::vector with SIZE elements preallocated into an internal buffer.
+ *
+ * Useful for avoiding the cost of malloc in cases where only SIZE or
+ * fewer elements are needed in the common case.
+ */
+template <typename T, size_t SIZE = 4>
+class FatVector : public std::vector<T, InlineStdAllocator<T, SIZE>> {
+public:
+ FatVector()
+ : std::vector<T, InlineStdAllocator<T, SIZE>>(InlineStdAllocator<T, SIZE>(mAllocation)) {
+ this->reserve(SIZE);
+ }
+
+ explicit FatVector(size_t capacity) : FatVector() { this->resize(capacity); }
+
+private:
+ typename InlineStdAllocator<T, SIZE>::Allocation mAllocation;
+};
+
+} // namespace android
+
+#endif // ANDROID_REGION_FAT_VECTOR_H
diff --git a/libs/ui/include/ui/FloatRect.h b/libs/ui/include/ui/FloatRect.h
index 4cd9a0b..bec2552 100644
--- a/libs/ui/include/ui/FloatRect.h
+++ b/libs/ui/include/ui/FloatRect.h
@@ -16,6 +16,8 @@
#pragma once
+#include <ostream>
+
namespace android {
class FloatRect {
@@ -52,4 +54,9 @@
return a.left == b.left && a.top == b.top && a.right == b.right && a.bottom == b.bottom;
}
+static inline void PrintTo(const FloatRect& rect, ::std::ostream* os) {
+ *os << "FloatRect(" << rect.left << ", " << rect.top << ", " << rect.right << ", "
+ << rect.bottom << ")";
+}
+
} // namespace android
diff --git a/libs/ui/include/ui/Gralloc.h b/libs/ui/include/ui/Gralloc.h
index 6cc23f0..e199648 100644
--- a/libs/ui/include/ui/Gralloc.h
+++ b/libs/ui/include/ui/Gralloc.h
@@ -17,13 +17,15 @@
#ifndef ANDROID_UI_GRALLOC_H
#define ANDROID_UI_GRALLOC_H
-#include <string>
-
+#include <gralloctypes/Gralloc4.h>
#include <hidl/HidlSupport.h>
+#include <ui/GraphicTypes.h>
#include <ui/PixelFormat.h>
#include <ui/Rect.h>
#include <utils/StrongPointer.h>
+#include <string>
+
namespace android {
// A wrapper to IMapper
@@ -33,6 +35,10 @@
virtual bool isLoaded() const = 0;
+ virtual std::string dumpBuffer(buffer_handle_t /*bufferHandle*/, bool /*less*/) const {
+ return "";
+ }
+
virtual status_t createDescriptor(void* bufferDescriptorInfo,
void* outBufferDescriptor) const = 0;
@@ -74,8 +80,176 @@
// allocated if resources are available. If false, a buffer with the given specifications will
// never successfully allocate on this device. Note that this function is not guaranteed to be
// supported on all devices, in which case a status_t of INVALID_OPERATION will be returned.
- virtual status_t isSupported(uint32_t width, uint32_t height, android::PixelFormat format,
- uint32_t layerCount, uint64_t usage, bool* outSupported) const = 0;
+ virtual status_t isSupported(uint32_t /*width*/, uint32_t /*height*/,
+ android::PixelFormat /*format*/, uint32_t /*layerCount*/,
+ uint64_t /*usage*/, bool* /*outSupported*/) const {
+ return INVALID_OPERATION;
+ }
+
+ virtual status_t getBufferId(buffer_handle_t /*bufferHandle*/,
+ uint64_t* /*outBufferId*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getName(buffer_handle_t /*bufferHandle*/, std::string* /*outName*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getWidth(buffer_handle_t /*bufferHandle*/, uint64_t* /*outWidth*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getHeight(buffer_handle_t /*bufferHandle*/, uint64_t* /*outHeight*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getLayerCount(buffer_handle_t /*bufferHandle*/,
+ uint64_t* /*outLayerCount*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getPixelFormatRequested(buffer_handle_t /*bufferHandle*/,
+ ui::PixelFormat* /*outPixelFormatRequested*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getPixelFormatFourCC(buffer_handle_t /*bufferHandle*/,
+ uint32_t* /*outPixelFormatFourCC*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getPixelFormatModifier(buffer_handle_t /*bufferHandle*/,
+ uint64_t* /*outPixelFormatModifier*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getUsage(buffer_handle_t /*bufferHandle*/, uint64_t* /*outUsage*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getAllocationSize(buffer_handle_t /*bufferHandle*/,
+ uint64_t* /*outAllocationSize*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getProtectedContent(buffer_handle_t /*bufferHandle*/,
+ uint64_t* /*outProtectedContent*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getCompression(
+ buffer_handle_t /*bufferHandle*/,
+ aidl::android::hardware::graphics::common::ExtendableType* /*outCompression*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getCompression(buffer_handle_t /*bufferHandle*/,
+ ui::Compression* /*outCompression*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getInterlaced(
+ buffer_handle_t /*bufferHandle*/,
+ aidl::android::hardware::graphics::common::ExtendableType* /*outInterlaced*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getInterlaced(buffer_handle_t /*bufferHandle*/,
+ ui::Interlaced* /*outInterlaced*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getChromaSiting(
+ buffer_handle_t /*bufferHandle*/,
+ aidl::android::hardware::graphics::common::ExtendableType* /*outChromaSiting*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getChromaSiting(buffer_handle_t /*bufferHandle*/,
+ ui::ChromaSiting* /*outChromaSiting*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getPlaneLayouts(buffer_handle_t /*bufferHandle*/,
+ std::vector<ui::PlaneLayout>* /*outPlaneLayouts*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getDataspace(buffer_handle_t /*bufferHandle*/,
+ ui::Dataspace* /*outDataspace*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getBlendMode(buffer_handle_t /*bufferHandle*/,
+ ui::BlendMode* /*outBlendMode*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getSmpte2086(buffer_handle_t /*bufferHandle*/,
+ std::optional<ui::Smpte2086>* /*outSmpte2086*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getCta861_3(buffer_handle_t /*bufferHandle*/,
+ std::optional<ui::Cta861_3>* /*outCta861_3*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getSmpte2094_40(
+ buffer_handle_t /*bufferHandle*/,
+ std::optional<std::vector<uint8_t>>* /*outSmpte2094_40*/) const {
+ return INVALID_OPERATION;
+ }
+
+ virtual status_t getDefaultPixelFormatFourCC(uint32_t /*width*/, uint32_t /*height*/,
+ PixelFormat /*format*/, uint32_t /*layerCount*/,
+ uint64_t /*usage*/,
+ uint32_t* /*outPixelFormatFourCC*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getDefaultPixelFormatModifier(uint32_t /*width*/, uint32_t /*height*/,
+ PixelFormat /*format*/, uint32_t /*layerCount*/,
+ uint64_t /*usage*/,
+ uint64_t* /*outPixelFormatModifier*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getDefaultAllocationSize(uint32_t /*width*/, uint32_t /*height*/,
+ PixelFormat /*format*/, uint32_t /*layerCount*/,
+ uint64_t /*usage*/,
+ uint64_t* /*outAllocationSize*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getDefaultProtectedContent(uint32_t /*width*/, uint32_t /*height*/,
+ PixelFormat /*format*/, uint32_t /*layerCount*/,
+ uint64_t /*usage*/,
+ uint64_t* /*outProtectedContent*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getDefaultCompression(
+ uint32_t /*width*/, uint32_t /*height*/, PixelFormat /*format*/,
+ uint32_t /*layerCount*/, uint64_t /*usage*/,
+ aidl::android::hardware::graphics::common::ExtendableType* /*outCompression*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getDefaultCompression(uint32_t /*width*/, uint32_t /*height*/,
+ PixelFormat /*format*/, uint32_t /*layerCount*/,
+ uint64_t /*usage*/,
+ ui::Compression* /*outCompression*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getDefaultInterlaced(
+ uint32_t /*width*/, uint32_t /*height*/, PixelFormat /*format*/,
+ uint32_t /*layerCount*/, uint64_t /*usage*/,
+ aidl::android::hardware::graphics::common::ExtendableType* /*outInterlaced*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getDefaultInterlaced(uint32_t /*width*/, uint32_t /*height*/,
+ PixelFormat /*format*/, uint32_t /*layerCount*/,
+ uint64_t /*usage*/,
+ ui::Interlaced* /*outInterlaced*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getDefaultChromaSiting(
+ uint32_t /*width*/, uint32_t /*height*/, PixelFormat /*format*/,
+ uint32_t /*layerCount*/, uint64_t /*usage*/,
+ aidl::android::hardware::graphics::common::ExtendableType* /*outChromaSiting*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getDefaultChromaSiting(uint32_t /*width*/, uint32_t /*height*/,
+ PixelFormat /*format*/, uint32_t /*layerCount*/,
+ uint64_t /*usage*/,
+ ui::ChromaSiting* /*outChromaSiting*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getDefaultPlaneLayouts(
+ uint32_t /*width*/, uint32_t /*height*/, PixelFormat /*format*/,
+ uint32_t /*layerCount*/, uint64_t /*usage*/,
+ std::vector<ui::PlaneLayout>* /*outPlaneLayouts*/) const {
+ return INVALID_OPERATION;
+ }
+
+ virtual std::vector<android::hardware::graphics::mapper::V4_0::IMapper::MetadataTypeDescription>
+ listSupportedMetadataTypes() const {
+ return {};
+ }
};
// A wrapper to IAllocator
@@ -85,16 +259,18 @@
virtual bool isLoaded() const = 0;
- virtual std::string dumpDebugInfo() const = 0;
+ virtual std::string dumpDebugInfo(bool less = true) const = 0;
/*
* The returned buffers are already imported and must not be imported
* again. outBufferHandles must point to a space that can contain at
* least "bufferCount" buffer_handle_t.
*/
- virtual status_t allocate(uint32_t width, uint32_t height, PixelFormat format,
- uint32_t layerCount, uint64_t usage, uint32_t bufferCount,
- uint32_t* outStride, buffer_handle_t* outBufferHandles) const = 0;
+ virtual status_t allocate(std::string requestorName, uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount, uint64_t usage,
+ uint32_t bufferCount, uint32_t* outStride,
+ buffer_handle_t* outBufferHandles,
+ bool importBuffers = true) const = 0;
};
} // namespace android
diff --git a/libs/ui/include/ui/Gralloc2.h b/libs/ui/include/ui/Gralloc2.h
index 948f597..f570c42 100644
--- a/libs/ui/include/ui/Gralloc2.h
+++ b/libs/ui/include/ui/Gralloc2.h
@@ -61,9 +61,6 @@
int unlock(buffer_handle_t bufferHandle) const override;
- status_t isSupported(uint32_t width, uint32_t height, android::PixelFormat format,
- uint32_t layerCount, uint64_t usage, bool* outSupported) const override;
-
private:
// Determines whether the passed info is compatible with the mapper.
status_t validateBufferDescriptorInfo(
@@ -81,11 +78,12 @@
bool isLoaded() const override;
- std::string dumpDebugInfo() const override;
+ std::string dumpDebugInfo(bool less = true) const override;
- status_t allocate(uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
- uint64_t usage, uint32_t bufferCount, uint32_t* outStride,
- buffer_handle_t* outBufferHandles) const override;
+ status_t allocate(std::string requestorName, uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t bufferCount,
+ uint32_t* outStride, buffer_handle_t* outBufferHandles,
+ bool importBuffers = true) const override;
private:
const Gralloc2Mapper& mMapper;
diff --git a/libs/ui/include/ui/Gralloc3.h b/libs/ui/include/ui/Gralloc3.h
index 0965f52..93a5077 100644
--- a/libs/ui/include/ui/Gralloc3.h
+++ b/libs/ui/include/ui/Gralloc3.h
@@ -79,11 +79,12 @@
bool isLoaded() const override;
- std::string dumpDebugInfo() const override;
+ std::string dumpDebugInfo(bool less = true) const override;
- status_t allocate(uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
- uint64_t usage, uint32_t bufferCount, uint32_t* outStride,
- buffer_handle_t* outBufferHandles) const override;
+ status_t allocate(std::string requestorName, uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t bufferCount,
+ uint32_t* outStride, buffer_handle_t* outBufferHandles,
+ bool importBuffers = true) const override;
private:
const Gralloc3Mapper& mMapper;
diff --git a/libs/ui/include/ui/Gralloc4.h b/libs/ui/include/ui/Gralloc4.h
new file mode 100644
index 0000000..4729cba
--- /dev/null
+++ b/libs/ui/include/ui/Gralloc4.h
@@ -0,0 +1,209 @@
+/*
+ * Copyright 2019 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.
+ */
+
+#ifndef ANDROID_UI_GRALLOC4_H
+#define ANDROID_UI_GRALLOC4_H
+
+#include <android/hardware/graphics/allocator/4.0/IAllocator.h>
+#include <android/hardware/graphics/common/1.1/types.h>
+#include <android/hardware/graphics/mapper/4.0/IMapper.h>
+#include <gralloctypes/Gralloc4.h>
+#include <ui/Gralloc.h>
+#include <ui/GraphicTypes.h>
+#include <ui/Rect.h>
+#include <utils/StrongPointer.h>
+
+#include <string>
+
+namespace android {
+
+class Gralloc4Mapper : public GrallocMapper {
+public:
+ static void preload();
+
+ Gralloc4Mapper();
+
+ bool isLoaded() const override;
+
+ std::string dumpBuffer(buffer_handle_t bufferHandle, bool less = true) const override;
+ std::string dumpBuffers(bool less = true) const;
+
+ status_t createDescriptor(void* bufferDescriptorInfo, void* outBufferDescriptor) const override;
+
+ status_t importBuffer(const hardware::hidl_handle& rawHandle,
+ buffer_handle_t* outBufferHandle) const override;
+
+ void freeBuffer(buffer_handle_t bufferHandle) const override;
+
+ status_t validateBufferSize(buffer_handle_t bufferHandle, uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount, uint64_t usage,
+ uint32_t stride) const override;
+
+ void getTransportSize(buffer_handle_t bufferHandle, uint32_t* outNumFds,
+ uint32_t* outNumInts) const override;
+
+ status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
+ int acquireFence, void** outData, int32_t* outBytesPerPixel,
+ int32_t* outBytesPerStride) const override;
+
+ status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
+ int acquireFence, android_ycbcr* ycbcr) const override;
+
+ int unlock(buffer_handle_t bufferHandle) const override;
+
+ status_t isSupported(uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
+ uint64_t usage, bool* outSupported) const override;
+
+ status_t getBufferId(buffer_handle_t bufferHandle, uint64_t* outBufferId) const override;
+ status_t getName(buffer_handle_t bufferHandle, std::string* outName) const override;
+ status_t getWidth(buffer_handle_t bufferHandle, uint64_t* outWidth) const override;
+ status_t getHeight(buffer_handle_t bufferHandle, uint64_t* outHeight) const override;
+ status_t getLayerCount(buffer_handle_t bufferHandle, uint64_t* outLayerCount) const override;
+ status_t getPixelFormatRequested(buffer_handle_t bufferHandle,
+ ui::PixelFormat* outPixelFormatRequested) const override;
+ status_t getPixelFormatFourCC(buffer_handle_t bufferHandle,
+ uint32_t* outPixelFormatFourCC) const override;
+ status_t getPixelFormatModifier(buffer_handle_t bufferHandle,
+ uint64_t* outPixelFormatModifier) const override;
+ status_t getUsage(buffer_handle_t bufferHandle, uint64_t* outUsage) const override;
+ status_t getAllocationSize(buffer_handle_t bufferHandle,
+ uint64_t* outAllocationSize) const override;
+ status_t getProtectedContent(buffer_handle_t bufferHandle,
+ uint64_t* outProtectedContent) const override;
+ status_t getCompression(buffer_handle_t bufferHandle,
+ aidl::android::hardware::graphics::common::ExtendableType*
+ outCompression) const override;
+ status_t getCompression(buffer_handle_t bufferHandle,
+ ui::Compression* outCompression) const override;
+ status_t getInterlaced(buffer_handle_t bufferHandle,
+ aidl::android::hardware::graphics::common::ExtendableType* outInterlaced)
+ const override;
+ status_t getInterlaced(buffer_handle_t bufferHandle,
+ ui::Interlaced* outInterlaced) const override;
+ status_t getChromaSiting(buffer_handle_t bufferHandle,
+ aidl::android::hardware::graphics::common::ExtendableType*
+ outChromaSiting) const override;
+ status_t getChromaSiting(buffer_handle_t bufferHandle,
+ ui::ChromaSiting* outChromaSiting) const override;
+ status_t getPlaneLayouts(buffer_handle_t bufferHandle,
+ std::vector<ui::PlaneLayout>* outPlaneLayouts) const override;
+ status_t getDataspace(buffer_handle_t bufferHandle, ui::Dataspace* outDataspace) const override;
+ status_t getBlendMode(buffer_handle_t bufferHandle, ui::BlendMode* outBlendMode) const override;
+ status_t getSmpte2086(buffer_handle_t bufferHandle,
+ std::optional<ui::Smpte2086>* outSmpte2086) const override;
+ status_t getCta861_3(buffer_handle_t bufferHandle,
+ std::optional<ui::Cta861_3>* outCta861_3) const override;
+ status_t getSmpte2094_40(buffer_handle_t bufferHandle,
+ std::optional<std::vector<uint8_t>>* outSmpte2094_40) const override;
+
+ status_t getDefaultPixelFormatFourCC(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ uint32_t* outPixelFormatFourCC) const override;
+ status_t getDefaultPixelFormatModifier(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ uint64_t* outPixelFormatModifier) const override;
+ status_t getDefaultAllocationSize(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ uint64_t* outAllocationSize) const override;
+ status_t getDefaultProtectedContent(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ uint64_t* outProtectedContent) const override;
+ status_t getDefaultCompression(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ aidl::android::hardware::graphics::common::ExtendableType*
+ outCompression) const override;
+ status_t getDefaultCompression(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ ui::Compression* outCompression) const override;
+ status_t getDefaultInterlaced(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ aidl::android::hardware::graphics::common::ExtendableType*
+ outInterlaced) const override;
+ status_t getDefaultInterlaced(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ ui::Interlaced* outInterlaced) const override;
+ status_t getDefaultChromaSiting(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ aidl::android::hardware::graphics::common::ExtendableType*
+ outChromaSiting) const override;
+ status_t getDefaultChromaSiting(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ ui::ChromaSiting* outChromaSiting) const override;
+ status_t getDefaultPlaneLayouts(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ std::vector<ui::PlaneLayout>* outPlaneLayouts) const override;
+
+ std::vector<android::hardware::graphics::mapper::V4_0::IMapper::MetadataTypeDescription>
+ listSupportedMetadataTypes() const;
+
+private:
+ friend class GraphicBufferAllocator;
+
+ // Determines whether the passed info is compatible with the mapper.
+ status_t validateBufferDescriptorInfo(
+ hardware::graphics::mapper::V4_0::IMapper::BufferDescriptorInfo* descriptorInfo) const;
+
+ template <class T>
+ using DecodeFunction = status_t (*)(const hardware::hidl_vec<uint8_t>& input, T* output);
+
+ template <class T>
+ status_t get(
+ buffer_handle_t bufferHandle,
+ const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+ DecodeFunction<T> decodeFunction, T* outMetadata) const;
+
+ template <class T>
+ status_t getDefault(
+ uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
+ uint64_t usage,
+ const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+ DecodeFunction<T> decodeFunction, T* outMetadata) const;
+
+ template <class T>
+ status_t metadataDumpHelper(
+ const android::hardware::graphics::mapper::V4_0::IMapper::BufferDump& bufferDump,
+ aidl::android::hardware::graphics::common::StandardMetadataType metadataType,
+ DecodeFunction<T> decodeFunction, T* outT) const;
+ status_t bufferDumpHelper(
+ const android::hardware::graphics::mapper::V4_0::IMapper::BufferDump& bufferDump,
+ std::ostringstream* outDump, uint64_t* outAllocationSize, bool less) const;
+
+ sp<hardware::graphics::mapper::V4_0::IMapper> mMapper;
+};
+
+class Gralloc4Allocator : public GrallocAllocator {
+public:
+ // An allocator relies on a mapper, and that mapper must be alive at all
+ // time.
+ Gralloc4Allocator(const Gralloc4Mapper& mapper);
+
+ bool isLoaded() const override;
+
+ std::string dumpDebugInfo(bool less = true) const override;
+
+ status_t allocate(std::string requestorName, uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t bufferCount,
+ uint32_t* outStride, buffer_handle_t* outBufferHandles,
+ bool importBuffers = true) const override;
+
+private:
+ const Gralloc4Mapper& mMapper;
+ sp<hardware::graphics::allocator::V4_0::IAllocator> mAllocator;
+};
+
+} // namespace android
+
+#endif // ANDROID_UI_GRALLOC4_H
diff --git a/libs/ui/include/ui/GraphicBuffer.h b/libs/ui/include/ui/GraphicBuffer.h
index c195342..57be686 100644
--- a/libs/ui/include/ui/GraphicBuffer.h
+++ b/libs/ui/include/ui/GraphicBuffer.h
@@ -38,10 +38,6 @@
namespace android {
-#ifndef LIBUI_IN_VNDK
-class BufferHubBuffer;
-#endif // LIBUI_IN_VNDK
-
class GraphicBufferMapper;
using GraphicBufferDeathCallback = std::function<void(void* /*context*/, uint64_t bufferId)>;
@@ -147,11 +143,6 @@
GraphicBuffer(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat,
uint32_t inUsage, std::string requestorName = "<Unknown>");
-#ifndef LIBUI_IN_VNDK
- // Create a GraphicBuffer from an existing BufferHubBuffer.
- GraphicBuffer(std::unique_ptr<BufferHubBuffer> buffer);
-#endif // LIBUI_IN_VNDK
-
// return status
status_t initCheck() const;
@@ -163,7 +154,6 @@
uint32_t getLayerCount() const { return static_cast<uint32_t>(layerCount); }
Rect getBounds() const { return Rect(width, height); }
uint64_t getId() const { return mId; }
- int32_t getBufferId() const { return mBufferId; }
uint32_t getGenerationNumber() const { return mGenerationNumber; }
void setGenerationNumber(uint32_t generation) {
@@ -225,11 +215,6 @@
void addDeathCallback(GraphicBufferDeathCallback deathCallback, void* context);
-#ifndef LIBUI_IN_VNDK
- // Returns whether this GraphicBuffer is backed by BufferHubBuffer.
- bool isBufferHubBuffer() const;
-#endif // LIBUI_IN_VNDK
-
private:
~GraphicBuffer();
@@ -275,10 +260,7 @@
uint64_t mId;
- // System unique buffer ID. Note that this is different from mId, which is process unique. For
- // GraphicBuffer backed by BufferHub, the mBufferId is a system unique identifier that stays the
- // same cross process for the same chunck of underlying memory. Also note that this only applies
- // to GraphicBuffers that are backed by BufferHub.
+ // Unused, but removing this may break GSI.
int32_t mBufferId = -1;
// Stores the generation number of this buffer. If this number does not
@@ -299,22 +281,6 @@
// and informs SurfaceFlinger that it should drop its strong pointer reference to the buffer.
std::vector<std::pair<GraphicBufferDeathCallback, void* /*mDeathCallbackContext*/>>
mDeathCallbacks;
-
-#ifndef LIBUI_IN_VNDK
- // Flatten this GraphicBuffer object if backed by BufferHubBuffer.
- status_t flattenBufferHubBuffer(void*& buffer, size_t& size) const;
-
- // Unflatten into BufferHubBuffer backed GraphicBuffer.
- // Unflatten will fail if the original GraphicBuffer object is destructed. For instance, a
- // GraphicBuffer backed by BufferHubBuffer_1 flatten in process/thread A, transport the token
- // to process/thread B through a socket, BufferHubBuffer_1 dies and bufferhub invalidated the
- // token. Race condition occurs between the invalidation of the token in bufferhub process and
- // process/thread B trying to unflatten and import the buffer with that token.
- status_t unflattenBufferHubBuffer(void const*& buffer, size_t& size);
-
- // Stores a BufferHubBuffer that handles buffer signaling, identification.
- std::unique_ptr<BufferHubBuffer> mBufferHubBuffer;
-#endif // LIBUI_IN_VNDK
};
}; // namespace android
diff --git a/libs/ui/include/ui/GraphicBufferAllocator.h b/libs/ui/include/ui/GraphicBufferAllocator.h
index 0635727..3ed988c 100644
--- a/libs/ui/include/ui/GraphicBufferAllocator.h
+++ b/libs/ui/include/ui/GraphicBufferAllocator.h
@@ -1,19 +1,19 @@
/*
-**
-** Copyright 2009, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** 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.
-*/
+ *
+ * Copyright 2009, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.
+ */
#ifndef ANDROID_BUFFER_ALLOCATOR_H
#define ANDROID_BUFFER_ALLOCATOR_H
@@ -42,6 +42,29 @@
public:
static inline GraphicBufferAllocator& get() { return getInstance(); }
+ /**
+ * Allocates and imports a gralloc buffer.
+ *
+ * The handle must be freed with GraphicBufferAllocator::free() when no longer needed.
+ */
+ status_t allocate(uint32_t w, uint32_t h, PixelFormat format, uint32_t layerCount,
+ uint64_t usage, buffer_handle_t* handle, uint32_t* stride,
+ std::string requestorName);
+
+ /**
+ * Allocates and does NOT import a gralloc buffer. Buffers cannot be used until they have
+ * been imported. This function is for advanced use cases only.
+ *
+ * The raw native handle must be freed by calling native_handle_close() followed by
+ * native_handle_delete().
+ */
+ status_t allocateRawHandle(uint32_t w, uint32_t h, PixelFormat format, uint32_t layerCount,
+ uint64_t usage, buffer_handle_t* handle, uint32_t* stride,
+ std::string requestorName);
+
+ /**
+ * DEPRECATED: GraphicBufferAllocator does not use the graphicBufferId.
+ */
status_t allocate(uint32_t w, uint32_t h, PixelFormat format,
uint32_t layerCount, uint64_t usage,
buffer_handle_t* handle, uint32_t* stride, uint64_t graphicBufferId,
@@ -51,8 +74,8 @@
uint64_t getTotalSize() const;
- void dump(std::string& res) const;
- static void dumpToSystemLog();
+ void dump(std::string& res, bool less = true) const;
+ static void dumpToSystemLog(bool less = true);
protected:
struct alloc_rec_t {
@@ -66,6 +89,10 @@
std::string requestorName;
};
+ status_t allocateHelper(uint32_t w, uint32_t h, PixelFormat format, uint32_t layerCount,
+ uint64_t usage, buffer_handle_t* handle, uint32_t* stride,
+ std::string requestorName, bool importBuffer);
+
static Mutex sLock;
static KeyedVector<buffer_handle_t, alloc_rec_t> sAllocList;
diff --git a/libs/ui/include/ui/GraphicBufferMapper.h b/libs/ui/include/ui/GraphicBufferMapper.h
index 2461454..837e3d8 100644
--- a/libs/ui/include/ui/GraphicBufferMapper.h
+++ b/libs/ui/include/ui/GraphicBufferMapper.h
@@ -22,10 +22,11 @@
#include <memory>
+#include <ui/GraphicTypes.h>
#include <ui/PixelFormat.h>
+#include <ui/Rect.h>
#include <utils/Singleton.h>
-
// Needed by code that still uses the GRALLOC_USAGE_* constants.
// when/if we get rid of gralloc, we should provide aliases or fix call sites.
#include <hardware/gralloc.h>
@@ -36,7 +37,6 @@
// ---------------------------------------------------------------------------
class GrallocMapper;
-class Rect;
class GraphicBufferMapper : public Singleton<GraphicBufferMapper>
{
@@ -44,10 +44,14 @@
enum Version {
GRALLOC_2,
GRALLOC_3,
+ GRALLOC_4,
};
static void preloadHal();
static inline GraphicBufferMapper& get() { return getInstance(); }
+ void dumpBuffer(buffer_handle_t bufferHandle, std::string& result, bool less = true) const;
+ static void dumpBufferToSystemLog(buffer_handle_t bufferHandle, bool less = true);
+
// The imported outHandle must be freed with freeBuffer when no longer
// needed. rawHandle is owned by the caller.
status_t importBuffer(buffer_handle_t rawHandle,
@@ -85,6 +89,86 @@
status_t isSupported(uint32_t width, uint32_t height, android::PixelFormat format,
uint32_t layerCount, uint64_t usage, bool* outSupported);
+ /**
+ * Gets the gralloc metadata associated with the buffer.
+ *
+ * These functions are supported by gralloc 4.0+.
+ */
+ status_t getBufferId(buffer_handle_t bufferHandle, uint64_t* outBufferId);
+ status_t getName(buffer_handle_t bufferHandle, std::string* outName);
+ status_t getWidth(buffer_handle_t bufferHandle, uint64_t* outWidth);
+ status_t getHeight(buffer_handle_t bufferHandle, uint64_t* outHeight);
+ status_t getLayerCount(buffer_handle_t bufferHandle, uint64_t* outLayerCount);
+ status_t getPixelFormatRequested(buffer_handle_t bufferHandle,
+ ui::PixelFormat* outPixelFormatRequested);
+ status_t getPixelFormatFourCC(buffer_handle_t bufferHandle, uint32_t* outPixelFormatFourCC);
+ status_t getPixelFormatModifier(buffer_handle_t bufferHandle, uint64_t* outPixelFormatModifier);
+ status_t getUsage(buffer_handle_t bufferHandle, uint64_t* outUsage);
+ status_t getAllocationSize(buffer_handle_t bufferHandle, uint64_t* outAllocationSize);
+ status_t getProtectedContent(buffer_handle_t bufferHandle, uint64_t* outProtectedContent);
+ status_t getCompression(
+ buffer_handle_t bufferHandle,
+ aidl::android::hardware::graphics::common::ExtendableType* outCompression);
+ status_t getCompression(buffer_handle_t bufferHandle, ui::Compression* outCompression);
+ status_t getInterlaced(
+ buffer_handle_t bufferHandle,
+ aidl::android::hardware::graphics::common::ExtendableType* outInterlaced);
+ status_t getInterlaced(buffer_handle_t bufferHandle, ui::Interlaced* outInterlaced);
+ status_t getChromaSiting(
+ buffer_handle_t bufferHandle,
+ aidl::android::hardware::graphics::common::ExtendableType* outChromaSiting);
+ status_t getChromaSiting(buffer_handle_t bufferHandle, ui::ChromaSiting* outChromaSiting);
+ status_t getPlaneLayouts(buffer_handle_t bufferHandle,
+ std::vector<ui::PlaneLayout>* outPlaneLayouts);
+ status_t getDataspace(buffer_handle_t bufferHandle, ui::Dataspace* outDataspace);
+ status_t getBlendMode(buffer_handle_t bufferHandle, ui::BlendMode* outBlendMode);
+ status_t getSmpte2086(buffer_handle_t bufferHandle, std::optional<ui::Smpte2086>* outSmpte2086);
+ status_t getCta861_3(buffer_handle_t bufferHandle, std::optional<ui::Cta861_3>* outCta861_3);
+ status_t getSmpte2094_40(buffer_handle_t bufferHandle,
+ std::optional<std::vector<uint8_t>>* outSmpte2094_40);
+
+ /**
+ * Gets the default metadata for a gralloc buffer allocated with the given parameters.
+ *
+ * These functions are supported by gralloc 4.0+.
+ */
+ status_t getDefaultPixelFormatFourCC(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ uint32_t* outPixelFormatFourCC);
+ status_t getDefaultPixelFormatModifier(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ uint64_t* outPixelFormatModifier);
+ status_t getDefaultAllocationSize(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ uint64_t* outAllocationSize);
+ status_t getDefaultProtectedContent(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ uint64_t* outProtectedContent);
+ status_t getDefaultCompression(
+ uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
+ uint64_t usage,
+ aidl::android::hardware::graphics::common::ExtendableType* outCompression);
+ status_t getDefaultCompression(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ ui::Compression* outCompression);
+ status_t getDefaultInterlaced(
+ uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
+ uint64_t usage,
+ aidl::android::hardware::graphics::common::ExtendableType* outInterlaced);
+ status_t getDefaultInterlaced(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ ui::Interlaced* outInterlaced);
+ status_t getDefaultChromaSiting(
+ uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
+ uint64_t usage,
+ aidl::android::hardware::graphics::common::ExtendableType* outChromaSiting);
+ status_t getDefaultChromaSiting(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ ui::ChromaSiting* outChromaSiting);
+ status_t getDefaultPlaneLayouts(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ std::vector<ui::PlaneLayout>* outPlaneLayouts);
+
const GrallocMapper& getGrallocMapper() const {
return reinterpret_cast<const GrallocMapper&>(*mMapper);
}
diff --git a/libs/ui/include/ui/GraphicTypes.h b/libs/ui/include/ui/GraphicTypes.h
index 5dc56c8..4bdacb0 100644
--- a/libs/ui/include/ui/GraphicTypes.h
+++ b/libs/ui/include/ui/GraphicTypes.h
@@ -16,28 +16,51 @@
#pragma once
-#include <cinttypes>
-#include <cstdint>
-
+#include <aidl/android/hardware/graphics/common/BlendMode.h>
+#include <aidl/android/hardware/graphics/common/ChromaSiting.h>
+#include <aidl/android/hardware/graphics/common/Compression.h>
+#include <aidl/android/hardware/graphics/common/Cta861_3.h>
+#include <aidl/android/hardware/graphics/common/Interlaced.h>
+#include <aidl/android/hardware/graphics/common/PlaneLayout.h>
+#include <aidl/android/hardware/graphics/common/Smpte2086.h>
#include <android/hardware/graphics/common/1.1/types.h>
#include <android/hardware/graphics/common/1.2/types.h>
#include <system/graphics.h>
-#define ANDROID_PHYSICAL_DISPLAY_ID_FORMAT PRIu64
-
namespace android {
-using PhysicalDisplayId = uint64_t;
-
-// android::ui::* in this header file will alias different types as
-// the HIDL interface is updated.
+/**
+ * android::ui::* in this header file will alias different types as
+ * the HIDL and stable AIDL interfaces are updated.
+ */
namespace ui {
+/**
+ * The following HIDL types should be moved to their stable AIDL
+ * equivalents when composer moves to stable AIDL.
+ */
using android::hardware::graphics::common::V1_1::RenderIntent;
using android::hardware::graphics::common::V1_2::ColorMode;
using android::hardware::graphics::common::V1_2::Dataspace;
using android::hardware::graphics::common::V1_2::Hdr;
using android::hardware::graphics::common::V1_2::PixelFormat;
+/**
+ * Stable AIDL types
+ */
+using aidl::android::hardware::graphics::common::BlendMode;
+using aidl::android::hardware::graphics::common::Cta861_3;
+using aidl::android::hardware::graphics::common::PlaneLayout;
+using aidl::android::hardware::graphics::common::Smpte2086;
+
+/**
+ * The following stable AIDL types below have standard platform definitions
+ * that can be extended by vendors. The extensions are not defined here
+ * because they cannot be understood by the framework.
+ */
+using ChromaSiting = aidl::android::hardware::graphics::common::ChromaSiting;
+using Compression = aidl::android::hardware::graphics::common::Compression;
+using Interlaced = aidl::android::hardware::graphics::common::Interlaced;
+
} // namespace ui
} // namespace android
diff --git a/libs/gui/tests/DummyConsumer.h b/libs/ui/include/ui/PhysicalDisplayId.h
similarity index 65%
copy from libs/gui/tests/DummyConsumer.h
copy to libs/ui/include/ui/PhysicalDisplayId.h
index 502bdf9..1a345ac 100644
--- a/libs/gui/tests/DummyConsumer.h
+++ b/libs/ui/include/ui/PhysicalDisplayId.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 The Android Open Source Project
+ * Copyright 2019 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.
@@ -14,14 +14,19 @@
* limitations under the License.
*/
-#include <gui/IConsumerListener.h>
+#pragma once
+
+#include <cinttypes>
+#include <cstdint>
+
+#define ANDROID_PHYSICAL_DISPLAY_ID_FORMAT PRIu64
namespace android {
-struct DummyConsumer : public BnConsumerListener {
- void onFrameAvailable(const BufferItem& /* item */) override {}
- void onBuffersReleased() override {}
- void onSidebandStreamChanged() override {}
-};
+using PhysicalDisplayId = uint64_t;
+
+constexpr uint8_t getPhysicalDisplayPort(PhysicalDisplayId displayId) {
+ return static_cast<uint8_t>(displayId);
+}
} // namespace android
diff --git a/libs/ui/include/ui/Rect.h b/libs/ui/include/ui/Rect.h
index 1768805..2f2229e 100644
--- a/libs/ui/include/ui/Rect.h
+++ b/libs/ui/include/ui/Rect.h
@@ -17,6 +17,8 @@
#ifndef ANDROID_UI_RECT
#define ANDROID_UI_RECT
+#include <ostream>
+
#include <utils/Flattenable.h>
#include <utils/Log.h>
#include <utils/TypeHelpers.h>
@@ -214,6 +216,12 @@
}
};
+// Defining PrintTo helps with Google Tests.
+static inline void PrintTo(const Rect& rect, ::std::ostream* os) {
+ *os << "Rect(" << rect.left << ", " << rect.top << ", " << rect.right << ", " << rect.bottom
+ << ")";
+}
+
ANDROID_BASIC_TYPES_TRAITS(Rect)
}; // namespace android
diff --git a/libs/ui/include/ui/Region.h b/libs/ui/include/ui/Region.h
index 79642ae..6bb7b8d 100644
--- a/libs/ui/include/ui/Region.h
+++ b/libs/ui/include/ui/Region.h
@@ -19,14 +19,15 @@
#include <stdint.h>
#include <sys/types.h>
-
-#include <utils/Vector.h>
+#include <ostream>
#include <ui/Rect.h>
#include <utils/Flattenable.h>
#include <android-base/macros.h>
+#include "FatVector.h"
+
#include <string>
namespace android {
@@ -119,6 +120,8 @@
// returns true if the regions share the same underlying storage
bool isTriviallyEqual(const Region& region) const;
+ // returns true if the regions consist of the same rectangle sequence
+ bool hasSameRects(const Region& region) const;
/* various ways to access the rectangle list */
@@ -177,7 +180,7 @@
// with an extra Rect as the last element which is set to the
// bounds of the region. However, if the region is
// a simple Rect then mStorage contains only that rect.
- Vector<Rect> mStorage;
+ FatVector<Rect> mStorage;
};
@@ -213,8 +216,22 @@
Region& Region::operator += (const Point& pt) {
return translateSelf(pt.x, pt.y);
}
+
+// Defining PrintTo helps with Google Tests.
+static inline void PrintTo(const Region& region, ::std::ostream* os) {
+ Region::const_iterator head = region.begin();
+ Region::const_iterator const tail = region.end();
+ bool first = true;
+ while (head != tail) {
+ *os << (first ? "Region(" : ", ");
+ PrintTo(*head, os);
+ head++;
+ first = false;
+ }
+ *os << ")";
+}
+
// ---------------------------------------------------------------------------
}; // namespace android
#endif // ANDROID_UI_REGION_H
-
diff --git a/libs/ui/include/ui/Rotation.h b/libs/ui/include/ui/Rotation.h
new file mode 100644
index 0000000..89008f6
--- /dev/null
+++ b/libs/ui/include/ui/Rotation.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2019 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 <type_traits>
+
+namespace android::ui {
+
+enum class Rotation { Rotation0 = 0, Rotation90 = 1, Rotation180 = 2, Rotation270 = 3 };
+
+// Equivalent to Surface.java constants.
+constexpr auto ROTATION_0 = Rotation::Rotation0;
+constexpr auto ROTATION_90 = Rotation::Rotation90;
+constexpr auto ROTATION_180 = Rotation::Rotation180;
+constexpr auto ROTATION_270 = Rotation::Rotation270;
+
+constexpr auto toRotation(std::underlying_type_t<Rotation> rotation) {
+ return static_cast<Rotation>(rotation);
+}
+
+constexpr auto toRotationInt(Rotation rotation) {
+ return static_cast<std::underlying_type_t<Rotation>>(rotation);
+}
+
+constexpr Rotation operator+(Rotation lhs, Rotation rhs) {
+ constexpr auto N = toRotationInt(ROTATION_270) + 1;
+ return toRotation((toRotationInt(lhs) + toRotationInt(rhs)) % N);
+}
+
+constexpr const char* toCString(Rotation rotation) {
+ switch (rotation) {
+ case ROTATION_0:
+ return "ROTATION_0";
+ case ROTATION_90:
+ return "ROTATION_90";
+ case ROTATION_180:
+ return "ROTATION_180";
+ case ROTATION_270:
+ return "ROTATION_270";
+ }
+}
+
+} // namespace android::ui
diff --git a/libs/ui/include/ui/Size.h b/libs/ui/include/ui/Size.h
index d9b713d..f1e8252 100644
--- a/libs/ui/include/ui/Size.h
+++ b/libs/ui/include/ui/Size.h
@@ -19,6 +19,7 @@
#include <algorithm>
#include <cstdint>
#include <limits>
+#include <ostream>
#include <type_traits>
#include <utility>
@@ -108,31 +109,70 @@
// Takes a value of type FromType, and ensures it can be represented as a value of type ToType,
// clamping the input value to the output range if necessary.
template <typename ToType, typename FromType>
- static Size::remove_cv_reference_t<ToType> clamp(
- typename std::enable_if<
- std::numeric_limits<Size::remove_cv_reference_t<ToType>>::is_bounded &&
- std::numeric_limits<Size::remove_cv_reference_t<FromType>>::is_bounded,
- FromType&&>::type v) {
- static constexpr auto toHighest = std::numeric_limits<remove_cv_reference_t<ToType>>::max();
- static constexpr auto toLowest =
- std::numeric_limits<remove_cv_reference_t<ToType>>::lowest();
- static constexpr auto fromHighest =
- std::numeric_limits<remove_cv_reference_t<FromType>>::max();
- static constexpr auto fromLowest =
- std::numeric_limits<remove_cv_reference_t<FromType>>::lowest();
+ static Size::remove_cv_reference_t<ToType>
+ clamp(typename std::enable_if<
+ std::numeric_limits<Size::remove_cv_reference_t<ToType>>::is_specialized &&
+ std::numeric_limits<Size::remove_cv_reference_t<FromType>>::is_specialized,
+ FromType>::type v) {
+ using BareToType = remove_cv_reference_t<ToType>;
+ using BareFromType = remove_cv_reference_t<FromType>;
+ static constexpr auto toHighest = std::numeric_limits<BareToType>::max();
+ static constexpr auto toLowest = std::numeric_limits<BareToType>::lowest();
+ static constexpr auto fromHighest = std::numeric_limits<BareFromType>::max();
+ static constexpr auto fromLowest = std::numeric_limits<BareFromType>::lowest();
- // A clamp is needed if the range of FromType is not a subset of the range of ToType
- static constexpr bool isClampNeeded = (toLowest > fromLowest) || (toHighest < fromHighest);
+ // Get the closest representation of [toLowest, toHighest] in type
+ // FromType to use to clamp the input value before conversion.
+
+ // std::common_type<...> is used to get a value-preserving type for the
+ // top end of the range.
+ using CommonHighestType = std::common_type_t<BareToType, BareFromType>;
+
+ // std::make_signed<std::common_type<...>> is used to get a
+ // value-preserving type for the bottom end of the range, except this is
+ // a bit trickier for non-integer types like float.
+ using CommonLowestType =
+ std::conditional_t<std::numeric_limits<CommonHighestType>::is_integer,
+ std::make_signed_t<std::conditional_t<
+ std::numeric_limits<CommonHighestType>::is_integer,
+ CommonHighestType, int /* not used */>>,
+ CommonHighestType>;
+
+ // We can then compute the clamp range in a way that can be later
+ // trivially converted to either the 'from' or 'to' types, and be
+ // representabile in either.
+ static constexpr auto commonClampHighest =
+ std::min(static_cast<CommonHighestType>(fromHighest),
+ static_cast<CommonHighestType>(toHighest));
+ static constexpr auto commonClampLowest =
+ std::max(static_cast<CommonLowestType>(fromLowest),
+ static_cast<CommonLowestType>(toLowest));
+
+ static constexpr auto fromClampHighest = static_cast<BareFromType>(commonClampHighest);
+ static constexpr auto fromClampLowest = static_cast<BareFromType>(commonClampLowest);
+
+ // A clamp is needed only if the range we are clamping to is not the
+ // same as the range of the input.
+ static constexpr bool isClampNeeded =
+ (fromLowest != fromClampLowest) || (fromHighest != fromClampHighest);
// If a clamp is not needed, the conversion is just a trivial cast.
if (!isClampNeeded) {
- return static_cast<ToType>(v);
+ return static_cast<BareToType>(v);
}
- // Otherwise we leverage implicit conversion to safely compare values of
- // different types, to ensure we return a value clamped to the range of
- // ToType.
- return v < toLowest ? toLowest : (static_cast<ToType>(v) > toHighest ? toHighest : static_cast<ToType>(v));
+ // Note: Clang complains about the value of INT32_MAX not being
+ // convertible back to int32_t from float if this is made "constexpr",
+ // when clamping a float value to an int32_t value. This is however
+ // covered by a test case to ensure the run-time cast works correctly.
+ const auto toClampHighest = static_cast<BareToType>(commonClampHighest);
+ const auto toClampLowest = static_cast<BareToType>(commonClampLowest);
+
+ // Otherwise clamping is done by using the already computed endpoints
+ // for each type.
+ return (v <= fromClampLowest)
+ ? toClampLowest
+ : ((v >= fromClampHighest) ? toClampHighest : static_cast<BareToType>(v));
}
};
@@ -154,5 +194,10 @@
return lhs.height < rhs.height;
}
+// Defining PrintTo helps with Google Tests.
+static inline void PrintTo(const Size& size, ::std::ostream* os) {
+ *os << "Size(" << size.width << ", " << size.height << ")";
+}
+
} // namespace ui
} // namespace android
diff --git a/libs/ui/include/ui/Transform.h b/libs/ui/include/ui/Transform.h
index f29a370..c6bb598 100644
--- a/libs/ui/include/ui/Transform.h
+++ b/libs/ui/include/ui/Transform.h
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-#ifndef ANDROID_TRANSFORM_H
-#define ANDROID_TRANSFORM_H
+#pragma once
#include <stdint.h>
#include <sys/types.h>
+#include <ostream>
#include <string>
#include <hardware/hardware.h>
@@ -27,6 +27,7 @@
#include <math/vec3.h>
#include <ui/Point.h>
#include <ui/Rect.h>
+#include <ui/Rotation.h>
namespace android {
@@ -38,16 +39,16 @@
public:
Transform();
Transform(const Transform& other);
- explicit Transform(uint32_t orientation);
+ explicit Transform(uint32_t orientation, int w = 0, int h = 0);
~Transform();
- enum orientation_flags {
- ROT_0 = 0x00000000,
- FLIP_H = HAL_TRANSFORM_FLIP_H,
- FLIP_V = HAL_TRANSFORM_FLIP_V,
- ROT_90 = HAL_TRANSFORM_ROT_90,
- ROT_180 = FLIP_H|FLIP_V,
- ROT_270 = ROT_180|ROT_90,
+ enum RotationFlags : uint32_t {
+ ROT_0 = 0,
+ FLIP_H = HAL_TRANSFORM_FLIP_H,
+ FLIP_V = HAL_TRANSFORM_FLIP_V,
+ ROT_90 = HAL_TRANSFORM_ROT_90,
+ ROT_180 = FLIP_H | FLIP_V,
+ ROT_270 = ROT_180 | ROT_90,
ROT_INVALID = 0x80
};
@@ -63,6 +64,7 @@
bool preserveRects() const;
uint32_t getType() const;
uint32_t getOrientation() const;
+ bool operator==(const Transform& other) const;
const vec3& operator [] (size_t i) const; // returns column i
float tx() const;
@@ -98,6 +100,8 @@
void dump(std::string& result, const char* name) const;
void dump(const char* name) const;
+ static RotationFlags toRotationFlags(Rotation);
+
private:
struct mat33 {
vec3 v[3];
@@ -115,7 +119,26 @@
mutable uint32_t mType;
};
+inline void PrintTo(const Transform& t, ::std::ostream* os) {
+ std::string out;
+ t.dump(out, "ui::Transform");
+ *os << out;
+}
+
+inline Transform::RotationFlags Transform::toRotationFlags(Rotation rotation) {
+ switch (rotation) {
+ case ROTATION_0:
+ return ROT_0;
+ case ROTATION_90:
+ return ROT_90;
+ case ROTATION_180:
+ return ROT_180;
+ case ROTATION_270:
+ return ROT_270;
+ default:
+ return ROT_INVALID;
+ }
+}
+
} // namespace ui
} // namespace android
-
-#endif /* ANDROID_TRANSFORM_H */
diff --git a/libs/ui/include_private/ui/RegionHelper.h b/libs/ui/include_private/ui/RegionHelper.h
new file mode 100644
index 0000000..92cfba8
--- /dev/null
+++ b/libs/ui/include_private/ui/RegionHelper.h
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.
+ */
+
+#ifndef ANDROID_UI_PRIVATE_REGION_HELPER_H
+#define ANDROID_UI_PRIVATE_REGION_HELPER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <limits>
+
+#include <limits>
+
+namespace android {
+// ----------------------------------------------------------------------------
+
+template <typename RECT>
+class region_operator {
+public:
+ typedef typename RECT::value_type TYPE;
+ static const TYPE max_value = std::numeric_limits<TYPE>::max();
+
+ /*
+ * Common boolean operations:
+ * value is computed as 0b101 op 0b110
+ * other boolean operation are possible, simply compute
+ * their corresponding value with the above formulae and use
+ * it when instantiating a region_operator.
+ */
+ static const uint32_t LHS = 0x5; // 0b101
+ static const uint32_t RHS = 0x6; // 0b110
+ enum { op_nand = LHS & ~RHS, op_and = LHS & RHS, op_or = LHS | RHS, op_xor = LHS ^ RHS };
+
+ struct region {
+ RECT const* rects;
+ size_t count;
+ TYPE dx;
+ TYPE dy;
+ inline region(const region& rhs)
+ : rects(rhs.rects), count(rhs.count), dx(rhs.dx), dy(rhs.dy) {}
+ inline region(RECT const* _r, size_t _c) : rects(_r), count(_c), dx(), dy() {}
+ inline region(RECT const* _r, size_t _c, TYPE _dx, TYPE _dy)
+ : rects(_r), count(_c), dx(_dx), dy(_dy) {}
+ };
+
+ class region_rasterizer {
+ friend class region_operator;
+ virtual void operator()(const RECT& rect) = 0;
+
+ public:
+ virtual ~region_rasterizer() {}
+ };
+
+ inline region_operator(uint32_t op, const region& lhs, const region& rhs)
+ : op_mask(op), spanner(lhs, rhs) {}
+
+ void operator()(region_rasterizer& rasterizer) {
+ RECT current(Rect::EMPTY_RECT);
+ do {
+ SpannerInner spannerInner(spanner.lhs, spanner.rhs);
+ int inside = spanner.next(current.top, current.bottom);
+ spannerInner.prepare(inside);
+ do {
+ int inner_inside = spannerInner.next(current.left, current.right);
+ if ((op_mask >> inner_inside) & 1) {
+ if (current.left < current.right && current.top < current.bottom) {
+ rasterizer(current);
+ }
+ }
+ } while (!spannerInner.isDone());
+ } while (!spanner.isDone());
+ }
+
+private:
+ uint32_t op_mask;
+
+ class SpannerBase {
+ public:
+ SpannerBase()
+ : lhs_head(max_value),
+ lhs_tail(max_value),
+ rhs_head(max_value),
+ rhs_tail(max_value) {}
+
+ enum { lhs_before_rhs = 0, lhs_after_rhs = 1, lhs_coincide_rhs = 2 };
+
+ protected:
+ TYPE lhs_head;
+ TYPE lhs_tail;
+ TYPE rhs_head;
+ TYPE rhs_tail;
+
+ inline int next(TYPE& head, TYPE& tail, bool& more_lhs, bool& more_rhs) {
+ int inside;
+ more_lhs = false;
+ more_rhs = false;
+ if (lhs_head < rhs_head) {
+ inside = lhs_before_rhs;
+ head = lhs_head;
+ if (lhs_tail <= rhs_head) {
+ tail = lhs_tail;
+ more_lhs = true;
+ } else {
+ lhs_head = rhs_head;
+ tail = rhs_head;
+ }
+ } else if (rhs_head < lhs_head) {
+ inside = lhs_after_rhs;
+ head = rhs_head;
+ if (rhs_tail <= lhs_head) {
+ tail = rhs_tail;
+ more_rhs = true;
+ } else {
+ rhs_head = lhs_head;
+ tail = lhs_head;
+ }
+ } else {
+ inside = lhs_coincide_rhs;
+ head = lhs_head;
+ if (lhs_tail <= rhs_tail) {
+ tail = rhs_head = lhs_tail;
+ more_lhs = true;
+ }
+ if (rhs_tail <= lhs_tail) {
+ tail = lhs_head = rhs_tail;
+ more_rhs = true;
+ }
+ }
+ return inside;
+ }
+ };
+
+ class Spanner : protected SpannerBase {
+ friend class region_operator;
+ region lhs;
+ region rhs;
+
+ public:
+ inline Spanner(const region& _lhs, const region& _rhs) : lhs(_lhs), rhs(_rhs) {
+ if (lhs.count) {
+ SpannerBase::lhs_head = lhs.rects->top + lhs.dy;
+ SpannerBase::lhs_tail = lhs.rects->bottom + lhs.dy;
+ }
+ if (rhs.count) {
+ SpannerBase::rhs_head = rhs.rects->top + rhs.dy;
+ SpannerBase::rhs_tail = rhs.rects->bottom + rhs.dy;
+ }
+ }
+
+ inline bool isDone() const { return !rhs.count && !lhs.count; }
+
+ inline int next(TYPE& top, TYPE& bottom) {
+ bool more_lhs = false;
+ bool more_rhs = false;
+ int inside = SpannerBase::next(top, bottom, more_lhs, more_rhs);
+ if (more_lhs) {
+ advance(lhs, SpannerBase::lhs_head, SpannerBase::lhs_tail);
+ }
+ if (more_rhs) {
+ advance(rhs, SpannerBase::rhs_head, SpannerBase::rhs_tail);
+ }
+ return inside;
+ }
+
+ private:
+ static inline void advance(region& reg, TYPE& aTop, TYPE& aBottom) {
+ // got to next span
+ size_t count = reg.count;
+ RECT const* rects = reg.rects;
+ RECT const* const end = rects + count;
+ const int top = rects->top;
+ while (rects != end && rects->top == top) {
+ rects++;
+ count--;
+ }
+ if (rects != end) {
+ aTop = rects->top + reg.dy;
+ aBottom = rects->bottom + reg.dy;
+ } else {
+ aTop = max_value;
+ aBottom = max_value;
+ }
+ reg.rects = rects;
+ reg.count = count;
+ }
+ };
+
+ class SpannerInner : protected SpannerBase {
+ region lhs;
+ region rhs;
+
+ public:
+ inline SpannerInner(const region& _lhs, const region& _rhs) : lhs(_lhs), rhs(_rhs) {}
+
+ inline void prepare(int inside) {
+ if (inside == SpannerBase::lhs_before_rhs) {
+ if (lhs.count) {
+ SpannerBase::lhs_head = lhs.rects->left + lhs.dx;
+ SpannerBase::lhs_tail = lhs.rects->right + lhs.dx;
+ }
+ SpannerBase::rhs_head = max_value;
+ SpannerBase::rhs_tail = max_value;
+ } else if (inside == SpannerBase::lhs_after_rhs) {
+ SpannerBase::lhs_head = max_value;
+ SpannerBase::lhs_tail = max_value;
+ if (rhs.count) {
+ SpannerBase::rhs_head = rhs.rects->left + rhs.dx;
+ SpannerBase::rhs_tail = rhs.rects->right + rhs.dx;
+ }
+ } else {
+ if (lhs.count) {
+ SpannerBase::lhs_head = lhs.rects->left + lhs.dx;
+ SpannerBase::lhs_tail = lhs.rects->right + lhs.dx;
+ }
+ if (rhs.count) {
+ SpannerBase::rhs_head = rhs.rects->left + rhs.dx;
+ SpannerBase::rhs_tail = rhs.rects->right + rhs.dx;
+ }
+ }
+ }
+
+ inline bool isDone() const {
+ return SpannerBase::lhs_head == max_value && SpannerBase::rhs_head == max_value;
+ }
+
+ inline int next(TYPE& left, TYPE& right) {
+ bool more_lhs = false;
+ bool more_rhs = false;
+ int inside = SpannerBase::next(left, right, more_lhs, more_rhs);
+ if (more_lhs) {
+ advance(lhs, SpannerBase::lhs_head, SpannerBase::lhs_tail);
+ }
+ if (more_rhs) {
+ advance(rhs, SpannerBase::rhs_head, SpannerBase::rhs_tail);
+ }
+ return inside;
+ }
+
+ private:
+ static inline void advance(region& reg, TYPE& left, TYPE& right) {
+ if (reg.rects && reg.count) {
+ const int cur_span_top = reg.rects->top;
+ reg.rects++;
+ reg.count--;
+ if (!reg.count || reg.rects->top != cur_span_top) {
+ left = max_value;
+ right = max_value;
+ } else {
+ left = reg.rects->left + reg.dx;
+ right = reg.rects->right + reg.dx;
+ }
+ }
+ }
+ };
+
+ Spanner spanner;
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+
+#endif /* ANDROID_UI_PRIVATE_REGION_HELPER_H */
diff --git a/libs/ui/include_vndk/ui/DeviceProductInfo.h b/libs/ui/include_vndk/ui/DeviceProductInfo.h
new file mode 120000
index 0000000..c8f1d43
--- /dev/null
+++ b/libs/ui/include_vndk/ui/DeviceProductInfo.h
@@ -0,0 +1 @@
+../../include/ui/DeviceProductInfo.h
\ No newline at end of file
diff --git a/libs/ui/include_vndk/ui/DisplayConfig.h b/libs/ui/include_vndk/ui/DisplayConfig.h
new file mode 120000
index 0000000..1450319
--- /dev/null
+++ b/libs/ui/include_vndk/ui/DisplayConfig.h
@@ -0,0 +1 @@
+../../include/ui/DisplayConfig.h
\ No newline at end of file
diff --git a/libs/ui/include_vndk/ui/DisplayState.h b/libs/ui/include_vndk/ui/DisplayState.h
new file mode 120000
index 0000000..4e92849
--- /dev/null
+++ b/libs/ui/include_vndk/ui/DisplayState.h
@@ -0,0 +1 @@
+../../include/ui/DisplayState.h
\ No newline at end of file
diff --git a/libs/ui/include_vndk/ui/FatVector.h b/libs/ui/include_vndk/ui/FatVector.h
new file mode 120000
index 0000000..bf30166
--- /dev/null
+++ b/libs/ui/include_vndk/ui/FatVector.h
@@ -0,0 +1 @@
+../../include/ui/FatVector.h
\ No newline at end of file
diff --git a/libs/ui/include_vndk/ui/Gralloc2.h b/libs/ui/include_vndk/ui/Gralloc2.h
deleted file mode 120000
index 66098c4..0000000
--- a/libs/ui/include_vndk/ui/Gralloc2.h
+++ /dev/null
@@ -1 +0,0 @@
-../../include/ui/Gralloc2.h
\ No newline at end of file
diff --git a/libs/ui/include_vndk/ui/PhysicalDisplayId.h b/libs/ui/include_vndk/ui/PhysicalDisplayId.h
new file mode 120000
index 0000000..6e3fb1e
--- /dev/null
+++ b/libs/ui/include_vndk/ui/PhysicalDisplayId.h
@@ -0,0 +1 @@
+../../include/ui/PhysicalDisplayId.h
\ No newline at end of file
diff --git a/libs/ui/include_vndk/ui/Rotation.h b/libs/ui/include_vndk/ui/Rotation.h
new file mode 120000
index 0000000..d84fb4b
--- /dev/null
+++ b/libs/ui/include_vndk/ui/Rotation.h
@@ -0,0 +1 @@
+../../include/ui/Rotation.h
\ No newline at end of file
diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp
index 0a07a48..b53342c 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -31,15 +31,15 @@
cc_test {
name: "GraphicBufferAllocator_test",
header_libs: [
- "libdvr_headers",
"libnativewindow_headers",
],
static_libs: [
"libgmock",
],
shared_libs: [
+ "libhidlbase",
"liblog",
- "libui",
+ "libui",
],
srcs: [
"GraphicBufferAllocator_test.cpp",
@@ -51,11 +51,9 @@
cc_test {
name: "GraphicBuffer_test",
header_libs: [
- "libdvr_headers",
"libnativewindow_headers",
],
shared_libs: [
- "android.frameworks.bufferhub@1.0",
"libcutils",
"libhidlbase",
"libui",
@@ -65,33 +63,23 @@
cflags: ["-Wall", "-Werror"],
}
+// This test has a main method, and requires a separate binary to be built.
cc_test {
- name: "BufferHub_test",
- header_libs: [
- "libdvr_headers",
- "libnativewindow_headers",
- ],
- static_libs: [
- "libgmock",
- ],
+ name: "GraphicBufferOverBinder_test",
+ srcs: ["GraphicBufferOverBinder_test.cpp"],
+ cflags: ["-Wall", "-Werror"],
shared_libs: [
- "android.frameworks.bufferhub@1.0",
- "libcutils",
- "libhidlbase",
+ "libbinder",
+ "libgui",
"liblog",
"libui",
- "libutils"
+ "libutils",
],
- srcs: [
- "BufferHubBuffer_test.cpp",
- "BufferHubEventFd_test.cpp",
- "BufferHubMetadata_test.cpp",
- ],
- cflags: ["-Wall", "-Werror"],
}
cc_test {
name: "Size_test",
+ test_suites: ["device-tests"],
shared_libs: ["libui"],
srcs: ["Size_test.cpp"],
cflags: ["-Wall", "-Werror"],
diff --git a/libs/ui/tests/BufferHubBuffer_test.cpp b/libs/ui/tests/BufferHubBuffer_test.cpp
deleted file mode 100644
index 0c73a72..0000000
--- a/libs/ui/tests/BufferHubBuffer_test.cpp
+++ /dev/null
@@ -1,477 +0,0 @@
-/*
- * Copyright 2018 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 "BufferHubBufferTest"
-
-#include <errno.h>
-#include <sys/epoll.h>
-
-#include <android/frameworks/bufferhub/1.0/IBufferHub.h>
-#include <android/hardware_buffer.h>
-#include <cutils/native_handle.h>
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <hidl/ServiceManagement.h>
-#include <hwbinder/IPCThreadState.h>
-#include <ui/BufferHubBuffer.h>
-#include <ui/BufferHubEventFd.h>
-
-namespace android {
-
-namespace {
-
-using ::android::BufferHubDefs::isAnyClientAcquired;
-using ::android::BufferHubDefs::isAnyClientGained;
-using ::android::BufferHubDefs::isAnyClientPosted;
-using ::android::BufferHubDefs::isClientAcquired;
-using ::android::BufferHubDefs::isClientGained;
-using ::android::BufferHubDefs::isClientPosted;
-using ::android::BufferHubDefs::isClientReleased;
-using ::android::BufferHubDefs::kMetadataHeaderSize;
-using ::android::frameworks::bufferhub::V1_0::IBufferHub;
-using ::testing::IsNull;
-using ::testing::NotNull;
-
-const int kWidth = 640;
-const int kHeight = 480;
-const int kLayerCount = 1;
-const int kFormat = HAL_PIXEL_FORMAT_RGBA_8888;
-const int kUsage = 0;
-const AHardwareBuffer_Desc kDesc = {kWidth, kHeight, kLayerCount, kFormat,
- kUsage, /*stride=*/0UL, /*rfu0=*/0UL, /*rfu1=*/0ULL};
-const size_t kUserMetadataSize = 1;
-
-class BufferHubBufferTest : public ::testing::Test {
-protected:
- void SetUp() override {
- android::hardware::ProcessState::self()->startThreadPool();
-
- if (!BufferHubServiceRunning()) {
- // TODO(b/112940221): Enforce the test cross all devices once BufferHub lands in Android
- // R for all Android varieties.
- GTEST_SKIP() << "Skip test as the BufferHub service is not running.";
- }
- }
-
- bool BufferHubServiceRunning() {
- sp<IBufferHub> bufferhub = IBufferHub::getService();
- return bufferhub.get() != nullptr;
- }
-};
-
-bool cmpAHardwareBufferDesc(const AHardwareBuffer_Desc& desc, const AHardwareBuffer_Desc& other) {
- // Not comparing stride because it's unknown before allocation
- return desc.format == other.format && desc.height == other.height &&
- desc.layers == other.layers && desc.usage == other.usage && desc.width == other.width;
-}
-
-class BufferHubBufferStateTransitionTest : public BufferHubBufferTest {
-protected:
- void SetUp() override {
- BufferHubBufferTest::SetUp();
-
- if (IsSkipped()) {
- // If the base class' SetUp() stated the test should be skipped, we should short
- // circuit this sub-class' logic.
- return;
- }
-
- CreateTwoClientsOfABuffer();
- }
-
- std::unique_ptr<BufferHubBuffer> b1;
- uint32_t b1ClientMask = 0U;
- std::unique_ptr<BufferHubBuffer> b2;
- uint32_t b2ClientMask = 0U;
-
-private:
- // Creates b1 and b2 as the clients of the same buffer for testing.
- void CreateTwoClientsOfABuffer();
-};
-
-void BufferHubBufferStateTransitionTest::CreateTwoClientsOfABuffer() {
- b1 = BufferHubBuffer::create(kWidth, kHeight, kLayerCount, kFormat, kUsage, kUserMetadataSize);
- ASSERT_THAT(b1, NotNull());
- b1ClientMask = b1->clientStateMask();
- ASSERT_NE(b1ClientMask, 0U);
-
- sp<NativeHandle> token = b1->duplicate();
- ASSERT_THAT(token, NotNull());
-
- b2 = BufferHubBuffer::import(token);
- ASSERT_THAT(b2, NotNull());
-
- b2ClientMask = b2->clientStateMask();
- ASSERT_NE(b2ClientMask, 0U);
- ASSERT_NE(b2ClientMask, b1ClientMask);
-}
-
-TEST_F(BufferHubBufferTest, CreateBufferFails) {
- // Buffer Creation will fail: BLOB format requires height to be 1.
- auto b1 = BufferHubBuffer::create(kWidth, /*height=*/2, kLayerCount,
- /*format=*/HAL_PIXEL_FORMAT_BLOB, kUsage, kUserMetadataSize);
-
- EXPECT_THAT(b1, IsNull());
-
- // Buffer Creation will fail: user metadata size too large.
- auto b2 = BufferHubBuffer::create(kWidth, kHeight, kLayerCount, kFormat, kUsage,
- /*userMetadataSize=*/std::numeric_limits<size_t>::max());
-
- EXPECT_THAT(b2, IsNull());
-
- // Buffer Creation will fail: user metadata size too large.
- const size_t userMetadataSize = std::numeric_limits<size_t>::max() - kMetadataHeaderSize;
- auto b3 = BufferHubBuffer::create(kWidth, kHeight, kLayerCount, kFormat, kUsage,
- userMetadataSize);
-
- EXPECT_THAT(b3, IsNull());
-}
-
-TEST_F(BufferHubBufferTest, CreateBuffer) {
- auto b1 = BufferHubBuffer::create(kWidth, kHeight, kLayerCount, kFormat, kUsage,
- kUserMetadataSize);
- ASSERT_THAT(b1, NotNull());
- EXPECT_TRUE(b1->isConnected());
- EXPECT_TRUE(b1->isValid());
- EXPECT_TRUE(cmpAHardwareBufferDesc(b1->desc(), kDesc));
- EXPECT_EQ(b1->userMetadataSize(), kUserMetadataSize);
-}
-
-TEST_F(BufferHubBufferTest, DuplicateAndImportBuffer) {
- auto b1 = BufferHubBuffer::create(kWidth, kHeight, kLayerCount, kFormat, kUsage,
- kUserMetadataSize);
- ASSERT_THAT(b1, NotNull());
- EXPECT_TRUE(b1->isValid());
-
- sp<NativeHandle> token = b1->duplicate();
- ASSERT_THAT(token, NotNull());
-
- // The detached buffer should still be valid.
- EXPECT_TRUE(b1->isConnected());
- EXPECT_TRUE(b1->isValid());
-
- std::unique_ptr<BufferHubBuffer> b2 = BufferHubBuffer::import(token);
-
- ASSERT_THAT(b2, NotNull());
- EXPECT_TRUE(b2->isValid());
-
- EXPECT_TRUE(cmpAHardwareBufferDesc(b1->desc(), b2->desc()));
- EXPECT_EQ(b1->userMetadataSize(), b2->userMetadataSize());
-
- // These two buffer instances are based on the same physical buffer under the
- // hood, so they should share the same id.
- EXPECT_EQ(b1->id(), b2->id());
- // We use clientStateMask() to tell those two instances apart.
- EXPECT_NE(b1->clientStateMask(), b2->clientStateMask());
-
- // Both buffer instances should be in released state currently.
- EXPECT_TRUE(b1->isReleased());
- EXPECT_TRUE(b2->isReleased());
-
- // The event fd should behave like duped event fds.
- const BufferHubEventFd& eventFd1 = b1->eventFd();
- ASSERT_GE(eventFd1.get(), 0);
- const BufferHubEventFd& eventFd2 = b2->eventFd();
- ASSERT_GE(eventFd2.get(), 0);
-
- base::unique_fd epollFd(epoll_create(64));
- ASSERT_GE(epollFd.get(), 0);
-
- // Add eventFd1 to epoll set, and signal eventFd2.
- epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}};
- ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd1.get(), &e), 0) << strerror(errno);
-
- std::array<epoll_event, 1> events;
- EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
-
- eventFd2.signal();
- EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1);
-
- // The epoll fd is edge triggered, so it only responds to the eventFd once.
- EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
-
- eventFd2.signal();
- eventFd2.clear();
- EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
-}
-
-TEST_F(BufferHubBufferTest, ImportFreedBuffer) {
- auto b1 = BufferHubBuffer::create(kWidth, kHeight, kLayerCount, kFormat, kUsage,
- kUserMetadataSize);
- ASSERT_THAT(b1, NotNull());
- EXPECT_TRUE(b1->isValid());
-
- sp<NativeHandle> token = b1->duplicate();
- ASSERT_THAT(token, NotNull());
-
- // Explicitly destroy b1. Backend buffer should be freed and token becomes invalid
- b1.reset();
-
- std::unique_ptr<BufferHubBuffer> b2 = BufferHubBuffer::import(token);
-
- // Import should fail with INVALID_TOKEN
- EXPECT_THAT(b2, IsNull());
-}
-
-// nullptr must not crash the service
-TEST_F(BufferHubBufferTest, ImportNullToken) {
- auto b1 = BufferHubBuffer::import(nullptr);
- EXPECT_THAT(b1, IsNull());
-}
-
-TEST_F(BufferHubBufferTest, ImportInvalidToken) {
- native_handle_t* token = native_handle_create(/*numFds=*/0, /*numInts=*/1);
- token->data[0] = 0;
-
- sp<NativeHandle> tokenHandle = NativeHandle::create(token, /*ownHandle=*/true);
- auto b1 = BufferHubBuffer::import(tokenHandle);
-
- EXPECT_THAT(b1, IsNull());
-}
-
-TEST_F(BufferHubBufferStateTransitionTest, GainBuffer_fromReleasedState) {
- ASSERT_TRUE(b1->isReleased());
-
- // Successful gaining the buffer should change the buffer state bit of b1 to
- // gained state, other client state bits to released state.
- EXPECT_EQ(b1->gain(), 0);
- EXPECT_TRUE(isClientGained(b1->bufferState(), b1ClientMask));
-}
-
-TEST_F(BufferHubBufferStateTransitionTest, GainBuffer_fromGainedState) {
- ASSERT_EQ(b1->gain(), 0);
- auto currentBufferState = b1->bufferState();
- ASSERT_TRUE(isClientGained(currentBufferState, b1ClientMask));
-
- // Gaining from gained state by the same client should not return error.
- EXPECT_EQ(b1->gain(), 0);
-
- // Gaining from gained state by another client should return error.
- EXPECT_EQ(b2->gain(), -EBUSY);
-}
-
-TEST_F(BufferHubBufferStateTransitionTest, GainBuffer_fromAcquiredState) {
- ASSERT_EQ(b1->gain(), 0);
- ASSERT_EQ(b1->post(), 0);
- ASSERT_EQ(b2->acquire(), 0);
- ASSERT_TRUE(isAnyClientAcquired(b1->bufferState()));
-
- // Gaining from acquired state should fail.
- EXPECT_EQ(b1->gain(), -EBUSY);
- EXPECT_EQ(b2->gain(), -EBUSY);
-}
-
-TEST_F(BufferHubBufferStateTransitionTest, GainBuffer_fromOtherClientInPostedState) {
- ASSERT_EQ(b1->gain(), 0);
- ASSERT_EQ(b1->post(), 0);
- ASSERT_TRUE(isAnyClientPosted(b1->bufferState()));
-
- // Gaining a buffer who has other posted client should succeed.
- EXPECT_EQ(b1->gain(), 0);
-}
-
-TEST_F(BufferHubBufferStateTransitionTest, GainBuffer_fromSelfInPostedState) {
- ASSERT_EQ(b1->gain(), 0);
- ASSERT_EQ(b1->post(), 0);
- ASSERT_TRUE(isAnyClientPosted(b1->bufferState()));
-
- // A posted client should be able to gain the buffer when there is no other clients in
- // acquired state.
- EXPECT_EQ(b2->gain(), 0);
-}
-
-TEST_F(BufferHubBufferStateTransitionTest, PostBuffer_fromOtherInGainedState) {
- ASSERT_EQ(b1->gain(), 0);
- ASSERT_TRUE(isClientGained(b1->bufferState(), b1ClientMask));
-
- EXPECT_EQ(b2->post(), -EBUSY);
-}
-
-TEST_F(BufferHubBufferStateTransitionTest, PostBuffer_fromSelfInGainedState) {
- ASSERT_EQ(b1->gain(), 0);
- ASSERT_TRUE(isClientGained(b1->bufferState(), b1ClientMask));
-
- EXPECT_EQ(b1->post(), 0);
- auto currentBufferState = b1->bufferState();
- EXPECT_TRUE(isClientReleased(currentBufferState, b1ClientMask));
- EXPECT_TRUE(isClientPosted(currentBufferState, b2ClientMask));
-}
-
-TEST_F(BufferHubBufferStateTransitionTest, PostBuffer_fromPostedState) {
- ASSERT_EQ(b1->gain(), 0);
- ASSERT_EQ(b1->post(), 0);
- ASSERT_TRUE(isAnyClientPosted(b1->bufferState()));
-
- // Post from posted state should fail.
- EXPECT_EQ(b1->post(), -EBUSY);
- EXPECT_EQ(b2->post(), -EBUSY);
-}
-
-TEST_F(BufferHubBufferStateTransitionTest, PostBuffer_fromAcquiredState) {
- ASSERT_EQ(b1->gain(), 0);
- ASSERT_EQ(b1->post(), 0);
- ASSERT_EQ(b2->acquire(), 0);
- ASSERT_TRUE(isAnyClientAcquired(b1->bufferState()));
-
- // Posting from acquired state should fail.
- EXPECT_EQ(b1->post(), -EBUSY);
- EXPECT_EQ(b2->post(), -EBUSY);
-}
-
-TEST_F(BufferHubBufferStateTransitionTest, PostBuffer_fromReleasedState) {
- ASSERT_TRUE(b1->isReleased());
-
- // Posting from released state should fail.
- EXPECT_EQ(b1->post(), -EBUSY);
- EXPECT_EQ(b2->post(), -EBUSY);
-}
-
-TEST_F(BufferHubBufferStateTransitionTest, AcquireBuffer_fromSelfInPostedState) {
- ASSERT_EQ(b1->gain(), 0);
- ASSERT_EQ(b1->post(), 0);
- ASSERT_TRUE(isClientPosted(b1->bufferState(), b2ClientMask));
-
- // Acquire from posted state should pass.
- EXPECT_EQ(b2->acquire(), 0);
-}
-
-TEST_F(BufferHubBufferStateTransitionTest, AcquireBuffer_fromOtherInPostedState) {
- ASSERT_EQ(b1->gain(), 0);
- ASSERT_EQ(b1->post(), 0);
- ASSERT_TRUE(isClientPosted(b1->bufferState(), b2ClientMask));
-
- // Acquire from released state should fail, although there are other clients
- // in posted state.
- EXPECT_EQ(b1->acquire(), -EBUSY);
-}
-
-TEST_F(BufferHubBufferStateTransitionTest, AcquireBuffer_fromSelfInAcquiredState) {
- ASSERT_EQ(b1->gain(), 0);
- ASSERT_EQ(b1->post(), 0);
- ASSERT_EQ(b2->acquire(), 0);
- auto currentBufferState = b1->bufferState();
- ASSERT_TRUE(isClientAcquired(currentBufferState, b2ClientMask));
-
- // Acquiring from acquired state by the same client should not error out.
- EXPECT_EQ(b2->acquire(), 0);
-}
-
-TEST_F(BufferHubBufferStateTransitionTest, AcquireBuffer_fromReleasedState) {
- ASSERT_TRUE(b1->isReleased());
-
- // Acquiring form released state should fail.
- EXPECT_EQ(b1->acquire(), -EBUSY);
- EXPECT_EQ(b2->acquire(), -EBUSY);
-}
-
-TEST_F(BufferHubBufferStateTransitionTest, AcquireBuffer_fromGainedState) {
- ASSERT_EQ(b1->gain(), 0);
- ASSERT_TRUE(isAnyClientGained(b1->bufferState()));
-
- // Acquiring from gained state should fail.
- EXPECT_EQ(b1->acquire(), -EBUSY);
- EXPECT_EQ(b2->acquire(), -EBUSY);
-}
-
-TEST_F(BufferHubBufferStateTransitionTest, ReleaseBuffer_fromSelfInReleasedState) {
- ASSERT_TRUE(b1->isReleased());
-
- EXPECT_EQ(b1->release(), 0);
-}
-
-TEST_F(BufferHubBufferStateTransitionTest, ReleaseBuffer_fromSelfInGainedState) {
- ASSERT_TRUE(b1->isReleased());
- ASSERT_EQ(b1->gain(), 0);
- ASSERT_TRUE(isAnyClientGained(b1->bufferState()));
-
- EXPECT_EQ(b1->release(), 0);
-}
-
-TEST_F(BufferHubBufferStateTransitionTest, ReleaseBuffer_fromSelfInPostedState) {
- ASSERT_EQ(b1->gain(), 0);
- ASSERT_EQ(b1->post(), 0);
- ASSERT_TRUE(isAnyClientPosted(b1->bufferState()));
-
- EXPECT_EQ(b2->release(), 0);
-}
-
-TEST_F(BufferHubBufferStateTransitionTest, ReleaseBuffer_fromSelfInAcquiredState) {
- ASSERT_EQ(b1->gain(), 0);
- ASSERT_EQ(b1->post(), 0);
- ASSERT_EQ(b2->acquire(), 0);
- ASSERT_TRUE(isAnyClientAcquired(b1->bufferState()));
-
- EXPECT_EQ(b2->release(), 0);
-}
-
-TEST_F(BufferHubBufferStateTransitionTest, BasicUsage) {
- // 1 producer buffer and 1 consumer buffer initialised in testcase setup.
- // Test if this set of basic operation succeed:
- // Producer post three times to the consumer, and released by consumer.
- for (int i = 0; i < 3; ++i) {
- ASSERT_EQ(b1->gain(), 0);
- ASSERT_EQ(b1->post(), 0);
- ASSERT_EQ(b2->acquire(), 0);
- ASSERT_EQ(b2->release(), 0);
- }
-}
-
-TEST_F(BufferHubBufferTest, createNewConsumerAfterGain) {
- // Create a poducer buffer and gain.
- std::unique_ptr<BufferHubBuffer> b1 =
- BufferHubBuffer::create(kWidth, kHeight, kLayerCount, kFormat, kUsage,
- kUserMetadataSize);
- ASSERT_THAT(b1, NotNull());
- ASSERT_EQ(b1->gain(), 0);
-
- // Create a consumer of the buffer and test if the consumer can acquire the
- // buffer if producer posts.
- sp<NativeHandle> token = b1->duplicate();
- ASSERT_THAT(token, NotNull());
-
- std::unique_ptr<BufferHubBuffer> b2 = BufferHubBuffer::import(token);
-
- ASSERT_THAT(b2, NotNull());
- ASSERT_NE(b1->clientStateMask(), b2->clientStateMask());
-
- ASSERT_EQ(b1->post(), 0);
- EXPECT_EQ(b2->acquire(), 0);
-}
-
-TEST_F(BufferHubBufferTest, createNewConsumerAfterPost) {
- // Create a poducer buffer and post.
- std::unique_ptr<BufferHubBuffer> b1 =
- BufferHubBuffer::create(kWidth, kHeight, kLayerCount, kFormat, kUsage,
- kUserMetadataSize);
- ASSERT_EQ(b1->gain(), 0);
- ASSERT_EQ(b1->post(), 0);
-
- // Create a consumer of the buffer and test if the consumer can acquire the
- // buffer if producer posts.
- sp<NativeHandle> token = b1->duplicate();
- ASSERT_THAT(token, NotNull());
-
- std::unique_ptr<BufferHubBuffer> b2 = BufferHubBuffer::import(token);
-
- ASSERT_THAT(b2, NotNull());
- ASSERT_NE(b1->clientStateMask(), b2->clientStateMask());
-
- EXPECT_EQ(b2->acquire(), 0);
-}
-
-} // namespace
-
-} // namespace android
diff --git a/libs/ui/tests/BufferHubEventFd_test.cpp b/libs/ui/tests/BufferHubEventFd_test.cpp
deleted file mode 100644
index ef1781f..0000000
--- a/libs/ui/tests/BufferHubEventFd_test.cpp
+++ /dev/null
@@ -1,428 +0,0 @@
-/*
- * Copyright 2018 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 "BufferHubEventFdTest"
-
-#include <sys/epoll.h>
-#include <sys/eventfd.h>
-
-#include <array>
-#include <condition_variable>
-#include <mutex>
-#include <thread>
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <log/log.h>
-#include <ui/BufferHubEventFd.h>
-
-namespace android {
-
-namespace {
-
-const int kTimeout = 100;
-const std::chrono::milliseconds kTimeoutMs(kTimeout);
-const int kTestRuns = 5;
-
-using ::testing::Contains;
-using BufferHubEventFdTest = ::testing::Test;
-
-} // namespace
-
-TEST_F(BufferHubEventFdTest, EventFd_testSingleEpollFd) {
- BufferHubEventFd eventFd;
- ASSERT_TRUE(eventFd.isValid());
-
- base::unique_fd epollFd(epoll_create(64));
- ASSERT_GE(epollFd.get(), 0);
-
- epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}};
- ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0);
-
- std::array<epoll_event, 1> events;
- EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
-
- eventFd.signal();
- EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1);
-
- // The epoll fd is edge triggered, so it only responds to the eventFd once.
- EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
-
- // Check that it can receive consecutive signal.
- eventFd.signal();
- EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1);
- EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
-
- // Check that it can receive consecutive signal from a duplicated eventfd.
- BufferHubEventFd dupEventFd(dup(eventFd.get()));
- ASSERT_TRUE(dupEventFd.isValid());
- dupEventFd.signal();
- EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1);
- EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
- dupEventFd.signal();
- EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1);
- EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
-}
-
-TEST_F(BufferHubEventFdTest, EventFd_testCreateEpollFdAndAddSignaledEventFd) {
- BufferHubEventFd eventFd;
- ASSERT_TRUE(eventFd.isValid());
- eventFd.signal();
-
- base::unique_fd epollFd(epoll_create(64));
- ASSERT_GE(epollFd.get(), 0);
-
- // Make sure that the epoll set has not been signal yet.
- std::array<epoll_event, 1> events;
- ASSERT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
-
- // Check that adding an signaled fd into this epoll set will trigger the epoll set.
- epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}};
- ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0);
- EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1);
-
- // The epoll fd is edge triggered, so it only responds to the eventFd once.
- EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
-}
-
-TEST_F(BufferHubEventFdTest, EventFd_testAddSignaledEventFdToEpollFd) {
- BufferHubEventFd eventFd;
- ASSERT_TRUE(eventFd.isValid());
-
- base::unique_fd epollFd(epoll_create(64));
- ASSERT_GE(epollFd.get(), 0);
-
- eventFd.signal();
-
- epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}};
- ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0);
-
- std::array<epoll_event, 1> events;
- EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1);
-
- // The epoll fd is edge triggered, so it only responds to the eventFd once.
- EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
-}
-
-TEST_F(BufferHubEventFdTest, EventFd_testConsecutiveSignalsFromAEventFd) {
- BufferHubEventFd eventFd;
- ASSERT_TRUE(eventFd.isValid());
- base::unique_fd epollFd(epoll_create(64));
- ASSERT_GE(epollFd.get(), 0);
- epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}};
- ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0);
-
- std::array<epoll_event, 1> events;
- for (int i = 0; i < kTestRuns; ++i) {
- eventFd.signal();
- EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1);
- EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
- }
-}
-
-TEST_F(BufferHubEventFdTest, EventFd_testConsecutiveSignalsFromADuplicatedEventFd) {
- BufferHubEventFd eventFd;
- ASSERT_TRUE(eventFd.isValid());
- base::unique_fd epollFd(epoll_create(64));
- ASSERT_GE(epollFd.get(), 0);
- epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}};
- ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0);
-
- BufferHubEventFd dupEventFd(dup(eventFd.get()));
- ASSERT_TRUE(dupEventFd.isValid());
-
- std::array<epoll_event, 1> events;
- for (int i = 0; i < kTestRuns; ++i) {
- dupEventFd.signal();
- EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1);
- EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
- }
-}
-
-TEST_F(BufferHubEventFdTest, EventFd_testClear) {
- BufferHubEventFd eventFd;
- ASSERT_TRUE(eventFd.isValid());
-
- base::unique_fd epollFd(epoll_create(64));
- epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}};
-
- ASSERT_GE(epollFd.get(), 0);
- ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0);
-
- eventFd.signal();
- eventFd.clear();
-
- std::array<epoll_event, 1> events;
- EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
-}
-
-TEST_F(BufferHubEventFdTest, EventFd_testDupEventFd) {
- BufferHubEventFd eventFd;
- ASSERT_TRUE(eventFd.isValid());
-
- base::unique_fd epollFd(epoll_create(64));
- epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}};
-
- ASSERT_GE(epollFd.get(), 0);
- ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0);
-
- // Technically, the dupliated eventFd and the original eventFd are pointing
- // to the same kernel object. This test signals the duplicated eventFd but epolls the origianl
- // eventFd.
- BufferHubEventFd dupedEventFd(dup(eventFd.get()));
- ASSERT_GE(dupedEventFd.get(), 0);
-
- std::array<epoll_event, 1> events;
- EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
-
- dupedEventFd.signal();
- EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1);
-
- // The epoll fd is edge triggered, so it only responds to the eventFd once.
- EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
-
- dupedEventFd.signal();
-
- dupedEventFd.clear();
- EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
-}
-
-TEST_F(BufferHubEventFdTest, EventFd_testTwoEpollFds) {
- BufferHubEventFd eventFd;
- ASSERT_TRUE(eventFd.isValid());
-
- base::unique_fd epollFd1(epoll_create(64));
- base::unique_fd epollFd2(epoll_create(64));
- epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}};
-
- ASSERT_GE(epollFd1.get(), 0);
- ASSERT_GE(epollFd2.get(), 0);
-
- // Register the same eventFd to two EpollFds.
- ASSERT_EQ(epoll_ctl(epollFd1.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0);
- ASSERT_EQ(epoll_ctl(epollFd2.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0);
-
- std::array<epoll_event, 1> events;
- EXPECT_EQ(epoll_wait(epollFd1.get(), events.data(), events.size(), 0), 0);
- EXPECT_EQ(epoll_wait(epollFd2.get(), events.data(), events.size(), 0), 0);
-
- eventFd.signal();
- EXPECT_EQ(epoll_wait(epollFd1.get(), events.data(), events.size(), 0), 1);
- EXPECT_EQ(epoll_wait(epollFd2.get(), events.data(), events.size(), 0), 1);
-
- // The epoll fd is edge triggered, so it only responds to the eventFd once.
- EXPECT_EQ(epoll_wait(epollFd1.get(), events.data(), events.size(), 0), 0);
- EXPECT_EQ(epoll_wait(epollFd2.get(), events.data(), events.size(), 0), 0);
-
- eventFd.signal();
- EXPECT_EQ(epoll_wait(epollFd1.get(), events.data(), events.size(), 0), 1);
-
- eventFd.clear();
- EXPECT_EQ(epoll_wait(epollFd1.get(), events.data(), events.size(), 0), 0);
- EXPECT_EQ(epoll_wait(epollFd2.get(), events.data(), events.size(), 0), 0);
-}
-
-TEST_F(BufferHubEventFdTest, EventFd_testTwoEventFds) {
- BufferHubEventFd eventFd1;
- BufferHubEventFd eventFd2;
-
- ASSERT_TRUE(eventFd1.isValid());
- ASSERT_TRUE(eventFd2.isValid());
-
- base::unique_fd epollFd(epoll_create(64));
- epoll_event e1 = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 1}};
- epoll_event e2 = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 2}};
-
- ASSERT_GE(epollFd.get(), 0);
- ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd1.get(), &e1), 0);
- ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd2.get(), &e2), 0);
-
- std::array<epoll_event, 2> events;
- EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
-
- // Signal one by one.
- eventFd1.signal();
- EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1);
- EXPECT_EQ(events[0].data.u32, e1.data.u32);
-
- eventFd2.signal();
- EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1);
- EXPECT_EQ(events[0].data.u32, e2.data.u32);
-
- // Signal both.
- eventFd1.signal();
- eventFd2.signal();
- EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 2);
-
- uint32_t u32s[] = {events[0].data.u32, events[1].data.u32};
- EXPECT_THAT(u32s, Contains(e1.data.u32));
- EXPECT_THAT(u32s, Contains(e2.data.u32));
-
- // The epoll fd is edge triggered, so it only responds to the eventFd once.
- EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
-
- eventFd1.signal();
- eventFd2.signal();
- eventFd2.clear();
- EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1);
-}
-
-TEST_F(BufferHubEventFdTest, EventFd_testPollingThreadWithTwoEventFds) {
- BufferHubEventFd eventFd1;
- BufferHubEventFd eventFd2;
-
- ASSERT_TRUE(eventFd1.isValid());
- ASSERT_TRUE(eventFd2.isValid());
-
- base::unique_fd epollFd(epoll_create(64));
- epoll_event e1 = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 1}};
- epoll_event e2 = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 2}};
-
- ASSERT_GE(epollFd.get(), 0);
- ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd1.get(), &e1), 0);
- ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd2.get(), &e2), 0);
-
- int countEvent1 = 0;
- int countEvent2 = 0;
- std::atomic<bool> stop{false};
- std::mutex mx;
- std::condition_variable cv;
-
- std::thread pollingThread([&] {
- std::array<epoll_event, 2> events;
- while (true) {
- if (stop.load()) {
- break;
- }
- int ret = epoll_wait(epollFd.get(), events.data(), events.size(), kTimeout);
- ALOGE_IF(ret < 0 && errno != ETIMEDOUT, "Epoll failed.");
-
- std::lock_guard<std::mutex> lock(mx);
- for (int i = 0; i < ret; i++) {
- if (events[i].data.u32 == e1.data.u32) {
- countEvent1++;
- cv.notify_one();
- } else if (events[i].data.u32 == e2.data.u32) {
- countEvent2++;
- cv.notify_one();
- }
- }
- }
- });
-
- {
- std::unique_lock<std::mutex> lock(mx);
-
- eventFd1.signal();
- EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEvent1 == 1; }));
-
- eventFd1.signal();
- EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEvent1 == 2; }));
-
- eventFd2.signal();
- EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEvent2 == 1; }));
-
- eventFd1.clear();
- eventFd2.clear();
- EXPECT_EQ(countEvent1, 2);
- EXPECT_EQ(countEvent2, 1);
-
- eventFd1.signal();
- EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEvent1 == 3; }));
-
- eventFd2.signal();
- EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEvent2 == 2; }));
- }
-
- stop.store(true);
- pollingThread.join();
-}
-
-TEST_F(BufferHubEventFdTest, EventFd_testTwoPollingThreads) {
- BufferHubEventFd eventFd;
- ASSERT_TRUE(eventFd.isValid());
-
- base::unique_fd epollFd1(epoll_create(64));
- base::unique_fd epollFd2(epoll_create(64));
- epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}};
-
- ASSERT_GE(epollFd1.get(), 0);
- ASSERT_GE(epollFd2.get(), 0);
-
- // Register the same eventFd to two EpollFds.
- ASSERT_EQ(epoll_ctl(epollFd1.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0);
- ASSERT_EQ(epoll_ctl(epollFd2.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0);
-
- int countEpoll1 = 0;
- int countEpoll2 = 0;
- std::atomic<bool> stop{false};
- std::mutex mx;
- std::condition_variable cv;
-
- std::thread pollingThread1([&] {
- std::array<epoll_event, 1> events;
- while (!stop.load()) {
- int ret = epoll_wait(epollFd1.get(), events.data(), events.size(), kTimeout);
- ALOGE_IF(ret < 0 && errno != ETIMEDOUT, "Epoll failed.");
-
- if (ret > 0) {
- std::lock_guard<std::mutex> lock(mx);
- countEpoll1++;
- cv.notify_one();
- }
- }
- });
-
- std::thread pollingThread2([&] {
- std::array<epoll_event, 1> events;
- while (!stop.load()) {
- int ret = epoll_wait(epollFd2.get(), events.data(), events.size(), kTimeout);
- ALOGE_IF(ret < 0 && errno != ETIMEDOUT, "Epoll failed.");
-
- if (ret > 0) {
- std::lock_guard<std::mutex> lock(mx);
- countEpoll2++;
- cv.notify_one();
- }
- }
- });
-
- {
- std::unique_lock<std::mutex> lock(mx);
-
- eventFd.signal();
- EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEpoll1 == 1; }));
- EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEpoll2 == 1; }));
-
- eventFd.signal();
- EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEpoll1 == 2; }));
- EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEpoll2 == 2; }));
-
- eventFd.clear();
- EXPECT_EQ(countEpoll1, 2);
- EXPECT_EQ(countEpoll2, 2);
-
- eventFd.signal();
- EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEpoll1 == 3; }));
- EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEpoll2 == 3; }));
- }
-
- stop.store(true);
- pollingThread1.join();
- pollingThread2.join();
-}
-
-} // namespace android
diff --git a/libs/ui/tests/BufferHubMetadata_test.cpp b/libs/ui/tests/BufferHubMetadata_test.cpp
deleted file mode 100644
index eb978ca..0000000
--- a/libs/ui/tests/BufferHubMetadata_test.cpp
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2018 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/BufferHubMetadata.h>
-
-namespace android {
-namespace dvr {
-
-constexpr size_t kEmptyUserMetadataSize = 0;
-
-class BufferHubMetadataTest : public ::testing::Test {};
-
-TEST_F(BufferHubMetadataTest, Create_UserMetdataSizeTooBig) {
- BufferHubMetadata m1 = BufferHubMetadata::create(std::numeric_limits<uint32_t>::max());
- EXPECT_FALSE(m1.isValid());
-}
-
-TEST_F(BufferHubMetadataTest, Create_Success) {
- BufferHubMetadata m1 = BufferHubMetadata::create(kEmptyUserMetadataSize);
- EXPECT_TRUE(m1.isValid());
- EXPECT_NE(m1.metadataHeader(), nullptr);
-}
-
-TEST_F(BufferHubMetadataTest, Import_Success) {
- BufferHubMetadata m1 = BufferHubMetadata::create(kEmptyUserMetadataSize);
- EXPECT_TRUE(m1.isValid());
- EXPECT_NE(m1.metadataHeader(), nullptr);
-
- unique_fd h2 = unique_fd(dup(m1.ashmemFd().get()));
- EXPECT_NE(h2.get(), -1);
-
- BufferHubMetadata m2 = BufferHubMetadata::import(std::move(h2));
- EXPECT_EQ(h2.get(), -1);
- EXPECT_TRUE(m1.isValid());
- BufferHubDefs::MetadataHeader* mh1 = m1.metadataHeader();
- EXPECT_NE(mh1, nullptr);
-
- // Check if the newly allocated buffer is initialized in released state (i.e.
- // state equals to 0U).
- EXPECT_TRUE(mh1->bufferState.load() == 0U);
-
- EXPECT_TRUE(m2.isValid());
- BufferHubDefs::MetadataHeader* mh2 = m2.metadataHeader();
- EXPECT_NE(mh2, nullptr);
-
- // Check if the newly allocated buffer is initialized in released state (i.e.
- // state equals to 0U).
- EXPECT_TRUE(mh2->bufferState.load() == 0U);
-}
-
-TEST_F(BufferHubMetadataTest, MoveMetadataInvalidatesOldOne) {
- BufferHubMetadata m1 = BufferHubMetadata::create(sizeof(int));
- EXPECT_TRUE(m1.isValid());
- EXPECT_NE(m1.metadataHeader(), nullptr);
- EXPECT_NE(m1.ashmemFd().get(), -1);
- EXPECT_EQ(m1.userMetadataSize(), sizeof(int));
-
- BufferHubMetadata m2 = std::move(m1);
-
- // After the move, the metadata header (a raw pointer) should be reset in the older buffer.
- EXPECT_EQ(m1.metadataHeader(), nullptr);
- EXPECT_NE(m2.metadataHeader(), nullptr);
-
- EXPECT_EQ(m1.ashmemFd().get(), -1);
- EXPECT_NE(m2.ashmemFd().get(), -1);
-
- EXPECT_EQ(m1.userMetadataSize(), 0U);
- EXPECT_EQ(m2.userMetadataSize(), sizeof(int));
-
- BufferHubMetadata m3{std::move(m2)};
-
- // After the move, the metadata header (a raw pointer) should be reset in the older buffer.
- EXPECT_EQ(m2.metadataHeader(), nullptr);
- EXPECT_NE(m3.metadataHeader(), nullptr);
-
- EXPECT_EQ(m2.ashmemFd().get(), -1);
- EXPECT_NE(m3.ashmemFd().get(), -1);
-
- EXPECT_EQ(m2.userMetadataSize(), 0U);
- EXPECT_EQ(m3.userMetadataSize(), sizeof(int));
-}
-
-} // namespace dvr
-} // namespace android
diff --git a/libs/ui/tests/GraphicBufferAllocator_test.cpp b/libs/ui/tests/GraphicBufferAllocator_test.cpp
index efca083..f4c0afa 100644
--- a/libs/ui/tests/GraphicBufferAllocator_test.cpp
+++ b/libs/ui/tests/GraphicBufferAllocator_test.cpp
@@ -51,7 +51,7 @@
std::cout << "Setting expected stride to " << stride << std::endl;
EXPECT_CALL(*(reinterpret_cast<const mock::MockGrallocAllocator*>(mAllocator.get())),
allocate)
- .WillOnce(DoAll(SetArgPointee<6>(stride), Return(err)));
+ .WillOnce(DoAll(SetArgPointee<7>(stride), Return(err)));
}
std::unique_ptr<const GrallocAllocator>& getAllocator() { return mAllocator; }
};
diff --git a/libs/ui/tests/GraphicBufferOverBinder_test.cpp b/libs/ui/tests/GraphicBufferOverBinder_test.cpp
new file mode 100644
index 0000000..126a945
--- /dev/null
+++ b/libs/ui/tests/GraphicBufferOverBinder_test.cpp
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2019 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 "GraphicBufferOverBinder_test"
+
+#include <binder/IServiceManager.h>
+#include <binder/Parcel.h>
+#include <binder/ProcessState.h>
+#include <gtest/gtest.h>
+#include <gui/BufferQueue.h>
+#include <gui/IGraphicBufferConsumer.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <ui/GraphicBuffer.h>
+#include <utils/Log.h>
+
+namespace android {
+
+constexpr uint32_t kTestWidth = 1024;
+constexpr uint32_t kTestHeight = 1;
+constexpr uint32_t kTestFormat = HAL_PIXEL_FORMAT_BLOB;
+constexpr uint32_t kTestLayerCount = 1;
+constexpr uint64_t kTestUsage = GraphicBuffer::USAGE_SW_WRITE_OFTEN;
+static const String16 kTestServiceName = String16("GraphicBufferOverBinderTestService");
+enum GraphicBufferOverBinderTestServiceCode {
+ GRAPHIC_BUFFER = IBinder::FIRST_CALL_TRANSACTION,
+};
+
+class GraphicBufferOverBinderTestService : public BBinder {
+public:
+ GraphicBufferOverBinderTestService() {
+ // GraphicBuffer
+ mGraphicBuffer = new GraphicBuffer(kTestWidth, kTestHeight, kTestFormat, kTestLayerCount,
+ kTestUsage);
+ }
+
+ ~GraphicBufferOverBinderTestService() = default;
+
+ virtual status_t onTransact(uint32_t code, const Parcel& /*data*/, Parcel* reply,
+ uint32_t /*flags*/ = 0) {
+ switch (code) {
+ case GRAPHIC_BUFFER: {
+ return reply->write(*mGraphicBuffer);
+ }
+ default:
+ return UNKNOWN_TRANSACTION;
+ };
+ }
+
+protected:
+ sp<GraphicBuffer> mGraphicBuffer;
+};
+
+static int runBinderServer() {
+ ProcessState::self()->startThreadPool();
+
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<GraphicBufferOverBinderTestService> service = new GraphicBufferOverBinderTestService;
+ sm->addService(kTestServiceName, service, false);
+
+ ALOGI("Binder server running...");
+
+ while (true) {
+ int stat, retval;
+ retval = wait(&stat);
+ if (retval == -1 && errno == ECHILD) {
+ break;
+ }
+ }
+
+ ALOGI("Binder server exiting...");
+ return 0;
+}
+
+class GraphicBufferOverBinderTest : public ::testing::TestWithParam<uint32_t> {
+protected:
+ virtual void SetUp() {
+ mService = defaultServiceManager()->getService(kTestServiceName);
+ if (mService == nullptr) {
+ ALOGE("Failed to connect to the test service.");
+ return;
+ }
+
+ ALOGI("Binder service is ready for client.");
+ }
+
+ status_t GetGraphicBuffer(sp<GraphicBuffer>* outBuf, uint32_t opCode) {
+ Parcel data;
+ Parcel reply;
+ status_t error = mService->transact(opCode, data, &reply);
+ if (error != NO_ERROR) {
+ ALOGE("Failed to get graphic buffer over binder, error=%d.", error);
+ return error;
+ }
+
+ *outBuf = new GraphicBuffer();
+ return reply.read(**outBuf);
+ }
+
+private:
+ sp<IBinder> mService;
+};
+
+TEST_F(GraphicBufferOverBinderTest, SendGraphicBufferOverBinder) {
+ sp<GraphicBuffer> gb;
+ EXPECT_EQ(GetGraphicBuffer(&gb, GRAPHIC_BUFFER), OK);
+ EXPECT_NE(gb, nullptr);
+ void* vaddr;
+ EXPECT_EQ(gb->lock(kTestUsage, &vaddr), OK);
+ EXPECT_EQ(gb->unlock(), OK);
+}
+
+} // namespace android
+
+int main(int argc, char** argv) {
+ pid_t pid = fork();
+ if (pid == 0) {
+ android::ProcessState::self()->startThreadPool();
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+
+ } else {
+ ALOGI("Test process pid: %d.", pid);
+ return android::runBinderServer();
+ }
+}
diff --git a/libs/ui/tests/GraphicBuffer_test.cpp b/libs/ui/tests/GraphicBuffer_test.cpp
index 127f7ee..19551b3 100644
--- a/libs/ui/tests/GraphicBuffer_test.cpp
+++ b/libs/ui/tests/GraphicBuffer_test.cpp
@@ -16,7 +16,6 @@
#define LOG_TAG "GraphicBufferTest"
-#include <ui/BufferHubBuffer.h>
#include <ui/GraphicBuffer.h>
#include <gtest/gtest.h>
@@ -27,7 +26,6 @@
constexpr uint32_t kTestWidth = 1024;
constexpr uint32_t kTestHeight = 1;
-constexpr uint32_t kTestFormat = HAL_PIXEL_FORMAT_BLOB;
constexpr uint32_t kTestLayerCount = 1;
constexpr uint64_t kTestUsage = GraphicBuffer::USAGE_SW_WRITE_OFTEN;
@@ -44,95 +42,28 @@
TEST_F(GraphicBufferTest, AllocateBadDimensions) {
PixelFormat format = PIXEL_FORMAT_RGBA_8888;
+ if (std::numeric_limits<size_t>::max() / std::numeric_limits<uint32_t>::max() /
+ bytesPerPixel(format) >=
+ std::numeric_limits<uint32_t>::max()) {
+ GTEST_SUCCEED() << "Cannot overflow with this format";
+ }
uint32_t width, height;
width = height = std::numeric_limits<uint32_t>::max();
sp<GraphicBuffer> gb(new GraphicBuffer(width, height, format, kTestLayerCount, kTestUsage,
std::string("test")));
ASSERT_EQ(BAD_VALUE, gb->initCheck());
-}
-TEST_F(GraphicBufferTest, CreateFromBufferHubBuffer) {
- std::unique_ptr<BufferHubBuffer> b1 =
- BufferHubBuffer::create(kTestWidth, kTestHeight, kTestLayerCount, kTestFormat,
- kTestUsage, /*userMetadataSize=*/0);
- ASSERT_NE(b1, nullptr);
- EXPECT_TRUE(b1->isValid());
-
- sp<GraphicBuffer> gb(new GraphicBuffer(std::move(b1)));
- EXPECT_TRUE(gb->isBufferHubBuffer());
-
- EXPECT_EQ(gb->getWidth(), kTestWidth);
- EXPECT_EQ(gb->getHeight(), kTestHeight);
- EXPECT_EQ(static_cast<uint32_t>(gb->getPixelFormat()), kTestFormat);
- EXPECT_EQ(gb->getUsage(), kTestUsage);
- EXPECT_EQ(gb->getLayerCount(), kTestLayerCount);
-}
-
-TEST_F(GraphicBufferTest, InvalidBufferIdForNoneBufferHubBuffer) {
- sp<GraphicBuffer> gb(
- new GraphicBuffer(kTestWidth, kTestHeight, kTestFormat, kTestLayerCount, kTestUsage));
- EXPECT_FALSE(gb->isBufferHubBuffer());
- EXPECT_EQ(gb->getBufferId(), -1);
-}
-
-TEST_F(GraphicBufferTest, BufferIdMatchesBufferHubBufferId) {
- std::unique_ptr<BufferHubBuffer> b1 =
- BufferHubBuffer::create(kTestWidth, kTestHeight, kTestLayerCount, kTestFormat,
- kTestUsage, /*userMetadataSize=*/0);
- EXPECT_NE(b1, nullptr);
- EXPECT_TRUE(b1->isValid());
-
- int b1_id = b1->id();
- EXPECT_GE(b1_id, 0);
-
- sp<GraphicBuffer> gb(new GraphicBuffer(std::move(b1)));
- EXPECT_TRUE(gb->isBufferHubBuffer());
- EXPECT_EQ(gb->getBufferId(), b1_id);
-}
-
-TEST_F(GraphicBufferTest, flattenAndUnflatten) {
- std::unique_ptr<BufferHubBuffer> b1 =
- BufferHubBuffer::create(kTestWidth, kTestHeight, kTestLayerCount, kTestFormat,
- kTestUsage, /*userMetadataSize=*/0);
- ASSERT_NE(b1, nullptr);
- sp<GraphicBuffer> gb1(new GraphicBuffer(std::move(b1)));
- gb1->setGenerationNumber(42);
-
- size_t flattenedSize = gb1->getFlattenedSize();
- EXPECT_EQ(flattenedSize, 48);
- size_t fdCount = gb1->getFdCount();
- EXPECT_EQ(fdCount, 0);
-
- int data[flattenedSize];
- int fds[0];
-
- // Make copies of needed items since flatten modifies them.
- size_t flattenedSizeCopy = flattenedSize;
- size_t fdCountCopy = fdCount;
- void* dataStart = data;
- int* fdsStart = fds;
- status_t err = gb1->flatten(dataStart, flattenedSizeCopy, fdsStart, fdCountCopy);
- ASSERT_EQ(err, NO_ERROR);
- EXPECT_EQ(flattenedSizeCopy, 0);
- EXPECT_EQ(fdCountCopy, 0);
-
- size_t unflattenSize = flattenedSize;
- size_t unflattenFdCount = fdCount;
- const void* unflattenData = static_cast<const void*>(dataStart);
- const int* unflattenFdData = static_cast<const int*>(fdsStart);
-
- GraphicBuffer* gb2 = new GraphicBuffer();
- err = gb2->unflatten(unflattenData, unflattenSize, unflattenFdData, unflattenFdCount);
- ASSERT_EQ(err, NO_ERROR);
- EXPECT_TRUE(gb2->isBufferHubBuffer());
-
- EXPECT_EQ(gb2->getWidth(), kTestWidth);
- EXPECT_EQ(gb2->getHeight(), kTestHeight);
- EXPECT_EQ(static_cast<uint32_t>(gb2->getPixelFormat()), kTestFormat);
- EXPECT_EQ(gb2->getUsage(), kTestUsage);
- EXPECT_EQ(gb2->getLayerCount(), kTestLayerCount);
- EXPECT_EQ(gb1->getBufferId(), gb2->getBufferId());
- EXPECT_EQ(gb2->getGenerationNumber(), 42);
+ const size_t targetArea = std::numeric_limits<size_t>::max() / bytesPerPixel(format);
+ const size_t widthCandidate = targetArea / std::numeric_limits<uint32_t>::max();
+ if (widthCandidate == 0) {
+ width = 1;
+ } else {
+ width = std::numeric_limits<uint32_t>::max();
+ }
+ height = (targetArea / width) + 1;
+ sp<GraphicBuffer> gb2(new GraphicBuffer(width, height, format, kTestLayerCount, kTestUsage,
+ std::string("test")));
+ ASSERT_EQ(BAD_VALUE, gb2->initCheck());
}
} // namespace android
diff --git a/libs/ui/tests/Region_test.cpp b/libs/ui/tests/Region_test.cpp
index b104a46..c6b826d 100644
--- a/libs/ui/tests/Region_test.cpp
+++ b/libs/ui/tests/Region_test.cpp
@@ -152,5 +152,20 @@
}
}
+TEST_F(RegionTest, EqualsToSelf) {
+ Region touchableRegion;
+ touchableRegion.orSelf(Rect(0, 0, 100, 100));
+
+ ASSERT_TRUE(touchableRegion.contains(50, 50));
+
+ // Compiler prevents us from directly calling 'touchableRegion = touchableRegion'
+ Region& referenceTouchableRegion = touchableRegion;
+ touchableRegion = referenceTouchableRegion;
+
+ ASSERT_FALSE(touchableRegion.isEmpty());
+
+ ASSERT_TRUE(touchableRegion.contains(50, 50));
+}
+
}; // namespace android
diff --git a/libs/ui/tests/Size_test.cpp b/libs/ui/tests/Size_test.cpp
index 69e1ac8..38f37ad 100644
--- a/libs/ui/tests/Size_test.cpp
+++ b/libs/ui/tests/Size_test.cpp
@@ -19,8 +19,18 @@
#include <cmath>
#include <cstdlib>
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic error "-Wimplicit-int-float-conversion"
+#pragma clang diagnostic error "-Wconversion"
+#endif // __clang__
+
#include <ui/Size.h>
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif // __clang__
+
#include <gtest/gtest.h>
namespace android {
@@ -176,9 +186,34 @@
TEST(SizeTest, FloatRangeIsClamped) {
ClampTest(std::numeric_limits<float>::max(), std::numeric_limits<int32_t>::max());
+ ClampTest(nexttowardf(std::numeric_limits<int32_t>::max(), std::numeric_limits<float>::max()),
+ std::numeric_limits<int32_t>::max());
+ ClampTest(static_cast<float>(std::numeric_limits<int32_t>::max()),
+ std::numeric_limits<int32_t>::max());
+ ClampTest(nexttowardf(std::numeric_limits<int32_t>::max(), 0),
+ static_cast<int32_t>(nexttowardf(std::numeric_limits<int32_t>::max(), 0)));
ClampTest(float(0), int32_t(0));
+ ClampTest(nexttowardf(std::numeric_limits<int32_t>::lowest(), 0),
+ static_cast<int32_t>(nexttowardf(std::numeric_limits<int32_t>::lowest(), 0)));
+ ClampTest(static_cast<float>(std::numeric_limits<int32_t>::lowest()),
+ std::numeric_limits<int32_t>::lowest());
+ ClampTest(nexttowardf(std::numeric_limits<int32_t>::lowest(),
+ std::numeric_limits<float>::lowest()),
+ std::numeric_limits<int32_t>::lowest());
ClampTest(std::numeric_limits<float>::lowest(), std::numeric_limits<int32_t>::lowest());
}
+TEST(SizeTest, Uint32RangeIsClamped) {
+ ClampTest(std::numeric_limits<uint32_t>::max(), std::numeric_limits<int32_t>::max());
+ ClampTest(std::numeric_limits<uint32_t>::max() - 1, std::numeric_limits<int32_t>::max());
+ ClampTest(static_cast<uint32_t>(std::numeric_limits<int32_t>::max()) + 1,
+ std::numeric_limits<int32_t>::max());
+ ClampTest(static_cast<uint32_t>(std::numeric_limits<int32_t>::max()),
+ std::numeric_limits<int32_t>::max());
+ ClampTest(static_cast<uint32_t>(std::numeric_limits<int32_t>::max()) - 1,
+ std::numeric_limits<int32_t>::max() - 1);
+ ClampTest(uint32_t(0), int32_t(0));
+}
+
} // namespace ui
} // namespace android
diff --git a/libs/ui/tests/TEST_MAPPING b/libs/ui/tests/TEST_MAPPING
new file mode 100644
index 0000000..7fcd7de
--- /dev/null
+++ b/libs/ui/tests/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "Size_test"
+ }
+ ]
+}
diff --git a/libs/ui/tests/mock/MockGrallocAllocator.h b/libs/ui/tests/mock/MockGrallocAllocator.h
index 22c80a4..d62e3e2 100644
--- a/libs/ui/tests/mock/MockGrallocAllocator.h
+++ b/libs/ui/tests/mock/MockGrallocAllocator.h
@@ -32,11 +32,11 @@
~MockGrallocAllocator() override;
MOCK_METHOD(bool, isLoaded, (), (const, override));
- MOCK_METHOD(std::string, dumpDebugInfo, (), (const, override));
+ MOCK_METHOD(std::string, dumpDebugInfo, (bool less), (const, override));
MOCK_METHOD(status_t, allocate,
- (uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
- uint64_t usage, uint32_t bufferCount, uint32_t* outStride,
- buffer_handle_t* outBufferHandles),
+ (std::string requestorName, uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage, uint32_t bufferCount, uint32_t* outStride,
+ buffer_handle_t* outBufferHandles, bool less),
(const, override));
};
diff --git a/libs/vr/libbufferhub/consumer_buffer.cpp b/libs/vr/libbufferhub/consumer_buffer.cpp
index 115e866..7823e36 100644
--- a/libs/vr/libbufferhub/consumer_buffer.cpp
+++ b/libs/vr/libbufferhub/consumer_buffer.cpp
@@ -52,12 +52,6 @@
while (!buffer_state_->compare_exchange_weak(
current_buffer_state, updated_buffer_state, std::memory_order_acq_rel,
std::memory_order_acquire)) {
- ALOGD(
- "%s Failed to acquire the buffer. Current buffer state was changed to "
- "%" PRIx32
- " when trying to acquire the buffer and modify the buffer state to "
- "%" PRIx32 ". About to try again if the buffer is still posted.",
- __FUNCTION__, current_buffer_state, updated_buffer_state);
if (!BufferHubDefs::isClientPosted(current_buffer_state,
client_state_mask())) {
ALOGE(
@@ -152,12 +146,6 @@
while (!buffer_state_->compare_exchange_weak(
current_buffer_state, updated_buffer_state, std::memory_order_acq_rel,
std::memory_order_acquire)) {
- ALOGD(
- "%s: Failed to release the buffer. Current buffer state was changed to "
- "%" PRIx32
- " when trying to release the buffer and modify the buffer state to "
- "%" PRIx32 ". About to try again.",
- __FUNCTION__, current_buffer_state, updated_buffer_state);
// The failure of compare_exchange_weak updates current_buffer_state.
updated_buffer_state = current_buffer_state & (~client_state_mask());
}
diff --git a/libs/vr/libbufferhub/producer_buffer.cpp b/libs/vr/libbufferhub/producer_buffer.cpp
index 3d88ba5..aa9d072 100644
--- a/libs/vr/libbufferhub/producer_buffer.cpp
+++ b/libs/vr/libbufferhub/producer_buffer.cpp
@@ -96,13 +96,6 @@
while (!buffer_state_->compare_exchange_weak(
current_buffer_state, updated_buffer_state, std::memory_order_acq_rel,
std::memory_order_acquire)) {
- ALOGD(
- "%s: Failed to post the buffer. Current buffer state was changed to "
- "%" PRIx32
- " when trying to post the buffer and modify the buffer state to "
- "%" PRIx32
- ". About to try again if the buffer is still gained by this client.",
- __FUNCTION__, current_buffer_state, updated_buffer_state);
if (!BufferHubDefs::isClientGained(current_buffer_state,
client_state_mask())) {
ALOGE(
@@ -186,15 +179,6 @@
while (!buffer_state_->compare_exchange_weak(
current_buffer_state, updated_buffer_state, std::memory_order_acq_rel,
std::memory_order_acquire)) {
- ALOGD(
- "%s: Failed to gain the buffer. Current buffer state was changed to "
- "%" PRIx32
- " when trying to gain the buffer and modify the buffer state to "
- "%" PRIx32
- ". About to try again if the buffer is still not read by other "
- "clients.",
- __FUNCTION__, current_buffer_state, updated_buffer_state);
-
if (BufferHubDefs::isAnyClientAcquired(current_buffer_state) ||
BufferHubDefs::isAnyClientGained(current_buffer_state) ||
(BufferHubDefs::isAnyClientPosted(
diff --git a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp
index 8cc7081..fab1097 100644
--- a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp
+++ b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp
@@ -108,7 +108,7 @@
void ConnectProducer() {
IGraphicBufferProducer::QueueBufferOutput output;
// Can connect the first time.
- ASSERT_EQ(OK, mProducer->connect(kDummyListener, kTestApi,
+ ASSERT_EQ(OK, mProducer->connect(kStubListener, kTestApi,
kTestControlledByApp, &output));
}
@@ -140,7 +140,7 @@
return QueueBufferInputBuilder().build();
}
- const sp<IProducerListener> kDummyListener{new DummyProducerListener};
+ const sp<IProducerListener> kStubListener{new StubProducerListener};
sp<BufferHubProducer> mProducer;
sp<Surface> mSurface;
@@ -150,11 +150,11 @@
IGraphicBufferProducer::QueueBufferOutput output;
// NULL output returns BAD_VALUE
- EXPECT_EQ(BAD_VALUE, mProducer->connect(kDummyListener, kTestApi,
+ EXPECT_EQ(BAD_VALUE, mProducer->connect(kStubListener, kTestApi,
kTestControlledByApp, nullptr));
// Invalid API returns bad value
- EXPECT_EQ(BAD_VALUE, mProducer->connect(kDummyListener, kTestApiInvalid,
+ EXPECT_EQ(BAD_VALUE, mProducer->connect(kStubListener, kTestApiInvalid,
kTestControlledByApp, &output));
}
@@ -163,7 +163,7 @@
// Can't connect when there is already a producer connected.
IGraphicBufferProducer::QueueBufferOutput output;
- EXPECT_EQ(BAD_VALUE, mProducer->connect(kDummyListener, kTestApi,
+ EXPECT_EQ(BAD_VALUE, mProducer->connect(kStubListener, kTestApi,
kTestControlledByApp, &output));
}
@@ -554,18 +554,18 @@
ProducerQueueParcelable producer_parcelable;
EXPECT_EQ(mProducer->TakeAsParcelable(&producer_parcelable), BAD_VALUE);
- // Create a valid dummy producer parcelable.
- auto dummy_channel_parcelable =
+ // Create a valid fake producer parcelable.
+ auto fake_channel_parcelable =
std::make_unique<pdx::default_transport::ChannelParcelable>(
LocalHandle(0), LocalHandle(0), LocalHandle(0));
- EXPECT_TRUE(dummy_channel_parcelable->IsValid());
- ProducerQueueParcelable dummy_producer_parcelable(
- std::move(dummy_channel_parcelable));
- EXPECT_TRUE(dummy_producer_parcelable.IsValid());
+ EXPECT_TRUE(fake_channel_parcelable->IsValid());
+ ProducerQueueParcelable fake_producer_parcelable(
+ std::move(fake_channel_parcelable));
+ EXPECT_TRUE(fake_producer_parcelable.IsValid());
// Disconnect producer can be taken out, but only to an invalid parcelable.
ASSERT_EQ(mProducer->disconnect(kTestApi), OK);
- EXPECT_EQ(mProducer->TakeAsParcelable(&dummy_producer_parcelable), BAD_VALUE);
+ EXPECT_EQ(mProducer->TakeAsParcelable(&fake_producer_parcelable), BAD_VALUE);
EXPECT_FALSE(producer_parcelable.IsValid());
EXPECT_EQ(mProducer->TakeAsParcelable(&producer_parcelable), OK);
EXPECT_TRUE(producer_parcelable.IsValid());
@@ -583,7 +583,7 @@
// But connect to API will fail.
IGraphicBufferProducer::QueueBufferOutput output;
- EXPECT_EQ(mProducer->connect(kDummyListener, kTestApi, kTestControlledByApp,
+ EXPECT_EQ(mProducer->connect(kStubListener, kTestApi, kTestControlledByApp,
&output),
BAD_VALUE);
@@ -592,8 +592,8 @@
sp<BufferHubProducer> new_producer =
BufferHubProducer::Create(std::move(producer_parcelable));
ASSERT_TRUE(new_producer != nullptr);
- EXPECT_EQ(new_producer->connect(kDummyListener, kTestApi,
- kTestControlledByApp, &output),
+ EXPECT_EQ(new_producer->connect(kStubListener, kTestApi, kTestControlledByApp,
+ &output),
OK);
}
diff --git a/libs/vr/libdisplay/display_client.cpp b/libs/vr/libdisplay/display_client.cpp
index f67e258..62856df 100644
--- a/libs/vr/libdisplay/display_client.cpp
+++ b/libs/vr/libdisplay/display_client.cpp
@@ -178,6 +178,10 @@
return status;
}
+Status<uint8_t> DisplayClient::GetDisplayIdentificationPort() {
+ return InvokeRemoteMethod<DisplayProtocol::GetDisplayIdentificationPort>();
+}
+
Status<std::unique_ptr<Surface>> DisplayClient::CreateSurface(
const SurfaceAttributes& attributes) {
int error;
diff --git a/libs/vr/libdisplay/include/private/dvr/display_client.h b/libs/vr/libdisplay/include/private/dvr/display_client.h
index f8f5b3d..81546ac 100644
--- a/libs/vr/libdisplay/include/private/dvr/display_client.h
+++ b/libs/vr/libdisplay/include/private/dvr/display_client.h
@@ -72,6 +72,7 @@
public:
pdx::Status<Metrics> GetDisplayMetrics();
pdx::Status<std::string> GetConfigurationData(ConfigFileType config_type);
+ pdx::Status<uint8_t> GetDisplayIdentificationPort();
pdx::Status<std::unique_ptr<IonBuffer>> SetupGlobalBuffer(
DvrGlobalBufferKey key, size_t size, uint64_t usage);
pdx::Status<void> DeleteGlobalBuffer(DvrGlobalBufferKey key);
diff --git a/libs/vr/libdisplay/include/private/dvr/display_protocol.h b/libs/vr/libdisplay/include/private/dvr/display_protocol.h
index 3786d1d..9f4cc4a 100644
--- a/libs/vr/libdisplay/include/private/dvr/display_protocol.h
+++ b/libs/vr/libdisplay/include/private/dvr/display_protocol.h
@@ -191,7 +191,8 @@
enum class ConfigFileType : uint32_t {
kLensMetrics,
kDeviceMetrics,
- kDeviceConfiguration
+ kDeviceConfiguration,
+ kDeviceEdid
};
struct DisplayProtocol {
@@ -210,6 +211,7 @@
kOpGetSurfaceInfo,
kOpCreateQueue,
kOpSetAttributes,
+ kOpGetDisplayIdentificationPort,
};
// Aliases.
@@ -220,6 +222,8 @@
PDX_REMOTE_METHOD(GetMetrics, kOpGetMetrics, Metrics(Void));
PDX_REMOTE_METHOD(GetConfigurationData, kOpGetConfigurationData,
std::string(ConfigFileType config_type));
+ PDX_REMOTE_METHOD(GetDisplayIdentificationPort,
+ kOpGetDisplayIdentificationPort, uint8_t(Void));
PDX_REMOTE_METHOD(SetupGlobalBuffer, kOpSetupGlobalBuffer,
LocalNativeBufferHandle(DvrGlobalBufferKey key, size_t size,
uint64_t usage));
diff --git a/libs/vr/libdvr/include/dvr/dvr_api.h b/libs/vr/libdvr/include/dvr/dvr_api.h
index e383bb2..b7abb99 100644
--- a/libs/vr/libdvr/include/dvr/dvr_api.h
+++ b/libs/vr/libdvr/include/dvr/dvr_api.h
@@ -85,6 +85,8 @@
DVR_CONFIGURATION_DATA_DEVICE_METRICS = 1,
// Request the per device configuration data file.
DVR_CONFIGURATION_DATA_DEVICE_CONFIG = 2,
+ // Request the edid data for the display.
+ DVR_CONFIGURATION_DATA_DEVICE_EDID = 3,
};
// dvr_display_manager.h
diff --git a/libs/vr/libpdx/encoder_performance_test.cpp b/libs/vr/libpdx/encoder_performance_test.cpp
index b7d94b3..7b477c4 100644
--- a/libs/vr/libpdx/encoder_performance_test.cpp
+++ b/libs/vr/libpdx/encoder_performance_test.cpp
@@ -158,12 +158,12 @@
size_t iterations,
ResetFunc* write_reset,
void* reset_data, size_t data_size) {
- std::vector<uint8_t> dummy_data(data_size);
+ std::vector<uint8_t> fake_data(data_size);
auto start = std::chrono::high_resolution_clock::now();
for (size_t i = 0; i < iterations; i++) {
write_reset(reset_data);
- memcpy(writer->GetNextWriteBufferSection(dummy_data.size()),
- dummy_data.data(), dummy_data.size());
+ memcpy(writer->GetNextWriteBufferSection(fake_data.size()),
+ fake_data.data(), fake_data.size());
}
auto stop = std::chrono::high_resolution_clock::now();
return stop - start;
@@ -177,17 +177,17 @@
MessageReader* reader, MessageWriter* writer, size_t iterations,
ResetFunc* read_reset, ResetFunc* write_reset, void* reset_data,
size_t data_size) {
- std::vector<uint8_t> dummy_data(data_size);
+ std::vector<uint8_t> fake_data(data_size);
write_reset(reset_data);
- memcpy(writer->GetNextWriteBufferSection(dummy_data.size()),
- dummy_data.data(), dummy_data.size());
+ memcpy(writer->GetNextWriteBufferSection(fake_data.size()), fake_data.data(),
+ fake_data.size());
auto start = std::chrono::high_resolution_clock::now();
for (size_t i = 0; i < iterations; i++) {
read_reset(reset_data);
auto section = reader->GetNextReadBufferSection();
- memcpy(dummy_data.data(), section.first, dummy_data.size());
+ memcpy(fake_data.data(), section.first, fake_data.size());
reader->ConsumeReadBufferSectionData(
- AdvancePointer(section.first, dummy_data.size()));
+ AdvancePointer(section.first, fake_data.size()));
}
auto stop = std::chrono::high_resolution_clock::now();
return stop - start;
diff --git a/libs/vr/libpdx/private/pdx/rpc/macros.h b/libs/vr/libpdx/private/pdx/rpc/macros.h
index aeae9d3..99325b5 100644
--- a/libs/vr/libpdx/private/pdx/rpc/macros.h
+++ b/libs/vr/libpdx/private/pdx/rpc/macros.h
@@ -28,7 +28,7 @@
// Clears any remaining contents wrapped in parentheses.
#define _PDX_CLEAR(...)
-// Introduces a first dummy argument and _PDX_CLEAR as second argument.
+// Introduces a first stub argument and _PDX_CLEAR as second argument.
#define _PDX_CLEAR_IF_LAST() _, _PDX_CLEAR
// Returns the first argument of a list.
@@ -45,7 +45,7 @@
// Returns next_func if the next element is not (), or _PDX_CLEAR
// otherwise.
//
-// _PDX_CLEAR_IF_LAST inserts an extra first dummy argument if peek is ().
+// _PDX_CLEAR_IF_LAST inserts an extra first stub argument if peek is ().
#define _PDX_NEXT_FUNC(next_element, next_func) \
_PDX_EXPAND_NEXT_FUNC(_PDX_CLEAR_IF_LAST next_element, next_func)
diff --git a/libs/vr/libpdx/service_dispatcher.cpp b/libs/vr/libpdx/service_dispatcher.cpp
index b112fa3..ba0d69c 100644
--- a/libs/vr/libpdx/service_dispatcher.cpp
+++ b/libs/vr/libpdx/service_dispatcher.cpp
@@ -92,9 +92,9 @@
if (thread_count_ > 0)
return -EBUSY;
- epoll_event dummy; // See BUGS in man 2 epoll_ctl.
+ epoll_event ee; // See BUGS in man 2 epoll_ctl.
if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_DEL, service->endpoint()->epoll_fd(),
- &dummy) < 0) {
+ &ee) < 0) {
ALOGE("Failed to remove service from dispatcher because: %s\n",
strerror(errno));
return -errno;
diff --git a/libs/vr/libpdx_uds/service_endpoint.cpp b/libs/vr/libpdx_uds/service_endpoint.cpp
index 9bc70ea..810eb19 100644
--- a/libs/vr/libpdx_uds/service_endpoint.cpp
+++ b/libs/vr/libpdx_uds/service_endpoint.cpp
@@ -334,8 +334,8 @@
int channel_fd = iter->second.data_fd.Get();
Status<void> status;
- epoll_event dummy; // See BUGS in man 2 epoll_ctl.
- if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_DEL, channel_fd, &dummy) < 0) {
+ epoll_event ee; // See BUGS in man 2 epoll_ctl.
+ if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_DEL, channel_fd, &ee) < 0) {
status.SetError(errno);
ALOGE(
"Endpoint::CloseChannelLocked: Failed to remove channel from endpoint: "
diff --git a/libs/vr/libvrflinger/Android.bp b/libs/vr/libvrflinger/Android.bp
index 12ce74b..abc64bd 100644
--- a/libs/vr/libvrflinger/Android.bp
+++ b/libs/vr/libvrflinger/Android.bp
@@ -35,11 +35,12 @@
]
sharedLibraries = [
- "android.frameworks.vr.composer@1.0",
+ "android.frameworks.vr.composer@2.0",
"android.hardware.graphics.allocator@2.0",
"android.hardware.graphics.composer@2.1",
"android.hardware.graphics.composer@2.2",
"android.hardware.graphics.composer@2.3",
+ "android.hardware.graphics.composer@2.4",
"libbinder",
"libbase",
"libbufferhubqueue",
@@ -65,6 +66,7 @@
"android.hardware.graphics.composer@2.1-command-buffer",
"android.hardware.graphics.composer@2.2-command-buffer",
"android.hardware.graphics.composer@2.3-command-buffer",
+ "android.hardware.graphics.composer@2.4-command-buffer",
"libdvr_headers",
"libsurfaceflinger_headers",
]
diff --git a/libs/vr/libvrflinger/display_service.cpp b/libs/vr/libvrflinger/display_service.cpp
index 87162c0..582fed3 100644
--- a/libs/vr/libvrflinger/display_service.cpp
+++ b/libs/vr/libvrflinger/display_service.cpp
@@ -18,6 +18,8 @@
#include <private/dvr/trusted_uids.h>
#include <private/dvr/types.h>
+#include "DisplayHardware/DisplayIdentification.h"
+
using android::dvr::display::DisplayProtocol;
using android::pdx::Channel;
using android::pdx::ErrorStatus;
@@ -139,6 +141,11 @@
*this, &DisplayService::OnGetConfigurationData, message);
return {};
+ case DisplayProtocol::GetDisplayIdentificationPort::Opcode:
+ DispatchRemoteMethod<DisplayProtocol::GetDisplayIdentificationPort>(
+ *this, &DisplayService::OnGetDisplayIdentificationPort, message);
+ return {};
+
case DisplayProtocol::CreateSurface::Opcode:
DispatchRemoteMethod<DisplayProtocol::CreateSurface>(
*this, &DisplayService::OnCreateSurface, message);
@@ -194,6 +201,7 @@
pdx::Status<std::string> DisplayService::OnGetConfigurationData(
pdx::Message& /*message*/, display::ConfigFileType config_type) {
std::string property_name;
+ DisplayIdentificationData display_identification_data;
switch (config_type) {
case display::ConfigFileType::kLensMetrics:
property_name = kDvrLensMetricsProperty;
@@ -204,6 +212,14 @@
case display::ConfigFileType::kDeviceConfiguration:
property_name = kDvrDeviceConfigProperty;
break;
+ case display::ConfigFileType::kDeviceEdid:
+ display_identification_data =
+ hardware_composer_.GetCurrentDisplayIdentificationData();
+ if (display_identification_data.size() == 0) {
+ return ErrorStatus(ENOENT);
+ }
+ return std::string(display_identification_data.begin(),
+ display_identification_data.end());
default:
return ErrorStatus(EINVAL);
}
@@ -220,6 +236,11 @@
return std::move(data);
}
+pdx::Status<uint8_t> DisplayService::OnGetDisplayIdentificationPort(
+ pdx::Message& /*message*/) {
+ return hardware_composer_.GetCurrentDisplayPort();
+}
+
// Creates a new DisplaySurface and associates it with this channel. This may
// only be done once per channel.
Status<display::SurfaceInfo> DisplayService::OnCreateSurface(
diff --git a/libs/vr/libvrflinger/display_service.h b/libs/vr/libvrflinger/display_service.h
index e0f2edd..89f1eae 100644
--- a/libs/vr/libvrflinger/display_service.h
+++ b/libs/vr/libvrflinger/display_service.h
@@ -80,6 +80,7 @@
pdx::Status<display::Metrics> OnGetMetrics(pdx::Message& message);
pdx::Status<std::string> OnGetConfigurationData(
pdx::Message& message, display::ConfigFileType config_type);
+ pdx::Status<uint8_t> OnGetDisplayIdentificationPort(pdx::Message& message);
pdx::Status<display::SurfaceInfo> OnCreateSurface(
pdx::Message& message, const display::SurfaceAttributes& attributes);
pdx::Status<BorrowedNativeBufferHandle> OnSetupGlobalBuffer(
diff --git a/libs/vr/libvrflinger/epoll_event_dispatcher.cpp b/libs/vr/libvrflinger/epoll_event_dispatcher.cpp
index 1cf5f17..0d5eb80 100644
--- a/libs/vr/libvrflinger/epoll_event_dispatcher.cpp
+++ b/libs/vr/libvrflinger/epoll_event_dispatcher.cpp
@@ -68,8 +68,8 @@
ALOGD_IF(TRACE, "EpollEventDispatcher::RemoveEventHandler: fd=%d", fd);
std::lock_guard<std::mutex> lock(lock_);
- epoll_event dummy; // See BUGS in man 2 epoll_ctl.
- if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_DEL, fd, &dummy) < 0) {
+ epoll_event ee; // See BUGS in man 2 epoll_ctl.
+ if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_DEL, fd, &ee) < 0) {
const int error = errno;
ALOGE("Failed to remove fd from epoll set because: %s", strerror(error));
return pdx::ErrorStatus(error);
diff --git a/libs/vr/libvrflinger/hardware_composer.cpp b/libs/vr/libvrflinger/hardware_composer.cpp
index 57a77cf..70f303b 100644
--- a/libs/vr/libvrflinger/hardware_composer.cpp
+++ b/libs/vr/libvrflinger/hardware_composer.cpp
@@ -50,8 +50,6 @@
const char kRightEyeOffsetProperty[] = "dvr.right_eye_offset_ns";
-const char kUseExternalDisplayProperty[] = "persist.vr.use_external_display";
-
// Surface flinger uses "VSYNC-sf" and "VSYNC-app" for its version of these
// events. Name ours similarly.
const char kVsyncTraceEventName[] = "VSYNC-vrflinger";
@@ -138,6 +136,20 @@
composer_callback_->SetVsyncService(nullptr);
}
+void HardwareComposer::UpdateEdidData(Hwc2::Composer* composer,
+ hwc2_display_t hw_id) {
+ const auto error = composer->getDisplayIdentificationData(
+ hw_id, &display_port_, &display_identification_data_);
+ if (error != android::hardware::graphics::composer::V2_1::Error::NONE) {
+ if (error !=
+ android::hardware::graphics::composer::V2_1::Error::UNSUPPORTED) {
+ ALOGI("hardware_composer: identification data error\n");
+ } else {
+ ALOGI("hardware_composer: identification data unsupported\n");
+ }
+ }
+}
+
bool HardwareComposer::Initialize(
Hwc2::Composer* composer, hwc2_display_t primary_display_id,
RequestDisplayCallback request_display_callback) {
@@ -163,6 +175,8 @@
"HardwareComposer: Failed to create interrupt event fd : %s",
strerror(errno));
+ UpdateEdidData(composer, primary_display_id);
+
post_thread_ = std::thread(&HardwareComposer::PostThread, this);
initialized_ = true;
@@ -932,18 +946,9 @@
external_display_ = GetDisplayParams(composer_.get(),
*displays.external_display, /*is_primary*/ false);
- if (property_get_bool(kUseExternalDisplayProperty, false)) {
- ALOGI("External display connected. Switching to external display.");
- target_display_ = &(*external_display_);
- target_display_changed = true;
- } else {
- ALOGI("External display connected, but sysprop %s is unset, so"
- " using primary display.", kUseExternalDisplayProperty);
- if (was_using_external_display) {
- target_display_ = &primary_display_;
- target_display_changed = true;
- }
- }
+ ALOGI("External display connected. Switching to external display.");
+ target_display_ = &(*external_display_);
+ target_display_changed = true;
} else {
// External display was disconnected
external_display_ = std::nullopt;
@@ -966,6 +971,9 @@
EnableDisplay(*external_display_, false);
}
+ // Update the cached edid data for the current display.
+ UpdateEdidData(composer_.get(), target_display_->id);
+
// Turn the new target display on.
EnableDisplay(*target_display_, true);
@@ -1160,6 +1168,26 @@
return Void();
}
+Return<void> HardwareComposer::ComposerCallback::onVsync_2_4(
+ Hwc2::Display /*display*/, int64_t /*timestamp*/,
+ Hwc2::VsyncPeriodNanos /*vsyncPeriodNanos*/) {
+ LOG_ALWAYS_FATAL("Unexpected onVsync_2_4 callback");
+ return Void();
+}
+
+Return<void> HardwareComposer::ComposerCallback::onVsyncPeriodTimingChanged(
+ Hwc2::Display /*display*/,
+ const Hwc2::VsyncPeriodChangeTimeline& /*updatedTimeline*/) {
+ LOG_ALWAYS_FATAL("Unexpected onVsyncPeriodTimingChanged callback");
+ return Void();
+}
+
+Return<void> HardwareComposer::ComposerCallback::onSeamlessPossible(
+ Hwc2::Display /*display*/) {
+ LOG_ALWAYS_FATAL("Unexpected onSeamlessPossible callback");
+ return Void();
+}
+
void HardwareComposer::ComposerCallback::SetVsyncService(
const sp<VsyncService>& vsync_service) {
std::lock_guard<std::mutex> lock(mutex_);
diff --git a/libs/vr/libvrflinger/hardware_composer.h b/libs/vr/libvrflinger/hardware_composer.h
index db0d6a7..bfce10b 100644
--- a/libs/vr/libvrflinger/hardware_composer.h
+++ b/libs/vr/libvrflinger/hardware_composer.h
@@ -25,6 +25,7 @@
#include <private/dvr/shared_buffer_helpers.h>
#include <private/dvr/vsync_service.h>
+#include "DisplayHardware/DisplayIdentification.h"
#include "acquired_buffer.h"
#include "display_surface.h"
@@ -334,6 +335,14 @@
int OnNewGlobalBuffer(DvrGlobalBufferKey key, IonBuffer& ion_buffer);
void OnDeletedGlobalBuffer(DvrGlobalBufferKey key);
+ // Gets the edid data for the current active display (internal or external)
+ DisplayIdentificationData GetCurrentDisplayIdentificationData() {
+ return display_identification_data_;
+ }
+
+ // Gets the edid port for the current active display (internal or external)
+ uint8_t GetCurrentDisplayPort() { return display_port_; }
+
private:
DisplayParams GetDisplayParams(Hwc2::Composer* composer,
hwc2_display_t display, bool is_primary);
@@ -366,6 +375,13 @@
hardware::Return<void> onRefresh(Hwc2::Display display) override;
hardware::Return<void> onVsync(Hwc2::Display display,
int64_t timestamp) override;
+ hardware::Return<void> onVsync_2_4(
+ Hwc2::Display display, int64_t timestamp,
+ Hwc2::VsyncPeriodNanos vsyncPeriodNanos) override;
+ hardware::Return<void> onVsyncPeriodTimingChanged(
+ Hwc2::Display display,
+ const Hwc2::VsyncPeriodChangeTimeline& updatedTimeline) override;
+ hardware::Return<void> onSeamlessPossible(Hwc2::Display display) override;
bool GotFirstHotplug() { return got_first_hotplug_; }
void SetVsyncService(const sp<VsyncService>& vsync_service);
@@ -544,6 +560,11 @@
bool vsync_trace_parity_ = false;
sp<VsyncService> vsync_service_;
+ // Edid section.
+ void UpdateEdidData(Hwc2::Composer* composer, hwc2_display_t hw_id);
+ DisplayIdentificationData display_identification_data_;
+ uint8_t display_port_;
+
static constexpr int kPostThreadInterrupted = 1;
HardwareComposer(const HardwareComposer&) = delete;
diff --git a/libs/vr/libvrflinger/tests/Android.bp b/libs/vr/libvrflinger/tests/Android.bp
index 410e234..7fafd3b 100644
--- a/libs/vr/libvrflinger/tests/Android.bp
+++ b/libs/vr/libvrflinger/tests/Android.bp
@@ -11,6 +11,7 @@
"libutils",
"libnativewindow",
"libpdx_default_transport",
+ "libSurfaceFlingerProp",
]
static_libs = [
@@ -32,5 +33,6 @@
"-Wall",
"-Werror",
],
+ header_libs: ["libsurfaceflinger_headers"],
name: "vrflinger_test",
}
diff --git a/libs/vr/libvrflinger/tests/vrflinger_test.cpp b/libs/vr/libvrflinger/tests/vrflinger_test.cpp
index 3b449f2..ac44f74 100644
--- a/libs/vr/libvrflinger/tests/vrflinger_test.cpp
+++ b/libs/vr/libvrflinger/tests/vrflinger_test.cpp
@@ -1,3 +1,4 @@
+#include <SurfaceFlingerProperties.h>
#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
#include <android/hardware/configstore/1.1/types.h>
#include <android/hardware_buffer.h>
@@ -144,9 +145,7 @@
// Exit immediately if the device doesn't support vr flinger. This ConfigStore
// check is the same mechanism used by surface flinger to decide if it should
// initialize vr flinger.
- bool vr_flinger_enabled =
- getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::useVrFlinger>(
- false);
+ bool vr_flinger_enabled = android::sysprop::use_vr_flinger(false);
if (!vr_flinger_enabled) {
return;
}