diff options
71 files changed, 5167 insertions, 407 deletions
diff --git a/Android.bp b/Android.bp index bf4cf5daf8..9829c7fbad 100644 --- a/Android.bp +++ b/Android.bp @@ -7,6 +7,7 @@ ndk_headers { } subdirs = [ + "adbd_auth", "cmds/*", "headers", "libs/*", diff --git a/cmds/installd/migrate_legacy_obb_data.sh b/cmds/installd/migrate_legacy_obb_data.sh index 10756881be..0e6d7b9c62 100644 --- a/cmds/installd/migrate_legacy_obb_data.sh +++ b/cmds/installd/migrate_legacy_obb_data.sh @@ -15,17 +15,17 @@ # See the License for the specific language governing permissions and # limitations under the License. -rm -rf /sdcard/Android/obb/test_probe -mkdir -p /sdcard/Android/obb/ -touch /sdcard/Android/obb/test_probe +rm -rf /data/media/Android/obb/test_probe +mkdir -p /data/media/Android/obb/ +touch /data/media/Android/obb/test_probe if ! test -f /data/media/0/Android/obb/test_probe ; then log -p i -t migrate_legacy_obb_data "No support for 'unshared_obb'. Not migrating" - rm -rf /sdcard/Android/obb/test_probe + rm -rf /data/media/Android/obb/test_probe exit 0 fi # Delete the test file, and remove the obb folder if it is empty -rm -rf /sdcard/Android/obb/test_probe +rm -rf /data/media/Android/obb/test_probe rmdir /data/media/obb if ! test -d /data/media/obb ; then diff --git a/headers/media_plugin/media/openmax/OMX_Video.h b/headers/media_plugin/media/openmax/OMX_Video.h index b6edaa900e..81ee5fb2e5 100644 --- a/headers/media_plugin/media/openmax/OMX_Video.h +++ b/headers/media_plugin/media/openmax/OMX_Video.h @@ -90,6 +90,7 @@ typedef enum OMX_VIDEO_CODINGTYPE { OMX_VIDEO_CodingHEVC, /**< ITU H.265/HEVC */ OMX_VIDEO_CodingDolbyVision,/**< Dolby Vision */ OMX_VIDEO_CodingImageHEIC, /**< HEIF image encoded with HEVC */ + OMX_VIDEO_CodingAV1, /**< AV1 */ OMX_VIDEO_CodingKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_VIDEO_CodingVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_VIDEO_CodingMax = 0x7FFFFFFF diff --git a/libs/adbd_auth/Android.bp b/libs/adbd_auth/Android.bp new file mode 100644 index 0000000000..9cf014380c --- /dev/null +++ b/libs/adbd_auth/Android.bp @@ -0,0 +1,44 @@ +// 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: "libadbd_auth", + cflags: [ + "-Wall", + "-Wextra", + "-Wthread-safety", + "-Werror", + ], + srcs: ["adbd_auth.cpp"], + export_include_dirs: ["include"], + + version_script: "libadbd_auth.map.txt", + stubs: { + symbol_file: "libadbd_auth.map.txt", + }, + + host_supported: true, + recovery_available: true, + target: { + darwin: { + enabled: false, + } + }, + + shared_libs: [ + "libbase", + "libcutils", + "liblog", + ], +} diff --git a/libs/adbd_auth/adbd_auth.cpp b/libs/adbd_auth/adbd_auth.cpp new file mode 100644 index 0000000000..64791098ee --- /dev/null +++ b/libs/adbd_auth/adbd_auth.cpp @@ -0,0 +1,443 @@ +/* + * 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 ANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION + +#include "include/adbd_auth.h" + +#include <inttypes.h> +#include <sys/epoll.h> +#include <sys/eventfd.h> +#include <sys/uio.h> + +#include <chrono> +#include <deque> +#include <string> +#include <string_view> +#include <tuple> +#include <unordered_map> +#include <utility> +#include <variant> +#include <vector> + +#include <android-base/file.h> +#include <android-base/logging.h> +#include <android-base/macros.h> +#include <android-base/strings.h> +#include <android-base/thread_annotations.h> +#include <android-base/unique_fd.h> +#include <cutils/sockets.h> + +using android::base::unique_fd; + +struct AdbdAuthPacketAuthenticated { + std::string public_key; +}; + +struct AdbdAuthPacketDisconnected { + std::string public_key; +}; + +struct AdbdAuthPacketRequestAuthorization { + std::string public_key; +}; + +using AdbdAuthPacket = std::variant<AdbdAuthPacketAuthenticated, AdbdAuthPacketDisconnected, + AdbdAuthPacketRequestAuthorization>; + +struct AdbdAuthContext { + static constexpr uint64_t kEpollConstSocket = 0; + static constexpr uint64_t kEpollConstEventFd = 1; + static constexpr uint64_t kEpollConstFramework = 2; + +public: + explicit AdbdAuthContext(AdbdAuthCallbacksV1* callbacks) : next_id_(0), callbacks_(*callbacks) { + epoll_fd_.reset(epoll_create1(EPOLL_CLOEXEC)); + if (epoll_fd_ == -1) { + PLOG(FATAL) << "failed to create epoll fd"; + } + + event_fd_.reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK)); + if (event_fd_ == -1) { + PLOG(FATAL) << "failed to create eventfd"; + } + + sock_fd_.reset(android_get_control_socket("adbd")); + if (sock_fd_ == -1) { + PLOG(ERROR) << "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"; + } + + if (fcntl(sock_fd_.get(), F_SETFL, O_NONBLOCK) != 0) { + PLOG(FATAL) << "failed to make adbd authentication socket nonblocking"; + } + + if (listen(sock_fd_.get(), 4) != 0) { + PLOG(FATAL) << "failed to listen on adbd authentication socket"; + } + } + } + + AdbdAuthContext(const AdbdAuthContext& copy) = delete; + AdbdAuthContext(AdbdAuthContext&& move) = delete; + AdbdAuthContext& operator=(const AdbdAuthContext& copy) = delete; + AdbdAuthContext& operator=(AdbdAuthContext&& move) = delete; + + uint64_t NextId() { return next_id_++; } + + void DispatchPendingPrompt() REQUIRES(mutex_) { + if (dispatched_prompt_) { + LOG(INFO) << "adbd_auth: prompt currently pending, skipping"; + return; + } + + if (pending_prompts_.empty()) { + LOG(INFO) << "adbd_auth: no prompts to send"; + return; + } + + LOG(INFO) << "adbd_auth: prompting user for adb authentication"; + auto [id, public_key, arg] = std::move(pending_prompts_.front()); + pending_prompts_.pop_front(); + + this->output_queue_.emplace_back( + AdbdAuthPacketRequestAuthorization{.public_key = public_key}); + + Interrupt(); + dispatched_prompt_ = std::make_tuple(id, public_key, arg); + } + + void UpdateFrameworkWritable() REQUIRES(mutex_) { + // This might result in redundant calls to EPOLL_CTL_MOD if, for example, we get notified + // at the same time as a framework connection, but that's unlikely and this doesn't need to + // be fast anyway. + if (framework_fd_ != -1) { + struct epoll_event event; + event.events = EPOLLIN; + if (!output_queue_.empty()) { + LOG(INFO) << "marking framework writable"; + event.events |= EPOLLOUT; + } + event.data.u64 = kEpollConstFramework; + CHECK_EQ(0, epoll_ctl(epoll_fd_.get(), EPOLL_CTL_MOD, framework_fd_.get(), &event)); + } + } + + void ReplaceFrameworkFd(unique_fd new_fd) REQUIRES(mutex_) { + LOG(INFO) << "received new framework fd " << new_fd.get() + << " (current = " << framework_fd_.get() << ")"; + + // If we already had a framework fd, clean up after ourselves. + if (framework_fd_ != -1) { + output_queue_.clear(); + dispatched_prompt_.reset(); + CHECK_EQ(0, epoll_ctl(epoll_fd_.get(), EPOLL_CTL_DEL, framework_fd_.get(), nullptr)); + framework_fd_.reset(); + } + + if (new_fd != -1) { + struct epoll_event event; + event.events = EPOLLIN; + if (!output_queue_.empty()) { + LOG(INFO) << "marking framework writable"; + event.events |= EPOLLOUT; + } + event.data.u64 = kEpollConstFramework; + CHECK_EQ(0, epoll_ctl(epoll_fd_.get(), EPOLL_CTL_ADD, new_fd.get(), &event)); + framework_fd_ = std::move(new_fd); + } + } + + void HandlePacket(std::string_view packet) REQUIRES(mutex_) { + LOG(INFO) << "received packet: " << packet; + + if (packet.length() < 2) { + LOG(ERROR) << "received packet of invalid length"; + ReplaceFrameworkFd(unique_fd()); + } + + if (packet[0] == 'O' && packet[1] == 'K') { + CHECK(this->dispatched_prompt_.has_value()); + auto& [id, key, arg] = *this->dispatched_prompt_; + keys_.emplace(id, std::move(key)); + + this->callbacks_.key_authorized(arg, id); + this->dispatched_prompt_ = std::nullopt; + } else if (packet[0] == 'N' && packet[1] == 'O') { + CHECK_EQ(2UL, packet.length()); + // TODO: Do we want a callback if the key is denied? + this->dispatched_prompt_ = std::nullopt; + DispatchPendingPrompt(); + } else { + LOG(ERROR) << "unhandled packet: " << packet; + ReplaceFrameworkFd(unique_fd()); + } + } + + bool SendPacket() REQUIRES(mutex_) { + if (output_queue_.empty()) { + return false; + } + + CHECK_NE(-1, framework_fd_.get()); + + auto& packet = output_queue_.front(); + struct iovec iovs[2]; + if (auto* p = std::get_if<AdbdAuthPacketAuthenticated>(&packet)) { + iovs[0].iov_base = const_cast<char*>("CK"); + iovs[0].iov_len = 2; + iovs[1].iov_base = p->public_key.data(); + iovs[1].iov_len = p->public_key.size(); + } else if (auto* p = std::get_if<AdbdAuthPacketDisconnected>(&packet)) { + iovs[0].iov_base = const_cast<char*>("DC"); + iovs[0].iov_len = 2; + iovs[1].iov_base = p->public_key.data(); + iovs[1].iov_len = p->public_key.size(); + } else if (auto* p = std::get_if<AdbdAuthPacketRequestAuthorization>(&packet)) { + iovs[0].iov_base = const_cast<char*>("PK"); + iovs[0].iov_len = 2; + iovs[1].iov_base = p->public_key.data(); + iovs[1].iov_len = p->public_key.size(); + } else { + LOG(FATAL) << "unhandled packet type?"; + } + + output_queue_.pop_front(); + + ssize_t rc = writev(framework_fd_.get(), iovs, 2); + if (rc == -1 && errno != EAGAIN && errno != EWOULDBLOCK) { + PLOG(ERROR) << "failed to write to framework fd"; + ReplaceFrameworkFd(unique_fd()); + return false; + } + + return true; + } + + void Run() { + if (sock_fd_ == -1) { + LOG(ERROR) << "adbd authentication socket unavailable, disabling user prompts"; + } else { + struct epoll_event event; + event.events = EPOLLIN; + event.data.u64 = kEpollConstSocket; + CHECK_EQ(0, epoll_ctl(epoll_fd_.get(), EPOLL_CTL_ADD, sock_fd_.get(), &event)); + } + + { + struct epoll_event event; + event.events = EPOLLIN; + event.data.u64 = kEpollConstEventFd; + CHECK_EQ(0, epoll_ctl(epoll_fd_.get(), EPOLL_CTL_ADD, event_fd_.get(), &event)); + } + + while (true) { + 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"; + } else if (rc == 0) { + LOG(FATAL) << "epoll_wait returned 0"; + } + + bool restart = false; + for (int i = 0; i < rc; ++i) { + if (restart) { + break; + } + + struct epoll_event& event = events[i]; + switch (event.data.u64) { + case kEpollConstSocket: { + 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"; + } + + LOG(INFO) << "adbd_auth: received a new framework connection"; + std::lock_guard<std::mutex> lock(mutex_); + ReplaceFrameworkFd(std::move(new_framework_fd)); + + // Stop iterating over events: one of the later ones might be the old + // framework fd. + restart = false; + break; + } + + case kEpollConstEventFd: { + // We were woken up to write something. + 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 << ")"; + } + + std::lock_guard<std::mutex> lock(mutex_); + UpdateFrameworkWritable(); + break; + } + + case kEpollConstFramework: { + char buf[4096]; + 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"; + } else if (rc == 0) { + LOG(INFO) << "hit EOF on framework fd"; + std::lock_guard<std::mutex> lock(mutex_); + ReplaceFrameworkFd(unique_fd()); + } else { + std::lock_guard<std::mutex> lock(mutex_); + HandlePacket(std::string_view(buf, rc)); + } + } + + if (event.events & EPOLLOUT) { + std::lock_guard<std::mutex> lock(mutex_); + while (SendPacket()) { + continue; + } + UpdateFrameworkWritable(); + } + + break; + } + } + } + } + } + + static constexpr const char* key_paths[] = {"/adb_keys", "/data/misc/adb/adb_keys"}; + void IteratePublicKeys(bool (*callback)(const char*, size_t, void*), void* arg) { + for (const auto& path : key_paths) { + if (access(path, R_OK) == 0) { + LOG(INFO) << "Loading keys from " << path; + std::string content; + if (!android::base::ReadFileToString(path, &content)) { + PLOG(ERROR) << "Couldn't read " << path; + continue; + } + for (const auto& line : android::base::Split(content, "\n")) { + if (!callback(line.data(), line.size(), arg)) { + return; + } + } + } + } + } + + uint64_t PromptUser(std::string_view public_key, void* arg) EXCLUDES(mutex_) { + uint64_t id = NextId(); + + std::lock_guard<std::mutex> lock(mutex_); + pending_prompts_.emplace_back(id, public_key, arg); + DispatchPendingPrompt(); + return id; + } + + uint64_t NotifyAuthenticated(std::string_view public_key) EXCLUDES(mutex_) { + uint64_t id = NextId(); + std::lock_guard<std::mutex> lock(mutex_); + keys_.emplace(id, public_key); + output_queue_.emplace_back( + AdbdAuthPacketDisconnected{.public_key = std::string(public_key)}); + return id; + } + + void NotifyDisconnected(uint64_t id) EXCLUDES(mutex_) { + 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"; + return; + } + output_queue_.emplace_back(AdbdAuthPacketDisconnected{.public_key = std::move(it->second)}); + keys_.erase(it); + } + + // Interrupt the worker thread to do some work. + void Interrupt() { + uint64_t value = 1; + ssize_t rc = write(event_fd_.get(), &value, sizeof(value)); + if (rc == -1) { + PLOG(FATAL) << "write to eventfd failed"; + } else if (rc != sizeof(value)) { + LOG(FATAL) << "write to eventfd returned short (" << rc << ")"; + } + } + + unique_fd epoll_fd_; + unique_fd event_fd_; + unique_fd sock_fd_; + unique_fd framework_fd_; + + std::atomic<uint64_t> next_id_; + AdbdAuthCallbacksV1 callbacks_; + + std::mutex mutex_; + std::unordered_map<uint64_t, std::string> keys_ GUARDED_BY(mutex_); + + // We keep two separate queues: one to handle backpressure from the socket (output_queue_) + // and one to make sure we only dispatch one authrequest at a time (pending_prompts_). + std::deque<AdbdAuthPacket> output_queue_; + + std::optional<std::tuple<uint64_t, std::string, void*>> dispatched_prompt_ GUARDED_BY(mutex_); + std::deque<std::tuple<uint64_t, std::string, void*>> pending_prompts_ GUARDED_BY(mutex_); +}; + +AdbdAuthContext* adbd_auth_new(AdbdAuthCallbacks* callbacks) { + if (callbacks->version != 1) { + LOG(ERROR) << "received unknown AdbdAuthCallbacks version " << callbacks->version; + return nullptr; + } + + return new AdbdAuthContext(&callbacks->callbacks.v1); +} + +void adbd_auth_delete(AdbdAuthContext* ctx) { + delete ctx; +} + +void adbd_auth_run(AdbdAuthContext* ctx) { + return ctx->Run(); +} + +void adbd_auth_get_public_keys(AdbdAuthContext* ctx, + bool (*callback)(const char* public_key, size_t len, void* arg), + void* arg) { + ctx->IteratePublicKeys(callback, arg); +} + +uint64_t adbd_auth_notify_auth(AdbdAuthContext* ctx, const char* public_key, size_t len) { + return ctx->NotifyAuthenticated(std::string_view(public_key, len)); +} + +void adbd_auth_notify_disconnect(AdbdAuthContext* ctx, uint64_t id) { + return ctx->NotifyDisconnected(id); +} + +void adbd_auth_prompt_user(AdbdAuthContext* ctx, const char* public_key, size_t len, + void* arg) { + ctx->PromptUser(std::string_view(public_key, len), arg); +} + +bool adbd_auth_supports_feature(AdbdAuthFeature) { + return false; +} diff --git a/libs/adbd_auth/include/adbd_auth.h b/libs/adbd_auth/include/adbd_auth.h new file mode 100644 index 0000000000..b7c1cb88cc --- /dev/null +++ b/libs/adbd_auth/include/adbd_auth.h @@ -0,0 +1,65 @@ +#pragma once + +/* + * 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 <stdbool.h> +#include <stdint.h> +#include <sys/types.h> + +extern "C" { + +struct AdbdAuthCallbacksV1 { + // Callback for a successful user authorization. + void (*key_authorized)(void* arg, uint64_t id); +}; + +struct AdbdAuthCallbacks { + uint32_t version; + union { + AdbdAuthCallbacksV1 v1; + } callbacks; +}; + +struct AdbdAuthContext; + +AdbdAuthContext* adbd_auth_new(AdbdAuthCallbacks* callbacks); +void adbd_auth_delete(AdbdAuthContext* ctx); + +void adbd_auth_run(AdbdAuthContext* ctx); + +// Iterate through the list of authorized public keys. +// Return false from the callback to stop iteration. +void adbd_auth_get_public_keys(AdbdAuthContext* ctx, + bool (*callback)(const char* public_key, size_t len, void* arg), + void* arg); + +// Let system_server know that a key has been successfully used for authentication. +uint64_t adbd_auth_notify_auth(AdbdAuthContext* ctx, const char* public_key, size_t len); + +// Let system_server know that a connection has been closed. +void adbd_auth_notify_disconnect(AdbdAuthContext* ctx, uint64_t id); + +// Prompt the user to authorize a public key. +// When this happens, a callback will be run on the auth thread with the result. +void adbd_auth_prompt_user(AdbdAuthContext* ctx, const char* public_key, size_t len, void* arg); + +enum AdbdAuthFeature { +}; + +bool adbd_auth_supports_feature(AdbdAuthFeature f); + +} diff --git a/libs/adbd_auth/libadbd_auth.map.txt b/libs/adbd_auth/libadbd_auth.map.txt new file mode 100644 index 0000000000..d01233c960 --- /dev/null +++ b/libs/adbd_auth/libadbd_auth.map.txt @@ -0,0 +1,13 @@ +LIBADBD_AUTH { + global: + adbd_auth_new; # apex + adbd_auth_delete; # apex + adbd_auth_run; # apex + adbd_auth_get_public_keys; #apex + adbd_auth_notify_auth; # apex + adbd_auth_notify_disconnect; # apex + adbd_auth_prompt_user; # apex + adbd_auth_supports_feature; # apex + local: + *; +}; diff --git a/libs/arect/Android.bp b/libs/arect/Android.bp index ad8287c203..2518b1427d 100644 --- a/libs/arect/Android.bp +++ b/libs/arect/Android.bp @@ -13,13 +13,18 @@ // limitations under the License. ndk_headers { - name: "libarect_headers", + name: "libarect_headers_for_ndk", from: "include/android", to: "android", srcs: ["include/android/*.h"], license: "NOTICE", } +cc_library_headers { + name: "libarect_headers", + export_include_dirs: ["include"], +} + cc_library_static { name: "libarect", host_supported: true, diff --git a/libs/binder/AppOpsManager.cpp b/libs/binder/AppOpsManager.cpp index 60f047fd90..4f0b7d31a2 100644 --- a/libs/binder/AppOpsManager.cpp +++ b/libs/binder/AppOpsManager.cpp @@ -115,23 +115,19 @@ int32_t AppOpsManager::checkAudioOpNoThrow(int32_t op, int32_t usage, int32_t ui } int32_t AppOpsManager::noteOp(int32_t op, int32_t uid, const String16& callingPackage) { - return noteOp(op, uid, callingPackage, String16(), String16()); + return noteOp(op, uid, callingPackage, std::unique_ptr<String16>(), + String16("Legacy AppOpsManager.noteOp call")); } int32_t AppOpsManager::noteOp(int32_t op, int32_t uid, const String16& callingPackage, - const String16& featureId, const String16& message) { + const std::unique_ptr<String16>& featureId, const String16& message) { sp<IAppOpsService> service = getService(); int32_t mode = service != nullptr - ? service->noteOperation(op, uid, callingPackage) + ? service->noteOperation(op, uid, callingPackage, featureId) : APP_OPS_MANAGER_UNAVAILABLE_MODE; if (mode == AppOpsManager::MODE_ALLOWED) { - if (message.size() == 0) { - markAppOpNoted(uid, callingPackage, op, featureId, - String16("noteOp from native code")); - } else { - markAppOpNoted(uid, callingPackage, op, featureId, message); - } + markAppOpNoted(uid, callingPackage, op, featureId, message); } return mode; @@ -139,32 +135,34 @@ int32_t AppOpsManager::noteOp(int32_t op, int32_t uid, const String16& callingPa int32_t AppOpsManager::startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage, bool startIfModeDefault) { - return startOpNoThrow(op, uid, callingPackage, startIfModeDefault, String16(), String16()); + return startOpNoThrow(op, uid, callingPackage, startIfModeDefault, std::unique_ptr<String16>(), + String16("Legacy AppOpsManager.startOpNoThrow call")); } int32_t AppOpsManager::startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage, - bool startIfModeDefault, const String16& featureId, const String16& message) { + bool startIfModeDefault, const std::unique_ptr<String16>& featureId, + const String16& message) { sp<IAppOpsService> service = getService(); int32_t mode = service != nullptr ? service->startOperation(getToken(service), op, uid, callingPackage, - startIfModeDefault) : APP_OPS_MANAGER_UNAVAILABLE_MODE; + featureId, startIfModeDefault) : APP_OPS_MANAGER_UNAVAILABLE_MODE; if (mode == AppOpsManager::MODE_ALLOWED) { - if (message.size() == 0) { - markAppOpNoted(uid, callingPackage, op, featureId, - String16("startOp from native code")); - } else { - markAppOpNoted(uid, callingPackage, op, featureId, message); - } + markAppOpNoted(uid, callingPackage, op, featureId, message); } return mode; } void AppOpsManager::finishOp(int32_t op, int32_t uid, const String16& callingPackage) { + finishOp(op, uid, callingPackage, std::unique_ptr<String16>()); +} + +void AppOpsManager::finishOp(int32_t op, int32_t uid, const String16& callingPackage, + const std::unique_ptr<String16>& callingFeatureId) { sp<IAppOpsService> service = getService(); if (service != nullptr) { - service->finishOperation(getToken(service), op, uid, callingPackage); + service->finishOperation(getToken(service), op, uid, callingPackage, callingFeatureId); } } @@ -207,7 +205,7 @@ bool AppOpsManager::shouldCollectNotes(int32_t opcode) { } void AppOpsManager::markAppOpNoted(int32_t uid, const String16& packageName, int32_t opCode, - const String16& featureId, const String16& message) { + const std::unique_ptr<String16>& featureId, const String16& message) { // check it the appops needs to be collected and cache result if (appOpsToNote[opCode] == 0) { if (shouldCollectNotes(opCode)) { @@ -221,11 +219,11 @@ void AppOpsManager::markAppOpNoted(int32_t uid, const String16& packageName, int return; } - noteAsyncOp(String16(), uid, packageName, opCode, featureId, message); + noteAsyncOp(std::unique_ptr<String16>(), uid, packageName, opCode, featureId, message); } -void AppOpsManager::noteAsyncOp(const String16& callingPackageName, int32_t uid, - const String16& packageName, int32_t opCode, const String16& featureId, +void AppOpsManager::noteAsyncOp(const std::unique_ptr<String16>& callingPackageName, int32_t uid, + const String16& packageName, int32_t opCode, const std::unique_ptr<String16>& featureId, const String16& message) { sp<IAppOpsService> service = getService(); if (service != nullptr) { diff --git a/libs/binder/IAppOpsService.cpp b/libs/binder/IAppOpsService.cpp index 9760e135a1..b85a5f298e 100644 --- a/libs/binder/IAppOpsService.cpp +++ b/libs/binder/IAppOpsService.cpp @@ -46,12 +46,14 @@ 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::unique_ptr<String16>& featureId) { Parcel data, reply; data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor()); data.writeInt32(code); data.writeInt32(uid); data.writeString16(packageName); + data.writeString16(featureId); remote()->transact(NOTE_OPERATION_TRANSACTION, data, &reply); // fail on exception if (reply.readExceptionCode() != 0) return MODE_ERRORED; @@ -59,13 +61,15 @@ 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::unique_ptr<String16>& featureId, + bool startIfModeDefault) { Parcel data, reply; data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor()); data.writeStrongBinder(token); data.writeInt32(code); data.writeInt32(uid); data.writeString16(packageName); + data.writeString16(featureId); data.writeInt32(startIfModeDefault ? 1 : 0); remote()->transact(START_OPERATION_TRANSACTION, data, &reply); // fail on exception @@ -74,13 +78,14 @@ public: } virtual void finishOperation(const sp<IBinder>& token, int32_t code, int32_t uid, - const String16& packageName) { + const String16& packageName, const std::unique_ptr<String16>& featureId) { Parcel data, reply; data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor()); data.writeStrongBinder(token); data.writeInt32(code); data.writeInt32(uid); data.writeString16(packageName); + data.writeString16(featureId); remote()->transact(FINISH_OPERATION_TRANSACTION, data, &reply); } @@ -144,37 +149,16 @@ public: remote()->transact(SET_CAMERA_AUDIO_RESTRICTION_TRANSACTION, data, &reply); } - virtual void noteAsyncOp(const String16& callingPackageName, int32_t uid, - const String16& packageName, int32_t opCode, const String16& featureId, + virtual void noteAsyncOp(const std::unique_ptr<String16>& callingPackageName, int32_t uid, + const String16& packageName, int32_t opCode, const std::unique_ptr<String16>& featureId, const String16& message) { Parcel data, reply; data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor()); - - // Convert empty callingPackage into null string - if (callingPackageName.size() != 0) { - data.writeString16(callingPackageName); - } else { - data.writeString16(nullptr, 0); - } - + data.writeString16(callingPackageName); data.writeInt32(uid); - - // Convert empty packageName into null string - if (packageName.size() != 0) { - data.writeString16(packageName); - } else { - data.writeString16(nullptr, 0); - } - + data.writeString16(packageName); data.writeInt32(opCode); - - // Convert empty featureId into null string - if (featureId.size() != 0) { - data.writeString16(featureId); - } else { - data.writeString16(nullptr, 0); - } - + data.writeString16(featureId); data.writeString16(message); remote()->transact(NOTE_ASYNC_OP_TRANSACTION, data, &reply); } @@ -217,7 +201,9 @@ 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::unique_ptr<String16> featureId; + data.readString16(&featureId); + int32_t res = noteOperation(code, uid, packageName, featureId); reply->writeNoException(); reply->writeInt32(res); return NO_ERROR; @@ -228,8 +214,11 @@ status_t BnAppOpsService::onTransact( int32_t code = data.readInt32(); int32_t uid = data.readInt32(); String16 packageName = data.readString16(); + std::unique_ptr<String16> featureId; + data.readString16(&featureId); bool startIfModeDefault = data.readInt32() == 1; - int32_t res = startOperation(token, code, uid, packageName, startIfModeDefault); + int32_t res = startOperation(token, code, uid, packageName, featureId, + startIfModeDefault); reply->writeNoException(); reply->writeInt32(res); return NO_ERROR; @@ -240,7 +229,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::unique_ptr<String16> featureId; + data.readString16(&featureId); + finishOperation(token, code, uid, packageName, featureId); reply->writeNoException(); return NO_ERROR; } break; @@ -296,11 +287,13 @@ status_t BnAppOpsService::onTransact( } break; case NOTE_ASYNC_OP_TRANSACTION: { CHECK_INTERFACE(IAppOpsService, data, reply); - String16 callingPackageName = data.readString16(); + std::unique_ptr<String16> callingPackageName; + data.readString16(&callingPackageName); int32_t uid = data.readInt32(); String16 packageName = data.readString16(); int32_t opCode = data.readInt32(); - String16 featureId = data.readString16(); + std::unique_ptr<String16> featureId; + data.readString16(&featureId); String16 message = data.readString16(); noteAsyncOp(callingPackageName, uid, packageName, opCode, featureId, message); reply->writeNoException(); diff --git a/libs/binder/include/binder/AppOpsManager.h b/libs/binder/include/binder/AppOpsManager.h index 2744ce126d..22a017941e 100644 --- a/libs/binder/include/binder/AppOpsManager.h +++ b/libs/binder/include/binder/AppOpsManager.h @@ -134,21 +134,26 @@ public: // 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 String16& featureId, const String16& message); + const std::unique_ptr<String16>& featureId, 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 String16& featureId, const String16& message); + bool startIfModeDefault, const std::unique_ptr<String16>& featureId, + 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::unique_ptr<String16>& featureId); 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); - void noteAsyncOp(const String16& callingPackageName, int32_t uid, const String16& packageName, - int32_t opCode, const String16& featureId, const String16& message); + void noteAsyncOp(const std::unique_ptr<String16>& callingPackageName, int32_t uid, + const String16& packageName, int32_t opCode, const std::unique_ptr<String16>& featureId, + const String16& message); private: Mutex mLock; @@ -156,7 +161,7 @@ private: sp<IAppOpsService> getService(); void markAppOpNoted(int32_t uid, const String16& packageName, int32_t opCode, - const String16& featureId, const String16& message); + const std::unique_ptr<String16>& featureId, const String16& message); bool shouldCollectNotes(int32_t opCode); }; diff --git a/libs/binder/include/binder/IAppOpsService.h b/libs/binder/include/binder/IAppOpsService.h index ad34bc5692..15ba005cec 100644 --- a/libs/binder/include/binder/IAppOpsService.h +++ b/libs/binder/include/binder/IAppOpsService.h @@ -35,11 +35,13 @@ 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::unique_ptr<String16>& featureId) = 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::unique_ptr<String16>& featureId, + bool startIfModeDefault) = 0; virtual void finishOperation(const sp<IBinder>& token, int32_t code, int32_t uid, - const String16& packageName) = 0; + const String16& packageName, const std::unique_ptr<String16>& featureId) = 0; virtual void startWatchingMode(int32_t op, const String16& packageName, const sp<IAppOpsCallback>& callback) = 0; virtual void stopWatchingMode(const sp<IAppOpsCallback>& callback) = 0; @@ -48,8 +50,8 @@ public: 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 void noteAsyncOp(const String16& callingPackageName, int32_t uid, - const String16& packageName, int32_t opCode, const String16& featureId, + virtual void noteAsyncOp(const std::unique_ptr<String16>& callingPackageName, int32_t uid, + const String16& packageName, int32_t opCode, const std::unique_ptr<String16>& featureId, const String16& message) = 0; virtual bool shouldCollectNotes(int32_t opCode) = 0; diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h index bd77567290..2c4326393e 100644 --- a/libs/binder/include/binder/IServiceManager.h +++ b/libs/binder/include/binder/IServiceManager.h @@ -113,6 +113,13 @@ sp<INTERFACE> waitForDeclaredService(const String16& name) { return interface_cast<INTERFACE>(sm->waitForService(name)); } +template <typename INTERFACE> +sp<INTERFACE> checkDeclaredService(const String16& name) { + const sp<IServiceManager> sm = defaultServiceManager(); + if (!sm->isDeclared(name)) return nullptr; + return interface_cast<INTERFACE>(sm->checkService(name)); +} + template<typename INTERFACE> sp<INTERFACE> waitForVintfService( const String16& instance = String16("default")) { @@ -121,6 +128,13 @@ sp<INTERFACE> waitForVintfService( } template<typename INTERFACE> +sp<INTERFACE> checkVintfService( + const String16& instance = String16("default")) { + return checkDeclaredService<INTERFACE>( + INTERFACE::descriptor + String16("/") + instance); +} + +template<typename INTERFACE> status_t getService(const String16& name, sp<INTERFACE>* outService) { const sp<IServiceManager> sm = defaultServiceManager(); diff --git a/libs/binder/include/binder/ParcelFileDescriptor.h b/libs/binder/include/binder/ParcelFileDescriptor.h index 662e56e864..4635ad84c6 100644 --- a/libs/binder/include/binder/ParcelFileDescriptor.h +++ b/libs/binder/include/binder/ParcelFileDescriptor.h @@ -42,6 +42,24 @@ public: android::status_t writeToParcel(android::Parcel* parcel) const override; android::status_t readFromParcel(const android::Parcel* parcel) override; + inline bool operator!=(const ParcelFileDescriptor& rhs) const { + return mFd != rhs.mFd; + } + inline bool operator<(const ParcelFileDescriptor& rhs) const { + return mFd < rhs.mFd; + } + inline bool operator<=(const ParcelFileDescriptor& rhs) const { + return mFd <= rhs.mFd; + } + inline bool operator==(const ParcelFileDescriptor& rhs) const { + return mFd == rhs.mFd; + } + inline bool operator>(const ParcelFileDescriptor& rhs) const { + return mFd > rhs.mFd; + } + inline bool operator>=(const ParcelFileDescriptor& rhs) const { + return mFd >= rhs.mFd; + } private: android::base::unique_fd mFd; }; diff --git a/libs/binder/ndk/include_ndk/android/binder_auto_utils.h b/libs/binder/ndk/include_ndk/android/binder_auto_utils.h index 8f37c5e3d3..946ccb79a5 100644 --- a/libs/binder/ndk/include_ndk/android/binder_auto_utils.h +++ b/libs/binder/ndk/include_ndk/android/binder_auto_utils.h @@ -159,13 +159,17 @@ class ScopedAResource { */ T* getR() { return &mT; } - // copy-constructing, or move/copy assignment is disallowed + // copy-constructing/assignment is disallowed ScopedAResource(const ScopedAResource&) = delete; ScopedAResource& operator=(const ScopedAResource&) = delete; - ScopedAResource& operator=(ScopedAResource&&) = delete; - // move-constructing is okay + // move-constructing/assignment is okay ScopedAResource(ScopedAResource&& other) : mT(std::move(other.mT)) { other.mT = DEFAULT; } + ScopedAResource& operator=(ScopedAResource&& other) { + set(other.mT); + other.mT = DEFAULT; + return *this; + } private: T mT; @@ -197,6 +201,7 @@ class ScopedAStatus : public impl::ScopedAResource<AStatus*, void, AStatus_delet explicit ScopedAStatus(AStatus* a = nullptr) : ScopedAResource(a) {} ~ScopedAStatus() {} ScopedAStatus(ScopedAStatus&&) = default; + ScopedAStatus& operator=(ScopedAStatus&&) = default; /** * See AStatus_isOk. diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp index 635ea69fb4..5a7f9a97fa 100644 --- a/libs/binder/tests/Android.bp +++ b/libs/binder/tests/Android.bp @@ -119,12 +119,7 @@ cc_test { srcs: ["binderSafeInterfaceTest.cpp"], cppflags: [ - "-Weverything", - "-Wno-c++98-compat", - "-Wno-c++98-compat-pedantic", - "-Wno-global-constructors", - "-Wno-padded", - "-Wno-weak-vtables", + "-Wextra", ], cpp_std: "experimental", diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp index b360a268db..4f605e0782 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -67,6 +67,9 @@ cc_library_shared { "SurfaceComposerClient.cpp", "SyncFeatures.cpp", "view/Surface.cpp", + "surfacetexture/SurfaceTexture.cpp", + "surfacetexture/ImageConsumer.cpp", + "surfacetexture/EGLConsumer.cpp", ], shared_libs: [ diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index 9a5017577d..3c31d7439f 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -33,6 +33,7 @@ BLASTBufferQueue::BLASTBufferQueue(const sp<SurfaceControl>& surface, int width, mBufferItemConsumer->setBufferFreedListener(this); mBufferItemConsumer->setDefaultBufferSize(mWidth, mHeight); mBufferItemConsumer->setDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888); + mBufferItemConsumer->setTransformHint(mSurfaceControl->getTransformHint()); } void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, int width, int height) { @@ -41,6 +42,7 @@ void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, int width, int mWidth = width; mHeight = height; mBufferItemConsumer->setDefaultBufferSize(mWidth, mHeight); + mBufferItemConsumer->setTransformHint(mSurfaceControl->getTransformHint()); } static void transactionCallbackThunk(void* context, nsecs_t latchTime, @@ -63,6 +65,7 @@ void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence ? stats[0].previousReleaseFence : Fence::NO_FENCE); mNextCallbackBufferItem = BufferItem(); + mBufferItemConsumer->setTransformHint(stats[0].transformHint); } mDequeueWaitCV.notify_all(); decStrong((void*)transactionCallbackThunk); diff --git a/libs/gui/ISurfaceComposerClient.cpp b/libs/gui/ISurfaceComposerClient.cpp index b98e48b52a..621cf5950b 100644 --- a/libs/gui/ISurfaceComposerClient.cpp +++ b/libs/gui/ISurfaceComposerClient.cpp @@ -49,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 { diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index e004e9584c..e392bc597f 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -406,6 +406,12 @@ 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 & what) != other.what) { ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? " "other.what=0x%" PRIu64 " what=0x%" PRIu64, diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index e9079efd29..a538e14dfc 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -224,6 +224,8 @@ void TransactionCompletedListener::onTransactionCompleted(ListenerStats listener surfaceStats.acquireTime, surfaceStats.previousReleaseFence, surfaceStats.transformHint); + surfaceControls[surfaceStats.surfaceControl]->setTransformHint( + surfaceStats.transformHint); } callbackFunction(transactionStats.latchTime, transactionStats.presentFence, @@ -1335,6 +1337,18 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setGeome 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; +} + // --------------------------------------------------------------------------- DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp<IBinder>& token) { @@ -1451,16 +1465,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; @@ -1469,11 +1486,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, true /* owned */, transformHint); } } return nullptr; @@ -1482,8 +1503,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; @@ -1496,11 +1517,15 @@ 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, true /* owned */, transformHint); } } return err; diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp index 071314f082..6292388ac3 100644 --- a/libs/gui/SurfaceControl.cpp +++ b/libs/gui/SurfaceControl.cpp @@ -45,20 +45,21 @@ 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, bool owned, + uint32_t transform) + : mClient(client), + mHandle(handle), + mGraphicBufferProducer(gbp), + mOwned(owned), + mTransformHint(transform) {} SurfaceControl::SurfaceControl(const sp<SurfaceControl>& other) { mClient = other->mClient; mHandle = other->mHandle; mGraphicBufferProducer = other->mGraphicBufferProducer; mOwned = false; + mTransformHint = other->mTransformHint; } SurfaceControl::~SurfaceControl() @@ -171,11 +172,22 @@ 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(const Parcel* parcel) { @@ -189,10 +201,12 @@ sp<SurfaceControl> SurfaceControl::readFromParcel(const 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), + false /* owned */, transformHint); } // ---------------------------------------------------------------------------- diff --git a/libs/gui/include/gui/ISurfaceComposerClient.h b/libs/gui/include/gui/ISurfaceComposerClient.h index 5fe7ca5344..2b65d2f42d 100644 --- a/libs/gui/include/gui/ISurfaceComposerClient.h +++ b/libs/gui/include/gui/ISurfaceComposerClient.h @@ -56,7 +56,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 +65,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 diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index a49ed525b6..c2b5119242 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -72,7 +72,7 @@ struct layer_state_t { eCropChanged_legacy = 0x00000100, eDeferTransaction_legacy = 0x00000200, eOverrideScalingModeChanged = 0x00000400, - // AVAILABLE 0x00000800, + eShadowRadiusChanged = 0x00000800, eReparentChildren = 0x00001000, eDetachChildren = 0x00002000, eRelativeLayerChanged = 0x00004000, @@ -126,7 +126,8 @@ struct layer_state_t { colorTransform(mat4()), bgColorAlpha(0), bgColorDataspace(ui::Dataspace::UNKNOWN), - colorSpaceAgnostic(false) { + colorSpaceAgnostic(false), + shadowRadius(0.0f) { matrix.dsdx = matrix.dtdy = 1.0f; matrix.dsdy = matrix.dtdx = 0.0f; hdrMetadata.validTypes = 0; @@ -204,6 +205,9 @@ struct layer_state_t { bool colorSpaceAgnostic; std::vector<ListenerCallbacks> listeners; + + // Draws a shadow around the surface. + float shadowRadius; }; struct ComposerState { diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index 08f4e9e9d3..37387acfc6 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -226,18 +226,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 @@ -246,8 +246,8 @@ 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. @@ -474,6 +474,7 @@ public: Transaction& setGeometry(const sp<SurfaceControl>& sc, const Rect& source, const Rect& dst, int transform); + Transaction& setShadowRadius(const sp<SurfaceControl>& sc, float cornerRadius); status_t setDisplaySurface(const sp<IBinder>& token, const sp<IGraphicBufferProducer>& bufferProducer); diff --git a/libs/gui/include/gui/SurfaceControl.h b/libs/gui/include/gui/SurfaceControl.h index ae4a14690f..7bc7c686c9 100644 --- a/libs/gui/include/gui/SurfaceControl.h +++ b/libs/gui/include/gui/SurfaceControl.h @@ -82,10 +82,14 @@ public: 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, bool owned, uint32_t transformHint = 0); private: // can't be copied @@ -106,6 +110,7 @@ private: mutable Mutex mLock; mutable sp<Surface> mSurfaceData; bool mOwned; + uint32_t mTransformHint; }; }; // namespace android diff --git a/libs/gui/include/gui/surfacetexture/EGLConsumer.h b/libs/gui/include/gui/surfacetexture/EGLConsumer.h new file mode 100644 index 0000000000..444722bf83 --- /dev/null +++ b/libs/gui/include/gui/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/gui/include/gui/surfacetexture/ImageConsumer.h b/libs/gui/include/gui/surfacetexture/ImageConsumer.h new file mode 100644 index 0000000000..35ae3d2144 --- /dev/null +++ b/libs/gui/include/gui/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/gui/include/gui/surfacetexture/SurfaceTexture.h b/libs/gui/include/gui/surfacetexture/SurfaceTexture.h new file mode 100644 index 0000000000..6eaa84e225 --- /dev/null +++ b/libs/gui/include/gui/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/gui/include/gui/surfacetexture/surface_texture_platform.h b/libs/gui/include/gui/surfacetexture/surface_texture_platform.h new file mode 100644 index 0000000000..72f98adc7b --- /dev/null +++ b/libs/gui/include/gui/surfacetexture/surface_texture_platform.h @@ -0,0 +1,91 @@ +/* + * 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 <system/graphics.h> + +#include <gui/IGraphicBufferProducer.h> +#include <gui/surfacetexture/SurfaceTexture.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 { + android::sp<android::SurfaceTexture> consumer; + android::sp<android::IGraphicBufferProducer> producer; +}; + +namespace android { + +/** + * 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. + */ +AHardwareBuffer* ASurfaceTexture_dequeueBuffer(ASurfaceTexture* st, int* outSlotid, + android_dataspace* outDataspace, + float* outTransformMatrix, bool* outNewContent, + ASurfaceTexture_createReleaseFence createFence, + ASurfaceTexture_fenceWait fenceWait, + void* fencePassThroughHandle); + +/** + * ASurfaceTexture_create creates an ASurfaceTexture, which is + * a simple struct containing a SurfaceTexture and an IGraphicBufferProducer. + */ +ASurfaceTexture* ASurfaceTexture_create(sp<SurfaceTexture> consumer, + sp<IGraphicBufferProducer> producer); + +} // namespace android + +#endif // _ANDROID_GRAPHICS_SURFACE_TEXTURE_PLATFORM_H diff --git a/libs/gui/surfacetexture/EGLConsumer.cpp b/libs/gui/surfacetexture/EGLConsumer.cpp new file mode 100644 index 0000000000..1f0f52c3f0 --- /dev/null +++ b/libs/gui/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 <gui/surfacetexture/EGLConsumer.h> +#include <gui/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/gui/surfacetexture/ImageConsumer.cpp b/libs/gui/surfacetexture/ImageConsumer.cpp new file mode 100644 index 0000000000..4bc4a7b3b1 --- /dev/null +++ b/libs/gui/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 <gui/surfacetexture/ImageConsumer.h> +#include <gui/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/gui/surfacetexture/SurfaceTexture.cpp b/libs/gui/surfacetexture/SurfaceTexture.cpp new file mode 100644 index 0000000000..25e561809c --- /dev/null +++ b/libs/gui/surfacetexture/SurfaceTexture.cpp @@ -0,0 +1,528 @@ +/* + * 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 <gui/surfacetexture/ImageConsumer.h> +#include <gui/surfacetexture/SurfaceTexture.h> +#include <gui/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; +} + +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)); + return reinterpret_cast<AHardwareBuffer*>(buffer.get()); +} + +ASurfaceTexture* ASurfaceTexture_create(sp<SurfaceTexture> consumer, + sp<IGraphicBufferProducer> producer) { + ASurfaceTexture* ast = new ASurfaceTexture; + ast->consumer = consumer; + ast->producer = producer; + return ast; +} + +} // namespace android diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp index db1ac249b5..ff22913226 100644 --- a/libs/gui/tests/BLASTBufferQueue_test.cpp +++ b/libs/gui/tests/BLASTBufferQueue_test.cpp @@ -26,6 +26,7 @@ #include <ui/DisplayInfo.h> #include <ui/GraphicBuffer.h> #include <ui/GraphicTypes.h> +#include <ui/Transform.h> #include <gtest/gtest.h> @@ -201,6 +202,7 @@ TEST_F(BLASTBufferQueueTest, onFrameAvailable_Apply) { igbProducer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, false, &qbOutput)); ASSERT_EQ(NO_ERROR, igbProducer->setMaxDequeuedBufferCount(3)); + ASSERT_NE(ui::Transform::orientation_flags::ROT_INVALID, qbOutput.transformHint); int slot; sp<Fence> fence; @@ -222,6 +224,7 @@ TEST_F(BLASTBufferQueueTest, onFrameAvailable_Apply) { NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE); igbProducer->queueBuffer(slot, input, &qbOutput); + ASSERT_NE(ui::Transform::orientation_flags::ROT_INVALID, qbOutput.transformHint); adapter.waitForCallback(); diff --git a/libs/nativedisplay/ADisplay.cpp b/libs/nativedisplay/ADisplay.cpp new file mode 100644 index 0000000000..666563520e --- /dev/null +++ b/libs/nativedisplay/ADisplay.cpp @@ -0,0 +1,262 @@ +/* + * 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/DisplayInfo.h> +#include <ui/GraphicTypes.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; + + /** + * 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); +} + +int64_t computeSfOffset(const DisplayInfo& info) { + // This should probably be part of the config instead of extrapolated from + // the presentation deadline and fudged here, but the way the math works out + // here we do get the right offset. + return static_cast<int64_t>((1000000000 / info.fps) - info.presentationDeadline + 1000000); +} +} // namespace + +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]); + Vector<DisplayInfo> configs; + const status_t status = SurfaceComposerClient::getDisplayConfigs(token, &configs); + if (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 DisplayInfo config = configs[j]; + configsPerDisplay[i].emplace_back( + DisplayConfigImpl{static_cast<int32_t>(config.w), + static_cast<int32_t>(config.h), config.density, config.fps, + computeSfOffset(config), config.appVsyncOffset}); + } + } + + const std::optional<PhysicalDisplayId> internalId = + SurfaceComposerClient::getInternalDisplayId(); + + // 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, 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; +} + +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; +} diff --git a/libs/nativedisplay/Android.bp b/libs/nativedisplay/Android.bp new file mode 100644 index 0000000000..66ebdfd6f8 --- /dev/null +++ b/libs/nativedisplay/Android.bp @@ -0,0 +1,50 @@ +// 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 { + name: "libnativedisplay", + export_include_dirs: [ + "include", + ], + + clang: true, + + cflags: [ + "-Wall", + "-Werror", + "-Wno-enum-compare", + "-Wno-unused-function", + ], + + srcs: [ + "ADisplay.cpp", + ], + + shared_libs: [ + "libgui", + "liblog", + "libui", + "libutils", + ], + + header_libs: [ + "libnativedisplay_headers", + ], + +} diff --git a/libs/nativedisplay/include/apex/display.h b/libs/nativedisplay/include/apex/display.h new file mode 100644 index 0000000000..7af452a782 --- /dev/null +++ b/libs/nativedisplay/include/apex/display.h @@ -0,0 +1,125 @@ +/* + * 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 <inttypes.h> + +__BEGIN_DECLS + +/** + * 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); + +/** + * 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); + +__END_DECLS diff --git a/libs/sensor/Android.bp b/libs/sensor/Android.bp index 940ff5afbc..e8154a6931 100644 --- a/libs/sensor/Android.bp +++ b/libs/sensor/Android.bp @@ -21,22 +21,7 @@ cc_library_shared { "-Werror", ], cppflags: [ - "-Weverything", - - // The static constructors and destructors in this library have not been noted to - // introduce significant overheads - "-Wno-exit-time-destructors", - "-Wno-global-constructors", - - // We only care about compiling as C++14 - "-Wno-c++98-compat-pedantic", - - // android/sensors.h uses nested anonymous unions and anonymous structs - "-Wno-nested-anon-types", - "-Wno-gnu-anonymous-struct", - - // Don't warn about struct padding - "-Wno-padded", + "-Wextra", ], srcs: [ diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp index 47137f1771..afa6a2b342 100644 --- a/libs/ui/Android.bp +++ b/libs/ui/Android.bp @@ -26,32 +26,18 @@ cc_library_shared { "-Werror", ], cppflags: [ - "-Weverything", - - // The static constructors and destructors in this library have not been noted to - // introduce significant overheads - "-Wno-exit-time-destructors", - "-Wno-global-constructors", - - // We only care about compiling as C++14 - "-Wno-c++98-compat-pedantic", - - // We are aware of the risks inherent in comparing floats for equality - "-Wno-float-equal", - - // We use four-character constants for the GraphicBuffer header, and don't care - // that they're non-portable as long as they're consistent within one execution - "-Wno-four-char-constants", - - // Don't warn about struct padding - "-Wno-padded", - - "-Wno-switch-enum", - "-Wno-format-pedantic", + "-Wextra", ], sanitize: { integer_overflow: true, + misc_undefined: ["bounds"], + diag: { + misc_undefined: ["bounds"], + no_recover: [ + "bounds", + ], + }, }, srcs: [ diff --git a/libs/vr/libpdx_default_transport/Android.bp b/libs/vr/libpdx_default_transport/Android.bp index 1176abf552..1ce9c991d5 100644 --- a/libs/vr/libpdx_default_transport/Android.bp +++ b/libs/vr/libpdx_default_transport/Android.bp @@ -39,7 +39,6 @@ cc_library_shared { "libcutils", "liblog", "libutils", - "libcrypto", "libselinux", ], } diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp index d1b4a4ec33..1fc7927e12 100644 --- a/opengl/libs/EGL/egl_platform_entries.cpp +++ b/opengl/libs/EGL/egl_platform_entries.cpp @@ -61,7 +61,7 @@ namespace android { using nsecs_t = int64_t; -struct extention_map_t { +struct extension_map_t { const char* name; __eglMustCastToProperFunctionPointerType address; }; @@ -154,7 +154,7 @@ char const * const gClientExtensionString = * (keep in sync with gExtensionString above) * */ -static const extention_map_t sExtensionMap[] = { +static const extension_map_t sExtensionMap[] = { // EGL_KHR_lock_surface { "eglLockSurfaceKHR", (__eglMustCastToProperFunctionPointerType)&eglLockSurfaceKHR }, @@ -257,13 +257,14 @@ static const extention_map_t sExtensionMap[] = { !strcmp((procname), "eglAwakenProcessIMG")) // accesses protected by sExtensionMapMutex -static std::unordered_map<std::string, __eglMustCastToProperFunctionPointerType> sGLExtentionMap; +static std::unordered_map<std::string, __eglMustCastToProperFunctionPointerType> sGLExtensionMap; +static std::unordered_map<std::string, int> sGLExtensionSlotMap; -static int sGLExtentionSlot = 0; +static int sGLExtensionSlot = 0; static pthread_mutex_t sExtensionMapMutex = PTHREAD_MUTEX_INITIALIZER; static void(*findProcAddress(const char* name, - const extention_map_t* map, size_t n))() { + const extension_map_t* map, size_t n))() { for (uint32_t i=0 ; i<n ; i++) { if (!strcmp(name, map[i].name)) { return map[i].address; @@ -1223,7 +1224,7 @@ __eglMustCastToProperFunctionPointerType eglGetProcAddressImpl(const char *procn addr = findBuiltinWrapper(procname); if (addr) return addr; - // this protects accesses to sGLExtentionMap and sGLExtentionSlot + // this protects accesses to sGLExtensionMap, sGLExtensionSlot, and sGLExtensionSlotMap pthread_mutex_lock(&sExtensionMapMutex); /* @@ -1244,51 +1245,69 @@ __eglMustCastToProperFunctionPointerType eglGetProcAddressImpl(const char *procn */ const std::string name(procname); + auto& extensionMap = sGLExtensionMap; + auto& extensionSlotMap = sGLExtensionSlotMap; + egl_connection_t* const cnx = &gEGLImpl; + LayerLoader& layer_loader(LayerLoader::getInstance()); - auto& extentionMap = sGLExtentionMap; - auto pos = extentionMap.find(name); - addr = (pos != extentionMap.end()) ? pos->second : nullptr; - const int slot = sGLExtentionSlot; + // See if we've already looked up this extension + auto pos = extensionMap.find(name); + addr = (pos != extensionMap.end()) ? pos->second : nullptr; - ALOGE_IF(slot >= MAX_NUMBER_OF_GL_EXTENSIONS, - "no more slots for eglGetProcAddress(\"%s\")", - procname); + if (!addr) { + // This is the first time we've looked this function up + // Ensure we have room to track it + const int slot = sGLExtensionSlot; + if (slot < MAX_NUMBER_OF_GL_EXTENSIONS) { - egl_connection_t* const cnx = &gEGLImpl; - LayerLoader& layer_loader(LayerLoader::getInstance()); + if (cnx->dso && cnx->egl.eglGetProcAddress) { - if (!addr && (slot < MAX_NUMBER_OF_GL_EXTENSIONS)) { + // Extensions are independent of the bound context + addr = cnx->egl.eglGetProcAddress(procname); + if (addr) { - if (cnx->dso && cnx->egl.eglGetProcAddress) { + // purposefully track the bottom of the stack in extensionMap + extensionMap[name] = addr; - // Extensions are independent of the bound context - addr = cnx->egl.eglGetProcAddress(procname); - if (addr) { + // Apply layers + addr = layer_loader.ApplyLayers(procname, addr); - // purposefully track the bottom of the stack in extensionMap - extentionMap[name] = addr; + // Track the top most entry point return the extension forwarder + cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[slot] = + cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[slot] = addr; + addr = gExtensionForwarders[slot]; - // Apply layers - addr = layer_loader.ApplyLayers(procname, addr); + // Remember the slot for this extension + extensionSlotMap[name] = slot; - // Track the top most entry point - cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[slot] = - cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[slot] = addr; - addr = gExtensionForwarders[slot]; - sGLExtentionSlot++; + // Increment the global extension index + sGLExtensionSlot++; + } } + } else { + // The extension forwarder has a fixed number of slots + ALOGE("no more slots for eglGetProcAddress(\"%s\")", procname); } - } else if (slot < MAX_NUMBER_OF_GL_EXTENSIONS) { + } else { + // We tracked an address, so we've seen this func before + // Look up the slot for this extension + auto slot_pos = extensionSlotMap.find(name); + int ext_slot = (slot_pos != extensionSlotMap.end()) ? slot_pos->second : -1; + if (ext_slot < 0) { + // Something has gone wrong, this should not happen + ALOGE("No extension slot found for %s", procname); + return nullptr; + } - // We've seen this func before, but we tracked the bottom, so re-apply layers - // More layers might have been enabled + // We tracked the bottom of the stack, so re-apply layers since + // more layers might have been enabled addr = layer_loader.ApplyLayers(procname, addr); - // Track the top most entry point - cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[slot] = - cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[slot] = addr; - addr = gExtensionForwarders[slot]; + // Track the top most entry point and return the extension forwarder + cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[ext_slot] = + cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[ext_slot] = addr; + addr = gExtensionForwarders[ext_slot]; } pthread_mutex_unlock(&sExtensionMapMutex); diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp index e7640dd6af..1043390f84 100644 --- a/services/inputflinger/InputManager.cpp +++ b/services/inputflinger/InputManager.cpp @@ -46,7 +46,6 @@ InputManager::~InputManager() { } void InputManager::initialize() { - mReaderThread = new InputReaderThread(mReader); mDispatcherThread = new InputDispatcherThread(mDispatcher); } @@ -57,9 +56,9 @@ status_t InputManager::start() { return result; } - result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY); + result = mReader->start(); if (result) { - ALOGE("Could not start InputReader thread due to error %d.", result); + ALOGE("Could not start InputReader due to error %d.", result); mDispatcherThread->requestExit(); return result; @@ -69,9 +68,9 @@ status_t InputManager::start() { } status_t InputManager::stop() { - status_t result = mReaderThread->requestExitAndWait(); + status_t result = mReader->stop(); if (result) { - ALOGW("Could not stop InputReader thread due to error %d.", result); + ALOGW("Could not stop InputReader due to error %d.", result); } result = mDispatcherThread->requestExitAndWait(); diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h index 40f66d82f4..2a7ed0ff44 100644 --- a/services/inputflinger/InputManager.h +++ b/services/inputflinger/InputManager.h @@ -43,15 +43,15 @@ class InputDispatcherThread; /* * The input manager is the core of the system event processing. * - * The input manager uses two threads. + * The input manager has two components. * - * 1. The InputReaderThread (called "InputReader") reads and preprocesses raw input events, - * applies policy, and posts messages to a queue managed by the DispatcherThread. + * 1. The InputReader class starts a thread that reads and preprocesses raw input events, applies + * policy, and posts messages to a queue managed by the InputDispatcherThread. * 2. The InputDispatcherThread (called "InputDispatcher") thread waits for new events on the * queue and asynchronously dispatches them to applications. * - * By design, the InputReaderThread class and InputDispatcherThread class do not share any - * internal state. Moreover, all communication is done one way from the InputReaderThread + * By design, the InputReader class and InputDispatcherThread class do not share any + * internal state. Moreover, all communication is done one way from the InputReader * into the InputDispatcherThread and never the reverse. Both classes may interact with the * InputDispatchPolicy, however. * @@ -102,7 +102,6 @@ public: private: sp<InputReaderInterface> mReader; - sp<InputReaderThread> mReaderThread; sp<InputClassifierInterface> mClassifier; diff --git a/services/inputflinger/InputReaderBase.cpp b/services/inputflinger/InputReaderBase.cpp index 0422d8342b..2d6f2c1cc9 100644 --- a/services/inputflinger/InputReaderBase.cpp +++ b/services/inputflinger/InputReaderBase.cpp @@ -33,20 +33,6 @@ using android::base::StringPrintf; namespace android { -// --- InputReaderThread --- - -InputReaderThread::InputReaderThread(const sp<InputReaderInterface>& reader) : - Thread(/*canCallJava*/ true), mReader(reader) { -} - -InputReaderThread::~InputReaderThread() { -} - -bool InputReaderThread::threadLoop() { - mReader->loopOnce(); - return true; -} - // --- InputReaderConfiguration --- std::string InputReaderConfiguration::changesToString(uint32_t changes) { diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h index 5d576b94f3..56c0a7356d 100644 --- a/services/inputflinger/include/InputReaderBase.h +++ b/services/inputflinger/include/InputReaderBase.h @@ -19,12 +19,12 @@ #include "PointerControllerInterface.h" +#include <input/DisplayViewport.h> #include <input/Input.h> #include <input/InputDevice.h> -#include <input/DisplayViewport.h> #include <input/VelocityControl.h> #include <input/VelocityTracker.h> -#include <utils/Thread.h> +#include <utils/Errors.h> #include <utils/RefBase.h> #include <stddef.h> @@ -44,7 +44,16 @@ namespace android { -/* Processes raw input events and sends cooked event data to an input listener. */ +// --- InputReaderInterface --- + +/* The interface for the InputReader shared library. + * + * Manages one or more threads that process raw input events and sends cooked event data to an + * input listener. + * + * The implementation must guarantee thread safety for this interface. However, since the input + * listener is NOT thread safe, all calls to the listener must happen from the same thread. + */ class InputReaderInterface : public virtual RefBase { protected: InputReaderInterface() { } @@ -56,18 +65,17 @@ public: * This method may be called on any thread (usually by the input manager). */ virtual void dump(std::string& dump) = 0; - /* Called by the heatbeat to ensures that the reader has not deadlocked. */ + /* Called by the heartbeat to ensures that the reader has not deadlocked. */ virtual void monitor() = 0; /* Returns true if the input device is enabled. */ virtual bool isInputDeviceEnabled(int32_t deviceId) = 0; - /* Runs a single iteration of the processing loop. - * Nominally reads and processes one incoming message from the EventHub. - * - * This method should be called on the input reader thread. - */ - virtual void loopOnce() = 0; + /* Makes the reader start processing events from the kernel. */ + virtual status_t start() = 0; + + /* Makes the reader stop processing any more events. */ + virtual status_t stop() = 0; /* Gets information about all input devices. * @@ -104,17 +112,7 @@ public: virtual bool canDispatchToDisplay(int32_t deviceId, int32_t displayId) = 0; }; -/* Reads raw events from the event hub and processes them, endlessly. */ -class InputReaderThread : public Thread { -public: - explicit InputReaderThread(const sp<InputReaderInterface>& reader); - virtual ~InputReaderThread(); - -private: - sp<InputReaderInterface> mReader; - - virtual bool threadLoop(); -}; +// --- InputReaderConfiguration --- /* * Input reader configuration. @@ -285,6 +283,8 @@ private: std::vector<DisplayViewport> mDisplays; }; +// --- TouchAffineTransformation --- + struct TouchAffineTransformation { float x_scale; float x_ymix; @@ -307,6 +307,8 @@ struct TouchAffineTransformation { void applyTo(float& x, float& y) const; }; +// --- InputReaderPolicyInterface --- + /* * Input reader policy interface. * @@ -316,8 +318,8 @@ struct TouchAffineTransformation { * The actual implementation is partially supported by callbacks into the DVM * via JNI. This interface is also mocked in the unit tests. * - * These methods must NOT re-enter the input reader since they may be called while - * holding the input reader lock. + * These methods will NOT re-enter the input reader interface, so they may be called from + * any method in the input reader interface. */ class InputReaderPolicyInterface : public virtual RefBase { protected: diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp index e57604cbe8..05f0db1329 100644 --- a/services/inputflinger/reader/InputReader.cpp +++ b/services/inputflinger/reader/InputReader.cpp @@ -38,16 +38,38 @@ #include <unistd.h> #include <log/log.h> +#include <utils/Errors.h> #include <android-base/stringprintf.h> #include <input/Keyboard.h> #include <input/VirtualKeyMap.h> - +#include <utils/Thread.h> using android::base::StringPrintf; namespace android { +// --- InputReader::InputReaderThread --- + +/* Thread that reads raw events from the event hub and processes them, endlessly. */ +class InputReader::InputReaderThread : public Thread { +public: + explicit InputReaderThread(InputReader* reader) + : Thread(/* canCallJava */ true), mReader(reader) {} + + ~InputReaderThread() {} + +private: + InputReader* mReader; + + bool threadLoop() override { + mReader->loopOnce(); + return true; + } +}; + +// --- InputReader --- + InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub, const sp<InputReaderPolicyInterface>& policy, const sp<InputListenerInterface>& listener) @@ -61,6 +83,7 @@ InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub, mNextTimeout(LLONG_MAX), mConfigurationChangesToRefresh(0) { mQueuedListener = new QueuedInputListener(listener); + mThread = new InputReaderThread(this); { // acquire lock AutoMutex _l(mLock); @@ -71,11 +94,33 @@ InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub, } InputReader::~InputReader() { - for (size_t i = 0; i < mDevices.size(); i++) { - delete mDevices.valueAt(i); + for (auto& devicePair : mDevices) { + delete devicePair.second; } } +status_t InputReader::start() { + if (mThread->isRunning()) { + return ALREADY_EXISTS; + } + return mThread->run("InputReader", PRIORITY_URGENT_DISPLAY); +} + +status_t InputReader::stop() { + if (!mThread->isRunning()) { + return OK; + } + if (gettid() == mThread->getTid()) { + ALOGE("InputReader can only be stopped from outside of the InputReaderThread!"); + return INVALID_OPERATION; + } + // Directly calling requestExitAndWait() causes the thread to not exit + // if mEventHub is waiting for a long timeout. + mThread->requestExit(); + mEventHub->wake(); + return mThread->requestExitAndWait(); +} + void InputReader::loopOnce() { int32_t oldGeneration; int32_t timeoutMillis; @@ -179,8 +224,7 @@ void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) { } void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) { - ssize_t deviceIndex = mDevices.indexOfKey(deviceId); - if (deviceIndex >= 0) { + if (mDevices.find(deviceId) != mDevices.end()) { ALOGW("Ignoring spurious device added event for deviceId %d.", deviceId); return; } @@ -201,7 +245,7 @@ void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) { device->getSources()); } - mDevices.add(deviceId, device); + mDevices.insert({deviceId, device}); bumpGenerationLocked(); if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) { @@ -210,15 +254,14 @@ void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) { } void InputReader::removeDeviceLocked(nsecs_t when, int32_t deviceId) { - InputDevice* device = nullptr; - ssize_t deviceIndex = mDevices.indexOfKey(deviceId); - if (deviceIndex < 0) { + auto deviceIt = mDevices.find(deviceId); + if (deviceIt == mDevices.end()) { ALOGW("Ignoring spurious device removed event for deviceId %d.", deviceId); return; } - device = mDevices.valueAt(deviceIndex); - mDevices.removeItemsAt(deviceIndex, 1); + InputDevice* device = deviceIt->second; + mDevices.erase(deviceIt); bumpGenerationLocked(); if (device->isIgnored()) { @@ -315,13 +358,13 @@ InputDevice* InputReader::createDeviceLocked(int32_t deviceId, int32_t controlle void InputReader::processEventsForDeviceLocked(int32_t deviceId, const RawEvent* rawEvents, size_t count) { - ssize_t deviceIndex = mDevices.indexOfKey(deviceId); - if (deviceIndex < 0) { + auto deviceIt = mDevices.find(deviceId); + if (deviceIt == mDevices.end()) { ALOGW("Discarding event for unknown deviceId %d.", deviceId); return; } - InputDevice* device = mDevices.valueAt(deviceIndex); + InputDevice* device = deviceIt->second; if (device->isIgnored()) { // ALOGD("Discarding event for ignored deviceId %d.", deviceId); return; @@ -331,8 +374,8 @@ void InputReader::processEventsForDeviceLocked(int32_t deviceId, const RawEvent* } void InputReader::timeoutExpiredLocked(nsecs_t when) { - for (size_t i = 0; i < mDevices.size(); i++) { - InputDevice* device = mDevices.valueAt(i); + for (auto& devicePair : mDevices) { + InputDevice* device = devicePair.second; if (!device->isIgnored()) { device->timeoutExpired(when); } @@ -360,8 +403,8 @@ void InputReader::refreshConfigurationLocked(uint32_t changes) { if (changes & InputReaderConfiguration::CHANGE_MUST_REOPEN) { mEventHub->requestReopenDevices(); } else { - for (size_t i = 0; i < mDevices.size(); i++) { - InputDevice* device = mDevices.valueAt(i); + for (auto& devicePair : mDevices) { + InputDevice* device = devicePair.second; device->configure(now, &mConfig, changes); } } @@ -371,8 +414,8 @@ void InputReader::refreshConfigurationLocked(uint32_t changes) { void InputReader::updateGlobalMetaStateLocked() { mGlobalMetaState = 0; - for (size_t i = 0; i < mDevices.size(); i++) { - InputDevice* device = mDevices.valueAt(i); + for (auto& devicePair : mDevices) { + InputDevice* device = devicePair.second; mGlobalMetaState |= device->getMetaState(); } } @@ -386,8 +429,8 @@ void InputReader::notifyExternalStylusPresenceChanged() { } void InputReader::getExternalStylusDevicesLocked(std::vector<InputDeviceInfo>& outDevices) { - for (size_t i = 0; i < mDevices.size(); i++) { - InputDevice* device = mDevices.valueAt(i); + for (auto& devicePair : mDevices) { + InputDevice* device = devicePair.second; if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS && !device->isIgnored()) { InputDeviceInfo info; device->getDeviceInfo(&info); @@ -397,8 +440,8 @@ void InputReader::getExternalStylusDevicesLocked(std::vector<InputDeviceInfo>& o } void InputReader::dispatchExternalStylusState(const StylusState& state) { - for (size_t i = 0; i < mDevices.size(); i++) { - InputDevice* device = mDevices.valueAt(i); + for (auto& devicePair : mDevices) { + InputDevice* device = devicePair.second; device->updateExternalStylusState(state); } } @@ -421,8 +464,8 @@ bool InputReader::shouldDropVirtualKeyLocked(nsecs_t now, InputDevice* device, i } void InputReader::fadePointerLocked() { - for (size_t i = 0; i < mDevices.size(); i++) { - InputDevice* device = mDevices.valueAt(i); + for (auto& devicePair : mDevices) { + InputDevice* device = devicePair.second; device->fadePointer(); } } @@ -446,9 +489,8 @@ void InputReader::getInputDevices(std::vector<InputDeviceInfo>& outInputDevices) void InputReader::getInputDevicesLocked(std::vector<InputDeviceInfo>& outInputDevices) { outInputDevices.clear(); - size_t numDevices = mDevices.size(); - for (size_t i = 0; i < numDevices; i++) { - InputDevice* device = mDevices.valueAt(i); + for (auto& devicePair : mDevices) { + InputDevice* device = devicePair.second; if (!device->isIgnored()) { InputDeviceInfo info; device->getDeviceInfo(&info); @@ -479,17 +521,16 @@ int32_t InputReader::getStateLocked(int32_t deviceId, uint32_t sourceMask, int32 GetStateFunc getStateFunc) { int32_t result = AKEY_STATE_UNKNOWN; if (deviceId >= 0) { - ssize_t deviceIndex = mDevices.indexOfKey(deviceId); - if (deviceIndex >= 0) { - InputDevice* device = mDevices.valueAt(deviceIndex); + auto deviceIt = mDevices.find(deviceId); + if (deviceIt != mDevices.end()) { + InputDevice* device = deviceIt->second; if (!device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) { result = (device->*getStateFunc)(sourceMask, code); } } } else { - size_t numDevices = mDevices.size(); - for (size_t i = 0; i < numDevices; i++) { - InputDevice* device = mDevices.valueAt(i); + for (auto& devicePair : mDevices) { + InputDevice* device = devicePair.second; if (!device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) { // If any device reports AKEY_STATE_DOWN or AKEY_STATE_VIRTUAL, return that // value. Otherwise, return AKEY_STATE_UP as long as one device reports it. @@ -506,13 +547,13 @@ int32_t InputReader::getStateLocked(int32_t deviceId, uint32_t sourceMask, int32 } void InputReader::toggleCapsLockState(int32_t deviceId) { - ssize_t deviceIndex = mDevices.indexOfKey(deviceId); - if (deviceIndex < 0) { + auto deviceIt = mDevices.find(deviceId); + if (deviceIt == mDevices.end()) { ALOGW("Ignoring toggleCapsLock for unknown deviceId %" PRId32 ".", deviceId); return; } - InputDevice* device = mDevices.valueAt(deviceIndex); + InputDevice* device = deviceIt->second; if (device->isIgnored()) { return; } @@ -533,17 +574,16 @@ bool InputReader::markSupportedKeyCodesLocked(int32_t deviceId, uint32_t sourceM uint8_t* outFlags) { bool result = false; if (deviceId >= 0) { - ssize_t deviceIndex = mDevices.indexOfKey(deviceId); - if (deviceIndex >= 0) { - InputDevice* device = mDevices.valueAt(deviceIndex); + auto deviceIt = mDevices.find(deviceId); + if (deviceIt != mDevices.end()) { + InputDevice* device = deviceIt->second; if (!device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) { result = device->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags); } } } else { - size_t numDevices = mDevices.size(); - for (size_t i = 0; i < numDevices; i++) { - InputDevice* device = mDevices.valueAt(i); + for (auto& devicePair : mDevices) { + InputDevice* device = devicePair.second; if (!device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) { result |= device->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags); } @@ -568,10 +608,9 @@ void InputReader::requestRefreshConfiguration(uint32_t changes) { void InputReader::vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize, ssize_t repeat, int32_t token) { AutoMutex _l(mLock); - - ssize_t deviceIndex = mDevices.indexOfKey(deviceId); - if (deviceIndex >= 0) { - InputDevice* device = mDevices.valueAt(deviceIndex); + auto deviceIt = mDevices.find(deviceId); + if (deviceIt != mDevices.end()) { + InputDevice* device = deviceIt->second; device->vibrate(pattern, patternSize, repeat, token); } } @@ -579,9 +618,9 @@ void InputReader::vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patte void InputReader::cancelVibrate(int32_t deviceId, int32_t token) { AutoMutex _l(mLock); - ssize_t deviceIndex = mDevices.indexOfKey(deviceId); - if (deviceIndex >= 0) { - InputDevice* device = mDevices.valueAt(deviceIndex); + auto deviceIt = mDevices.find(deviceId); + if (deviceIt != mDevices.end()) { + InputDevice* device = deviceIt->second; device->cancelVibrate(token); } } @@ -589,9 +628,9 @@ void InputReader::cancelVibrate(int32_t deviceId, int32_t token) { bool InputReader::isInputDeviceEnabled(int32_t deviceId) { AutoMutex _l(mLock); - ssize_t deviceIndex = mDevices.indexOfKey(deviceId); - if (deviceIndex >= 0) { - InputDevice* device = mDevices.valueAt(deviceIndex); + auto deviceIt = mDevices.find(deviceId); + if (deviceIt != mDevices.end()) { + InputDevice* device = deviceIt->second; return device->isEnabled(); } ALOGW("Ignoring invalid device id %" PRId32 ".", deviceId); @@ -601,13 +640,13 @@ bool InputReader::isInputDeviceEnabled(int32_t deviceId) { bool InputReader::canDispatchToDisplay(int32_t deviceId, int32_t displayId) { AutoMutex _l(mLock); - ssize_t deviceIndex = mDevices.indexOfKey(deviceId); - if (deviceIndex < 0) { + auto deviceIt = mDevices.find(deviceId); + if (deviceIt == mDevices.end()) { ALOGW("Ignoring invalid device id %" PRId32 ".", deviceId); return false; } - InputDevice* device = mDevices.valueAt(deviceIndex); + InputDevice* device = deviceIt->second; if (!device->isEnabled()) { ALOGW("Ignoring disabled device %s", device->getName().c_str()); return false; @@ -635,8 +674,9 @@ void InputReader::dump(std::string& dump) { dump += "Input Reader State:\n"; - for (size_t i = 0; i < mDevices.size(); i++) { - mDevices.valueAt(i)->dump(dump); + for (const auto& devicePair : mDevices) { + InputDevice* const device = devicePair.second; + device->dump(dump); } dump += INDENT "Configuration:\n"; diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h index 7b4321ea82..0a4e80894a 100644 --- a/services/inputflinger/reader/include/InputReader.h +++ b/services/inputflinger/reader/include/InputReader.h @@ -23,9 +23,9 @@ #include "InputReaderContext.h" #include <utils/Condition.h> -#include <utils/KeyedVector.h> #include <utils/Mutex.h> +#include <unordered_map> #include <vector> namespace android { @@ -38,12 +38,12 @@ struct StylusState; * that it sends to the input listener. Some functions of the input reader, such as early * event filtering in low power states, are controlled by a separate policy object. * - * The InputReader owns a collection of InputMappers. Most of the work it does happens - * on the input reader thread but the InputReader can receive queries from other system + * The InputReader owns a collection of InputMappers. InputReader starts its own thread, where + * most of the work happens, but the InputReader can receive queries from other system * components running on arbitrary threads. To keep things manageable, the InputReader * uses a single Mutex to guard its state. The Mutex may be held while calling into the * EventHub or the InputReaderPolicy but it is never held while calling into the - * InputListener. + * InputListener. All calls to InputListener must happen from InputReader's thread. */ class InputReader : public InputReaderInterface { public: @@ -55,7 +55,8 @@ public: virtual void dump(std::string& dump) override; virtual void monitor() override; - virtual void loopOnce() override; + virtual status_t start() override; + virtual status_t stop() override; virtual void getInputDevices(std::vector<InputDeviceInfo>& outInputDevices) override; @@ -111,6 +112,9 @@ protected: friend class ContextImpl; private: + class InputReaderThread; + sp<InputReaderThread> mThread; + Mutex mLock; Condition mReaderIsAliveCondition; @@ -131,7 +135,11 @@ private: static const int EVENT_BUFFER_SIZE = 256; RawEvent mEventBuffer[EVENT_BUFFER_SIZE]; - KeyedVector<int32_t, InputDevice*> mDevices; + std::unordered_map<int32_t /*deviceId*/, InputDevice*> mDevices; + + // With each iteration of the loop, InputReader reads and processes one incoming message from + // the EventHub. + void loopOnce(); // low-level input event decoding and device management void processEventsLocked(const RawEvent* rawEvents, size_t count); diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index c1c912214a..8d4ab6afbd 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -1133,12 +1133,8 @@ class InputReaderPolicyTest : public testing::Test { protected: sp<FakeInputReaderPolicy> mFakePolicy; - virtual void SetUp() { - mFakePolicy = new FakeInputReaderPolicy(); - } - virtual void TearDown() { - mFakePolicy.clear(); - } + virtual void SetUp() override { mFakePolicy = new FakeInputReaderPolicy(); } + virtual void TearDown() override { mFakePolicy.clear(); } }; /** @@ -1321,18 +1317,20 @@ protected: sp<TestInputListener> mFakeListener; sp<FakeInputReaderPolicy> mFakePolicy; std::shared_ptr<FakeEventHub> mFakeEventHub; - sp<InstrumentedInputReader> mReader; + std::unique_ptr<InstrumentedInputReader> mReader; - virtual void SetUp() { + virtual void SetUp() override { mFakeEventHub = std::make_unique<FakeEventHub>(); mFakePolicy = new FakeInputReaderPolicy(); mFakeListener = new TestInputListener(); - mReader = new InstrumentedInputReader(mFakeEventHub, mFakePolicy, mFakeListener); + mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy, + mFakeListener); + ASSERT_EQ(OK, mReader->start()); } - virtual void TearDown() { - mReader.clear(); + virtual void TearDown() override { + ASSERT_EQ(OK, mReader->stop()); mFakeListener.clear(); mFakePolicy.clear(); @@ -1346,24 +1344,18 @@ protected: mFakeEventHub->addConfigurationMap(deviceId, configuration); } mFakeEventHub->finishDeviceScan(); - mReader->loopOnce(); - mReader->loopOnce(); ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); ASSERT_NO_FATAL_FAILURE(mFakeEventHub->assertQueueIsEmpty()); } void disableDevice(int32_t deviceId, InputDevice* device) { mFakePolicy->addDisabledDevice(deviceId); - configureDevice(InputReaderConfiguration::CHANGE_ENABLED_STATE, device); + mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_ENABLED_STATE); } void enableDevice(int32_t deviceId, InputDevice* device) { mFakePolicy->removeDisabledDevice(deviceId); - configureDevice(InputReaderConfiguration::CHANGE_ENABLED_STATE, device); - } - - void configureDevice(uint32_t changes, InputDevice* device) { - device->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes); + mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_ENABLED_STATE); } FakeInputMapper* addDeviceWithFakeInputMapper(int32_t deviceId, int32_t controllerNumber, @@ -1417,28 +1409,22 @@ TEST_F(InputReaderTest, WhenEnabledChanges_SendsDeviceResetNotification) { NotifyDeviceResetArgs resetArgs; ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); - ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime); ASSERT_EQ(deviceId, resetArgs.deviceId); ASSERT_EQ(device->isEnabled(), true); disableDevice(deviceId, device); - mReader->loopOnce(); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); - ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime); ASSERT_EQ(deviceId, resetArgs.deviceId); ASSERT_EQ(device->isEnabled(), false); disableDevice(deviceId, device); - mReader->loopOnce(); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasNotCalled()); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasNotCalled()); ASSERT_EQ(device->isEnabled(), false); enableDevice(deviceId, device); - mReader->loopOnce(); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); - ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime); ASSERT_EQ(deviceId, resetArgs.deviceId); ASSERT_EQ(device->isEnabled(), true); } @@ -1560,7 +1546,7 @@ TEST_F(InputReaderTest, MarkSupportedKeyCodes_ForwardsRequestsToMappers) { ASSERT_TRUE(flags[0] && flags[1] && !flags[2] && !flags[3]); } -TEST_F(InputReaderTest, LoopOnce_WhenDeviceScanFinished_SendsConfigurationChanged) { +TEST_F(InputReaderTest, WhenDeviceScanFinished_SendsConfigurationChanged) { addDevice(1, "ignored", INPUT_DEVICE_CLASS_KEYBOARD, nullptr); NotifyConfigurationChangedArgs args; @@ -1569,13 +1555,12 @@ TEST_F(InputReaderTest, LoopOnce_WhenDeviceScanFinished_SendsConfigurationChange ASSERT_EQ(ARBITRARY_TIME, args.eventTime); } -TEST_F(InputReaderTest, LoopOnce_ForwardsRawEventsToMappers) { +TEST_F(InputReaderTest, ForwardsRawEventsToMappers) { FakeInputMapper* mapper = nullptr; ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, "fake", INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, nullptr)); mFakeEventHub->enqueueEvent(0, 1, EV_KEY, KEY_A, 1); - mReader->loopOnce(); ASSERT_NO_FATAL_FAILURE(mFakeEventHub->assertQueueIsEmpty()); RawEvent event; @@ -1602,19 +1587,16 @@ TEST_F(InputReaderTest, DeviceReset_IncrementsSequenceNumber) { uint32_t prevSequenceNum = resetArgs.sequenceNum; disableDevice(deviceId, device); - mReader->loopOnce(); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); ASSERT_TRUE(prevSequenceNum < resetArgs.sequenceNum); prevSequenceNum = resetArgs.sequenceNum; enableDevice(deviceId, device); - mReader->loopOnce(); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); ASSERT_TRUE(prevSequenceNum < resetArgs.sequenceNum); prevSequenceNum = resetArgs.sequenceNum; disableDevice(deviceId, device); - mReader->loopOnce(); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); ASSERT_TRUE(prevSequenceNum < resetArgs.sequenceNum); prevSequenceNum = resetArgs.sequenceNum; @@ -1629,7 +1611,6 @@ TEST_F(InputReaderTest, Device_CanDispatchToDisplay) { FakeInputMapper* mapper = new FakeInputMapper(device, AINPUT_SOURCE_TOUCHSCREEN); device->addMapper(mapper); mReader->setNextDevice(device); - addDevice(deviceId, "fake", deviceClass, nullptr); const uint8_t hdmi1 = 1; @@ -1637,13 +1618,20 @@ TEST_F(InputReaderTest, Device_CanDispatchToDisplay) { mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi1); // Add default and second display. + mFakePolicy->clearViewports(); mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0, "local:0", NO_PORT, ViewportType::VIEWPORT_INTERNAL); mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0, "local:1", hdmi1, ViewportType::VIEWPORT_EXTERNAL); mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_DISPLAY_INFO); - mReader->loopOnce(); + + // Add the device, and make sure all of the callbacks are triggered. + // The device is added after the input port associations are processed since + // we do not yet support dynamic device-to-display associations. + ASSERT_NO_FATAL_FAILURE(addDevice(deviceId, "fake", deviceClass, nullptr)); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled()); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled()); + ASSERT_NO_FATAL_FAILURE(mapper->assertConfigureWasCalled()); // Device should only dispatch to the specified display. ASSERT_EQ(deviceId, device->getId()); @@ -1652,6 +1640,8 @@ TEST_F(InputReaderTest, Device_CanDispatchToDisplay) { // Can't dispatch event from a disabled device. disableDevice(deviceId, device); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled()); + ASSERT_NO_FATAL_FAILURE(mapper->assertConfigureWasCalled()); ASSERT_FALSE(mReader->canDispatchToDisplay(deviceId, SECONDARY_DISPLAY_ID)); } @@ -1674,7 +1664,7 @@ protected: InputDevice* mDevice; - virtual void SetUp() { + virtual void SetUp() override { mFakeEventHub = std::make_unique<FakeEventHub>(); mFakePolicy = new FakeInputReaderPolicy(); mFakeListener = new TestInputListener(); @@ -1688,7 +1678,7 @@ protected: DEVICE_CONTROLLER_NUMBER, identifier, DEVICE_CLASSES); } - virtual void TearDown() { + virtual void TearDown() override { delete mDevice; delete mFakeContext; @@ -1912,7 +1902,7 @@ protected: FakeInputReaderContext* mFakeContext; InputDevice* mDevice; - virtual void SetUp() { + virtual void SetUp() override { mFakeEventHub = std::make_unique<FakeEventHub>(); mFakePolicy = new FakeInputReaderPolicy(); mFakeListener = new TestInputListener(); @@ -1926,7 +1916,7 @@ protected: mFakeEventHub->addDevice(mDevice->getId(), DEVICE_NAME, 0); } - virtual void TearDown() { + virtual void TearDown() override { delete mDevice; delete mFakeContext; mFakeListener.clear(); @@ -2589,7 +2579,7 @@ protected: sp<FakePointerController> mFakePointerController; - virtual void SetUp() { + virtual void SetUp() override { InputMapperTest::SetUp(); mFakePointerController = new FakePointerController(); diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp index e5d23d0d1f..4226e9a452 100644 --- a/services/surfaceflinger/Android.bp +++ b/services/surfaceflinger/Android.bp @@ -165,6 +165,7 @@ filegroup { "Scheduler/PhaseOffsets.cpp", "Scheduler/Scheduler.cpp", "Scheduler/SchedulerUtils.cpp", + "Scheduler/VSyncDispatch.cpp", "Scheduler/VSyncModulator.cpp", "StartPropertySetThread.cpp", "SurfaceFlinger.cpp", diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h index 289bb1756f..8e6a70c748 100644 --- a/services/surfaceflinger/BufferStateLayer.h +++ b/services/surfaceflinger/BufferStateLayer.h @@ -63,6 +63,7 @@ public: } Rect getCrop(const Layer::State& s) const; + uint32_t getTransformHint() const { return mTransformHint; } bool setTransform(uint32_t transform) override; bool setTransformToDisplayInverse(bool transformToDisplayInverse) override; bool setCrop(const Rect& crop) override; diff --git a/services/surfaceflinger/Client.cpp b/services/surfaceflinger/Client.cpp index c7ed9b0412..f3313645fa 100644 --- a/services/surfaceflinger/Client.cpp +++ b/services/surfaceflinger/Client.cpp @@ -75,17 +75,18 @@ sp<Layer> Client::getLayerUser(const sp<IBinder>& handle) const status_t Client::createSurface(const String8& name, uint32_t w, uint32_t h, PixelFormat format, uint32_t flags, const sp<IBinder>& parentHandle, LayerMetadata metadata, sp<IBinder>* handle, - sp<IGraphicBufferProducer>* gbp) { + sp<IGraphicBufferProducer>* gbp, uint32_t* outTransformHint) { // We rely on createLayer to check permissions. return mFlinger->createLayer(name, this, w, h, format, flags, std::move(metadata), handle, gbp, - parentHandle); + parentHandle, nullptr, outTransformHint); } status_t Client::createWithSurfaceParent(const String8& name, uint32_t w, uint32_t h, PixelFormat format, uint32_t flags, const sp<IGraphicBufferProducer>& parent, LayerMetadata metadata, sp<IBinder>* handle, - sp<IGraphicBufferProducer>* gbp) { + sp<IGraphicBufferProducer>* gbp, + uint32_t* outTransformHint) { if (mFlinger->authenticateSurfaceTexture(parent) == false) { ALOGE("failed to authenticate surface texture"); // The extra parent layer check below before returning is to help with debugging @@ -103,7 +104,7 @@ status_t Client::createWithSurfaceParent(const String8& name, uint32_t w, uint32 } return mFlinger->createLayer(name, this, w, h, format, flags, std::move(metadata), handle, gbp, - nullptr, layer); + nullptr, layer, outTransformHint); } status_t Client::mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle) { diff --git a/services/surfaceflinger/Client.h b/services/surfaceflinger/Client.h index 7d7cef8c50..e9063e5bb6 100644 --- a/services/surfaceflinger/Client.h +++ b/services/surfaceflinger/Client.h @@ -54,13 +54,15 @@ private: 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); + sp<IGraphicBufferProducer>* gbp, + uint32_t* outTransformHint = nullptr); virtual status_t createWithSurfaceParent(const String8& name, uint32_t w, uint32_t h, PixelFormat format, uint32_t flags, const sp<IGraphicBufferProducer>& parent, LayerMetadata metadata, sp<IBinder>* handle, - sp<IGraphicBufferProducer>* gbp); + sp<IGraphicBufferProducer>* gbp, + uint32_t* outTransformHint = nullptr); status_t mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* handle); diff --git a/services/surfaceflinger/ContainerLayer.h b/services/surfaceflinger/ContainerLayer.h index b48d471bde..9b7bab1e1d 100644 --- a/services/surfaceflinger/ContainerLayer.h +++ b/services/surfaceflinger/ContainerLayer.h @@ -34,6 +34,7 @@ public: bool isCreatedFromMainThread() const override { return true; } protected: + bool canDrawShadows() const override { return false; } sp<Layer> createClone() override; }; diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index e19b79b7c5..d5a9ae1b3b 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -339,7 +339,8 @@ FloatRect Layer::getBoundsPreScaling(const ui::Transform& bufferScaleTransform) return bufferScaleTransform.inverse().transform(mBounds); } -void Layer::computeBounds(FloatRect parentBounds, ui::Transform parentTransform) { +void Layer::computeBounds(FloatRect parentBounds, ui::Transform parentTransform, + float parentShadowRadius) { const State& s(getDrawingState()); // Calculate effective layer transform @@ -362,11 +363,23 @@ void Layer::computeBounds(FloatRect parentBounds, ui::Transform parentTransform) mBounds = bounds; mScreenBounds = mEffectiveTransform.transform(mBounds); + // Use the layer's own shadow radius if set. Otherwise get the radius from + // parent. + if (s.shadowRadius > 0.f) { + mEffectiveShadowRadius = s.shadowRadius; + } else { + mEffectiveShadowRadius = parentShadowRadius; + } + + // Shadow radius is passed down to only one layer so if the layer can draw shadows, + // don't pass it to its children. + const float childShadowRadius = canDrawShadows() ? 0.f : mEffectiveShadowRadius; + // Add any buffer scaling to the layer's children. ui::Transform bufferScaleTransform = getBufferScaleTransform(); for (const sp<Layer>& child : mDrawingChildren) { child->computeBounds(getBoundsPreScaling(bufferScaleTransform), - getTransformWithScale(bufferScaleTransform)); + getTransformWithScale(bufferScaleTransform), childShadowRadius); } } @@ -466,11 +479,13 @@ void Layer::latchPerFrameState(compositionengine::LayerFECompositionState& compo compositionState.hasProtectedContent = isProtected(); const bool usesRoundedCorners = getRoundedCornerState().radius != 0.f; + const bool drawsShadows = mEffectiveShadowRadius != 0.f; + compositionState.isOpaque = isOpaque(drawingState) && !usesRoundedCorners && getAlpha() == 1.0_hf; // Force client composition for special cases known only to the front-end. - if (isHdrY410() || usesRoundedCorners) { + if (isHdrY410() || usesRoundedCorners || drawsShadows) { compositionState.forceClientComposition = true; } } @@ -1116,6 +1131,18 @@ uint32_t Layer::getLayerStack() const { return p->getLayerStack(); } +bool Layer::setShadowRadius(float shadowRadius) { + if (mCurrentState.shadowRadius == shadowRadius) { + return false; + } + + mCurrentState.sequence++; + mCurrentState.shadowRadius = shadowRadius; + mCurrentState.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + void Layer::deferTransactionUntil_legacy(const sp<Layer>& barrierLayer, uint64_t frameNumber) { ATRACE_CALL(); mCurrentState.barrierLayer_legacy = barrierLayer; @@ -1410,8 +1437,8 @@ void Layer::setChildrenDrawingParent(const sp<Layer>& newParent) { for (const sp<Layer>& child : mDrawingChildren) { child->mDrawingParent = newParent; child->computeBounds(newParent->mBounds, - newParent->getTransformWithScale( - newParent->getBufferScaleTransform())); + newParent->getTransformWithScale(newParent->getBufferScaleTransform()), + newParent->mEffectiveShadowRadius); } } @@ -1540,8 +1567,10 @@ void Layer::setParent(const sp<Layer>& layer) { mCurrentParent = layer; } -int32_t Layer::getZ() const { - return mDrawingState.z; +int32_t Layer::getZ(LayerVector::StateSet stateSet) const { + const bool useDrawing = stateSet == LayerVector::StateSet::Drawing; + const State& state = useDrawing ? mDrawingState : mCurrentState; + return state.z; } bool Layer::usingRelativeZ(LayerVector::StateSet stateSet) const { @@ -1601,7 +1630,7 @@ void Layer::traverseInZOrder(LayerVector::StateSet stateSet, const LayerVector:: continue; } - if (relative->getZ() >= 0) { + if (relative->getZ(stateSet) >= 0) { break; } relative->traverseInZOrder(stateSet, visitor); @@ -1635,7 +1664,7 @@ void Layer::traverseInReverseZOrder(LayerVector::StateSet stateSet, continue; } - if (relative->getZ() < 0) { + if (relative->getZ(stateSet) < 0) { break; } relative->traverseInReverseZOrder(stateSet, visitor); @@ -1693,7 +1722,7 @@ void Layer::traverseChildrenInZOrderInner(const std::vector<Layer*>& layersInTre size_t i = 0; for (; i < list.size(); i++) { const auto& relative = list[i]; - if (relative->getZ() >= 0) { + if (relative->getZ(stateSet) >= 0) { break; } relative->traverseChildrenInZOrderInner(layersInTree, stateSet, visitor); diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index fdac98f317..fb723914d8 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -216,6 +216,7 @@ public: std::deque<sp<CallbackHandle>> callbackHandles; bool colorSpaceAgnostic; nsecs_t desiredPresentTime = -1; + float shadowRadius; }; explicit Layer(const LayerCreationArgs& args); @@ -329,6 +330,7 @@ public: }; virtual bool setBackgroundColor(const half3& color, float alpha, ui::Dataspace dataspace); virtual bool setColorSpaceAgnostic(const bool agnostic); + bool setShadowRadius(float shadowRadius); virtual ui::Dataspace getDataSpace() const { return ui::Dataspace::UNKNOWN; } @@ -362,7 +364,7 @@ public: FloatRect getBounds() const; // Compute bounds for the layer and cache the results. - void computeBounds(FloatRect parentBounds, ui::Transform parentTransform); + void computeBounds(FloatRect parentBounds, ui::Transform parentTransform, float shadowRadius); // Returns the buffer scale transform if a scaling mode is set. ui::Transform getBufferScaleTransform() const; @@ -672,7 +674,7 @@ public: // Copy the current list of children to the drawing state. Called by // SurfaceFlinger to complete a transaction. void commitChildList(); - int32_t getZ() const; + int32_t getZ(LayerVector::StateSet stateSet) const; virtual void pushPendingState(); /** @@ -942,6 +944,14 @@ private: // this layer will update it's buffer. When mClonedFrom updates it's drawing state, children, // and relatives, this layer will update as well. wp<Layer> mClonedFrom; + + // The inherited shadow radius after taking into account the layer hierarchy. This is the + // final shadow radius for this layer. If a shadow is specified for a layer, then effective + // shadow radius is the set shadow radius, otherwise its the parent's shadow radius. + float mEffectiveShadowRadius; + + // Returns true if the layer can draw shadows on its border. + virtual bool canDrawShadows() const { return true; } }; } // namespace android diff --git a/services/surfaceflinger/Scheduler/TimeKeeper.h b/services/surfaceflinger/Scheduler/TimeKeeper.h new file mode 100644 index 0000000000..699cd504fa --- /dev/null +++ b/services/surfaceflinger/Scheduler/TimeKeeper.h @@ -0,0 +1,53 @@ +/* + * 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 <utils/Timers.h> +#include <functional> + +namespace android::scheduler { + +/* + * TimeKeeper is the interface for a single-shot timer primitive. + */ +class TimeKeeper { +public: + virtual ~TimeKeeper(); + + /* + * Arms callback to fired in time nanoseconds. + * There is only one timer, and subsequent calls will reset the callback function and the time. + */ + virtual void alarmIn(std::function<void()> const& callback, nsecs_t time) = 0; + + /* + * Cancels an existing pending callback + */ + virtual void alarmCancel() = 0; + + /* + * Returns the SYSTEM_TIME_MONOTONIC, used by testing infra to stub time. + */ + virtual nsecs_t now() const = 0; + +protected: + TimeKeeper(TimeKeeper const&) = delete; + TimeKeeper& operator=(TimeKeeper const&) = delete; + TimeKeeper() = default; +}; + +} // namespace android::scheduler diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.cpp b/services/surfaceflinger/Scheduler/VSyncDispatch.cpp new file mode 100644 index 0000000000..c9b2e77871 --- /dev/null +++ b/services/surfaceflinger/Scheduler/VSyncDispatch.cpp @@ -0,0 +1,285 @@ +/* + * 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 <utils/Trace.h> +#include <vector> + +#include "TimeKeeper.h" +#include "VSyncDispatch.h" +#include "VSyncTracker.h" + +namespace android::scheduler { + +VSyncTracker::~VSyncTracker() = default; +TimeKeeper::~TimeKeeper() = default; + +impl::VSyncDispatchEntry::VSyncDispatchEntry(std::string const& name, + std::function<void(nsecs_t)> const& cb) + : mName(name), mCallback(cb), mWorkDuration(0), mEarliestVsync(0) {} + +std::optional<nsecs_t> impl::VSyncDispatchEntry::lastExecutedVsyncTarget() const { + return mLastDispatchTime; +} + +std::string_view impl::VSyncDispatchEntry::name() const { + return mName; +} + +std::optional<nsecs_t> impl::VSyncDispatchEntry::wakeupTime() const { + if (!mArmedInfo) { + return {}; + } + return {mArmedInfo->mActualWakeupTime}; +} + +nsecs_t impl::VSyncDispatchEntry::schedule(nsecs_t workDuration, nsecs_t earliestVsync, + VSyncTracker& tracker, nsecs_t now) { + mWorkDuration = workDuration; + mEarliestVsync = earliestVsync; + arm(tracker, now); + return mArmedInfo->mActualWakeupTime; +} + +void impl::VSyncDispatchEntry::update(VSyncTracker& tracker, nsecs_t now) { + if (!mArmedInfo) { + return; + } + arm(tracker, now); +} + +void impl::VSyncDispatchEntry::arm(VSyncTracker& tracker, nsecs_t now) { + auto const nextVsyncTime = + tracker.nextAnticipatedVSyncTimeFrom(std::max(mEarliestVsync, now + mWorkDuration)); + mArmedInfo = {nextVsyncTime - mWorkDuration, nextVsyncTime}; +} + +void impl::VSyncDispatchEntry::disarm() { + mArmedInfo.reset(); +} + +nsecs_t impl::VSyncDispatchEntry::executing() { + mLastDispatchTime = mArmedInfo->mActualVsyncTime; + disarm(); + return *mLastDispatchTime; +} + +void impl::VSyncDispatchEntry::callback(nsecs_t t) { + { + std::lock_guard<std::mutex> lk(mRunningMutex); + mRunning = true; + } + + mCallback(t); + + std::lock_guard<std::mutex> lk(mRunningMutex); + mRunning = false; + mCv.notify_all(); +} + +void impl::VSyncDispatchEntry::ensureNotRunning() { + std::unique_lock<std::mutex> lk(mRunningMutex); + mCv.wait(lk, [this]() REQUIRES(mRunningMutex) { return !mRunning; }); +} + +VSyncDispatch::VSyncDispatch(std::unique_ptr<TimeKeeper> tk, VSyncTracker& tracker, + nsecs_t timerSlack) + : mTimeKeeper(std::move(tk)), mTracker(tracker), mTimerSlack(timerSlack) {} + +VSyncDispatch::~VSyncDispatch() { + std::lock_guard<decltype(mMutex)> lk(mMutex); + cancelTimer(); +} + +void VSyncDispatch::cancelTimer() { + mIntendedWakeupTime = kInvalidTime; + mTimeKeeper->alarmCancel(); +} + +void VSyncDispatch::setTimer(nsecs_t targetTime, nsecs_t now) { + mIntendedWakeupTime = targetTime; + mTimeKeeper->alarmIn(std::bind(&VSyncDispatch::timerCallback, this), targetTime - now); +} + +void VSyncDispatch::rearmTimer(nsecs_t now) { + rearmTimerSkippingUpdateFor(now, mCallbacks.end()); +} + +void VSyncDispatch::rearmTimerSkippingUpdateFor(nsecs_t now, + CallbackMap::iterator const& skipUpdateIt) { + std::optional<nsecs_t> min; + for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) { + auto& callback = it->second; + if (!callback->wakeupTime()) { + continue; + } + + if (it != skipUpdateIt) { + callback->update(mTracker, now); + } + auto const wakeupTime = *callback->wakeupTime(); + if (!min || (min && *min > wakeupTime)) { + min = wakeupTime; + } + } + + if (min && (min < mIntendedWakeupTime)) { + setTimer(*min, now); + } else { + cancelTimer(); + } +} + +void VSyncDispatch::timerCallback() { + struct Invocation { + std::shared_ptr<impl::VSyncDispatchEntry> callback; + nsecs_t timestamp; + }; + std::vector<Invocation> invocations; + { + std::lock_guard<decltype(mMutex)> lk(mMutex); + for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) { + auto& callback = it->second; + auto const wakeupTime = callback->wakeupTime(); + if (!wakeupTime) { + continue; + } + + if (*wakeupTime < mIntendedWakeupTime + mTimerSlack) { + callback->executing(); + invocations.emplace_back( + Invocation{callback, *callback->lastExecutedVsyncTarget()}); + } + } + + mIntendedWakeupTime = kInvalidTime; + rearmTimer(mTimeKeeper->now()); + } + + for (auto const& invocation : invocations) { + invocation.callback->callback(invocation.timestamp); + } +} + +VSyncDispatch::CallbackToken VSyncDispatch::registerCallback( + std::function<void(nsecs_t)> const& callbackFn, std::string callbackName) { + std::lock_guard<decltype(mMutex)> lk(mMutex); + return CallbackToken{ + mCallbacks + .emplace(++mCallbackToken, + std::make_shared<impl::VSyncDispatchEntry>(callbackName, callbackFn)) + .first->first}; +} + +void VSyncDispatch::unregisterCallback(CallbackToken token) { + std::shared_ptr<impl::VSyncDispatchEntry> entry = nullptr; + { + std::lock_guard<decltype(mMutex)> lk(mMutex); + auto it = mCallbacks.find(token); + if (it != mCallbacks.end()) { + entry = it->second; + mCallbacks.erase(it); + } + } + + if (entry) { + entry->ensureNotRunning(); + } +} + +ScheduleResult VSyncDispatch::schedule(CallbackToken token, nsecs_t workDuration, + nsecs_t earliestVsync) { + auto result = ScheduleResult::Error; + { + std::lock_guard<decltype(mMutex)> lk(mMutex); + + auto it = mCallbacks.find(token); + if (it == mCallbacks.end()) { + return result; + } + auto& callback = it->second; + result = callback->wakeupTime() ? ScheduleResult::ReScheduled : ScheduleResult::Scheduled; + + auto const now = mTimeKeeper->now(); + auto const wakeupTime = callback->schedule(workDuration, earliestVsync, mTracker, now); + + if (wakeupTime < now - mTimerSlack || callback->lastExecutedVsyncTarget() > wakeupTime) { + return ScheduleResult::CannotSchedule; + } + + if (wakeupTime < mIntendedWakeupTime - mTimerSlack) { + rearmTimerSkippingUpdateFor(now, it); + } + } + + return result; +} + +CancelResult VSyncDispatch::cancel(CallbackToken token) { + std::lock_guard<decltype(mMutex)> lk(mMutex); + + auto it = mCallbacks.find(token); + if (it == mCallbacks.end()) { + return CancelResult::Error; + } + auto& callback = it->second; + + if (callback->wakeupTime()) { + callback->disarm(); + mIntendedWakeupTime = kInvalidTime; + rearmTimer(mTimeKeeper->now()); + return CancelResult::Cancelled; + } + return CancelResult::TooLate; +} + +VSyncCallbackRegistration::VSyncCallbackRegistration(VSyncDispatch& dispatch, + std::function<void(nsecs_t)> const& callbackFn, + std::string const& callbackName) + : mDispatch(dispatch), + mToken(dispatch.registerCallback(callbackFn, callbackName)), + mValidToken(true) {} + +VSyncCallbackRegistration::VSyncCallbackRegistration(VSyncCallbackRegistration&& other) + : mDispatch(other.mDispatch), + mToken(std::move(other.mToken)), + mValidToken(std::move(other.mValidToken)) { + other.mValidToken = false; +} + +VSyncCallbackRegistration& VSyncCallbackRegistration::operator=(VSyncCallbackRegistration&& other) { + mDispatch = std::move(other.mDispatch); + mToken = std::move(other.mToken); + mValidToken = std::move(other.mValidToken); + other.mValidToken = false; + return *this; +} + +VSyncCallbackRegistration::~VSyncCallbackRegistration() { + if (mValidToken) mDispatch.get().unregisterCallback(mToken); +} + +ScheduleResult VSyncCallbackRegistration::schedule(nsecs_t workDuration, nsecs_t earliestVsync) { + if (!mValidToken) return ScheduleResult::Error; + return mDispatch.get().schedule(mToken, workDuration, earliestVsync); +} + +CancelResult VSyncCallbackRegistration::cancel() { + if (!mValidToken) return CancelResult::Error; + return mDispatch.get().cancel(mToken); +} + +} // namespace android::scheduler diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h new file mode 100644 index 0000000000..00504957a9 --- /dev/null +++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h @@ -0,0 +1,230 @@ +/* + * 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-base/thread_annotations.h> +#include <utils/Timers.h> +#include <functional> +#include <memory> +#include <mutex> +#include <string> +#include <string_view> +#include <unordered_map> + +#include "StrongTyping.h" + +namespace android::scheduler { +class TimeKeeper; +class VSyncTracker; + +enum class ScheduleResult { Scheduled, ReScheduled, CannotSchedule, Error }; +enum class CancelResult { Cancelled, TooLate, Error }; + +namespace impl { + +// VSyncDispatchEntry is a helper class representing internal state for each entry in VSyncDispatch +// hoisted to public for unit testing. +class VSyncDispatchEntry { +public: + // This is the state of the entry. There are 3 states, armed, running, disarmed. + // Valid transition: disarmed -> armed ( when scheduled ) + // Valid transition: armed -> running -> disarmed ( when timer is called) + // Valid transition: armed -> disarmed ( when cancelled ) + VSyncDispatchEntry(std::string const& name, std::function<void(nsecs_t)> const& fn); + std::string_view name() const; + + // Start: functions that are not threadsafe. + // Return the last vsync time this callback was invoked. + std::optional<nsecs_t> lastExecutedVsyncTarget() const; + + // This moves the state from disarmed->armed and will calculate the wakeupTime. + nsecs_t schedule(nsecs_t workDuration, nsecs_t earliestVsync, VSyncTracker& tracker, + nsecs_t now); + // This will update armed entries with the latest vsync information. Entry remains armed. + void update(VSyncTracker& tracker, nsecs_t now); + + // This will return empty if not armed, or the next calculated wakeup time if armed. + // It will not update the wakeupTime. + std::optional<nsecs_t> wakeupTime() const; + + // This moves state from armed->disarmed. + void disarm(); + + // This moves the state from armed->running. + // Store the timestamp that this was intended for as the last called timestamp. + nsecs_t executing(); + // End: functions that are not threadsafe. + + // Invoke the callback with the timestamp, moving the state from running->disarmed. + void callback(nsecs_t timestamp); + // Block calling thread while the callback is executing. + void ensureNotRunning(); + +private: + void arm(VSyncTracker& tracker, nsecs_t now); + std::string const mName; + std::function<void(nsecs_t)> const mCallback; + + nsecs_t mWorkDuration; + nsecs_t mEarliestVsync; + + struct ArmingInfo { + nsecs_t mActualWakeupTime; + nsecs_t mActualVsyncTime; + }; + std::optional<ArmingInfo> mArmedInfo; + std::optional<nsecs_t> mLastDispatchTime; + + std::mutex mRunningMutex; + std::condition_variable mCv; + bool mRunning GUARDED_BY(mRunningMutex) = false; +}; + +} // namespace impl + +/* + * VSyncDispatch is a class that will dispatch callbacks relative to system vsync events. + */ +class VSyncDispatch { +public: + using CallbackToken = StrongTyping<size_t, class CallbackTokenTag, Compare>; + + /* creates a VsyncDispatch. + * \param [in] a timekeeper object for dispatching events. + * \param [in] a tracker object that is monitoring expected vsync events. + * \param [in] a tunable in nanoseconds that indicates when events that fall close together + * should be dispatched in one timer wakeup. + */ + explicit VSyncDispatch(std::unique_ptr<TimeKeeper> tk, VSyncTracker& tracker, + nsecs_t timerSlack); + ~VSyncDispatch(); + + /* + * Registers a callback that will be called at designated points on the vsync timeline. + * The callback can be scheduled, rescheduled targeting vsync times, or cancelled. + * The token returned must be cleaned up via unregisterCallback. + * + * \param [in] callbackFn A function to schedule for callback. The resources needed to invoke + * callbackFn must have lifetimes encompassing the lifetime of the + * CallbackToken returned. + * \param [in] callbackName A human-readable, unique name to identify the callback. + * \return A token that can be used to schedule, reschedule, or cancel the + * invocation of callbackFn. + * + */ + CallbackToken registerCallback(std::function<void(nsecs_t)> const& callbackFn, + std::string callbackName); + + /* + * Unregisters a callback. + * + * \param [in] token The callback to unregister. + * + */ + void unregisterCallback(CallbackToken token); + + /* + * Schedules the registered callback to be dispatched. + * + * The callback will be dispatched at 'workDuration' nanoseconds before a vsync event. + * + * The caller designates the earliest vsync event that should be targeted by the earliestVsync + * parameter. + * The callback will be scheduled at (workDuration - predictedVsync), where predictedVsync + * is the first vsync event time where ( predictedVsync >= earliestVsync ). + * + * If (workDuration - earliestVsync) is in the past, or if a callback has already been + * dispatched for the predictedVsync, an error will be returned. + * + * It is valid to reschedule a callback to a different time. + * + * \param [in] token The callback to schedule. + * \param [in] workDuration The time before the actual vsync time to invoke the callback + * associated with token. + * \param [in] earliestVsync The targeted display time. This will be snapped to the closest + * predicted vsync time after earliestVsync. + * \return A ScheduleResult::Scheduled if callback was scheduled. + * A ScheduleResult::ReScheduled if callback was rescheduled. + * A ScheduleResult::CannotSchedule + * if (workDuration - earliestVsync) is in the past, or + * if a callback was dispatched for the predictedVsync already. + * A ScheduleResult::Error if there was another error. + */ + ScheduleResult schedule(CallbackToken token, nsecs_t workDuration, nsecs_t earliestVsync); + + /* Cancels a scheduled callback, if possible. + * + * \param [in] token The callback to cancel. + * \return A CancelResult::TooLate if the callback was already dispatched. + * A CancelResult::Cancelled if the callback was successfully cancelled. + * A CancelResult::Error if there was an pre-condition violation. + */ + CancelResult cancel(CallbackToken token); + +private: + VSyncDispatch(VSyncDispatch const&) = delete; + VSyncDispatch& operator=(VSyncDispatch const&) = delete; + + using CallbackMap = std::unordered_map<size_t, std::shared_ptr<impl::VSyncDispatchEntry>>; + + void timerCallback(); + void setTimer(nsecs_t, nsecs_t) REQUIRES(mMutex); + void rearmTimer(nsecs_t now) REQUIRES(mMutex); + void rearmTimerSkippingUpdateFor(nsecs_t now, CallbackMap::iterator const& skipUpdate) + REQUIRES(mMutex); + void cancelTimer() REQUIRES(mMutex); + + static constexpr nsecs_t kInvalidTime = std::numeric_limits<int64_t>::max(); + std::unique_ptr<TimeKeeper> const mTimeKeeper; + VSyncTracker& mTracker; + nsecs_t const mTimerSlack; + + std::mutex mutable mMutex; + size_t mCallbackToken GUARDED_BY(mMutex) = 0; + + CallbackMap mCallbacks GUARDED_BY(mMutex); + nsecs_t mIntendedWakeupTime GUARDED_BY(mMutex) = kInvalidTime; +}; + +/* + * Helper class to operate on registered callbacks. It is up to user of the class to ensure + * that VsyncDispatch lifetime exceeds the lifetime of VSyncCallbackRegistation. + */ +class VSyncCallbackRegistration { +public: + VSyncCallbackRegistration(VSyncDispatch&, std::function<void(nsecs_t)> const& callbackFn, + std::string const& callbackName); + VSyncCallbackRegistration(VSyncCallbackRegistration&&); + VSyncCallbackRegistration& operator=(VSyncCallbackRegistration&&); + ~VSyncCallbackRegistration(); + + // See documentation for VSyncDispatch::schedule. + ScheduleResult schedule(nsecs_t workDuration, nsecs_t earliestVsync); + + // See documentation for VSyncDispatch::cancel. + CancelResult cancel(); + +private: + VSyncCallbackRegistration(VSyncCallbackRegistration const&) = delete; + VSyncCallbackRegistration& operator=(VSyncCallbackRegistration const&) = delete; + + std::reference_wrapper<VSyncDispatch> mDispatch; + VSyncDispatch::CallbackToken mToken; + bool mValidToken; +}; + +} // namespace android::scheduler diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h new file mode 100644 index 0000000000..97b9620cd6 --- /dev/null +++ b/services/surfaceflinger/Scheduler/VSyncTracker.h @@ -0,0 +1,56 @@ +/* + * 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 <utils/Timers.h> +#include "VSyncDispatch.h" + +namespace android::scheduler { +/* + * VSyncTracker is an interface for providing estimates on future Vsync signal times based on + * historical vsync timing data. + */ +class VSyncTracker { +public: + virtual ~VSyncTracker(); + + /* + * Adds a known timestamp from a vsync timing source (HWVsync signal, present fence) + * to the model. + * + * \param [in] timestamp The timestamp when the vsync signal was. + */ + virtual void addVsyncTimestamp(nsecs_t timestamp) = 0; + + /* + * Access the next anticipated vsync time such that the anticipated time >= timePoint. + * This will always give the best accurate at the time of calling; multiple + * calls with the same timePoint might give differing values if the internal model + * is updated. + * + * \param [in] timePoint The point in time after which to estimate a vsync event. + * \return A prediction of the timestamp of a vsync event. + */ + virtual nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const = 0; + +protected: + VSyncTracker(VSyncTracker const&) = delete; + VSyncTracker& operator=(VSyncTracker const&) = delete; + VSyncTracker() = default; +}; + +} // namespace android::scheduler diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 1acb2da051..76fd51f8ea 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -2011,7 +2011,8 @@ void SurfaceFlinger::computeLayerBounds() { continue; } - layer->computeBounds(displayDevice->getViewport().toFloatRect(), ui::Transform()); + layer->computeBounds(displayDevice->getViewport().toFloatRect(), ui::Transform(), + 0.f /* shadowRadius */); } } } @@ -3335,6 +3336,9 @@ uint32_t SurfaceFlinger::setClientStateLocked( flags |= eTraversalNeeded; } } + if (what & layer_state_t::eShadowRadiusChanged) { + if (layer->setShadowRadius(s.shadowRadius)) flags |= eTraversalNeeded; + } // This has to happen after we reparent children because when we reparent to null we remove // child layers from current state and remove its relative z. If the children are reparented in // the same transaction, then we have to make sure we reparent the children first so we do not @@ -3432,8 +3436,8 @@ status_t SurfaceFlinger::createLayer(const String8& name, const sp<Client>& clie uint32_t h, PixelFormat format, uint32_t flags, LayerMetadata metadata, sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp, - const sp<IBinder>& parentHandle, - const sp<Layer>& parentLayer) { + const sp<IBinder>& parentHandle, const sp<Layer>& parentLayer, + uint32_t* outTransformHint) { if (int32_t(w|h) < 0) { ALOGE("createLayer() failed, w or h is negative (w=%d, h=%d)", int(w), int(h)); @@ -3470,7 +3474,7 @@ status_t SurfaceFlinger::createLayer(const String8& name, const sp<Client>& clie break; case ISurfaceComposerClient::eFXSurfaceBufferState: result = createBufferStateLayer(client, std::move(uniqueName), w, h, flags, - std::move(metadata), handle, &layer); + std::move(metadata), handle, outTransformHint, &layer); break; case ISurfaceComposerClient::eFXSurfaceColor: // check if buffer size is set for color layer. @@ -3585,11 +3589,14 @@ status_t SurfaceFlinger::createBufferQueueLayer(const sp<Client>& client, std::s status_t SurfaceFlinger::createBufferStateLayer(const sp<Client>& client, std::string name, uint32_t w, uint32_t h, uint32_t flags, LayerMetadata metadata, sp<IBinder>* handle, - sp<Layer>* outLayer) { + uint32_t* outTransformHint, sp<Layer>* outLayer) { LayerCreationArgs args(this, client, std::move(name), w, h, flags, std::move(metadata)); args.displayDevice = getDefaultDisplayDevice(); args.textureName = getNewTexture(); sp<BufferStateLayer> layer = getFactory().createBufferStateLayer(args); + if (outTransformHint) { + *outTransformHint = layer->getTransformHint(); + } *handle = layer->getHandle(); *outLayer = layer; @@ -4438,18 +4445,7 @@ status_t SurfaceFlinger::CheckTransactCodeCredentials(uint32_t code) { case SET_DISPLAY_BRIGHTNESS: { return OK; } - case CAPTURE_LAYERS: { - IPCThreadState* ipc = IPCThreadState::self(); - const int pid = ipc->getCallingPid(); - const int uid = ipc->getCallingUid(); - // allow media to capture layer for video thumbnails - if ((uid != AID_GRAPHICS && uid != AID_MEDIA) && - !PermissionCache::checkPermission(sReadFramebuffer, pid, uid)) { - ALOGE("Permission Denial: can't capture layer pid=%d, uid=%d", pid, uid); - return PERMISSION_DENIED; - } - return OK; - } + case CAPTURE_LAYERS: case CAPTURE_SCREEN: case ADD_REGION_SAMPLING_LISTENER: case REMOVE_REGION_SAMPLING_LISTENER: { @@ -5010,7 +5006,8 @@ status_t SurfaceFlinger::captureLayers( const Rect& drawingBounds) : oldParent(oldParent), newParent(newParent) { // Compute and cache the bounds for the new parent layer. - newParent->computeBounds(drawingBounds.toFloatRect(), ui::Transform()); + newParent->computeBounds(drawingBounds.toFloatRect(), ui::Transform(), + 0.f /* shadowRadius */); oldParent->setChildrenDrawingParent(newParent); } ~ReparentForDrawing() { oldParent->setChildrenDrawingParent(oldParent); } diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index abb8b821ba..e7ad295b2d 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -602,7 +602,8 @@ private: status_t createLayer(const String8& name, const sp<Client>& client, uint32_t w, uint32_t h, PixelFormat format, uint32_t flags, LayerMetadata metadata, sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp, - const sp<IBinder>& parentHandle, const sp<Layer>& parentLayer = nullptr); + const sp<IBinder>& parentHandle, const sp<Layer>& parentLayer = nullptr, + uint32_t* outTransformHint = nullptr); status_t createBufferQueueLayer(const sp<Client>& client, std::string name, uint32_t w, uint32_t h, uint32_t flags, LayerMetadata metadata, @@ -611,7 +612,8 @@ private: status_t createBufferStateLayer(const sp<Client>& client, std::string name, uint32_t w, uint32_t h, uint32_t flags, LayerMetadata metadata, - sp<IBinder>* outHandle, sp<Layer>* outLayer); + sp<IBinder>* outHandle, uint32_t* outTransformHint, + sp<Layer>* outLayer); status_t createColorLayer(const sp<Client>& client, std::string name, uint32_t w, uint32_t h, uint32_t flags, LayerMetadata metadata, sp<IBinder>* outHandle, diff --git a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp index a48f553d74..999e82dbd2 100644 --- a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp +++ b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp @@ -15,9 +15,9 @@ */ #include <gui/BufferItemConsumer.h> +#include <ui/Transform.h> #include <thread> #include "TransactionTestHarnesses.h" - namespace android { using android::hardware::graphics::common::V1_1::BufferUsage; @@ -188,6 +188,15 @@ TEST_P(LayerRenderTypeTransactionTest, SetSizeWithScaleToWindow_BufferQueue) { getScreenCapture()->expectColor(Rect(0, 0, 64, 64), Color::RED); } +TEST_P(LayerRenderTypeTransactionTest, CreateLayer_BufferState) { + uint32_t transformHint = ui::Transform::orientation_flags::ROT_INVALID; + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32, + ISurfaceComposerClient::eFXSurfaceBufferState, + /*parent*/ nullptr, &transformHint)); + ASSERT_NE(ui::Transform::orientation_flags::ROT_INVALID, transformHint); +} + void LayerRenderTypeTransactionTest::setRelativeZBasicHelper(uint32_t layerType) { sp<SurfaceControl> layerR; sp<SurfaceControl> layerG; diff --git a/services/surfaceflinger/tests/LayerTransactionTest.h b/services/surfaceflinger/tests/LayerTransactionTest.h index 7edddb6ea3..f7a6d964f0 100644 --- a/services/surfaceflinger/tests/LayerTransactionTest.h +++ b/services/surfaceflinger/tests/LayerTransactionTest.h @@ -53,9 +53,10 @@ protected: virtual sp<SurfaceControl> createLayer(const sp<SurfaceComposerClient>& client, const char* name, uint32_t width, uint32_t height, - uint32_t flags = 0, SurfaceControl* parent = nullptr) { - auto layer = - createSurface(client, name, width, height, PIXEL_FORMAT_RGBA_8888, flags, parent); + uint32_t flags = 0, SurfaceControl* parent = nullptr, + uint32_t* outTransformHint = nullptr) { + auto layer = createSurface(client, name, width, height, PIXEL_FORMAT_RGBA_8888, flags, + parent, outTransformHint); Transaction t; t.setLayerStack(layer, mDisplayLayerStack).setLayer(layer, mLayerZBase); @@ -72,15 +73,18 @@ protected: virtual sp<SurfaceControl> createSurface(const sp<SurfaceComposerClient>& client, const char* name, uint32_t width, uint32_t height, PixelFormat format, uint32_t flags, - SurfaceControl* parent = nullptr) { - auto layer = client->createSurface(String8(name), width, height, format, flags, parent); + SurfaceControl* parent = nullptr, + uint32_t* outTransformHint = nullptr) { + auto layer = client->createSurface(String8(name), width, height, format, flags, parent, + LayerMetadata(), outTransformHint); EXPECT_NE(nullptr, layer.get()) << "failed to create SurfaceControl"; return layer; } virtual sp<SurfaceControl> createLayer(const char* name, uint32_t width, uint32_t height, - uint32_t flags = 0, SurfaceControl* parent = nullptr) { - return createLayer(mClient, name, width, height, flags, parent); + uint32_t flags = 0, SurfaceControl* parent = nullptr, + uint32_t* outTransformHint = nullptr) { + return createLayer(mClient, name, width, height, flags, parent, outTransformHint); } sp<SurfaceControl> createColorLayer(const char* name, const Color& color, diff --git a/services/surfaceflinger/tests/TransactionTestHarnesses.h b/services/surfaceflinger/tests/TransactionTestHarnesses.h index 8fdcde40b6..5612bb21c7 100644 --- a/services/surfaceflinger/tests/TransactionTestHarnesses.h +++ b/services/surfaceflinger/tests/TransactionTestHarnesses.h @@ -114,12 +114,14 @@ public: LayerTypeTransactionHarness(uint32_t layerType) : mLayerType(layerType) {} sp<SurfaceControl> createLayer(const char* name, uint32_t width, uint32_t height, - uint32_t flags = 0, SurfaceControl* parent = nullptr) { + uint32_t flags = 0, SurfaceControl* parent = nullptr, + uint32_t* outTransformHint = nullptr) { // if the flags already have a layer type specified, return an error if (flags & ISurfaceComposerClient::eFXSurfaceMask) { return nullptr; } - return LayerTransactionTest::createLayer(name, width, height, flags | mLayerType, parent); + return LayerTransactionTest::createLayer(name, width, height, flags | mLayerType, parent, + outTransformHint); } void fillLayerColor(const sp<SurfaceControl>& layer, const Color& color, int32_t bufferWidth, diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp index f85da205c0..246a62f707 100644 --- a/services/surfaceflinger/tests/unittests/Android.bp +++ b/services/surfaceflinger/tests/unittests/Android.bp @@ -54,6 +54,7 @@ cc_test { "FrameTracerTest.cpp", "TransactionApplicationTest.cpp", "StrongTypingTest.cpp", + "VSyncDispatchTest.cpp", "mock/DisplayHardware/MockComposer.cpp", "mock/DisplayHardware/MockDisplay.cpp", "mock/DisplayHardware/MockPowerAdvisor.cpp", diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp index 60da70fd25..143a7a0fb5 100644 --- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp +++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp @@ -803,7 +803,7 @@ struct BaseLayerVariant { layerDrawingState.active.h = 100; layerDrawingState.color = half4(LayerProperties::COLOR[0], LayerProperties::COLOR[1], LayerProperties::COLOR[2], LayerProperties::COLOR[3]); - layer->computeBounds(FloatRect(0, 0, 100, 100), ui::Transform()); + layer->computeBounds(FloatRect(0, 0, 100, 100), ui::Transform(), 0.f /* shadowRadius */); return layer; } diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTest.cpp new file mode 100644 index 0000000000..d1ed7e3dbe --- /dev/null +++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTest.cpp @@ -0,0 +1,680 @@ +/* + * 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. + */ + +#undef LOG_TAG +#define LOG_TAG "LibSurfaceFlingerUnittests" +#define LOG_NDEBUG 0 + +#include "Scheduler/TimeKeeper.h" +#include "Scheduler/VSyncDispatch.h" +#include "Scheduler/VSyncTracker.h" + +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <thread> + +using namespace testing; +using namespace std::literals; +namespace android::scheduler { + +class MockVSyncTracker : public VSyncTracker { +public: + MockVSyncTracker(nsecs_t period) : mPeriod{period} { + ON_CALL(*this, nextAnticipatedVSyncTimeFrom(_)) + .WillByDefault(Invoke(this, &MockVSyncTracker::nextVSyncTime)); + } + + MOCK_METHOD1(addVsyncTimestamp, void(nsecs_t)); + MOCK_CONST_METHOD1(nextAnticipatedVSyncTimeFrom, nsecs_t(nsecs_t)); + + nsecs_t nextVSyncTime(nsecs_t timePoint) const { + if (timePoint % mPeriod == 0) { + return timePoint; + } + return (timePoint - (timePoint % mPeriod) + mPeriod); + } + +protected: + nsecs_t const mPeriod; +}; + +class ControllableClock : public TimeKeeper { +public: + ControllableClock() { + ON_CALL(*this, alarmIn(_, _)) + .WillByDefault(Invoke(this, &ControllableClock::alarmInDefaultBehavior)); + ON_CALL(*this, now()).WillByDefault(Invoke(this, &ControllableClock::fakeTime)); + } + + MOCK_CONST_METHOD0(now, nsecs_t()); + MOCK_METHOD2(alarmIn, void(std::function<void()> const&, nsecs_t time)); + MOCK_METHOD0(alarmCancel, void()); + + void alarmInDefaultBehavior(std::function<void()> const& callback, nsecs_t time) { + mCallback = callback; + mNextCallbackTime = time + mCurrentTime; + } + + nsecs_t fakeTime() const { return mCurrentTime; } + + void advanceToNextCallback() { + mCurrentTime = mNextCallbackTime; + if (mCallback) { + mCallback(); + } + } + + void advanceBy(nsecs_t advancement) { + mCurrentTime += advancement; + if (mCurrentTime >= mNextCallbackTime && mCallback) { + mCallback(); + } + }; + +private: + std::function<void()> mCallback; + nsecs_t mNextCallbackTime = 0; + nsecs_t mCurrentTime = 0; +}; + +class CountingCallback { +public: + CountingCallback(VSyncDispatch& dispatch) + : mDispatch(dispatch), + mToken(dispatch.registerCallback(std::bind(&CountingCallback::counter, this, + std::placeholders::_1), + "test")) {} + ~CountingCallback() { mDispatch.unregisterCallback(mToken); } + + operator VSyncDispatch::CallbackToken() const { return mToken; } + + void counter(nsecs_t time) { mCalls.push_back(time); } + + VSyncDispatch& mDispatch; + VSyncDispatch::CallbackToken mToken; + std::vector<nsecs_t> mCalls; +}; + +class PausingCallback { +public: + PausingCallback(VSyncDispatch& dispatch, std::chrono::milliseconds pauseAmount) + : mDispatch(dispatch), + mToken(dispatch.registerCallback(std::bind(&PausingCallback::pause, this, + std::placeholders::_1), + "test")), + mRegistered(true), + mPauseAmount(pauseAmount) {} + ~PausingCallback() { unregister(); } + + operator VSyncDispatch::CallbackToken() const { return mToken; } + + void pause(nsecs_t) { + std::unique_lock<std::mutex> lk(mMutex); + mPause = true; + mCv.notify_all(); + + mCv.wait_for(lk, mPauseAmount, [this] { return !mPause; }); + + mResourcePresent = (mResource.lock() != nullptr); + } + + bool waitForPause() { + std::unique_lock<std::mutex> lk(mMutex); + auto waiting = mCv.wait_for(lk, 10s, [this] { return mPause; }); + return waiting; + } + + void stashResource(std::weak_ptr<void> const& resource) { mResource = resource; } + + bool resourcePresent() { return mResourcePresent; } + + void unpause() { + std::unique_lock<std::mutex> lk(mMutex); + mPause = false; + mCv.notify_all(); + } + + void unregister() { + if (mRegistered) { + mDispatch.unregisterCallback(mToken); + mRegistered = false; + } + } + + VSyncDispatch& mDispatch; + VSyncDispatch::CallbackToken mToken; + bool mRegistered = true; + + std::mutex mMutex; + std::condition_variable mCv; + bool mPause = false; + std::weak_ptr<void> mResource; + bool mResourcePresent = false; + std::chrono::milliseconds const mPauseAmount; +}; + +class VSyncDispatchTest : public testing::Test { +protected: + std::unique_ptr<TimeKeeper> createTimeKeeper() { + class TimeKeeperWrapper : public TimeKeeper { + public: + TimeKeeperWrapper(TimeKeeper& control) : mControllableClock(control) {} + void alarmIn(std::function<void()> const& callback, nsecs_t time) final { + mControllableClock.alarmIn(callback, time); + } + void alarmCancel() final { mControllableClock.alarmCancel(); } + nsecs_t now() const final { return mControllableClock.now(); } + + private: + TimeKeeper& mControllableClock; + }; + return std::make_unique<TimeKeeperWrapper>(mMockClock); + } + + ~VSyncDispatchTest() { + // destructor of dispatch will cancelAlarm(). Ignore final cancel in common test. + Mock::VerifyAndClearExpectations(&mMockClock); + } + + void advanceToNextCallback() { mMockClock.advanceToNextCallback(); } + + NiceMock<ControllableClock> mMockClock; + static nsecs_t constexpr mDispatchGroupThreshold = 5; + nsecs_t const mPeriod = 1000; + NiceMock<MockVSyncTracker> mStubTracker{mPeriod}; + VSyncDispatch mDispatch{createTimeKeeper(), mStubTracker, mDispatchGroupThreshold}; +}; + +TEST_F(VSyncDispatchTest, unregistersSetAlarmOnDestruction) { + EXPECT_CALL(mMockClock, alarmIn(_, 900)); + EXPECT_CALL(mMockClock, alarmCancel()); + { + VSyncDispatch mDispatch{createTimeKeeper(), mStubTracker, mDispatchGroupThreshold}; + CountingCallback cb(mDispatch); + EXPECT_EQ(mDispatch.schedule(cb, 100, 1000), ScheduleResult::Scheduled); + } +} + +TEST_F(VSyncDispatchTest, basicAlarmSettingFuture) { + auto intended = mPeriod - 230; + EXPECT_CALL(mMockClock, alarmIn(_, 900)); + + CountingCallback cb(mDispatch); + EXPECT_EQ(mDispatch.schedule(cb, 100, intended), ScheduleResult::Scheduled); + advanceToNextCallback(); + + ASSERT_THAT(cb.mCalls.size(), Eq(1)); + EXPECT_THAT(cb.mCalls[0], Eq(mPeriod)); +} + +TEST_F(VSyncDispatchTest, basicAlarmSettingFutureWithAdjustmentToTrueVsync) { + EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000)).WillOnce(Return(1150)); + EXPECT_CALL(mMockClock, alarmIn(_, 1050)); + + CountingCallback cb(mDispatch); + mDispatch.schedule(cb, 100, mPeriod); + advanceToNextCallback(); + + ASSERT_THAT(cb.mCalls.size(), Eq(1)); + EXPECT_THAT(cb.mCalls[0], Eq(1150)); +} + +TEST_F(VSyncDispatchTest, basicAlarmSettingAdjustmentPast) { + auto const now = 234; + mMockClock.advanceBy(234); + auto const workDuration = 10 * mPeriod; + EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(now + workDuration)) + .WillOnce(Return(mPeriod * 11)); + EXPECT_CALL(mMockClock, alarmIn(_, mPeriod - now)); + + CountingCallback cb(mDispatch); + EXPECT_EQ(mDispatch.schedule(cb, workDuration, mPeriod), ScheduleResult::Scheduled); +} + +TEST_F(VSyncDispatchTest, basicAlarmCancel) { + EXPECT_CALL(mMockClock, alarmIn(_, 900)); + EXPECT_CALL(mMockClock, alarmCancel()); + + CountingCallback cb(mDispatch); + EXPECT_EQ(mDispatch.schedule(cb, 100, mPeriod), ScheduleResult::Scheduled); + EXPECT_EQ(mDispatch.cancel(cb), CancelResult::Cancelled); +} + +TEST_F(VSyncDispatchTest, basicAlarmCancelTooLate) { + EXPECT_CALL(mMockClock, alarmIn(_, 900)); + EXPECT_CALL(mMockClock, alarmCancel()); + + CountingCallback cb(mDispatch); + EXPECT_EQ(mDispatch.schedule(cb, 100, mPeriod), ScheduleResult::Scheduled); + mMockClock.advanceBy(950); + EXPECT_EQ(mDispatch.cancel(cb), CancelResult::TooLate); +} + +TEST_F(VSyncDispatchTest, basicAlarmCancelTooLateWhenRunning) { + EXPECT_CALL(mMockClock, alarmIn(_, 900)); + EXPECT_CALL(mMockClock, alarmCancel()); + + PausingCallback cb(mDispatch, std::chrono::duration_cast<std::chrono::milliseconds>(1s)); + EXPECT_EQ(mDispatch.schedule(cb, 100, mPeriod), ScheduleResult::Scheduled); + + std::thread pausingThread([&] { mMockClock.advanceToNextCallback(); }); + EXPECT_TRUE(cb.waitForPause()); + EXPECT_EQ(mDispatch.cancel(cb), CancelResult::TooLate); + cb.unpause(); + pausingThread.join(); +} + +TEST_F(VSyncDispatchTest, unregisterSynchronizes) { + EXPECT_CALL(mMockClock, alarmIn(_, 900)); + EXPECT_CALL(mMockClock, alarmCancel()); + + auto resource = std::make_shared<int>(110); + + PausingCallback cb(mDispatch, 50ms); + cb.stashResource(resource); + EXPECT_EQ(mDispatch.schedule(cb, 100, mPeriod), ScheduleResult::Scheduled); + + std::thread pausingThread([&] { mMockClock.advanceToNextCallback(); }); + EXPECT_TRUE(cb.waitForPause()); + + cb.unregister(); + resource.reset(); + + cb.unpause(); + pausingThread.join(); + + EXPECT_TRUE(cb.resourcePresent()); +} + +TEST_F(VSyncDispatchTest, basicTwoAlarmSetting) { + EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000)) + .Times(4) + .WillOnce(Return(1055)) + .WillOnce(Return(1063)) + .WillOnce(Return(1063)) + .WillOnce(Return(1075)); + + Sequence seq; + EXPECT_CALL(mMockClock, alarmIn(_, 955)).InSequence(seq); + EXPECT_CALL(mMockClock, alarmIn(_, 813)).InSequence(seq); + EXPECT_CALL(mMockClock, alarmIn(_, 162)).InSequence(seq); + + CountingCallback cb0(mDispatch); + CountingCallback cb1(mDispatch); + + mDispatch.schedule(cb0, 100, mPeriod); + mDispatch.schedule(cb1, 250, mPeriod); + + advanceToNextCallback(); + advanceToNextCallback(); + + ASSERT_THAT(cb0.mCalls.size(), Eq(1)); + EXPECT_THAT(cb0.mCalls[0], Eq(1075)); + ASSERT_THAT(cb1.mCalls.size(), Eq(1)); + EXPECT_THAT(cb1.mCalls[0], Eq(1063)); +} + +TEST_F(VSyncDispatchTest, rearmsFaroutTimeoutWhenCancellingCloseOne) { + EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(_)) + .Times(4) + .WillOnce(Return(10000)) + .WillOnce(Return(1000)) + .WillOnce(Return(10000)) + .WillOnce(Return(10000)); + + Sequence seq; + EXPECT_CALL(mMockClock, alarmIn(_, 9900)).InSequence(seq); + EXPECT_CALL(mMockClock, alarmIn(_, 750)).InSequence(seq); + EXPECT_CALL(mMockClock, alarmIn(_, 9900)).InSequence(seq); + + CountingCallback cb0(mDispatch); + CountingCallback cb1(mDispatch); + + mDispatch.schedule(cb0, 100, mPeriod * 10); + mDispatch.schedule(cb1, 250, mPeriod); + mDispatch.cancel(cb1); +} + +TEST_F(VSyncDispatchTest, noUnnecessaryRearmsWhenRescheduling) { + Sequence seq; + EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq); + EXPECT_CALL(mMockClock, alarmIn(_, 100)).InSequence(seq); + + CountingCallback cb0(mDispatch); + CountingCallback cb1(mDispatch); + + mDispatch.schedule(cb0, 400, 1000); + mDispatch.schedule(cb1, 200, 1000); + mDispatch.schedule(cb1, 300, 1000); + advanceToNextCallback(); +} + +TEST_F(VSyncDispatchTest, necessaryRearmsWhenModifying) { + Sequence seq; + EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq); + EXPECT_CALL(mMockClock, alarmIn(_, 500)).InSequence(seq); + EXPECT_CALL(mMockClock, alarmIn(_, 100)).InSequence(seq); + + CountingCallback cb0(mDispatch); + CountingCallback cb1(mDispatch); + + mDispatch.schedule(cb0, 400, 1000); + mDispatch.schedule(cb1, 200, 1000); + mDispatch.schedule(cb1, 500, 1000); + advanceToNextCallback(); +} + +TEST_F(VSyncDispatchTest, modifyIntoGroup) { + Sequence seq; + EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq); + EXPECT_CALL(mMockClock, alarmIn(_, 1000)).InSequence(seq); + EXPECT_CALL(mMockClock, alarmIn(_, 990)).InSequence(seq); + EXPECT_CALL(mMockClock, alarmIn(_, 10)).InSequence(seq); + + auto offset = 400; + auto closeOffset = offset + mDispatchGroupThreshold - 1; + auto notCloseOffset = offset + 2 * mDispatchGroupThreshold; + + CountingCallback cb0(mDispatch); + CountingCallback cb1(mDispatch); + + mDispatch.schedule(cb0, 400, 1000); + mDispatch.schedule(cb1, 200, 1000); + mDispatch.schedule(cb1, closeOffset, 1000); + + advanceToNextCallback(); + ASSERT_THAT(cb0.mCalls.size(), Eq(1)); + EXPECT_THAT(cb0.mCalls[0], Eq(mPeriod)); + ASSERT_THAT(cb1.mCalls.size(), Eq(1)); + EXPECT_THAT(cb1.mCalls[0], Eq(mPeriod)); + + mDispatch.schedule(cb0, 400, 2000); + mDispatch.schedule(cb1, notCloseOffset, 2000); + advanceToNextCallback(); + ASSERT_THAT(cb1.mCalls.size(), Eq(2)); + EXPECT_THAT(cb1.mCalls[1], Eq(2000)); + + advanceToNextCallback(); + ASSERT_THAT(cb0.mCalls.size(), Eq(2)); + EXPECT_THAT(cb0.mCalls[1], Eq(2000)); +} + +TEST_F(VSyncDispatchTest, rearmsWhenEndingAndDoesntCancel) { + EXPECT_CALL(mMockClock, alarmIn(_, 900)); + EXPECT_CALL(mMockClock, alarmIn(_, 800)); + EXPECT_CALL(mMockClock, alarmIn(_, 100)); + EXPECT_CALL(mMockClock, alarmCancel()); + + CountingCallback cb0(mDispatch); + CountingCallback cb1(mDispatch); + + mDispatch.schedule(cb0, 100, 1000); + mDispatch.schedule(cb1, 200, 1000); + advanceToNextCallback(); + EXPECT_EQ(mDispatch.cancel(cb0), CancelResult::Cancelled); +} + +TEST_F(VSyncDispatchTest, setAlarmCallsAtCorrectTimeWithChangingVsync) { + EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(_)) + .Times(3) + .WillOnce(Return(950)) + .WillOnce(Return(1975)) + .WillOnce(Return(2950)); + + CountingCallback cb(mDispatch); + mDispatch.schedule(cb, 100, 920); + + mMockClock.advanceBy(850); + EXPECT_THAT(cb.mCalls.size(), Eq(1)); + + mDispatch.schedule(cb, 100, 1900); + mMockClock.advanceBy(900); + EXPECT_THAT(cb.mCalls.size(), Eq(1)); + mMockClock.advanceBy(125); + EXPECT_THAT(cb.mCalls.size(), Eq(2)); + + mDispatch.schedule(cb, 100, 2900); + mMockClock.advanceBy(975); + EXPECT_THAT(cb.mCalls.size(), Eq(3)); +} + +TEST_F(VSyncDispatchTest, callbackReentrancy) { + Sequence seq; + EXPECT_CALL(mMockClock, alarmIn(_, 900)).InSequence(seq); + EXPECT_CALL(mMockClock, alarmIn(_, 1000)).InSequence(seq); + + VSyncDispatch::CallbackToken tmp; + tmp = mDispatch.registerCallback([&](auto) { mDispatch.schedule(tmp, 100, 2000); }, "o.o"); + + mDispatch.schedule(tmp, 100, 1000); + advanceToNextCallback(); +} + +TEST_F(VSyncDispatchTest, callbackReentrantWithPastWakeup) { + VSyncDispatch::CallbackToken tmp; + tmp = mDispatch.registerCallback( + [&](auto) { + EXPECT_EQ(mDispatch.schedule(tmp, 400, 1000), ScheduleResult::CannotSchedule); + }, + "oo"); + + mDispatch.schedule(tmp, 999, 1000); + advanceToNextCallback(); +} + +TEST_F(VSyncDispatchTest, modificationsAroundVsyncTime) { + Sequence seq; + EXPECT_CALL(mMockClock, alarmIn(_, 1000)).InSequence(seq); + EXPECT_CALL(mMockClock, alarmIn(_, 200)).InSequence(seq); + EXPECT_CALL(mMockClock, alarmIn(_, 1000)).InSequence(seq); + EXPECT_CALL(mMockClock, alarmIn(_, 150)).InSequence(seq); + + CountingCallback cb(mDispatch); + mDispatch.schedule(cb, 0, 1000); + + mMockClock.advanceBy(750); + mDispatch.schedule(cb, 50, 1000); + + advanceToNextCallback(); + mDispatch.schedule(cb, 50, 2000); + + mMockClock.advanceBy(800); + mDispatch.schedule(cb, 100, 2000); +} + +TEST_F(VSyncDispatchTest, lateModifications) { + Sequence seq; + EXPECT_CALL(mMockClock, alarmIn(_, 500)).InSequence(seq); + EXPECT_CALL(mMockClock, alarmIn(_, 400)).InSequence(seq); + EXPECT_CALL(mMockClock, alarmIn(_, 350)).InSequence(seq); + EXPECT_CALL(mMockClock, alarmIn(_, 950)).InSequence(seq); + + CountingCallback cb0(mDispatch); + CountingCallback cb1(mDispatch); + + mDispatch.schedule(cb0, 500, 1000); + mDispatch.schedule(cb1, 100, 1000); + + advanceToNextCallback(); + mDispatch.schedule(cb0, 200, 2000); + mDispatch.schedule(cb1, 150, 1000); + + advanceToNextCallback(); + advanceToNextCallback(); +} + +TEST_F(VSyncDispatchTest, doesntCancelPriorValidTimerForFutureMod) { + Sequence seq; + EXPECT_CALL(mMockClock, alarmIn(_, 500)).InSequence(seq); + + CountingCallback cb0(mDispatch); + CountingCallback cb1(mDispatch); + mDispatch.schedule(cb0, 500, 1000); + mDispatch.schedule(cb1, 500, 20000); +} + +TEST_F(VSyncDispatchTest, setsTimerAfterCancellation) { + Sequence seq; + EXPECT_CALL(mMockClock, alarmIn(_, 500)).InSequence(seq); + EXPECT_CALL(mMockClock, alarmCancel()).InSequence(seq); + EXPECT_CALL(mMockClock, alarmIn(_, 900)).InSequence(seq); + + CountingCallback cb0(mDispatch); + mDispatch.schedule(cb0, 500, 1000); + mDispatch.cancel(cb0); + mDispatch.schedule(cb0, 100, 1000); +} + +TEST_F(VSyncDispatchTest, makingUpIdsError) { + VSyncDispatch::CallbackToken token(100); + EXPECT_THAT(mDispatch.schedule(token, 100, 1000), Eq(ScheduleResult::Error)); + EXPECT_THAT(mDispatch.cancel(token), Eq(CancelResult::Error)); +} + +TEST_F(VSyncDispatchTest, distinguishesScheduleAndReschedule) { + CountingCallback cb0(mDispatch); + EXPECT_EQ(mDispatch.schedule(cb0, 500, 1000), ScheduleResult::Scheduled); + EXPECT_EQ(mDispatch.schedule(cb0, 100, 1000), ScheduleResult::ReScheduled); +} + +TEST_F(VSyncDispatchTest, helperMove) { + EXPECT_CALL(mMockClock, alarmIn(_, 500)).Times(1); + EXPECT_CALL(mMockClock, alarmCancel()).Times(1); + + VSyncCallbackRegistration cb( + mDispatch, [](auto) {}, ""); + VSyncCallbackRegistration cb1(std::move(cb)); + cb.schedule(100, 1000); + cb.cancel(); + + cb1.schedule(500, 1000); + cb1.cancel(); +} + +TEST_F(VSyncDispatchTest, helperMoveAssign) { + EXPECT_CALL(mMockClock, alarmIn(_, 500)).Times(1); + EXPECT_CALL(mMockClock, alarmCancel()).Times(1); + + VSyncCallbackRegistration cb( + mDispatch, [](auto) {}, ""); + VSyncCallbackRegistration cb1( + mDispatch, [](auto) {}, ""); + cb1 = std::move(cb); + cb.schedule(100, 1000); + cb.cancel(); + + cb1.schedule(500, 1000); + cb1.cancel(); +} + +class VSyncDispatchEntryTest : public testing::Test { +protected: + nsecs_t const mPeriod = 1000; + NiceMock<MockVSyncTracker> mStubTracker{mPeriod}; +}; + +TEST_F(VSyncDispatchEntryTest, stateAfterInitialization) { + std::string name("basicname"); + impl::VSyncDispatchEntry entry(name, [](auto) {}); + EXPECT_THAT(entry.name(), Eq(name)); + EXPECT_FALSE(entry.lastExecutedVsyncTarget()); + EXPECT_FALSE(entry.wakeupTime()); +} + +TEST_F(VSyncDispatchEntryTest, stateScheduling) { + impl::VSyncDispatchEntry entry("test", [](auto) {}); + + EXPECT_FALSE(entry.wakeupTime()); + auto const wakeup = entry.schedule(100, 500, mStubTracker, 0); + auto const queried = entry.wakeupTime(); + ASSERT_TRUE(queried); + EXPECT_THAT(*queried, Eq(wakeup)); + EXPECT_THAT(*queried, Eq(900)); + + entry.disarm(); + EXPECT_FALSE(entry.wakeupTime()); +} + +TEST_F(VSyncDispatchEntryTest, stateSchedulingReallyLongWakeupLatency) { + auto const duration = 500; + auto const now = 8750; + + EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(now + duration)) + .Times(1) + .WillOnce(Return(10000)); + impl::VSyncDispatchEntry entry("test", [](auto) {}); + + EXPECT_FALSE(entry.wakeupTime()); + auto const wakeup = entry.schedule(500, 994, mStubTracker, now); + auto const queried = entry.wakeupTime(); + ASSERT_TRUE(queried); + EXPECT_THAT(*queried, Eq(wakeup)); + EXPECT_THAT(*queried, Eq(9500)); +} + +TEST_F(VSyncDispatchEntryTest, runCallback) { + auto callCount = 0; + auto calledTime = 0; + impl::VSyncDispatchEntry entry("test", [&](auto time) { + callCount++; + calledTime = time; + }); + + auto const wakeup = entry.schedule(100, 500, mStubTracker, 0); + EXPECT_THAT(wakeup, Eq(900)); + + entry.callback(entry.executing()); + + EXPECT_THAT(callCount, Eq(1)); + EXPECT_THAT(calledTime, Eq(mPeriod)); + EXPECT_FALSE(entry.wakeupTime()); + auto lastCalledTarget = entry.lastExecutedVsyncTarget(); + ASSERT_TRUE(lastCalledTarget); + EXPECT_THAT(*lastCalledTarget, Eq(mPeriod)); +} + +TEST_F(VSyncDispatchEntryTest, updateCallback) { + EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(_)) + .Times(2) + .WillOnce(Return(1000)) + .WillOnce(Return(1020)); + + impl::VSyncDispatchEntry entry("test", [](auto) {}); + + EXPECT_FALSE(entry.wakeupTime()); + entry.update(mStubTracker, 0); + EXPECT_FALSE(entry.wakeupTime()); + + auto const wakeup = entry.schedule(100, 500, mStubTracker, 0); + EXPECT_THAT(wakeup, Eq(900)); + + entry.update(mStubTracker, 0); + auto const queried = entry.wakeupTime(); + ASSERT_TRUE(queried); + EXPECT_THAT(*queried, Eq(920)); +} + +TEST_F(VSyncDispatchEntryTest, skipsUpdateIfJustScheduled) { + impl::VSyncDispatchEntry entry("test", [](auto) {}); + auto const wakeup = entry.schedule(100, 500, mStubTracker, 0); + entry.update(mStubTracker, 0); + + auto const queried = entry.wakeupTime(); + ASSERT_TRUE(queried); + EXPECT_THAT(*queried, Eq(wakeup)); +} + +} // namespace android::scheduler diff --git a/vulkan/libvulkan/Android.bp b/vulkan/libvulkan/Android.bp index 5686891eab..018f2004cd 100644 --- a/vulkan/libvulkan/Android.bp +++ b/vulkan/libvulkan/Android.bp @@ -41,12 +41,13 @@ cc_library_shared { "-DVK_NO_PROTOTYPES", "-fvisibility=hidden", "-fstrict-aliasing", - "-Weverything", + "-Wextra", "-Werror", "-Wno-padded", + "-Wno-sign-compare", "-Wno-switch-enum", - "-Wno-undef", - "-Wno-format-pedantic", + "-Wno-unused-variable", + "-Wno-unused-function", // Have clang emit complete debug_info. "-fstandalone-debug", diff --git a/vulkan/libvulkan/api.cpp b/vulkan/libvulkan/api.cpp index 24039b1fee..47253440d4 100644 --- a/vulkan/libvulkan/api.cpp +++ b/vulkan/libvulkan/api.cpp @@ -144,7 +144,7 @@ class OverrideLayerNames { } void GetLayersFromSettings() { - // These will only be available if conditions are met in GraphicsEnvironemnt + // These will only be available if conditions are met in GraphicsEnvironment // gpu_debug_layers = layer1:layer2:layerN const std::string layers = android::GraphicsEnv::getInstance().getDebugLayers(); if (!layers.empty()) { diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp index d92f35ac23..0fb3a29ac8 100644 --- a/vulkan/libvulkan/driver.cpp +++ b/vulkan/libvulkan/driver.cpp @@ -754,7 +754,7 @@ void FreeDeviceData(DeviceData* data, const VkAllocationCallbacks& allocator) { } // anonymous namespace bool Debuggable() { - return (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) >= 0); + return prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) > 0; } bool OpenHAL() { diff --git a/vulkan/nulldrv/Android.bp b/vulkan/nulldrv/Android.bp index dedf419dc3..ba025046fa 100644 --- a/vulkan/nulldrv/Android.bp +++ b/vulkan/nulldrv/Android.bp @@ -23,18 +23,11 @@ cc_library_shared { "-fvisibility=hidden", "-fstrict-aliasing", "-DLOG_TAG=\"vknulldrv\"", - "-Weverything", + "-Wextra", "-Werror", - "-Wno-padded", - "-Wno-undef", - "-Wno-zero-length-array", "-DLOG_NDEBUG=0", ], - cppflags: [ - "-Wno-c++98-compat-pedantic", - "-Wno-c99-extensions", - ], srcs: [ "null_driver.cpp", |