diff options
| author | 2020-09-10 17:22:18 +0000 | |
|---|---|---|
| committer | 2020-09-10 17:22:18 +0000 | |
| commit | cdb6b16dec3a541b455be99d075004cb2f0a0cd7 (patch) | |
| tree | f7110d50445c67a337105034b1f2db3946a88fef /libs | |
| parent | 171cac1b603e4bb83412eb596d05a500af5d7a76 (diff) | |
| parent | ac07d0f5ab16bb9e8bbbabb589d1c7d36817baa9 (diff) | |
Merge "Merge Android R"
Diffstat (limited to 'libs')
296 files changed, 22054 insertions, 4883 deletions
diff --git a/libs/adbd_auth/adbd_auth.cpp b/libs/adbd_auth/adbd_auth.cpp index 5a0d3f6168..dae6eebaa5 100644 --- a/libs/adbd_auth/adbd_auth.cpp +++ b/libs/adbd_auth/adbd_auth.cpp @@ -83,28 +83,28 @@ public: 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 @@ public: 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 @@ public: } 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 @@ public: 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 @@ public: } 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 @@ public: } } 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ AdbdAuthContext* adbd_auth_new(AdbdAuthCallbacks* callbacks) { 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_notify_disconnect(AdbdAuthContext* ctx, uint64_t id) { 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 6ee3166e3a..8f834df62b 100644 --- a/libs/adbd_auth/include/adbd_auth.h +++ b/libs/adbd_auth/include/adbd_auth.h @@ -122,9 +122,23 @@ void adbd_auth_notify_disconnect(AdbdAuthContext* ctx, * @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 5857ecb98e..7584ca3f53 100644 --- a/libs/adbd_auth/libadbd_auth.map.txt +++ b/libs/adbd_auth/libadbd_auth.map.txt @@ -7,6 +7,7 @@ LIBADBD_AUTH { 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 75eb7a3848..9675a5321e 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -168,7 +168,9 @@ cc_library { 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 0a6685e14a..1c6b49135d 100644 --- a/libs/binder/AppOpsManager.cpp +++ b/libs/binder/AppOpsManager.cpp @@ -21,44 +21,34 @@ #include <utils/SystemClock.h> -namespace android { - -namespace { +#include <sys/types.h> -#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__) +#ifdef LOG_TAG +#undef LOG_TAG +#endif +#define LOG_TAG "AppOpsManager" -} // namespace +namespace android { -static String16 _appops("appops"); -static pthread_mutex_t gTokenMutex = PTHREAD_MUTEX_INITIALIZER; -static sp<IBinder> gToken; +static const sp<IBinder>& getClientId() { + static pthread_mutex_t gClientIdMutex = PTHREAD_MUTEX_INITIALIZER; + static sp<IBinder> gClientId; -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 @@ sp<IAppOpsService> AppOpsManager::getService() } 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 @@ int32_t AppOpsManager::checkAudioOpNoThrow(int32_t op, int32_t usage, int32_t ui 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 @@ int32_t AppOpsManager::permissionToOpCode(const String16& permission) { 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 0ce1dd59cf..b9eb281870 100644 --- a/libs/binder/IAppOpsCallback.cpp +++ b/libs/binder/IAppOpsCallback.cpp @@ -39,7 +39,7 @@ public: 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 @@ status_t BnAppOpsCallback::onTransact( 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 e5f0a11aa0..cd78866624 100644 --- a/libs/binder/IAppOpsService.cpp +++ b/libs/binder/IAppOpsService.cpp @@ -48,12 +48,17 @@ public: 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 @@ public: } 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 @@ public: } 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 @@ public: 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 @@ public: } 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 @@ status_t BnAppOpsService::onTransact( 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 @@ status_t BnAppOpsService::onTransact( 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 @@ status_t BnAppOpsService::onTransact( 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 @@ status_t BnAppOpsService::onTransact( 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 @@ status_t BnAppOpsService::onTransact( 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 84805ff3d6..d8b44f957d 100644 --- a/libs/binder/IMemory.cpp +++ b/libs/binder/IMemory.cpp @@ -150,10 +150,6 @@ void* IMemory::fastPointer(const sp<IBinder>& binder, ssize_t offset) const } 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 @@ void* IMemory::pointer() const { 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 038e6bf6ea..b21af960d2 100644 --- a/libs/binder/IUidObserver.cpp +++ b/libs/binder/IUidObserver.cpp @@ -56,13 +56,15 @@ public: 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 @@ status_t BnUidObserver::onTransact( 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 333b95badc..19f3606bfa 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -1063,12 +1063,22 @@ status_t Parcel::writeCString(const char* str) 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 @@ const char* Parcel::readCString() const 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 @@ void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize, 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 c110ff67b9..a53056560e 100644 --- a/libs/binder/ProcessState.cpp +++ b/libs/binder/ProcessState.cpp @@ -415,7 +415,9 @@ ProcessState::ProcessState(const char *driver) } } +#ifdef __ANDROID__ LOG_ALWAYS_FATAL_IF(mDriverFD < 0, "Binder driver '%s' could not be opened. Terminating.", driver); +#endif } ProcessState::~ProcessState() diff --git a/libs/binder/aidl/android/content/pm/IPackageChangeObserver.aidl b/libs/binder/aidl/android/content/pm/IPackageChangeObserver.aidl new file mode 100644 index 0000000000..6929a6cb49 --- /dev/null +++ b/libs/binder/aidl/android/content/pm/IPackageChangeObserver.aidl @@ -0,0 +1,28 @@ +/* + * 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; + +import android.content.pm.PackageChangeEvent; + +/** + * 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 a7a7292125..dc8d74c052 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 @@ interface IPackageManagerNative { * 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 0000000000..e30e9072fc --- /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 0000000000..d2b4d52acc --- /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 0000000000..52c730cfb8 --- /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/binder/fuzzer/binder.h b/libs/binder/fuzzer/binder.h new file mode 100644 index 0000000000..b224ef49a1 --- /dev/null +++ b/libs/binder/fuzzer/binder.h @@ -0,0 +1,22 @@ +/* + * 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 <binder/Parcel.h> +#include <vector> + +#include "parcel_fuzzer.h" + +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 0000000000..29da8f7537 --- /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 0000000000..622cafc7df --- /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 0000000000..0fec393e55 --- /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/binder/fuzzer/hwbinder.h b/libs/binder/fuzzer/hwbinder.h new file mode 100644 index 0000000000..a6c66beb44 --- /dev/null +++ b/libs/binder/fuzzer/hwbinder.h @@ -0,0 +1,22 @@ +/* + * 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 <hwbinder/Parcel.h> +#include <vector> + +#include "parcel_fuzzer.h" + +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 0000000000..6657edb46d --- /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/binder/fuzzer/parcel_fuzzer.h b/libs/binder/fuzzer/parcel_fuzzer.h new file mode 100644 index 0000000000..10cf17c328 --- /dev/null +++ b/libs/binder/fuzzer/parcel_fuzzer.h @@ -0,0 +1,18 @@ +/* + * 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. + */ + +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 0000000000..479f406d8c --- /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 0000000000..aa504d29f2 --- /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 b19cde75b6..6d04f13211 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 @@ public: 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 @@ public: 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 b74c623e44..a4a20c8b10 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 @@ public: 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 @@ public: 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 @@ public: } // 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 cabfc7ff8e..7116154951 100644 --- a/libs/binder/include/binder/IInterface.h +++ b/libs/binder/include/binder/IInterface.h @@ -259,7 +259,6 @@ constexpr const char* const kManualInterfaces[] = { "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 @@ constexpr const char* const kManualInterfaces[] = { "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 98e92c4441..1a36eb0436 100644 --- a/libs/binder/include/binder/IMemory.h +++ b/libs/binder/include/binder/IMemory.h @@ -76,13 +76,34 @@ public: // 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 09e50a9de8..d0703901d7 100644 --- a/libs/binder/include/binder/IUidObserver.h +++ b/libs/binder/include/binder/IUidObserver.h @@ -34,7 +34,8 @@ public: 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 97f1aeeb7b..b6cfb8ec0f 100644 --- a/libs/binder/include/binder/Parcel.h +++ b/libs/binder/include/binder/Parcel.h @@ -119,6 +119,7 @@ public: 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 @@ public: 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 ab009143f7..4fd06573b7 100644 --- a/libs/binder/ndk/Android.bp +++ b/libs/binder/ndk/Android.bp @@ -29,7 +29,7 @@ cc_defaults { }, } -cc_library_shared { +cc_library { name: "libbinder_ndk", defaults: ["libbinder_ndk_host_user"], @@ -73,6 +73,12 @@ cc_library_shared { ], 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 ae761d8bab..a03835b34c 100644 --- a/libs/binder/tests/Android.bp +++ b/libs/binder/tests/Android.bp @@ -27,7 +27,9 @@ cc_test { 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 @@ cc_test { "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 0000000000..369b55dc22 --- /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 f3ed6a613c..8cc3054f80 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 @@ TEST_F(BinderDriverInterfaceTest, RequestDeathNotification) { } 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 565338bac3..145c09940b 100644 --- a/libs/binder/tests/binderLibTest.cpp +++ b/libs/binder/tests/binderLibTest.cpp @@ -33,6 +33,8 @@ #include <sys/epoll.h> #include <sys/prctl.h> +#include "binderAbiHelper.h" + #define ARRAY_SIZE(array) (sizeof array / sizeof array[0]) using namespace android; @@ -81,6 +83,7 @@ enum BinderLibTestTranscationCode { BINDER_LIB_TEST_NOP_TRANSACTION_WAIT, BINDER_LIB_TEST_GETPID, BINDER_LIB_TEST_ECHO_VECTOR, + BINDER_LIB_TEST_REJECT_BUF, }; pid_t start_server_process(int arg2, bool usePoll = false) @@ -1084,6 +1087,34 @@ TEST_F(BinderLibTest, VectorSent) { 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: @@ -1382,6 +1413,9 @@ class BinderLibTestService : public BBinder reply->writeUint64Vector(vector); return NO_ERROR; } + case BINDER_LIB_TEST_REJECT_BUF: { + return data.objectsCount() == 0 ? BAD_VALUE : NO_ERROR; + } default: return UNKNOWN_TRANSACTION; }; @@ -1415,6 +1449,9 @@ int run_server(int index, int readypipefd, bool usePoll) */ 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 @@ -1491,6 +1528,8 @@ int run_server(int index, int readypipefd, bool usePoll) } 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 0000000000..bab267466c --- /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 0000000000..b1896fab6f --- /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 0000000000..84bceba374 --- /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 e56c79989d..5e785b67c8 100644 --- a/libs/cputimeinstate/cputimeinstate.cpp +++ b/libs/cputimeinstate/cputimeinstate.cpp @@ -489,7 +489,7 @@ bool clearUidTimes(uint32_t uid) { 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 32baa5f016..2e144084c1 100644 --- a/libs/dumputils/dump_utils.cpp +++ b/libs/dumputils/dump_utils.cpp @@ -35,7 +35,7 @@ static const char* native_processes_to_dump[] = { "/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 @@ static const char* hal_interfaces_to_dump[] { "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 0000000000..66fb295a19 --- /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 0000000000..53c68b7230 --- /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 0000000000..84448839a1 --- /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 0000000000..dc22385883 --- /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 0000000000..a3fe2d9db7 --- /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 0000000000..8779fa232c --- /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 0000000000..479f406d8c --- /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 0000000000..aa504d29f2 --- /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 0000000000..1a7c2c946a --- /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 0000000000..b939c1db59 --- /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 0000000000..89cbf4ac4a --- /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 56521bf2b4..642c5f2cc0 100644 --- a/libs/graphicsenv/Android.bp +++ b/libs/graphicsenv/Android.bp @@ -32,5 +32,9 @@ cc_library_shared { "libutils", ], + header_libs: [ + "libnativeloader-headers", + ], + export_include_dirs: ["include"], } diff --git a/libs/graphicsenv/GpuStatsInfo.cpp b/libs/graphicsenv/GpuStatsInfo.cpp index 4a801bec38..f2d0943e86 100644 --- a/libs/graphicsenv/GpuStatsInfo.cpp +++ b/libs/graphicsenv/GpuStatsInfo.cpp @@ -86,6 +86,8 @@ status_t GpuStatsAppInfo::writeToParcel(Parcel* parcel) const { 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 @@ status_t GpuStatsAppInfo::readFromParcel(const Parcel* parcel) { 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 @@ std::string GpuStatsAppInfo::toString() const { 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 5d51d2b253..3d0f8bbb11 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::setDriverPathAndSphalLibraries(const std::string path, 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 @@ void GraphicsEnv::setGpuStats(const std::string& driverPackageName, 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::setDriverToLoad(GraphicsEnv::Driver driver) { } } -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 @@ void GraphicsEnv::setDriverLoaded(GraphicsEnv::Api api, bool isDriverLoaded, } 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 @@ static sp<IGpuService> getGpuService() { 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::setTargetStats(const Stats stats, const uint64_t value) { } } -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 @@ void GraphicsEnv::sendGpuStatsLocked(GraphicsEnv::Api api, bool isDriverLoaded, 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 @@ void GraphicsEnv::sendGpuStatsLocked(GraphicsEnv::Api api, bool isDriverLoaded, } } +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 @@ android_namespace_t* GraphicsEnv::getDriverNamespace() { } 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 @@ android_namespace_t* GraphicsEnv::getDriverNamespace() { 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 db16f3cfa6..fa25c5516d 100644 --- a/libs/graphicsenv/IGpuService.cpp +++ b/libs/graphicsenv/IGpuService.cpp @@ -27,11 +27,11 @@ class BpGpuService : public BpInterface<IGpuService> { 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,61 +48,38 @@ public: 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; - + void setTargetStats(const std::string& appPackageName, const uint64_t driverVersionCode, + const GpuStatsInfo::Stats stats, const uint64_t value) override { 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; + data.writeInterfaceToken(IGpuService::getInterfaceDescriptor()); - int32_t result = 0; - if ((status = reply.readInt32(&result)) != OK) return status; - if (result != OK) return result; + data.writeUtf8AsUtf16(appPackageName); + data.writeUint64(driverVersionCode); + data.writeInt32(static_cast<int32_t>(stats)); + data.writeUint64(value); - outStats->clear(); - return reply.readParcelableVector(outStats); + remote()->transact(BnGpuService::SET_TARGET_STATS, data, &reply, IBinder::FLAG_ONEWAY); } - virtual status_t getGpuStatsAppInfo(std::vector<GpuStatsAppInfo>* outStats) const { - if (!outStats) return UNEXPECTED_NULL; - + void setUpdatableDriverPath(const std::string& driverPath) override { 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; + data.writeInterfaceToken(IGpuService::getInterfaceDescriptor()); + data.writeUtf8AsUtf16(driverPath); - outStats->clear(); - return reply.readParcelableVector(outStats); + remote()->transact(BnGpuService::SET_UPDATABLE_DRIVER_PATH, data, &reply, + IBinder::FLAG_ONEWAY); } - virtual void setTargetStats(const std::string& appPackageName, const uint64_t driverVersionCode, - const GraphicsEnv::Stats stats, const uint64_t value) { + std::string getUpdatableDriverPath() override { Parcel data, reply; data.writeInterfaceToken(IGpuService::getInterfaceDescriptor()); - data.writeUtf8AsUtf16(appPackageName); - data.writeUint64(driverVersionCode); - data.writeInt32(static_cast<int32_t>(stats)); - data.writeUint64(value); - - remote()->transact(BnGpuService::SET_TARGET_STATS, data, &reply, IBinder::FLAG_ONEWAY); + status_t error = remote()->transact(BnGpuService::GET_UPDATABLE_DRIVER_PATH, data, &reply); + std::string driverPath; + if (error == OK) { + error = reply.readUtf8FromUtf16(&driverPath); + } + return driverPath; } }; @@ -145,37 +122,11 @@ status_t BnGpuService::onTransact(uint32_t code, const Parcel& data, Parcel* rep 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 @@ status_t BnGpuService::onTransact(uint32_t code, const Parcel& data, Parcel* rep 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 edcccfea4a..9aba69fd07 100644 --- a/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h +++ b/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h @@ -70,6 +70,53 @@ public: 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 227b4587cc..22a2332589 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 @@ struct NativeLoaderNamespace; 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 @@ public: // 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 @@ public: // 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 @@ public: // /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 b8d0bd173c..2d59fa0165 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 @@ public: 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; - - // get GPU global stats from GpuStats module. - virtual status_t getGpuStatsGlobalInfo(std::vector<GpuStatsGlobalInfo>* outStats) const = 0; + const GpuStatsInfo::Stats stats, const uint64_t value = 0) = 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 9fc16ba930..4a4510e047 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -41,17 +41,26 @@ cc_library_shared { 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 @@ cc_library_shared { "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 @@ cc_library_shared { "libdvr_headers", "libpdx_headers", ], + + aidl: { + export_aidl_headers: true, + } } // Used by media codec services exclusively as a static lib for @@ -115,9 +138,37 @@ cc_library_static { 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 @@ cc_defaults { }, }, - 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 @@ cc_defaults { "android.hardware.graphics.common@1.2", "android.hidl.token@1.0-utils", "libbase", - "libbinder", "libcutils", "libEGL", "libGLESv2", @@ -206,7 +228,6 @@ cc_defaults { ], export_shared_lib_headers: [ - "libbinder", "libEGL", "libnativewindow", "libui", @@ -226,4 +247,21 @@ cc_defaults { ], } +// 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 0000000000..56591bdc63 --- /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 4be014fbb1..1f71e23b4d 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 @@ String8 BufferHubProducer::getConsumerName() const { // 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 5fb3f0b80f..c1d92a2a7f 100644 --- a/libs/gui/BufferQueue.cpp +++ b/libs/gui/BufferQueue.cpp @@ -43,6 +43,27 @@ void BufferQueue::ProxyConsumerListener::onDisconnect() { } } +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 3a7cb44450..da6143c59f 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 @@ status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer, 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::getSidebandStream(sp<NativeHandle>* outStream) con 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 @@ status_t BufferQueueConsumer::dumpState(const String8& prefix, String8* outResul 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 0264bd24a6..5023b6bb81 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 @@ static uint64_t getUniqueId() { 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 @@ void BufferQueueCore::dumpState(const String8& prefix, String8* outResult) const 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("\n%sFIFO(%zu):\n", prefix.string(), mQueue.size()); + 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("%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 4f8eb6b5e8..a7cf39add9 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 @@ status_t BufferQueueProducer::dequeueBuffer(int* outSlot, sp<android::Fence>* ou 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 @@ status_t BufferQueueProducer::dequeueBuffer(int* outSlot, sp<android::Fence>* ou 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 @@ status_t BufferQueueProducer::dequeueBuffer(int* outSlot, sp<android::Fence>* ou 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 @@ status_t BufferQueueProducer::detachBuffer(int slot) { 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 @@ status_t BufferQueueProducer::queueBuffer(int slot, 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 @@ status_t BufferQueueProducer::cancelBuffer(int slot, const sp<Fence>& fence) { 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 @@ int BufferQueueProducer::query(int what, int *outValue) { 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 @@ status_t BufferQueueProducer::connect(const sp<IProducerListener>& listener, 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 @@ status_t BufferQueueProducer::connect(const sp<IProducerListener>& listener, } mCore->mLinkedToDeath = listener; } +#endif mCore->mConnectedProducerListener = listener; mCore->mBufferReleasedCbEnabled = listener->needsReleaseNotify(); } @@ -1289,6 +1335,7 @@ status_t BufferQueueProducer::disconnect(int api, DisconnectMode mode) { 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 @@ status_t BufferQueueProducer::disconnect(int api, DisconnectMode mode) { token->unlinkToDeath( static_cast<IBinder::DeathRecipient*>(this)); } +#endif mCore->mSharedBufferSlot = BufferQueueCore::INVALID_BUFFER_SLOT; mCore->mLinkedToDeath = nullptr; @@ -1306,6 +1354,7 @@ status_t BufferQueueProducer::disconnect(int api, DisconnectMode mode) { 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 @@ status_t BufferQueueProducer::setSidebandStream(const sp<NativeHandle>& stream) 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 @@ void BufferQueueProducer::allocateBuffers(uint32_t width, uint32_t height, 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 @@ void BufferQueueProducer::allocateBuffers(uint32_t width, uint32_t height, 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 @@ status_t BufferQueueProducer::getConsumerUsage(uint64_t* outUsage) const { 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 c13030b1ed..976c9b9d50 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 abd9921fa9..9f91d9d3aa 100644 --- a/libs/gui/ConsumerBase.cpp +++ b/libs/gui/ConsumerBase.cpp @@ -101,6 +101,48 @@ void ConsumerBase::freeBufferLocked(int slotIndex) { 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 ab6f36444a..5762dabc55 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 @@ private: 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 0000000000..b33bc9e556 --- /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 b8faa2df4c..1fed509003 100644 --- a/libs/gui/DisplayEventReceiver.cpp +++ b/libs/gui/DisplayEventReceiver.cpp @@ -79,6 +79,13 @@ status_t DisplayEventReceiver::requestNextVsync() { 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 3215eca50f..e2ea3f9ab1 100644 --- a/libs/gui/FrameTimestamps.cpp +++ b/libs/gui/FrameTimestamps.cpp @@ -364,6 +364,10 @@ void ConsumerFrameEventHistory::onDisconnect() { 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 8199c98582..30d19e3af4 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 @@ status_t GLConsumer::releaseTexImage() { 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 add3ef0458..058cd9aa3b 100644 --- a/libs/gui/HdrMetadata.cpp +++ b/libs/gui/HdrMetadata.cpp @@ -28,8 +28,8 @@ size_t HdrMetadata::getFlattenedSize() const { 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 @@ status_t HdrMetadata::flatten(void* buffer, size_t size) const { 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 @@ status_t HdrMetadata::unflatten(void const* buffer, size_t size) { 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 85ac304ab8..f3bd90cffb 100644 --- a/libs/gui/IConsumerListener.cpp +++ b/libs/gui/IConsumerListener.cpp @@ -28,7 +28,10 @@ enum class Tag : uint32_t { 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 @@ public: 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 @@ public: // 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 @@ status_t BnConsumerListener::onTransact(uint32_t code, const Parcel& data, Parce 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 c0e246fa15..aa74bfd3f8 100644 --- a/libs/gui/IDisplayEventConnection.cpp +++ b/libs/gui/IDisplayEventConnection.cpp @@ -26,7 +26,8 @@ enum class Tag : uint32_t { 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 @@ public: 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 @@ status_t BnDisplayEventConnection::onTransact(uint32_t code, const Parcel& data, 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 0e03b7d393..ad00939976 100644 --- a/libs/gui/IGraphicBufferProducer.cpp +++ b/libs/gui/IGraphicBufferProducer.cpp @@ -73,6 +73,7 @@ enum { GET_UNIQUE_ID, GET_CONSUMER_USAGE, SET_LEGACY_BUFFER_DROP, + SET_AUTO_PREROTATION, }; class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer> @@ -547,6 +548,17 @@ public: } 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 @@ public: 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 @@ status_t IGraphicBufferProducer::setLegacyBufferDrop(bool drop) { 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 @@ status_t BnGraphicBufferProducer::onTransact( 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 @@ IGraphicBufferProducer::QueueBufferInput::QueueBufferInput(const Parcel& parcel) 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 808e3369f1..0683087211 100644 --- a/libs/gui/IProducerListener.cpp +++ b/libs/gui/IProducerListener.cpp @@ -119,9 +119,7 @@ status_t BnProducerListener::onTransact(uint32_t code, const Parcel& data, 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 12deaf0bd6..e62a61fc55 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 @@ public: 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 @@ public: 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 @@ public: } 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: } 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 @@ public: 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.writeInt32(defaultConfig); + if (result != NO_ERROR) { + ALOGE("setDesiredDisplayConfigSpecs failed to write defaultConfig: %d", result); + return result; + } + result = data.writeFloat(primaryRefreshRateMin); + if (result != NO_ERROR) { + 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.writeInt32Vector(allowedConfigs); + result = data.writeFloat(appRequestRefreshRateMin); if (result != NO_ERROR) { - ALOGE("setAllowedDisplayConfigs failed to writeInt32Vector: %d", result); + ALOGE("setDesiredDisplayConfigSpecs failed to write appRequestRefreshRateMin: %d", + result); return result; } - result = remote()->transact(BnSurfaceComposer::SET_ALLOWED_DISPLAY_CONFIGS, data, &reply); + result = data.writeFloat(appRequestRefreshRateMax); if (result != NO_ERROR) { - ALOGE("setAllowedDisplayConfigs failed to transact: %d", result); + 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 @@ public: 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 @@ public: } 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 @@ status_t BnSurfaceComposer::onTransact( 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 @@ status_t BnSurfaceComposer::onTransact( 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 @@ status_t BnSurfaceComposer::onTransact( 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 @@ status_t BnSurfaceComposer::onTransact( 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 @@ status_t BnSurfaceComposer::onTransact( 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 @@ status_t BnSurfaceComposer::onTransact( 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 @@ status_t BnSurfaceComposer::onTransact( } 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 @@ status_t BnSurfaceComposer::onTransact( } 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 129558bd15..621cf5950b 100644 --- a/libs/gui/ISurfaceComposerClient.cpp +++ b/libs/gui/ISurfaceComposerClient.cpp @@ -34,7 +34,8 @@ enum class Tag : uint32_t { 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 @@ public: 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 @@ public: &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 @@ status_t BnSurfaceComposerClient::onTransact(uint32_t code, const Parcel& data, 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 74cd4f1ede..69f7894d07 100644 --- a/libs/gui/ITransactionCompletedListener.cpp +++ b/libs/gui/ITransactionCompletedListener.cpp @@ -30,6 +30,66 @@ enum class Tag : uint32_t { } // 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 @@ status_t SurfaceStats::writeToParcel(Parcel* output) const { } 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 @@ status_t SurfaceStats::readFromParcel(const Parcel* input) { 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 @@ status_t ListenerStats::readFromParcel(const Parcel* input) { 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 04d2871c77..b3eb9940b2 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 @@ void LayerMetadata::setInt32(uint32_t key, int32_t value) { 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 42eb9213d6..0281279d69 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 @@ status_t layer_state_t::write(Parcel& output) const 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 @@ status_t layer_state_t::write(Parcel& output) const 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 @@ status_t layer_state_t::read(const Parcel& input) 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 @@ status_t layer_state_t::read(const Parcel& input) 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 @@ status_t ComposerState::read(const Parcel& input) { DisplayState::DisplayState() : what(0), layerStack(0), - orientation(eOrientationDefault), viewport(Rect::EMPTY_RECT), frame(Rect::EMPTY_RECT), width(0), @@ -194,7 +231,7 @@ status_t DisplayState::write(Parcel& output) const { 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 @@ status_t DisplayState::read(const Parcel& input) { 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 @@ void layer_state_t::merge(const layer_state_t& other) { } 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 @@ void layer_state_t::merge(const layer_state_t& other) { 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 @@ void layer_state_t::merge(const layer_state_t& other) { what |= eOverrideScalingModeChanged; overrideScalingMode = other.overrideScalingMode; } - if (other.what & eGeometryAppliesWithResize) { - what |= eGeometryAppliesWithResize; - } if (other.what & eReparentChildren) { what |= eReparentChildren; reparentHandle = other.reparentHandle; @@ -365,7 +404,6 @@ void layer_state_t::merge(const layer_state_t& other) { } if (other.what & eHasListenerCallbacksChanged) { what |= eHasListenerCallbacksChanged; - hasListenerCallbacks = other.hasListenerCallbacks; } #ifndef NO_INPUT @@ -389,6 +427,23 @@ void layer_state_t::merge(const layer_state_t& other) { 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 @@ void layer_state_t::merge(const layer_state_t& other) { // ------------------------------- 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 0000000000..30f0ef6785 --- /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 b822319d22..c3323fefb2 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 @@ namespace android { 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 @@ Surface::Surface(const sp<IGraphicBufferProducer>& bufferProducer, bool controll mConnectedToCpu = false; mProducerControlledByApp = controlledByApp; mSwapIntervalZero = false; + mMaxBufferCount = NUM_BUFFER_SLOTS; } Surface::~Surface() { @@ -365,18 +379,58 @@ int Surface::hook_setSwapInterval(ANativeWindow* window, int interval) { 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 @@ int Surface::hook_queueBuffer_DEPRECATED(ANativeWindow* window, 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 @@ int Surface::query(int what, int* value) const { *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 @@ int Surface::perform(int operation, va_list args) 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 @@ int Surface::dispatchGetConsumerUsage64(va_list args) { 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 @@ int Surface::connect( 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 @@ int Surface::disconnect(int api, IGraphicBufferProducer::DisconnectMode mode) { 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 @@ int Surface::getConsumerUsage(uint64_t* outUsage) const { 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 @@ status_t Surface::attachAndQueueBufferWithDataspace(Surface* surface, sp<Graphic 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 @@ void Surface::ProducerListenerProxy::onBuffersDiscarded(const std::vector<int32_ 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 dfc61851c2..83bc06997a 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::addSurfaceControlToCallbacks( } 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 @@ public: evictLeastRecentlyUsedBuffer(); } - buffer->addDeathCallback(bufferCacheCallback, nullptr); + buffer->addDeathCallback(removeDeadBufferCallback, nullptr); mBuffers[buffer->getId()] = getCounter(); return buffer->getId(); @@ -310,7 +341,7 @@ private: 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 @@ SurfaceComposerClient::Transaction::Transaction(const Transaction& other) 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 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::merge(Tr 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 @@ void SurfaceComposerClient::doUncacheBufferTransaction(uint64_t cacheId) { 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 @@ void SurfaceComposerClient::Transaction::cacheBuffers() { } 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 @@ void SurfaceComposerClient::Transaction::cacheBuffers() { 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 @@ status_t SurfaceComposerClient::Transaction::apply(bool synchronous) { 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 @@ status_t SurfaceComposerClient::Transaction::apply(bool synchronous) { 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 @@ status_t SurfaceComposerClient::Transaction::apply(bool synchronous) { 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 @@ void SurfaceComposerClient::Transaction::setEarlyWakeup() { 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 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setRelat 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 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCorne 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::setColor } 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 @@ SurfaceComposerClient::Transaction::addTransactionCompletedCallback( 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 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setOverr 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 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setInput 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,8 +1432,55 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setGeome 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; } @@ -1242,9 +1525,9 @@ void SurfaceComposerClient::Transaction::setDisplayLayerStack(const sp<IBinder>& } 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 @@ void SurfaceComposerClient::dispose() { 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<SurfaceControl> SurfaceComposerClient::createWithSurfaceParent(const String8& 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 @@ sp<SurfaceControl> SurfaceComposerClient::createWithSurfaceParent(const String8& 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 @@ status_t SurfaceComposerClient::createSurfaceChecked(const String8& name, uint32 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 @@ status_t SurfaceComposerClient::injectVSync(nsecs_t when) { 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 @@ status_t SurfaceComposerClient::getDisplayInfo(const sp<IBinder>& display, 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 @@ int SurfaceComposerClient::getActiveConfig(const sp<IBinder>& display) { return ComposerService::getComposerService()->getActiveConfig(display); } -status_t SurfaceComposerClient::setActiveConfig(const sp<IBinder>& display, int id) { - return ComposerService::getComposerService()->setActiveConfig(display, id); -} - -status_t SurfaceComposerClient::setAllowedDisplayConfigs( - const sp<IBinder>& displayToken, const std::vector<int32_t>& allowedConfigs) { - return ComposerService::getComposerService()->setAllowedDisplayConfigs(displayToken, - allowedConfigs); +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::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 @@ status_t SurfaceComposerClient::setActiveColorMode(const sp<IBinder>& display, 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 @@ status_t SurfaceComposerClient::notifyPowerHint(int32_t hintId) { 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 @@ status_t ScreenshotClient::capture(uint64_t displayOrLayerStack, ui::Dataspace* 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::captureLayers(const sp<IBinder>& layerHandle, } 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 @@ status_t ScreenshotClient::captureChildLayers( excludeHandles, frameScale, true /* childrenOnly */); return ret; } -// ---------------------------------------------------------------------------- -}; // namespace android + +} // namespace android diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp index 55488dad0b..a332a1f2a8 100644 --- a/libs/gui/SurfaceControl.cpp +++ b/libs/gui/SurfaceControl.cpp @@ -45,42 +45,23 @@ namespace android { // 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<Surface> SurfaceControl::createSurface() const sp<IBinder> SurfaceControl::getHandle() const { - Mutex::Autolock lock(mLock); return mHandle; } @@ -179,15 +159,25 @@ sp<SurfaceComposerClient> SurfaceControl::getClient() const 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<SurfaceControl> SurfaceControl::readFromParcel(Parcel* parcel) 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 0000000000..1c435304a8 --- /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 5cb35933e9..3e20a37d54 100644 --- a/libs/gui/bufferqueue/1.0/Conversion.cpp +++ b/libs/gui/bufferqueue/1.0/Conversion.cpp @@ -109,20 +109,6 @@ status_t toStatusT(Return<void> const& t) { } /** - * \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 @@ status_t unflatten( 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 2712f42c89..598c8dea9b 100644 --- a/libs/gui/bufferqueue/1.0/H2BProducerListener.cpp +++ b/libs/gui/bufferqueue/1.0/H2BProducerListener.cpp @@ -32,7 +32,11 @@ namespace utils { 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 78dc4e8b18..56b64b9ddd 100644 --- a/libs/gui/bufferqueue/1.0/WProducerListener.cpp +++ b/libs/gui/bufferqueue/1.0/WProducerListener.cpp @@ -46,5 +46,7 @@ void LWProducerListener::onBufferReleased() { 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 e891ec581e..c76d771262 100644 --- a/libs/gui/bufferqueue/2.0/B2HGraphicBufferProducer.cpp +++ b/libs/gui/bufferqueue/2.0/B2HGraphicBufferProducer.cpp @@ -249,6 +249,24 @@ Return<void> B2HGraphicBufferProducer::query(int32_t what, query_cb _hidl_cb) { 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 @@ Return<void> B2HGraphicBufferProducer::connect( &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 @@ Return<HStatus> B2HGraphicBufferProducer::disconnect( } 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 b81a357d63..b2bd1172d6 100644 --- a/libs/gui/bufferqueue/2.0/H2BProducerListener.cpp +++ b/libs/gui/bufferqueue/2.0/H2BProducerListener.cpp @@ -32,7 +32,11 @@ namespace utils { 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 @@ bool H2BProducerListener::needsReleaseNotify() { 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 0000000000..2320771a38 --- /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 da952744f3..91f80d2f17 100644 --- a/libs/gui/include/gui/BufferQueue.h +++ b/libs/gui/include/gui/BufferQueue.h @@ -63,6 +63,9 @@ public: 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 17617bce13..557c28b1b7 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 @@ private: 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 d2a47a6aa8..a7f7d1defa 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 @@ public: // 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 366ced380b..8ff0cd0f6e 100644 --- a/libs/gui/include/gui/ConsumerBase.h +++ b/libs/gui/include/gui/ConsumerBase.h @@ -45,6 +45,9 @@ public: // 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 @@ protected: // 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 0000000000..f210c34196 --- /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 a558cf9e18..8d49184caf 100644 --- a/libs/gui/include/gui/DisplayEventReceiver.h +++ b/libs/gui/include/gui/DisplayEventReceiver.h @@ -65,6 +65,7 @@ public: struct VSync { uint32_t count; + nsecs_t expectedVSyncTimestamp; }; struct Hotplug { @@ -73,6 +74,7 @@ public: struct Config { int32_t configId; + nsecs_t vsyncPeriod; }; Header header; @@ -144,6 +146,12 @@ public: */ 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 4670edda99..0750080e1c 100644 --- a/libs/gui/include/gui/FrameTimestamps.h +++ b/libs/gui/include/gui/FrameTimestamps.h @@ -175,7 +175,6 @@ struct NewFrameEventsEntry { 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 @@ public: ~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 ddd868d7d1..2f538ffb86 100644 --- a/libs/gui/include/gui/GLConsumer.h +++ b/libs/gui/include/gui/GLConsumer.h @@ -499,7 +499,7 @@ private: // 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 c0828820e3..0ab2399eb2 100644 --- a/libs/gui/include/gui/IConsumerListener.h +++ b/libs/gui/include/gui/IConsumerListener.h @@ -43,6 +43,17 @@ public: // 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 @@ public: FrameEventHistoryDelta* /*outDelta*/) {} }; +#ifndef NO_BINDER class IConsumerListener : public ConsumerListener, public IInterface { public: DECLARE_META_INTERFACE(ConsumerListener) @@ -94,4 +106,11 @@ public: 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 d783f74d7c..674aafd81c 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 @@ public: * 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 9fb7580912..0b92e7df62 100644 --- a/libs/gui/include/gui/IGraphicBufferConsumer.h +++ b/libs/gui/include/gui/IGraphicBufferConsumer.h @@ -35,10 +35,14 @@ class Fence; 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 @@ public: } }; +#ifndef NO_BINDER class BnGraphicBufferConsumer : public SafeBnInterface<IGraphicBufferConsumer> { public: BnGraphicBufferConsumer() @@ -290,5 +295,9 @@ public: 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 3dde8c8c80..45e0a134ba 100644 --- a/libs/gui/include/gui/IGraphicBufferProducer.h +++ b/libs/gui/include/gui/IGraphicBufferProducer.h @@ -45,6 +45,13 @@ class IProducerListener; 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 @@ class Surface; * * 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 @@ public: 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 @@ public: 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 @@ public: // 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 @@ public: // 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 @@ protected: // 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 @@ public: 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 32a3690ff2..f7ffbb99ea 100644 --- a/libs/gui/include/gui/IProducerListener.h +++ b/libs/gui/include/gui/IProducerListener.h @@ -51,6 +51,7 @@ public: 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 @@ public: 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 c84910b6ec..8d3160a815 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 IRegionSamplingListener; 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 @@ class ISurfaceComposer: public IInterface { public: DECLARE_META_INTERFACE(SurfaceComposer) + static constexpr size_t MAX_LAYERS = 4096; + // flags for setTransactionState() enum { eSynchronous = 0x01, - 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 - }; - - enum Rotation { - eRotateNone = 0, - eRotate90 = 1, - eRotate180 = 2, - eRotate270 = 3 + eAnimation = 0x02, + + // DEPRECATED - use eExplicitEarlyWakeup[Start|End] + eEarlyWakeup = 0x04, + + // 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 @@ public: 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 @@ public: */ 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 @@ public: 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 @@ public: 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 @@ public: * 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 @@ public: * 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 @@ public: */ 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 @@ public: * * 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 @@ public: */ 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 @@ public: */ 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 - * returned from getDisplayConfigs(). - */ - virtual status_t setAllowedDisplayConfigs(const sp<IBinder>& displayToken, - const std::vector<int32_t>& allowedConfigs) = 0; - - /* - * Returns the allowed display configurations currently set. - * 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 getAllowedDisplayConfigs(const sp<IBinder>& displayToken, - std::vector<int32_t>* outAllowedConfigs) = 0; + virtual status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken, + int32_t defaultConfig, + float primaryRefreshRateMin, + float primaryRefreshRateMax, + float appRequestRefreshRateMin, + float appRequestRefreshRateMax) = 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 @@ public: * 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 @@ public: * 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 @@ public: // 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 @@ public: 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 @@ public: 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 @@ public: 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 32ac9e8928..3afbabf1dc 100644 --- a/libs/gui/include/gui/ISurfaceComposerClient.h +++ b/libs/gui/include/gui/ISurfaceComposerClient.h @@ -33,7 +33,7 @@ public: 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 @@ public: eProtectedByApp = 0x00000800, eProtectedByDRM = 0x00001000, eCursorWindow = 0x00002000, + eNoColorFill = 0x00004000, eFXSurfaceBufferQueue = 0x00000000, - eFXSurfaceColor = 0x00020000, + eFXSurfaceEffect = 0x00020000, eFXSurfaceBufferState = 0x00040000, eFXSurfaceContainer = 0x00080000, eFXSurfaceMask = 0x000F0000, @@ -56,7 +57,7 @@ public: 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 @@ public: 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 @@ public: * 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 774ad46b15..c58634b8a2 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 @@ public: 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 @@ public: 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 f438eb3d01..e60f6777ae 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 @@ struct layer_state_t { eCropChanged_legacy = 0x00000100, eDeferTransaction_legacy = 0x00000200, eOverrideScalingModeChanged = 0x00000400, - eGeometryAppliesWithResize = 0x00000800, + eShadowRadiusChanged = 0x00000800, eReparentChildren = 0x00001000, eDetachChildren = 0x00002000, eRelativeLayerChanged = 0x00004000, @@ -97,6 +100,11 @@ struct layer_state_t { 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 @@ struct layer_state_t { reserved(0), crop_legacy(Rect::INVALID_RECT), cornerRadius(0.0f), + backgroundBlurRadius(0), frameNumber_legacy(0), overrideScalingMode(-1), transform(0), @@ -123,10 +132,14 @@ struct layer_state_t { 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 @@ struct layer_state_t { 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 @@ struct layer_state_t { sp<NativeHandle> sidebandStream; mat4 colorTransform; - bool hasListenerCallbacks; #ifndef NO_INPUT InputWindowInfo inputInfo; #endif @@ -203,10 +216,30 @@ struct layer_state_t { // 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 ComposerState { 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 @@ struct DisplayState { // 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 DisplayState { }; 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 @@ struct InputWindowCommands { }; 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 @@ static inline int compare_type(const DisplayState& lhs, const DisplayState& rhs) 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 a5641b07e5..49c83da319 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 @@ public: 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 @@ private: 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 @@ private: 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 @@ public: 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 @@ protected: // 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 @@ protected: // 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 @@ protected: 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 9d96485efe..adcb8982a0 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 @@ public: 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 the DisplayInfo for the currently-active configuration - static status_t getDisplayInfo(const sp<IBinder>& display, - DisplayInfo* info); + // Get transactional state of given display. + static status_t getDisplayState(const sp<IBinder>& display, ui::DisplayState*); - // Get the index of the current active configuration (relative to the list - // returned by getDisplayInfo) - static int getActiveConfig(const sp<IBinder>& display); + // Get immutable information about given physical display. + static status_t getDisplayInfo(const sp<IBinder>& display, DisplayInfo*); - // 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); + // Get configurations supported by given physical display. + static status_t getDisplayConfigs(const sp<IBinder>& display, Vector<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); + // Get the ID of the active DisplayConfig, as getDisplayConfigs index. + static int getActiveConfig(const sp<IBinder>& display); - // 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); + // Shorthand for getDisplayConfigs element at getActiveConfig index. + static status_t getActiveDisplayConfig(const sp<IBinder>& display, DisplayConfig*); + + // 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 @@ public: 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 @@ public: 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 @@ public: */ 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 @@ public: 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 @@ public: 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 @@ public: } }; + 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: * 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 @@ public: 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 @@ class ScreenshotClient { 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 @@ class TransactionCompletedListener : public BnTransactionCompletedListener { 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 @@ public: 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 23bfc0256b..ac2bbccfd2 100644 --- a/libs/gui/include/gui/SurfaceControl.h +++ b/libs/gui/include/gui/SurfaceControl.h @@ -44,7 +44,7 @@ class SurfaceComposerClient; 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 @@ public: 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 @@ public: 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 @@ private: 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 627845c0ea..811dcbeb02 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 @@ int native_handle_read_fd(native_handle_t const* nh, int index = 0); */ /** - * \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 211fdd5351..cda5103334 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 HProducerListener = ::android::hardware::graphics::bufferqueue::V1_0:: 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 029dcc047f..004d87574a 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::hardware::graphics::bufferqueue::V1_0:: 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 51dff5b8be..197db26444 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 @@ public: 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 1c58167752..16d054ba9e 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_string; using ::android::hardware::hidl_vec; using ::android::hardware::graphics::common::V1_2::HardwareBuffer; +struct Obituary; class B2HGraphicBufferProducer : public HGraphicBufferProducer { public: @@ -108,6 +109,7 @@ public: 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 898920bf8a..92650b701b 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 HProducerListener = ::android::hardware::graphics::bufferqueue::V2_0:: 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 0000000000..98f24c2d44 --- /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 0000000000..c98f39f43c --- /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/mock/GraphicBufferConsumer.cpp b/libs/gui/mock/GraphicBufferConsumer.cpp new file mode 100644 index 0000000000..4a6c0815e1 --- /dev/null +++ b/libs/gui/mock/GraphicBufferConsumer.cpp @@ -0,0 +1,27 @@ +/* + * 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 <gui/mock/GraphicBufferConsumer.h> + +namespace android { +namespace mock { + +// Explicit default instantiation is recommended. +GraphicBufferConsumer::GraphicBufferConsumer() = default; +GraphicBufferConsumer::~GraphicBufferConsumer() = default; + +} // namespace mock +} // namespace android
\ No newline at end of file diff --git a/libs/gui/mock/GraphicBufferProducer.cpp b/libs/gui/mock/GraphicBufferProducer.cpp new file mode 100644 index 0000000000..239a80a2e1 --- /dev/null +++ b/libs/gui/mock/GraphicBufferProducer.cpp @@ -0,0 +1,27 @@ +/* + * 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 <gui/mock/GraphicBufferProducer.h> + +namespace android { +namespace mock { + +// Explicit default instantiation is recommended. +GraphicBufferProducer::GraphicBufferProducer() = default; +GraphicBufferProducer::~GraphicBufferProducer() = default; + +} // 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 cbda6b23ff..a6bcd107af 100644 --- a/libs/gui/tests/Android.bp +++ b/libs/gui/tests/Android.bp @@ -13,7 +13,8 @@ cc_test { ], srcs: [ - "BufferItemConsumer_test.cpp", + "BLASTBufferQueue_test.cpp", + "BufferItemConsumer_test.cpp", "BufferQueue_test.cpp", "CpuConsumer_test.cpp", "EndToEndNativeInputTest.cpp", @@ -38,6 +39,7 @@ cc_test { shared_libs: [ "android.hardware.configstore@1.0", "android.hardware.configstore-utils", + "libSurfaceFlingerProp", "libbase", "liblog", "libEGL", @@ -52,6 +54,8 @@ cc_test { "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 c02e020be6..5e09fff6bb 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 0000000000..da5bbdd2e6 --- /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 b87cbbdec8..fc6551c8e6 100644 --- a/libs/gui/tests/BufferItemConsumer_test.cpp +++ b/libs/gui/tests/BufferItemConsumer_test.cpp @@ -51,7 +51,7 @@ class BufferItemConsumerTest : public ::testing::Test { 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 @@ class BufferItemConsumerTest : public ::testing::Test { // 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 406f21faf6..d1208ee5ae 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 @@ TEST_F(BufferQueueTest, DISABLED_BufferQueueInAnotherProcess) { 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 @@ TEST_F(BufferQueueTest, DISABLED_BufferQueueInAnotherProcess) { 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, AcquireBuffer_ExceedsMaxAcquireCount_Fails) { 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, SetMaxAcquiredBufferCountWithIllegalValues_ReturnsError) 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, SetMaxAcquiredBufferCountWithLegalValues_Succeeds) { 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, SetMaxBufferCountWithLegalValues_Succeeds) { 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, SetMaxBufferCountWithIllegalValues_ReturnsError) { 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, DetachAndReattachOnProducerSide) { 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, DetachAndReattachOnConsumerSide) { 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, MoveFromConsumerToProducer) { 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, TestDisallowingAllocation) { 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, TestGenerationNumbers) { 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, TestSharedBufferModeWithoutAutoRefresh) { 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, TestSharedBufferModeWithAutoRefresh) { 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, TestSharedBufferModeUsingAlreadyDequeuedBuffer) { 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, TestTimeouts) { 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, CanAttachWhileDisallowingAllocation) { 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, CanRetrieveLastQueuedBuffer) { 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 @@ private: 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, TestDiscardFreeBuffers) { 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, TestBufferReplacedInQueueBuffer) { 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, TestStaleBufferHandleSentAfterDisconnect) { 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 03b9cd75db..b1d3ecbf36 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 @@ public: mSurfaceControl = sc; InputChannel::openInputChannelPair("testchannels", mServerChannel, mClientChannel); - mServerChannel->setToken(new BBinder()); mInputFlinger = getInputFlinger(); mInputFlinger->registerInputChannel(mServerChannel); @@ -83,7 +82,8 @@ public: 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 @@ public: 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 @@ public: 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 @@ private: } 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 @@ private: 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 @@ public: 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 @@ void injectTap(int x, int y) { 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 @@ TEST_F(InputSurfacesTest, input_respects_layering) { 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 @@ TEST_F(InputSurfacesTest, input_respects_layering) { 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 @@ TEST_F(InputSurfacesTest, input_respects_layering) { 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 @@ TEST_F(InputSurfacesTest, input_respects_surface_insets) { 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 @@ TEST_F(InputSurfacesTest, input_respects_cropped_surface_insets) { 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 @@ TEST_F(InputSurfacesTest, input_respects_scaled_surface_insets) { 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 @@ TEST_F(InputSurfacesTest, input_respects_scaled_surface_insets_overflow) { // 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 @@ TEST_F(InputSurfacesTest, input_ignores_transparent_region) { t.setTransparentRegionHint(sc, transparentRegion); }); surface->showAt(100, 100); + surface->assertFocusChange(true); injectTap(101, 101); surface->expectTap(1, 1); } @@ -415,7 +465,10 @@ TEST_F(InputSurfacesTest, input_ignores_buffer_layer_buffer) { 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 @@ TEST_F(InputSurfacesTest, input_ignores_buffer_layer_alpha) { 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 @@ TEST_F(InputSurfacesTest, input_ignores_color_layer_alpha) { 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 @@ TEST_F(InputSurfacesTest, input_respects_container_layer_visiblity) { 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_container_layer_visiblity) { 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 aef7aed52c..15bd32d354 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 @@ protected: 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 @@ protected: } // 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 @@ protected: } private: // hide from test body - sp<DummyConsumer> mDC; + sp<MockConsumer> mMC; protected: // accessible from test body sp<IGraphicBufferProducer> mProducer; @@ -543,7 +543,6 @@ TEST_P(IGraphicBufferProducerTest, SetMaxDequeuedBufferCount_Succeeds) { // 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 acd42979c2..58d7cc6f35 100644 --- a/libs/gui/tests/Malicious.cpp +++ b/libs/gui/tests/Malicious.cpp @@ -129,7 +129,7 @@ private: 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<MaliciousBQP> getMaliciousBQP() { 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 index 502bdf981b..4a6c51c17d 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 d33ecfbdb9..6746b0a827 100644 --- a/libs/gui/tests/RegionSampling_test.cpp +++ b/libs/gui/tests/RegionSampling_test.cpp @@ -183,7 +183,7 @@ protected: 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 @@ protected: 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 @@ TEST_F(RegionSamplingTest, DISABLED_CollectsLumaFromTwoRegions) { 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 9891587fe2..5c1bebb960 100644 --- a/libs/gui/tests/SamplingDemo.cpp +++ b/libs/gui/tests/SamplingDemo.cpp @@ -39,7 +39,7 @@ public: 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 @@ public: .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 @@ public: 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 ad6e051684..b65cddaea3 100644 --- a/libs/gui/tests/StreamSplitter_test.cpp +++ b/libs/gui/tests/StreamSplitter_test.cpp @@ -48,7 +48,7 @@ protected: } }; -struct DummyListener : public BnConsumerListener { +struct FakeListener : public BnConsumerListener { virtual void onFrameAvailable(const BufferItem& /* item */) {} virtual void onBuffersReleased() {} virtual void onSidebandStreamChanged() {} @@ -64,7 +64,7 @@ TEST_F(StreamSplitterTest, OneInputOneOutput) { 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 @@ TEST_F(StreamSplitterTest, OneInputOneOutput) { 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 @@ TEST_F(StreamSplitterTest, OneInputMultipleOutputs) { 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 @@ TEST_F(StreamSplitterTest, OneInputMultipleOutputs) { } 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 @@ TEST_F(StreamSplitterTest, OutputAbandonment) { 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 @@ TEST_F(StreamSplitterTest, OutputAbandonment) { 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 65e09f2540..c7458a3755 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 @@ protected: 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 a8516872fd..592913c46b 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 ui::ColorMode; 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 @@ protected: 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 @@ TEST_F(SurfaceTest, GetConsumerName) { 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 @@ TEST_F(SurfaceTest, GetWideColorSupport) { 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 @@ TEST_F(SurfaceTest, GetHdrSupport) { 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 @@ TEST_F(SurfaceTest, SetHdrMetadata) { 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 @@ TEST_F(SurfaceTest, DynamicSetBufferCount) { 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 @@ TEST_F(SurfaceTest, GetAndFlushRemovedBuffers) { 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 @@ TEST_F(SurfaceTest, TestGetLastDequeueStartTime) { 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 @@ public: 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 @@ public: } 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 @@ public: 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 @@ public: 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 @@ public: } 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 @@ public: return NO_ERROR; } status_t setDisplayBrightness(const sp<IBinder>& /*displayToken*/, - float /*brightness*/) const override { + float /*brightness*/) override { return NO_ERROR; } @@ -822,16 +833,37 @@ public: 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 @@ TEST_F(GetFrameTimestampsTest, PresentUnsupportedNoSync) { 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 da437b14bc..6a5d434515 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -52,6 +52,7 @@ cc_library { "InputTransport.cpp", "InputWindow.cpp", "ISetInputWindowsListener.cpp", + "LatencyStatistics.cpp", "VelocityControl.cpp", "VelocityTracker.cpp", ], @@ -59,7 +60,7 @@ cc_library { shared_libs: [ "libutils", "libbinder", - "libui" + "libui", ], sanitize: { diff --git a/libs/input/IInputFlinger.cpp b/libs/input/IInputFlinger.cpp index de3a23d76e..8ec51653a8 100644 --- a/libs/input/IInputFlinger.cpp +++ b/libs/input/IInputFlinger.cpp @@ -82,15 +82,13 @@ status_t BnInputFlinger::onTransact( } 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 9fd25f9cb7..31aa685391 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 @@ const char* motionClassificationToString(MotionClassification classification) { } } +// --- 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 @@ int32_t KeyEvent::getKeyCodeFromLabel(const char* label) { 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 @@ void KeyEvent::initialize(const KeyEvent& from) { 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 @@ void PointerProperties::copyFrom(const PointerProperties& other) { // --- 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 @@ void MotionEvent::initialize( 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::initialize( } 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 @@ void MotionEvent::copyFrom(const MotionEvent* other, bool keepHistory) { 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 @@ void MotionEvent::addSample( 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 MotionEvent::getAxisValue(int32_t axis, size_t pointerIndex) const { 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 MotionEvent::getHistoricalAxisValue(int32_t axis, size_t pointerIndex, 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 @@ void MotionEvent::transform(const float matrix[9]) { 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 @@ status_t MotionEvent::readFromParcel(Parcel* parcel) { 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 @@ status_t MotionEvent::readFromParcel(Parcel* parcel) { 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 @@ status_t MotionEvent::writeToParcel(Parcel* parcel) const { 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 @@ status_t MotionEvent::writeToParcel(Parcel* parcel) const { 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 @@ status_t MotionEvent::writeToParcel(Parcel* parcel) const { } #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 @@ int32_t MotionEvent::getAxisFromLabel(const char* label) { 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(size_t maxPoolSize) : } 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 d02cb8ea46..11af23e1a2 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 @@ inline static bool isPointerEvent(int32_t source) { 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 @@ bool InputMessage::isValid(size_t actualSize) const { 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 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { // 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 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { 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 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { 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 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { 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 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { 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 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { 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 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { } 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 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { // --- 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 - - setFd(fd); +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); } -InputChannel::~InputChannel() { -#if DEBUG_CHANNEL_LIFECYCLE - ALOGD("Input channel destroyed: name='%s', fd=%d", - mName.c_str(), mFd); -#endif - - ::close(mFd); +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()); + } } -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); +InputChannel::~InputChannel() { + if (DEBUG_CHANNEL_LIFECYCLE) { + ALOGD("Input channel destroyed: name='%s', fd=%d", mName.c_str(), mFd.get()); } } @@ -271,13 +290,15 @@ status_t InputChannel::openInputChannelPair(const std::string& name, 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 serverChannelName = name + " (server)"; + android::base::unique_fd serverFd(sockets[0]); + outServerChannel = InputChannel::create(serverChannelName, std::move(serverFd), token); - std::string clientChannelName = name; - clientChannelName += " (client)"; - outClientChannel = new InputChannel(clientChannelName, sockets[1]); + 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 @@ status_t InputChannel::sendMessage(const InputMessage* msg) { 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::sendMessage(const InputMessage* msg) { 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 @@ status_t InputChannel::receiveMessage(InputMessage* msg) { } 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(const sp<InputChannel>& channel) : 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 @@ status_t InputPublisher::publishKeyEvent( } 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::publishKeyEvent( } 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 @@ status_t InputPublisher::publishMotionEvent( } 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 @@ status_t InputPublisher::publishMotionEvent( 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 @@ status_t InputPublisher::publishMotionEvent( 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 @@ status_t InputPublisher::receiveFinishedSignal(uint32_t* outSeq, bool* outHandle *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 @@ bool InputConsumer::isTouchResamplingEnabled() { 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 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, 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 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, } switch (mMsg.header.type) { - 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 + 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); + } 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 + 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; - } 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; + } + + // 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()); } -#if DEBUG_TRANSPORT_ACTIONS - ALOGD("channel '%s' consumer ~ consumed batch event and " - "deferred current event, seq=%u", - mChannel->getName().c_str(), *outSeq); -#endif 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 + 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 @@ bool InputConsumer::shouldResampleTool(int32_t toolType) { } 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::sendFinishedSignal(uint32_t seq, bool handled) { 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 @@ bool InputConsumer::hasPendingBatch() const { 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 @@ ssize_t InputConsumer::findTouchState(int32_t deviceId, int32_t source) const { } 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 @@ void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage 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 ec28757933..85a2015e43 100644 --- a/libs/input/InputWindow.cpp +++ b/libs/input/InputWindow.cpp @@ -42,17 +42,18 @@ bool InputWindowInfo::frameContainsPoint(int32_t x, int32_t y) const { && 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 @@ bool InputWindowInfo::overlaps(const InputWindowInfo* other) const { } status_t InputWindowInfo::write(Parcel& output) const { - if (token == nullptr) { + if (name.empty()) { output.writeInt32(0); return OK; } @@ -73,6 +74,7 @@ status_t InputWindowInfo::write(Parcel& output) const { 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 @@ status_t InputWindowInfo::write(Parcel& output) const { 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 @@ InputWindowInfo InputWindowInfo::read(const Parcel& from) { 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 @@ InputWindowInfo InputWindowInfo::read(const Parcel& from) { 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 e189d20e28..cb68165433 100644 --- a/libs/input/KeyCharacterMap.cpp +++ b/libs/input/KeyCharacterMap.cpp @@ -487,9 +487,9 @@ void KeyCharacterMap::addKey(Vector<KeyEvent>& outEvents, 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 0c22bfefed..56900c129e 100644 --- a/libs/input/Keyboard.cpp +++ b/libs/input/Keyboard.cpp @@ -38,29 +38,29 @@ KeyMap::KeyMap() { 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 @@ status_t KeyMap::load(const InputDeviceIdentifier& deviceIdenfifier, } // 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 0000000000..394da22a62 --- /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 ce76e3fa6d..c62e0985f1 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 ade931e01a..3b57146461 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 0000000000..f7fc3c0ef2 --- /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 f1675c0d36..ada275d014 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 @@ TEST_F(InputChannelTest, ConstructorAndDestructor_TakesOwnershipOfFileDescriptor // 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 @@ TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) { // 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 @@ TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) { // 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 @@ TEST_F(InputChannelTest, SendSignal_WhenPeerClosed_ReturnsAnError) { 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 @@ TEST_F(InputChannelTest, SendAndReceive_MotionClassification) { }; 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 2b75c82bb1..553dc4c068 100644 --- a/libs/input/tests/InputEvent_test.cpp +++ b/libs/input/tests/InputEvent_test.cpp @@ -26,7 +26,12 @@ namespace android { // 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, ClearSetsBitsToZero) { } TEST_F(PointerCoordsTest, AxisValues) { - float* valuePtr; PointerCoords coords; coords.clear(); @@ -176,16 +180,19 @@ TEST_F(KeyEventTest, Properties) { 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 @@ TEST_F(KeyEventTest, Properties) { // 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 @@ TEST_F(KeyEventTest, Properties) { 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 @@ void MotionEventTest::initializeEventWithHistory(MotionEvent* event) { 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::initializeEventWithHistory(MotionEvent* event) { 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 @@ void MotionEventTest::assertEqualsEventWithHistory(const MotionEvent* event) { 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 @@ TEST_F(MotionEventTest, Properties) { // 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 @@ TEST_F(MotionEventTest, Scale) { 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 @@ TEST_F(MotionEventTest, Transform) { 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 @@ TEST_F(MotionEventTest, Transform) { 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 @@ TEST_F(MotionEventTest, Initialize_SetsClassification) { } 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 f2cd1be33f..8e2eec85ed 100644 --- a/libs/input/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp @@ -38,6 +38,7 @@ protected: 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 @@ protected: void PublishAndConsumeKeyEvent(); void PublishAndConsumeMotionEvent(); + void PublishAndConsumeFocusEvent(); }; TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) { @@ -71,9 +73,13 @@ void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() { 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 @@ void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() { 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 @@ void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() { 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 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { 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 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { 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 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { 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 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { 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 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { 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 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { << "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 @@ TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_EndToEnd) { 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 @@ TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenSequenceNumberIsZer 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 @@ TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenPointerCountLessTha 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 @@ TEST_F(InputPublisherAndConsumerTest, 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 @@ TEST_F(InputPublisherAndConsumerTest, PublishMultipleEvents_EndToEnd) { 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 6db18abacf..d1cb527a57 100644 --- a/libs/input/tests/InputWindow_test.cpp +++ b/libs/input/tests/InputWindow_test.cpp @@ -40,6 +40,7 @@ TEST(InputWindowInfo, Parcelling) { 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 @@ TEST(InputWindowInfo, Parcelling) { 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 @@ TEST(InputWindowInfo, Parcelling) { 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 @@ TEST(InputWindowInfo, Parcelling) { 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 0000000000..eb12d4ef6f --- /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 62023fb328..1fe7bb90ca 100644 --- a/libs/input/tests/StructLayout_test.cpp +++ b/libs/input/tests/StructLayout_test.cpp @@ -35,40 +35,100 @@ void TestInputMessageAlignment() { 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 fe06cad72c..654b236bda 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 368446ff4d..bf452c07a3 100644 --- a/libs/input/tests/VelocityTracker_test.cpp +++ b/libs/input/tests/VelocityTracker_test.cpp @@ -176,12 +176,14 @@ static std::vector<MotionEvent> createMotionEventStream( 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 0000000000..4e8e840d1c --- /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 0000000000..e458b2ecfb --- /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 0000000000..277635cd34 --- /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 0000000000..f56b3a2178 --- /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 0000000000..e69de29bb2 --- /dev/null +++ b/libs/nativedisplay/MODULE_LICENSE_APACHE2 diff --git a/libs/nativedisplay/NOTICE b/libs/nativedisplay/NOTICE new file mode 100644 index 0000000000..c5b1efa7aa --- /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 0000000000..21649304bf --- /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 0000000000..683abc4142 --- /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 0000000000..a7eaf87b9e --- /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 0000000000..444722bf83 --- /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 0000000000..35ae3d2144 --- /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 0000000000..6eaa84e225 --- /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 0000000000..f3716674c5 --- /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 0000000000..fc59431d08 --- /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 0000000000..2f31888bf6 --- /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 0000000000..16afc68b3d --- /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 0000000000..62db6d069f --- /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 0000000000..ebe4484873 --- /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 8435dac636..fd1793b6bc 100644 --- a/libs/nativewindow/ANativeWindow.cpp +++ b/libs/nativewindow/ANativeWindow.cpp @@ -33,6 +33,12 @@ static int32_t query(ANativeWindow* window, int what) { 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 @@ int32_t ANativeWindow_setBuffersDataSpace(ANativeWindow* window, int32_t dataSpa 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 @@ int32_t ANativeWindow_getBuffersDataSpace(ANativeWindow* window) { 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_setSharedBufferMode(ANativeWindow* window, bool sharedBufferMo 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 a756bc7bd0..52d73e0a56 100644 --- a/libs/nativewindow/Android.bp +++ b/libs/nativewindow/Android.bp @@ -62,7 +62,6 @@ cc_library { ], shared_libs: [ - "libhardware", "libcutils", "liblog", "libutils", diff --git a/libs/nativewindow/TEST_MAPPING b/libs/nativewindow/TEST_MAPPING new file mode 100644 index 0000000000..3d7f3c28f4 --- /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 2899bcf1f7..e759513a63 100644 --- a/libs/nativewindow/include/android/data_space.h +++ b/libs/nativewindow/include/android/data_space.h @@ -101,6 +101,56 @@ enum ADataSpace { * 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 3e436e3b07..36aad2eced 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 @@ int32_t ANativeWindow_getBuffersDataSpace(ANativeWindow* window) __INTRODUCED_IN #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 0000000000..2d1354cdf1 --- /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 61590e0196..b78fc5dbbc 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 @@ __BEGIN_DECLS /* 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 @@ enum { */ 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 @@ enum { /* * 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 { */ 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 @@ static inline int native_window_get_consumer_usage(struct ANativeWindow* window, 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 995ba44d20..500052c936 100644 --- a/libs/nativewindow/include/vndk/window.h +++ b/libs/nativewindow/include/vndk/window.h @@ -316,6 +316,15 @@ int ANativeWindow_setSharedBufferMode(ANativeWindow* window, bool sharedBufferMo */ 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 db1c9b784e..1b5d20dff7 100644 --- a/libs/nativewindow/libnativewindow.map.txt +++ b/libs/nativewindow/libnativewindow.map.txt @@ -22,12 +22,20 @@ LIBNATIVEWINDOW { 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 @@ LIBNATIVEWINDOW { 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 0000000000..6cf8291da2 --- /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 20071be668..cdb3d2054f 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 befd88fd07..aa9b4f7e78 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 cc252d67ae..eb6080fc21 100644 --- a/libs/renderengine/Android.bp +++ b/libs/renderengine/Android.bp @@ -52,9 +52,15 @@ filegroup { "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 @@ cc_library_static { "-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 f5387f28ea..ed2f45fdf5 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 @@ float* Mesh::getCropCoords() { 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 @@ size_t Mesh::getTexCoordsSize() const { 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 @@ size_t Mesh::getStride() const { 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 166c267bc8..0fdf093b2f 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 056df5ead8..2139acb715 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 @@ static status_t selectConfigForAttribute(EGLDisplay dpy, EGLint const* attrs, EG 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 @@ static status_t selectEGLConfig(EGLDisplay display, EGLint format, EGLint render 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 @@ static status_t selectEGLConfig(EGLDisplay display, EGLint format, EGLint render 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 @@ static status_t selectEGLConfig(EGLDisplay display, EGLint format, EGLint render 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 @@ std::unique_ptr<GLESRenderEngine> GLESRenderEngine::create(int hwcFormat, uint32 // 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 @@ std::unique_ptr<GLESRenderEngine> GLESRenderEngine::create(int hwcFormat, uint32 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 @@ EGLConfig GLESRenderEngine::chooseEglConfig(EGLDisplay display, int format, bool 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 @@ GLESRenderEngine::GLESRenderEngine(uint32_t featureFlags, EGLDisplay display, EG // 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 @@ GLESRenderEngine::GLESRenderEngine(uint32_t featureFlags, EGLDisplay display, EG 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 @@ Framebuffer* GLESRenderEngine::getFramebufferForDrawing() { 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 @@ void GLESRenderEngine::fillRegionWithColor(const Region& region, float red, floa 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 @@ void GLESRenderEngine::handleRoundedCorners(const DisplaySettings& display, // 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 @@ void GLESRenderEngine::handleRoundedCorners(const DisplaySettings& display, 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 @@ status_t GLESRenderEngine::bindFrameBuffer(Framebuffer* framebuffer) { 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 @@ bool GLESRenderEngine::useProtectedContext(bool useProtectedContext) { 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 @@ EGLImageKHR GLESRenderEngine::createFramebufferImageIfNeeded(ANativeWindowBuffer } 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 @@ status_t GLESRenderEngine::drawLayers(const DisplaySettings& display, 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 @@ status_t GLESRenderEngine::drawLayers(const DisplaySettings& display, 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 @@ status_t GLESRenderEngine::drawLayers(const DisplaySettings& display, // 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(); + + 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; + 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 @@ status_t GLESRenderEngine::drawLayers(const DisplaySettings& display, 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 @@ status_t GLESRenderEngine::drawLayers(const DisplaySettings& display, // 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 @@ void GLESRenderEngine::setupLayerTexturing(const Texture& texture) { 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 @@ void GLESRenderEngine::drawMesh(const Mesh& mesh) { 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 @@ void GLESRenderEngine::drawMesh(const Mesh& mesh) { managedState.outputTransferFunction = Description::dataSpaceToTransferFunction(outputTransfer); } + } - ProgramCache::getInstance().useProgram(mInProtectedContext ? mProtectedEGLContext - : mEGLContext, - managedState); - - glDrawArrays(mesh.getPrimitive(), 0, mesh.getVertexCount()); + ProgramCache::getInstance().useProgram(mInProtectedContext ? mProtectedEGLContext : mEGLContext, + managedState); - 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 @@ void GLESRenderEngine::drawMesh(const Mesh& mesh) { 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 @@ EGLContext GLESRenderEngine::createEglContext(EGLDisplay display, EGLConfig conf 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 @@ EGLSurface GLESRenderEngine::createDummyEglPbufferSurface(EGLDisplay display, EG } 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::FlushTracer::loop() { } } +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 dd60e50c67..61986ff721 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 @@ class Texture; 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 @@ public: 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 @@ protected: 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 @@ private: 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 @@ private: // 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 @@ private: 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 @@ private: }; 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 5fbb5ba7d7..383486b6b8 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 @@ bool GLFramebuffer::setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, boo 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 b7650bbfd9..6757695ddb 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 GLESRenderEngine; 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 0000000000..2423a3467e --- /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 0000000000..250a9d77d0 --- /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 0000000000..3181f9bebb --- /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 0000000000..112f97623b --- /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 0000000000..da8b435854 --- /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 0000000000..912c8bb7b3 --- /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 0000000000..e50c471b6d --- /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 0000000000..c0fd0c1b04 --- /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 fe9d909923..f4fbf3524a 100644 --- a/libs/renderengine/gl/Program.cpp +++ b/libs/renderengine/gl/Program.cpp @@ -37,6 +37,8 @@ Program::Program(const ProgramCache::Key& /*needs*/, const char* vertex, const c 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 @@ Program::Program(const ProgramCache::Key& /*needs*/, const char* vertex, const c 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 @@ void Program::setUniforms(const Description& desc) { 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 bc9cf08b8b..fc3755e78f 100644 --- a/libs/renderengine/gl/Program.h +++ b/libs/renderengine/gl/Program.h @@ -44,7 +44,13 @@ public: 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 @@ private: /* 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 d242677f0c..3ae35ec4e9 100644 --- a/libs/renderengine/gl/ProgramCache.cpp +++ b/libs/renderengine/gl/ProgramCache.cpp @@ -77,9 +77,38 @@ Formatter& dedent(Formatter& f) { 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 @@ ProgramCache::Key ProgramCache::computeKey(const Description& description) { .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 @@ void ProgramCache::generateToneMappingProcess(Formatter& fs, const Key& needs) { 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 @@ void ProgramCache::generateOETF(Formatter& fs, const Key& needs) { 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 @@ String8 ProgramCache::generateVertexShader(const Key& needs) { 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 @@ String8 ProgramCache::generateFragmentShader(const Key& needs) { 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 @@ String8 ProgramCache::generateFragmentShader(const Key& needs) { )__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 @@ String8 ProgramCache::generateFragmentShader(const Key& needs) { } 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 @@ String8 ProgramCache::generateFragmentShader(const Key& needs) { } 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 400ad74fca..901e6315a0 100644 --- a/libs/renderengine/gl/ProgramCache.h +++ b/libs/renderengine/gl/ProgramCache.h @@ -112,6 +112,11 @@ public: 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 @@ public: } 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 @@ public: 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 @@ public: ~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 0000000000..19f18c0a7f --- /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 0000000000..593a8fd54e --- /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 0000000000..bb35889665 --- /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 0000000000..6da2a5af58 --- /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 9c9884a35c..ca16d2c727 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 @@ struct DisplaySettings { // 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 @@ struct DisplaySettings { 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 b8bf8019de..95e9367fea 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 @@ struct Buffer { // 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 @@ struct Buffer { // 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 @@ struct PixelSource { 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 @@ struct LayerSettings { // 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 7618424e85..167f13f1bc 100644 --- a/libs/renderengine/include/renderengine/Mesh.h +++ b/libs/renderengine/include/renderengine/Mesh.h @@ -26,13 +26,14 @@ namespace renderengine { 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 @@ public: 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 @@ public: 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 @@ public: // 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 @@ public: // 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 @@ public: 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 @@ private: 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 c6a7bd843a..e06e1287c1 100644 --- a/libs/renderengine/include/renderengine/RenderEngine.h +++ b/libs/renderengine/include/renderengine/RenderEngine.h @@ -48,6 +48,7 @@ class BindNativeBufferAsFramebuffer; class Image; class Mesh; class Texture; +struct RenderEngineCreationArgs; namespace impl { class RenderEngine; @@ -60,16 +61,13 @@ enum class Protection { 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 @@ public: // 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 @@ public: 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 @@ public: // 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 @@ public: // @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 @@ protected: 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 @@ public: 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 b4d3ef24ba..df0f17a6d5 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 @@ public: 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 @@ public: 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 @@ public: 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 bd2055f4e5..a62161a8a8 100644 --- a/libs/renderengine/include/renderengine/private/Description.h +++ b/libs/renderengine/include/renderengine/private/Description.h @@ -71,6 +71,8 @@ struct Description { TransferFunction outputTransferFunction = TransferFunction::LINEAR; float displayMaxLuminance; + float maxMasteringLuminance; + float maxContentLuminance; // projection matrix mat4 projectionMatrix; @@ -79,6 +81,9 @@ struct Description { 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 fce5e69e5b..0b5b1e4be8 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 @@ struct RenderEngineTest : public ::testing::Test { 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 @@ struct RenderEngineTest : public ::testing::Test { 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 @@ struct RenderEngineTest : public ::testing::Test { 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 @@ struct RenderEngineTest : public ::testing::Test { } 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 @@ struct RenderEngineTest : public ::testing::Test { 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 @@ struct RenderEngineTest : public ::testing::Test { void fillBufferPhysicalOffset(); template <typename SourceVariant> - void fillBufferCheckers(mat4 transform); + void fillBufferCheckers(uint32_t rotation); template <typename SourceVariant> void fillBufferCheckersRotate0(); @@ -199,6 +331,9 @@ struct RenderEngineTest : public ::testing::Test { void fillBufferWithRoundedCorners(); template <typename SourceVariant> + void fillBufferAndBlurBackground(); + + template <typename SourceVariant> void overlayCorners(); void fillRedBufferTextureTransform(); @@ -219,6 +354,11 @@ struct RenderEngineTest : public ::testing::Test { 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 @@ void RenderEngineTest::fillBuffer(half r, half g, half b, half a) { 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 @@ void RenderEngineTest::fillRedOffsetBuffer() { 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 @@ void RenderEngineTest::fillBufferPhysicalOffset() { } 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 @@ void RenderEngineTest::fillBufferCheckers(mat4 transform) { 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 @@ void RenderEngineTest::fillBufferCheckersRotate0() { 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 @@ void RenderEngineTest::fillBufferCheckersRotate90() { 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 @@ void RenderEngineTest::fillBufferCheckersRotate180() { 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 @@ void RenderEngineTest::fillBufferWithLayerTransform() { // 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 @@ void RenderEngineTest::fillBufferWithLayerTransform() { 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 @@ void RenderEngineTest::fillBufferWithColorTransform() { 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 @@ void RenderEngineTest::fillBufferWithColorTransform() { 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 @@ void RenderEngineTest::fillRedBufferWithRoundedCorners() { 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 @@ void RenderEngineTest::fillRedBufferWithRoundedCorners() { 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 @@ void RenderEngineTest::fillBufferWithRoundedCorners() { } 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 @@ void RenderEngineTest::overlayCorners() { 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 @@ void RenderEngineTest::overlayCorners() { 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 @@ void RenderEngineTest::fillRedBufferTextureTransform() { 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 @@ void RenderEngineTest::fillRedBufferTextureTransform() { 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 @@ void RenderEngineTest::fillRedBufferWithPremultiplyAlpha() { // 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 @@ void RenderEngineTest::fillRedBufferWithPremultiplyAlpha() { 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 @@ void RenderEngineTest::fillRedBufferWithoutPremultiplyAlpha() { // 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 @@ void RenderEngineTest::fillRedBufferWithoutPremultiplyAlpha() { 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 @@ void RenderEngineTest::clearLeftRegion() { 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 @@ void RenderEngineTest::clearRegion() { 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 @@ TEST_F(RenderEngineTest, drawLayers_nullOutputFence) { 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 @@ TEST_F(RenderEngineTest, drawLayers_doesNotCacheFramebuffer) { 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 @@ TEST_F(RenderEngineTest, drawLayers_fillBufferRoundedCorners_colorSource) { fillBufferWithRoundedCorners<ColorSourceVariant>(); } +TEST_F(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_colorSource) { + fillBufferAndBlurBackground<ColorSourceVariant>(); +} + TEST_F(RenderEngineTest, drawLayers_overlayCorners_colorSource) { overlayCorners<ColorSourceVariant>(); } @@ -916,6 +1135,10 @@ TEST_F(RenderEngineTest, drawLayers_fillBufferRoundedCorners_opaqueBufferSource) 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 @@ TEST_F(RenderEngineTest, drawLayers_fillBufferRoundedCorners_bufferSource) { 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 @@ TEST_F(RenderEngineTest, drawLayers_fillsBufferAndCachesImages) { 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 @@ TEST_F(RenderEngineTest, drawLayers_fillsBufferAndCachesImages) { 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 @@ TEST_F(RenderEngineTest, drawLayers_bindExternalBufferCachesImages) { 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 @@ TEST_F(RenderEngineTest, drawLayers_cacheExternalBufferWithNullBuffer) { 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 @@ TEST_F(RenderEngineTest, drawLayers_cacheExternalBufferCachesImages) { 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 5200545a53..8ed09f8ff0 100644 --- a/libs/sensor/ISensorServer.cpp +++ b/libs/sensor/ISensorServer.cpp @@ -199,6 +199,10 @@ status_t BnSensorServer::onTransact( 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 abc910302c..9d817ae0bd 100644 --- a/libs/sensor/Sensor.cpp +++ b/libs/sensor/Sensor.cpp @@ -268,6 +268,10 @@ Sensor::Sensor(struct sensor_t const& hwSensor, const uuid_t& uuid, int halVersi 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 bf8b9f73fe..a4a5d135c0 100644 --- a/libs/sensor/SensorManager.cpp +++ b/libs/sensor/SensorManager.cpp @@ -209,7 +209,7 @@ Sensor const* SensorManager::getDefaultSensor(int type) 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 8388743c22..1ee8c7105c 100644 --- a/libs/ui/Android.bp +++ b/libs/ui/Android.bp @@ -36,9 +36,6 @@ cc_library_shared { srcs: [ "ColorSpace.cpp", - "BufferHubBuffer.cpp", - "BufferHubEventFd.cpp", - "BufferHubMetadata.cpp", "DebugUtils.cpp", "Fence.cpp", "FenceTime.cpp", @@ -46,6 +43,7 @@ cc_library_shared { "Gralloc.cpp", "Gralloc2.cpp", "Gralloc3.cpp", + "Gralloc4.cpp", "GraphicBuffer.cpp", "GraphicBufferAllocator.cpp", "GraphicBufferMapper.cpp", @@ -62,20 +60,27 @@ cc_library_shared { 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 @@ cc_library_shared { 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 @@ cc_library_shared { 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 @@ subdirs = [ "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 da91a979fe..0000000000 --- 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 bffc2ca803..0000000000 --- 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 05bc7ddfbe..0000000000 --- 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 ee06d930d8..f394635aa2 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 @@ std::string decodeStandard(android_dataspace dataspace) { 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 @@ std::string decodeTransfer(android_dataspace dataspace) { 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 @@ std::string decodeRange(android_dataspace dataspace) { 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 @@ std::string decodeColorMode(ColorMode colorMode) { 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 @@ std::string decodeColorTransform(android_color_transform colorTransform) { 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 @@ std::string decodePixelFormat(android::PixelFormat format) { 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 decodeRenderIntent(RenderIntent renderIntent) { 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 4ce891e148..33ab7c470e 100644 --- a/libs/ui/Fence.cpp +++ b/libs/ui/Fence.cpp @@ -62,8 +62,26 @@ status_t Fence::waitForever(const char* logname) { 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 5dc453005d..040a62b542 100644 --- a/libs/ui/Gralloc2.cpp +++ b/libs/ui/Gralloc2.cpp @@ -351,12 +351,6 @@ int Gralloc2Mapper::unlock(buffer_handle_t bufferHandle) const { 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 @@ bool Gralloc2Allocator::isLoaded() const { 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 @@ std::string Gralloc2Allocator::dumpDebugInfo() const { 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 @@ status_t Gralloc2Allocator::allocate(uint32_t width, uint32_t height, PixelForma 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; + } + } + } 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; + } } - return; } } - *outStride = tmpStride; }); diff --git a/libs/ui/Gralloc3.cpp b/libs/ui/Gralloc3.cpp index eb43765733..882674f479 100644 --- a/libs/ui/Gralloc3.cpp +++ b/libs/ui/Gralloc3.cpp @@ -352,7 +352,7 @@ bool Gralloc3Allocator::isLoaded() const { 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 @@ std::string Gralloc3Allocator::dumpDebugInfo() const { 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 @@ status_t Gralloc3Allocator::allocate(uint32_t width, uint32_t height, android::P 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; + } + } + } 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; + } } - return; } } *outStride = tmpStride; diff --git a/libs/ui/Gralloc4.cpp b/libs/ui/Gralloc4.cpp new file mode 100644 index 0000000000..f799ce4cb0 --- /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 3fc6a2d34a..3732fee7f2 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 @@ GraphicBuffer::GraphicBuffer(const native_handle_t* inHandle, HandleWrapMethod m 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 @@ status_t GraphicBuffer::isSupported(uint32_t inWidth, uint32_t inHeight, PixelFo } 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 @@ status_t GraphicBuffer::unflatten(void const*& buffer, size_t& size, int const*& } 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 @@ void GraphicBuffer::addDeathCallback(GraphicBufferDeathCallback deathCallback, v 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 b54ce0fcf7..943d13ec68 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 @@ KeyedVector<buffer_handle_t, 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; } - - if (!mAllocator->isLoaded()) { - LOG_ALWAYS_FATAL("gralloc-allocator is missing"); + mAllocator = std::make_unique<const Gralloc2Allocator>( + reinterpret_cast<const Gralloc2Mapper&>(mMapper.getGrallocMapper())); + if (mAllocator->isLoaded()) { + return; } + + LOG_ALWAYS_FATAL("gralloc-allocator is missing"); } GraphicBufferAllocator::~GraphicBufferAllocator() {} @@ -70,11 +78,11 @@ uint64_t GraphicBufferAllocator::getTotalSize() const { 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 @@ void GraphicBufferAllocator::dump(std::string& result) const { } 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 @@ status_t GraphicBufferAllocator::allocate(uint32_t width, uint32_t height, // 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 @@ status_t GraphicBufferAllocator::allocate(uint32_t width, uint32_t height, 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; +} +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); +} - 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; - } +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 25b7247b1b..d20bd7a899 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 @@ ANDROID_SINGLETON_STATIC_INSTANCE( GraphicBufferMapper ) 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; } - - if (!mMapper->isLoaded()) { - LOG_ALWAYS_FATAL("gralloc-mapper is missing"); + mMapper = std::make_unique<const Gralloc2Mapper>(); + if (mMapper->isLoaded()) { + mMapperVersion = Version::GRALLOC_2; + return; } + + 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 @@ status_t GraphicBufferMapper::isSupported(uint32_t width, uint32_t height, 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 1222cd6fad..e01309b679 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 @@ const Region Region::INVALID_REGION(Rect::INVALID_RECT); // ---------------------------------------------------------------------------- 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 @@ Region::~Region() * 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 @@ static void reverseRectsResolvingJunctions(const Rect* begin, const Rect* end, // add first span immediately do { - dst.add(*current); + dst.push_back(*current); current--; } while (current->top == lastTop && current >= begin); @@ -148,12 +148,12 @@ static void reverseRectsResolvingJunctions(const Rect* begin, const Rect* end, 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 @@ static void reverseRectsResolvingJunctions(const Rect* begin, const Rect* end, 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 @@ static void reverseRectsResolvingJunctions(const Rect* begin, const Rect* end, } if (left < right) { - dst.add(Rect(left, top, right, bottom)); + dst.push_back(Rect(left, top, right, bottom)); } current--; @@ -202,13 +202,14 @@ Region Region::createTJunctionFreeRegion(const Region& r) { 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 @@ Region& Region::operator = (const Region& rhs) 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 @@ Region& Region::makeBoundsSelf() if (mStorage.size() >= 2) { const Rect bounds(getBounds()); mStorage.clear(); - mStorage.add(bounds); + mStorage.push_back(bounds); } return *this; } @@ -256,43 +263,60 @@ bool Region::contains(int x, int y) const { 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::operationSelf(const Rect& r, uint32_t op) { // ---------------------------------------------------------------------------- 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::translateSelf(int x, int y) { 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 @@ const Region Region::operation(const Region& rhs, int dx, int dy, uint32_t op) c 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 @@ Region::rasterizer::~rasterizer() 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 @@ Region::rasterizer::~rasterizer() bounds.left = 0; bounds.right = 0; } - storage.add(bounds); + storage.push_back(bounds); } void Region::rasterizer::operator()(const Rect& rect) @@ -496,15 +524,15 @@ void Region::rasterizer::operator()(const Rect& rect) 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 @@ void Region::rasterizer::flushSpan() } } 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 @@ void Region::rasterizer::flushSpan() 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 @@ void Region::boolean_operation(uint32_t op, Region& dst, } 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 @@ void Region::translate(Region& reg, int dx, int dy) 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 @@ status_t Region::unflatten(void const* buffer, size_t size) { 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 28c3f7bdd9..06b6bfe797 100644 --- a/libs/ui/Transform.cpp +++ b/libs/ui/Transform.cpp @@ -33,8 +33,8 @@ Transform::Transform(const Transform& other) : 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 @@ bool Transform::absIsOne(float f) { 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 5ba189c2c3..0000000000 --- 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 8772304c0f..0000000000 --- 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 3482507399..0000000000 --- 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 92b2bfb282..4685575441 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 decodeColorTransform(android_color_transform colorTransform); 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 0000000000..af00342c0c --- /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 0000000000..d6fbaab387 --- /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 8976d2d584..897060c2ed 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 { -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}; -}; +enum class DisplayConnectionType { Internal, External }; -/* 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 +// Immutable information about physical display. +struct DisplayInfo { + DisplayConnectionType connectionType = DisplayConnectionType::Internal; + float density = 0.f; + bool secure = false; + std::optional<DeviceProductInfo> deviceProductInfo; }; -}; // namespace android +static_assert(std::is_trivially_copyable_v<DisplayInfo>); -#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 0000000000..64efc84f1b --- /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 0000000000..25fe3a0a2a --- /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 4cd9a0b236..bec2552fde 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 @@ inline bool operator==(const FloatRect& a, const FloatRect& b) { 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 6cc23f0ef8..e199648a8b 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 @@ public: 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 @@ public: // 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 @@ public: 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 948f5976eb..f570c428b6 100644 --- a/libs/ui/include/ui/Gralloc2.h +++ b/libs/ui/include/ui/Gralloc2.h @@ -61,9 +61,6 @@ public: 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 @@ public: 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 0965f52772..93a5077313 100644 --- a/libs/ui/include/ui/Gralloc3.h +++ b/libs/ui/include/ui/Gralloc3.h @@ -79,11 +79,12 @@ public: 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 0000000000..4729cbaf67 --- /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 c195342705..57be686592 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ private: 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 @@ private: // 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 06357270f5..3ed988c4c2 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 @@ class GraphicBufferAllocator : public Singleton<GraphicBufferAllocator> 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 @@ public: 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 @@ protected: 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 24614549be..837e3d826b 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 @@ namespace android { // --------------------------------------------------------------------------- class GrallocMapper; -class Rect; class GraphicBufferMapper : public Singleton<GraphicBufferMapper> { @@ -44,10 +44,14 @@ public: 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 @@ public: 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 5dc56c8181..4bdacb0883 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/ui/include/ui/PhysicalDisplayId.h b/libs/ui/include/ui/PhysicalDisplayId.h new file mode 100644 index 0000000000..1a345acc86 --- /dev/null +++ b/libs/ui/include/ui/PhysicalDisplayId.h @@ -0,0 +1,32 @@ +/* + * 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 <cinttypes> +#include <cstdint> + +#define ANDROID_PHYSICAL_DISPLAY_ID_FORMAT PRIu64 + +namespace android { + +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 17688057a9..2f2229e67c 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 @@ public: } }; +// 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 79642ae032..6bb7b8d21c 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 @@ public: // 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 @@ private: // 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 Region& rhs) { 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 0000000000..89008f6694 --- /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 d9b713df4c..f1e825286e 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 @@ struct Size { // 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(); - - // 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); + 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(); + + // 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 @@ inline bool operator<(const Size& lhs, const Size& rhs) { 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 f29a370194..c6bb598d7f 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 @@ class Transform { 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 @@ public: 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 @@ public: 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 @@ private: 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 0000000000..92cfba8114 --- /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 0000000000..c8f1d43bb8 --- /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 0000000000..1450319502 --- /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 0000000000..4e9284925d --- /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 0000000000..bf30166784 --- /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 66098c4edb..0000000000 --- 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 0000000000..6e3fb1e62e --- /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 0000000000..d84fb4b46b --- /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 0a07a48bd1..b53342cb79 100644 --- a/libs/ui/tests/Android.bp +++ b/libs/ui/tests/Android.bp @@ -31,15 +31,15 @@ cc_test { 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 { 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 @@ cc_test { 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" - ], - srcs: [ - "BufferHubBuffer_test.cpp", - "BufferHubEventFd_test.cpp", - "BufferHubMetadata_test.cpp", + "libutils", ], - 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 0c73a72deb..0000000000 --- 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 ef1781f9d2..0000000000 --- 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 eb978cabc6..0000000000 --- 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 efca083e6e..f4c0afa71b 100644 --- a/libs/ui/tests/GraphicBufferAllocator_test.cpp +++ b/libs/ui/tests/GraphicBufferAllocator_test.cpp @@ -51,7 +51,7 @@ public: 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 0000000000..126a945a0a --- /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 127f7eedd6..19551b3604 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 @@ namespace { 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, AllocateNoError) { 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 b104a46364..c6b826d66e 100644 --- a/libs/ui/tests/Region_test.cpp +++ b/libs/ui/tests/Region_test.cpp @@ -152,5 +152,20 @@ TEST_F(RegionTest, Random_TJunction) { } } +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 69e1ac8b27..38f37ad827 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, Int8RangeIsNotClamped) { 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 0000000000..7fcd7de319 --- /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 22c80a4638..d62e3e2192 100644 --- a/libs/ui/tests/mock/MockGrallocAllocator.h +++ b/libs/ui/tests/mock/MockGrallocAllocator.h @@ -32,11 +32,11 @@ public: ~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 115e8666e5..7823e36d3d 100644 --- a/libs/vr/libbufferhub/consumer_buffer.cpp +++ b/libs/vr/libbufferhub/consumer_buffer.cpp @@ -52,12 +52,6 @@ int ConsumerBuffer::LocalAcquire(DvrNativeBufferMetadata* out_meta, 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 @@ int ConsumerBuffer::LocalRelease(const DvrNativeBufferMetadata* meta, 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 3d88ba5dbe..aa9d07282b 100644 --- a/libs/vr/libbufferhub/producer_buffer.cpp +++ b/libs/vr/libbufferhub/producer_buffer.cpp @@ -96,13 +96,6 @@ int ProducerBuffer::LocalPost(const DvrNativeBufferMetadata* meta, 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 @@ int ProducerBuffer::LocalGain(DvrNativeBufferMetadata* out_meta, 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 8cc7081e4f..fab10978d6 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 @@ class BufferHubQueueProducerTest : public ::testing::Test { 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 @@ class BufferHubQueueProducerTest : public ::testing::Test { 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 @@ TEST_F(BufferHubQueueProducerTest, ConnectFirst_ReturnsError) { 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 @@ TEST_F(BufferHubQueueProducerTest, ConnectAgain_ReturnsError) { // 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 @@ TEST_F(BufferHubQueueProducerTest, TakeAsParcelable) { 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 @@ TEST_F(BufferHubQueueProducerTest, TakeAsParcelable) { // 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 @@ TEST_F(BufferHubQueueProducerTest, TakeAsParcelable) { 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 f67e258cee..62856dfbf8 100644 --- a/libs/vr/libdisplay/display_client.cpp +++ b/libs/vr/libdisplay/display_client.cpp @@ -178,6 +178,10 @@ Status<std::string> DisplayClient::GetConfigurationData( 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 f8f5b3ddb3..81546ac5c2 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 @@ class DisplayClient : public pdx::ClientBase<DisplayClient> { 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 3786d1d5e9..9f4cc4afcc 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 @@ struct SurfaceInfo { enum class ConfigFileType : uint32_t { kLensMetrics, kDeviceMetrics, - kDeviceConfiguration + kDeviceConfiguration, + kDeviceEdid }; struct DisplayProtocol { @@ -210,6 +211,7 @@ struct DisplayProtocol { kOpGetSurfaceInfo, kOpCreateQueue, kOpSetAttributes, + kOpGetDisplayIdentificationPort, }; // Aliases. @@ -220,6 +222,8 @@ struct DisplayProtocol { 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 e383bb2cb3..b7abb99559 100644 --- a/libs/vr/libdvr/include/dvr/dvr_api.h +++ b/libs/vr/libdvr/include/dvr/dvr_api.h @@ -85,6 +85,8 @@ enum { 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 b7d94b3471..7b477c4ce2 100644 --- a/libs/vr/libpdx/encoder_performance_test.cpp +++ b/libs/vr/libpdx/encoder_performance_test.cpp @@ -158,12 +158,12 @@ std::chrono::nanoseconds SerializeBaseTest(MessageWriter* writer, 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 @@ std::chrono::nanoseconds DeserializeBaseTest( 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 aeae9d3e5e..99325b5d65 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 b112fa30eb..ba0d69c178 100644 --- a/libs/vr/libpdx/service_dispatcher.cpp +++ b/libs/vr/libpdx/service_dispatcher.cpp @@ -92,9 +92,9 @@ int ServiceDispatcher::RemoveService(const std::shared_ptr<Service>& service) { 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 9bc70ea9c0..810eb196ca 100644 --- a/libs/vr/libpdx_uds/service_endpoint.cpp +++ b/libs/vr/libpdx_uds/service_endpoint.cpp @@ -334,8 +334,8 @@ Status<void> Endpoint::CloseChannelLocked(int32_t channel_id) { 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 12ce74b1ea..abc64bde5a 100644 --- a/libs/vr/libvrflinger/Android.bp +++ b/libs/vr/libvrflinger/Android.bp @@ -35,11 +35,12 @@ staticLibraries = [ ] 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 @@ headerLibraries = [ "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 87162c0b5a..582fed3a4a 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 @@ Status<void> DisplayService::HandleMessage(pdx::Message& message) { *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 @@ Status<display::Metrics> DisplayService::OnGetMetrics( 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 @@ pdx::Status<std::string> DisplayService::OnGetConfigurationData( 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 @@ pdx::Status<std::string> DisplayService::OnGetConfigurationData( 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 e0f2eddfea..89f1eaee33 100644 --- a/libs/vr/libvrflinger/display_service.h +++ b/libs/vr/libvrflinger/display_service.h @@ -80,6 +80,7 @@ class DisplayService : public pdx::ServiceBase<DisplayService> { 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 1cf5f17f62..0d5eb8080f 100644 --- a/libs/vr/libvrflinger/epoll_event_dispatcher.cpp +++ b/libs/vr/libvrflinger/epoll_event_dispatcher.cpp @@ -68,8 +68,8 @@ pdx::Status<void> EpollEventDispatcher::RemoveEventHandler(int fd) { 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 57a77cffd4..70f303b208 100644 --- a/libs/vr/libvrflinger/hardware_composer.cpp +++ b/libs/vr/libvrflinger/hardware_composer.cpp @@ -50,8 +50,6 @@ const char kDvrPerformanceProperty[] = "sys.dvr.performance"; 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 @@ HardwareComposer::~HardwareComposer(void) { 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 @@ bool HardwareComposer::Initialize( "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 @@ bool HardwareComposer::UpdateTargetDisplay() { 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 @@ bool HardwareComposer::UpdateTargetDisplay() { 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> HardwareComposer::ComposerCallback::onVsync(Hwc2::Display display, 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 db0d6a7670..bfce10b5b0 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 @@ class HardwareComposer { 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 @@ class HardwareComposer { 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 @@ class HardwareComposer { 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 410e2344ce..7fafd3bf50 100644 --- a/libs/vr/libvrflinger/tests/Android.bp +++ b/libs/vr/libvrflinger/tests/Android.bp @@ -11,6 +11,7 @@ shared_libs = [ "libutils", "libnativewindow", "libpdx_default_transport", + "libSurfaceFlingerProp", ] static_libs = [ @@ -32,5 +33,6 @@ cc_test { "-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 3b449f298f..ac44f74151 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 @@ TEST(VrFlingerTest, ActivateDeactivate) { // 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; } |